mirror of
https://github.com/LibreTranslate/LibreTranslate.git
synced 2024-11-25 09:21:04 +00:00
commit
b6111b95f8
7 changed files with 123 additions and 10 deletions
51
README.md
51
README.md
|
@ -193,10 +193,12 @@ docker-compose -f docker-compose.cuda.yml up -d --build
|
||||||
| --require-api-key-origin | Require use of an API key for programmatic access to the API, unless the request origin matches this domain | `No restrictions on domain origin` | LT_REQUIRE_API_KEY_ORIGIN |
|
| --require-api-key-origin | Require use of an API key for programmatic access to the API, unless the request origin matches this domain | `No restrictions on domain origin` | LT_REQUIRE_API_KEY_ORIGIN |
|
||||||
| --load-only | Set available languages | `all from argostranslate` | LT_LOAD_ONLY |
|
| --load-only | Set available languages | `all from argostranslate` | LT_LOAD_ONLY |
|
||||||
| --threads | Set number of threads | `4` | LT_THREADS |
|
| --threads | Set number of threads | `4` | LT_THREADS |
|
||||||
| --suggestions | Allow user suggestions | `false` | LT_SUGGESTIONS |
|
| --suggestions | Allow user suggestions | `False` | LT_SUGGESTIONS |
|
||||||
| --disable-files-translation | Disable files translation | `false` | LT_DISABLE_FILES_TRANSLATION |
|
| --disable-files-translation | Disable files translation | `False` | LT_DISABLE_FILES_TRANSLATION |
|
||||||
| --disable-web-ui | Disable web ui | `false` | LT_DISABLE_WEB_UI |
|
| --disable-web-ui | Disable web ui | `False` | LT_DISABLE_WEB_UI |
|
||||||
| --update-models | Update language models at startup | `false` | LT_UPDATE_MODELS |
|
| --update-models | Update language models at startup | `False` | LT_UPDATE_MODELS |
|
||||||
|
| --metrics | Enable the /metrics endpoint for exporting [Prometheus](https://prometheus.io/) usage metrics | `Disabled` | LT_METRICS |
|
||||||
|
| --metrics-auth-token | Protect the /metrics endpoint by allowing only clients that have a valid Authorization Bearer token | `No auth` | LT_METRICS_AUTH_TOKEN |
|
||||||
|
|
||||||
Note that each argument has an equivalent environment variable that can be used instead. The env. variables overwrite the default values but have lower priority than the command arguments and are particularly useful if used with Docker. The environment variable names are the upper-snake-case of the equivalent command argument's name with a `LT` prefix.
|
Note that each argument has an equivalent environment variable that can be used instead. The env. variables overwrite the default values but have lower priority than the command arguments and are particularly useful if used with Docker. The environment variable names are the upper-snake-case of the equivalent command argument's name with a `LT` prefix.
|
||||||
|
|
||||||
|
@ -267,6 +269,47 @@ ltmanage keys remove <api-key>
|
||||||
ltmanage keys
|
ltmanage keys
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Prometheus Metrics
|
||||||
|
|
||||||
|
LibreTranslate has Prometheus [exporter](https://prometheus.io/docs/instrumenting/exporters/) capabilities when you pass the `--metrics` argument at startup (disabled by default). When metrics are enabled, a `/metrics` endpoint is mounted on the instance:
|
||||||
|
|
||||||
|
http://localhost:5000/metrics
|
||||||
|
|
||||||
|
```
|
||||||
|
# HELP request_inprogress Multiprocess metric
|
||||||
|
# TYPE request_inprogress gauge
|
||||||
|
request_inprogress{api_key="",endpoint="/translate",request_ip="127.0.0.1"} 0.0
|
||||||
|
# HELP request_seconds Multiprocess metric
|
||||||
|
# TYPE request_seconds summary
|
||||||
|
request_seconds_count{api_key="",endpoint="/translate",request_ip="127.0.0.1",status="200"} 0.0
|
||||||
|
request_seconds_sum{api_key="",endpoint="/translate",request_ip="127.0.0.1",status="200"} 0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
You can then configure `prometheus.yml` to read the metrics:
|
||||||
|
|
||||||
|
```
|
||||||
|
scrape_configs:
|
||||||
|
- job_name: "libretranslate"
|
||||||
|
|
||||||
|
# Needed only if you use --metrics-auth-token
|
||||||
|
#authorization:
|
||||||
|
#credentials: "mytoken"
|
||||||
|
|
||||||
|
static_configs:
|
||||||
|
- targets: ["localhost:5000"]
|
||||||
|
```
|
||||||
|
|
||||||
|
To secure the `/metrics` endpoint you can also use `--metrics-auth-token mytoken`.
|
||||||
|
|
||||||
|
If you use Gunicorn, make sure to create a directory for storing multiprocess data metrics and set `PROMETHEUS_MULTIPROC_DIR`:
|
||||||
|
|
||||||
|
```
|
||||||
|
mkdir -p /tmp/prometheus_data
|
||||||
|
rm /tmp/prometheus_data/*
|
||||||
|
export PROMETHEUS_MULTIPROC_DIR=/tmp/prometheus_data
|
||||||
|
gunicorn -c gunicorn_conf.py --bind 0.0.0.0:5000 'wsgi:app(metrics=True)'
|
||||||
|
```
|
||||||
|
|
||||||
## Language Bindings
|
## Language Bindings
|
||||||
|
|
||||||
You can use the LibreTranslate API using the following bindings:
|
You can use the LibreTranslate API using the following bindings:
|
||||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
1.3.4
|
1.3.5
|
||||||
|
|
49
app/app.py
49
app/app.py
|
@ -4,15 +4,17 @@ import tempfile
|
||||||
import uuid
|
import uuid
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from html import unescape
|
from html import unescape
|
||||||
|
from timeit import default_timer
|
||||||
|
|
||||||
import argostranslatefiles
|
import argostranslatefiles
|
||||||
from argostranslatefiles import get_supported_formats
|
from argostranslatefiles import get_supported_formats
|
||||||
from flask import (Flask, abort, jsonify, render_template, request, send_file,
|
from flask import (Flask, abort, jsonify, render_template, request, send_file,
|
||||||
url_for)
|
url_for, Response)
|
||||||
from flask_swagger import swagger
|
from flask_swagger import swagger
|
||||||
from flask_swagger_ui import get_swaggerui_blueprint
|
from flask_swagger_ui import get_swaggerui_blueprint
|
||||||
from translatehtml import translate_html
|
from translatehtml import translate_html
|
||||||
from werkzeug.utils import secure_filename
|
from werkzeug.utils import secure_filename
|
||||||
|
from werkzeug.exceptions import HTTPException
|
||||||
|
|
||||||
from app import flood, remove_translated_files, security
|
from app import flood, remove_translated_files, security
|
||||||
from app.language import detect_languages, improve_translation_formatting
|
from app.language import detect_languages, improve_translation_formatting
|
||||||
|
@ -174,6 +176,28 @@ def create_app(args):
|
||||||
if args.req_flood_threshold > 0:
|
if args.req_flood_threshold > 0:
|
||||||
flood.setup(args.req_flood_threshold)
|
flood.setup(args.req_flood_threshold)
|
||||||
|
|
||||||
|
measure_request = None
|
||||||
|
gauge_request = None
|
||||||
|
if args.metrics:
|
||||||
|
from prometheus_client import CONTENT_TYPE_LATEST, Summary, Gauge, CollectorRegistry, multiprocess, generate_latest
|
||||||
|
|
||||||
|
@app.route("/metrics")
|
||||||
|
def prometheus_metrics():
|
||||||
|
if args.metrics_auth_token:
|
||||||
|
authorization = request.headers.get('Authorization')
|
||||||
|
if authorization != "Bearer " + args.metrics_auth_token:
|
||||||
|
abort(401, description="Unauthorized")
|
||||||
|
|
||||||
|
registry = CollectorRegistry()
|
||||||
|
multiprocess.MultiProcessCollector(registry)
|
||||||
|
return Response(generate_latest(registry), mimetype=CONTENT_TYPE_LATEST)
|
||||||
|
|
||||||
|
measure_request = Summary('request_seconds', 'Time spent on request', ['endpoint', 'status', 'request_ip', 'api_key'])
|
||||||
|
measure_request.labels('/translate', 200, '127.0.0.1', '')
|
||||||
|
|
||||||
|
gauge_request = Gauge('request_inprogress', 'Active requests', ['endpoint', 'request_ip', 'api_key'], multiprocess_mode='livesum')
|
||||||
|
gauge_request.labels('/translate', '127.0.0.1', '')
|
||||||
|
|
||||||
def access_check(f):
|
def access_check(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def func(*a, **kw):
|
def func(*a, **kw):
|
||||||
|
@ -203,10 +227,29 @@ def create_app(args):
|
||||||
403,
|
403,
|
||||||
description=description,
|
description=description,
|
||||||
)
|
)
|
||||||
|
|
||||||
return f(*a, **kw)
|
return f(*a, **kw)
|
||||||
|
|
||||||
return func
|
if args.metrics:
|
||||||
|
@wraps(func)
|
||||||
|
def measure_func(*a, **kw):
|
||||||
|
start_t = default_timer()
|
||||||
|
status = 200
|
||||||
|
ip = get_remote_address()
|
||||||
|
ak = get_req_api_key() or ''
|
||||||
|
g = gauge_request.labels(request.path, ip, ak)
|
||||||
|
try:
|
||||||
|
g.inc()
|
||||||
|
return func(*a, **kw)
|
||||||
|
except HTTPException as e:
|
||||||
|
status = e.code
|
||||||
|
raise e
|
||||||
|
finally:
|
||||||
|
duration = max(default_timer() - start_t, 0)
|
||||||
|
measure_request.labels(request.path, status, ip, ak).observe(duration)
|
||||||
|
g.dec()
|
||||||
|
return measure_func
|
||||||
|
else:
|
||||||
|
return func
|
||||||
|
|
||||||
@app.errorhandler(400)
|
@app.errorhandler(400)
|
||||||
def invalid_api(e):
|
def invalid_api(e):
|
||||||
|
|
|
@ -161,6 +161,16 @@ _default_options_objects = [
|
||||||
'default_value': False,
|
'default_value': False,
|
||||||
'value_type': 'bool'
|
'value_type': 'bool'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
'name': 'METRICS',
|
||||||
|
'default_value': False,
|
||||||
|
'value_type': 'bool'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'METRICS_AUTH_TOKEN',
|
||||||
|
'default_value': '',
|
||||||
|
'value_type': 'str'
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
12
app/main.py
12
app/main.py
|
@ -147,6 +147,18 @@ def get_args():
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--update-models", default=DEFARGS['UPDATE_MODELS'], action="store_true", help="Update language models at startup"
|
"--update-models", default=DEFARGS['UPDATE_MODELS'], action="store_true", help="Update language models at startup"
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--metrics",
|
||||||
|
default=DEFARGS['METRICS'],
|
||||||
|
action="store_true",
|
||||||
|
help="Enable the /metrics endpoint for exporting Prometheus usage metrics",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--metrics-auth-token",
|
||||||
|
default=DEFARGS['METRICS_AUTH_TOKEN'],
|
||||||
|
type=str,
|
||||||
|
help="Protect the /metrics endpoint by allowing only clients that have a valid Authorization Bearer token (%(default)s)",
|
||||||
|
)
|
||||||
return parser.parse_args()
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
|
4
gunicorn_conf.py
Normal file
4
gunicorn_conf.py
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
from prometheus_client import multiprocess
|
||||||
|
|
||||||
|
def child_exit(server, worker):
|
||||||
|
multiprocess.mark_process_dead(worker.pid)
|
|
@ -15,3 +15,4 @@ itsdangerous==2.1.2
|
||||||
Werkzeug==2.2.2
|
Werkzeug==2.2.2
|
||||||
requests==2.28.1
|
requests==2.28.1
|
||||||
redis==4.3.4
|
redis==4.3.4
|
||||||
|
prometheus-client==0.15.0
|
Loading…
Reference in a new issue