mirror of
https://github.com/FlareSolverr/FlareSolverr.git
synced 2025-06-07 12:05:37 +00:00
Implement Prometheus metrics
This commit is contained in:
parent
313fb2c14b
commit
d2e144ea12
@ -56,6 +56,7 @@ COPY src .
|
||||
COPY package.json ../
|
||||
|
||||
EXPOSE 8191
|
||||
EXPOSE 8192
|
||||
|
||||
# dumb-init avoids zombie chromium processes
|
||||
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
|
||||
|
30
README.md
30
README.md
@ -227,7 +227,7 @@ This is the same as `request.get` but it takes one more param:
|
||||
## Environment variables
|
||||
|
||||
| Name | Default | Notes |
|
||||
|-----------------|------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
|--------------------|------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| LOG_LEVEL | info | Verbosity of the logging. Use `LOG_LEVEL=debug` for more information. |
|
||||
| LOG_HTML | false | Only for debugging. If `true` all HTML that passes through the proxy will be logged to the console in `debug` level. |
|
||||
| CAPTCHA_SOLVER | none | Captcha solving method. It is used when a captcha is encountered. See the Captcha Solvers section. |
|
||||
@ -237,12 +237,40 @@ This is the same as `request.get` but it takes one more param:
|
||||
| TEST_URL | https://www.google.com | FlareSolverr makes a request on start to make sure the web browser is working. You can change that URL if it is blocked in your country. |
|
||||
| PORT | 8191 | Listening port. You don't need to change this if you are running on Docker. |
|
||||
| HOST | 0.0.0.0 | Listening interface. You don't need to change this if you are running on Docker. |
|
||||
| PROMETHEUS_ENABLED | false | Enable Prometheus exporter. See the Prometheus section below. |
|
||||
| PROMETHEUS_PORT | 8192 | Listening port for Prometheus exporter. See the Prometheus section below. |
|
||||
|
||||
Environment variables are set differently depending on the operating system. Some examples:
|
||||
* Docker: Take a look at the Docker section in this document. Environment variables can be set in the `docker-compose.yml` file or in the Docker CLI command.
|
||||
* Linux: Run `export LOG_LEVEL=debug` and then start FlareSolverr in the same shell.
|
||||
* Windows: Open `cmd.exe`, run `set LOG_LEVEL=debug` and then start FlareSolverr in the same shell.
|
||||
|
||||
## Prometheus exporter
|
||||
|
||||
The Prometheus exporter for FlareSolverr is disabled by default. It can be enabled with the environment variable `PROMETHEUS_ENABLED`. If you are using Docker make sure you expose the `PROMETHEUS_PORT`.
|
||||
|
||||
Example metrics:
|
||||
```shell
|
||||
# HELP flaresolverr_request_total Total requests with result
|
||||
# TYPE flaresolverr_request_total counter
|
||||
flaresolverr_request_total{domain="nowsecure.nl",result="solved"} 1.0
|
||||
# HELP flaresolverr_request_created Total requests with result
|
||||
# TYPE flaresolverr_request_created gauge
|
||||
flaresolverr_request_created{domain="nowsecure.nl",result="solved"} 1.690141657157109e+09
|
||||
# HELP flaresolverr_request_duration Request duration in seconds
|
||||
# TYPE flaresolverr_request_duration histogram
|
||||
flaresolverr_request_duration_bucket{domain="nowsecure.nl",le="0.0"} 0.0
|
||||
flaresolverr_request_duration_bucket{domain="nowsecure.nl",le="10.0"} 1.0
|
||||
flaresolverr_request_duration_bucket{domain="nowsecure.nl",le="25.0"} 1.0
|
||||
flaresolverr_request_duration_bucket{domain="nowsecure.nl",le="50.0"} 1.0
|
||||
flaresolverr_request_duration_bucket{domain="nowsecure.nl",le="+Inf"} 1.0
|
||||
flaresolverr_request_duration_count{domain="nowsecure.nl"} 1.0
|
||||
flaresolverr_request_duration_sum{domain="nowsecure.nl"} 5.858
|
||||
# HELP flaresolverr_request_duration_created Request duration in seconds
|
||||
# TYPE flaresolverr_request_duration_created gauge
|
||||
flaresolverr_request_duration_created{domain="nowsecure.nl"} 1.6901416571570296e+09
|
||||
```
|
||||
|
||||
## Captcha Solvers
|
||||
|
||||
> **Warning**
|
||||
|
@ -2,9 +2,10 @@ bottle==0.12.25
|
||||
waitress==2.1.2
|
||||
selenium==4.10.0
|
||||
func-timeout==4.3.5
|
||||
prometheus-client==0.17.1
|
||||
# required by undetected_chromedriver
|
||||
requests==2.31.0
|
||||
certifi==2023.5.7
|
||||
certifi==2023.7.22
|
||||
websockets==11.0.3
|
||||
# only required for linux
|
||||
xvfbwrapper==0.2.9
|
||||
|
53
src/bottle_plugins/prometheus_plugin.py
Normal file
53
src/bottle_plugins/prometheus_plugin.py
Normal file
@ -0,0 +1,53 @@
|
||||
import logging
|
||||
import os
|
||||
import urllib.parse
|
||||
|
||||
from dtos import V1ResponseBase
|
||||
from metrics import start_metrics_http_server, REQUEST_COUNTER, REQUEST_DURATION
|
||||
|
||||
PROMETHEUS_ENABLED = os.environ.get('PROMETHEUS_ENABLED', 'false').lower() == 'true'
|
||||
PROMETHEUS_PORT = int(os.environ.get('PROMETHEUS_PORT', 8192))
|
||||
|
||||
|
||||
def setup():
|
||||
if PROMETHEUS_ENABLED:
|
||||
start_metrics_http_server(PROMETHEUS_PORT)
|
||||
|
||||
|
||||
def prometheus_plugin(callback):
|
||||
"""
|
||||
Bottle plugin to expose Prometheus metrics
|
||||
http://bottlepy.org/docs/dev/plugindev.html
|
||||
"""
|
||||
def wrapper(*args, **kwargs):
|
||||
actual_response = callback(*args, **kwargs)
|
||||
|
||||
if PROMETHEUS_ENABLED:
|
||||
try:
|
||||
export_metrics(actual_response)
|
||||
except Exception as e:
|
||||
logging.warning("Error exporting metrics: " + str(e))
|
||||
|
||||
return actual_response
|
||||
|
||||
def export_metrics(actual_response):
|
||||
res = V1ResponseBase(actual_response)
|
||||
|
||||
domain = "unknown"
|
||||
if res.solution and res.solution.url:
|
||||
parsed_url = urllib.parse.urlparse(res.solution.url)
|
||||
domain = parsed_url.hostname
|
||||
|
||||
run_time = (res.endTimestamp - res.startTimestamp) / 1000
|
||||
REQUEST_DURATION.labels(domain=domain).observe(run_time)
|
||||
|
||||
result = "unknown"
|
||||
if res.message == "Challenge solved!":
|
||||
result = "solved"
|
||||
elif res.message == "Challenge not detected!":
|
||||
result = "not_detected"
|
||||
elif res.message.startswith("Error"):
|
||||
result = "error"
|
||||
REQUEST_COUNTER.labels(domain=domain, result=result).inc()
|
||||
|
||||
return wrapper
|
@ -8,6 +8,7 @@ from bottle import run, response, Bottle, request, ServerAdapter
|
||||
|
||||
from bottle_plugins.error_plugin import error_plugin
|
||||
from bottle_plugins.logger_plugin import logger_plugin
|
||||
from bottle_plugins import prometheus_plugin
|
||||
from dtos import V1RequestBase
|
||||
import flaresolverr_service
|
||||
import utils
|
||||
@ -24,10 +25,6 @@ class JSONErrorBottle(Bottle):
|
||||
|
||||
app = JSONErrorBottle()
|
||||
|
||||
# plugin order is important
|
||||
app.install(logger_plugin)
|
||||
app.install(error_plugin)
|
||||
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
@ -101,6 +98,13 @@ if __name__ == "__main__":
|
||||
# test browser installation
|
||||
flaresolverr_service.test_browser_installation()
|
||||
|
||||
# start bootle plugins
|
||||
# plugin order is important
|
||||
app.install(logger_plugin)
|
||||
app.install(error_plugin)
|
||||
prometheus_plugin.setup()
|
||||
app.install(prometheus_plugin.prometheus_plugin)
|
||||
|
||||
# start webserver
|
||||
# default server 'wsgiref' does not support concurrent requests
|
||||
# https://github.com/FlareSolverr/FlareSolverr/issues/680
|
||||
|
32
src/metrics.py
Normal file
32
src/metrics.py
Normal file
@ -0,0 +1,32 @@
|
||||
import logging
|
||||
|
||||
from prometheus_client import Counter, Histogram, start_http_server
|
||||
import time
|
||||
|
||||
REQUEST_COUNTER = Counter(
|
||||
name='flaresolverr_request',
|
||||
documentation='Total requests with result',
|
||||
labelnames=['domain', 'result']
|
||||
)
|
||||
REQUEST_DURATION = Histogram(
|
||||
name='flaresolverr_request_duration',
|
||||
documentation='Request duration in seconds',
|
||||
labelnames=['domain'],
|
||||
buckets=[0, 10, 25, 50]
|
||||
)
|
||||
|
||||
|
||||
def serve(port):
|
||||
start_http_server(port=port)
|
||||
while True:
|
||||
time.sleep(600)
|
||||
|
||||
|
||||
def start_metrics_http_server(prometheus_port: int):
|
||||
logging.info(f"Serving Prometheus exporter on http://0.0.0.0:{prometheus_port}/metrics")
|
||||
from threading import Thread
|
||||
Thread(
|
||||
target=serve,
|
||||
kwargs=dict(port=prometheus_port),
|
||||
daemon=True,
|
||||
).start()
|
Loading…
x
Reference in New Issue
Block a user