diff options
Diffstat (limited to 'python-prometheus-fastapi-instrumentator.spec')
| -rw-r--r-- | python-prometheus-fastapi-instrumentator.spec | 1034 |
1 files changed, 1034 insertions, 0 deletions
diff --git a/python-prometheus-fastapi-instrumentator.spec b/python-prometheus-fastapi-instrumentator.spec new file mode 100644 index 0000000..4c8de3d --- /dev/null +++ b/python-prometheus-fastapi-instrumentator.spec @@ -0,0 +1,1034 @@ +%global _empty_manifest_terminate_build 0 +Name: python-prometheus-fastapi-instrumentator +Version: 6.0.0 +Release: 1 +Summary: Instrument your FastAPI with Prometheus metrics. +License: ISC +URL: https://github.com/trallnag/prometheus-fastapi-instrumentator +Source0: https://mirrors.nju.edu.cn/pypi/web/packages/28/26/2cf61a59fd24ee2c0ac964fae54b08d40de8083c316745a924aaad1576fd/prometheus_fastapi_instrumentator-6.0.0.tar.gz +BuildArch: noarch + +Requires: python3-fastapi +Requires: python3-prometheus-client + +%description +# Prometheus FastAPI Instrumentator <!-- omit in toc --> + +[](https://pypi.python.org/pypi/prometheus-fastapi-instrumentator) +[](https://pypi.python.org/pypi/prometheus-fastapi-instrumentator) +[](https://pepy.tech/project/prometheus-fastapi-instrumentator/month) +[](https://github.com/trallnag/kubestatus2cloudwatch/actions) +[](https://codecov.io/gh/trallnag/prometheus-fastapi-instrumentator) + +A configurable and modular Prometheus Instrumentator for your FastAPI. Install +`prometheus-fastapi-instrumentator` from +[PyPI](https://pypi.python.org/pypi/prometheus-fastapi-instrumentator/). Here is +the fast track to get started with a pre-configured instrumentator. Import the +instrumentator class: + +```python +from prometheus_fastapi_instrumentator import Instrumentator +``` + +Instrument your app with default metrics and expose the metrics: + +```python +Instrumentator().instrument(app).expose(app) +``` + +Depending on your code you might have to use the following instead: + +```python +instrumentator = Instrumentator().instrument(app) + +@app.on_event("startup") +async def _startup(): + instrumentator.expose(app) +``` + +With this, your FastAPI is instrumented and metrics are ready to be scraped. The +defaults give you: + +- Counter `http_requests_total` with `handler`, `status` and `method`. Total + number of requests. +- Summary `http_request_size_bytes` with `handler`. Added up total of the + content lengths of all incoming requests. +- Summary `http_response_size_bytes` with `handler`. Added up total of the + content lengths of all outgoing responses. +- Histogram `http_request_duration_seconds` with `handler`. Only a few buckets + to keep cardinality low. +- Histogram `http_request_duration_highr_seconds` without any labels. Large + number of buckets (>20). + +In addition, following behavior is active: + +- Status codes are grouped into `2xx`, `3xx` and so on. +- Requests without a matching template are grouped into the handler `none`. + +If one of these presets does not suit your needs you can do one of multiple +things: + +- Pick one of the already existing closures from + [`metrics`](./src/prometheus_fastapi_instrumentator/metrics.py) and pass it to + the instrumentator instance. See [here](#adding-metrics) how to do that. +- Create your own instrumentation function that you can pass to an + instrumentator instance. See [here](#creating-new-metrics) to learn how more. +- Don't use this package at all and just use the source code as inspiration on + how to instrument your FastAPI. + +Important: This package is not made for generic Prometheus instrumentation in +Python. Use the Prometheus client library for that. This packages uses it as +well. + +## Table of Contents <!-- omit in toc --> + +<!--TOC--> + +- [Features](#features) +- [Advanced Usage](#advanced-usage) + - [Creating the Instrumentator](#creating-the-instrumentator) + - [Adding metrics](#adding-metrics) + - [Creating new metrics](#creating-new-metrics) + - [Perform instrumentation](#perform-instrumentation) + - [Specify namespace and subsystem](#specify-namespace-and-subsystem) + - [Exposing endpoint](#exposing-endpoint) +- [Contributing](#contributing) +- [Licensing](#licensing) + +<!--TOC--> + +## Features + +Beyond the fast track, this instrumentator is **highly configurable** and it is +very easy to customize and adapt to your specific use case. Here is a list of +some of these options you may opt-in to: + +- Regex patterns to ignore certain routes. +- Completely ignore untemplated routes. +- Control instrumentation and exposition with an env var. +- Rounding of latencies to a certain decimal number. +- Renaming of labels and the metric. +- Metrics endpoint can compress data with gzip. +- Opt-in metric to monitor the number of requests in progress. + +It also features a **modular approach to metrics** that should instrument all +FastAPI endpoints. You can either choose from a set of already existing metrics +or create your own. And every metric function by itself can be configured as +well. You can see ready to use metrics +[here](https://trallnag.github.io/prometheus-fastapi-instrumentator/metrics.html). + +## Advanced Usage + +This chapter contains an example on the advanced usage of the Prometheus FastAPI +Instrumentator to showcase most of it's features. Fore more concrete info check +out the +[automatically generated documentation](https://trallnag.github.io/prometheus-fastapi-instrumentator/). + +### Creating the Instrumentator + +We start by creating an instance of the Instrumentator. Notice the additional +`metrics` import. This will come in handy later. + +```python +from prometheus_fastapi_instrumentator import Instrumentator, metrics + +instrumentator = Instrumentator( + should_group_status_codes=False, + should_ignore_untemplated=True, + should_respect_env_var=True, + should_instrument_requests_inprogress=True, + excluded_handlers=[".*admin.*", "/metrics"], + env_var_name="ENABLE_METRICS", + inprogress_name="inprogress", + inprogress_labels=True, +) +``` + +Unlike in the fast track example, now the instrumentation and exposition will +only take place if the environment variable `ENABLE_METRICS` is `true` at +run-time. This can be helpful in larger deployments with multiple services +depending on the same base FastAPI. + +### Adding metrics + +Let's say we also want to instrument the size of requests and responses. For +this we use the `add()` method. This method does nothing more than taking a +function and adding it to a list. Then during run-time every time FastAPI +handles a request all functions in this list will be called while giving them a +single argument that stores useful information like the request and response +objects. If no `add()` at all is used, the default metric gets added in the +background. This is what happens in the fast track example. + +All instrumentation functions are stored as closures in the `metrics` module. +Fore more concrete info check out the +[automatically generated documentation](https://trallnag.github.io/prometheus-fastapi-instrumentator/). + +Closures come in handy here because it allows us to configure the functions +within. + +```python +instrumentator.add(metrics.latency(buckets=(1, 2, 3,))) +``` + +This simply adds the metric you also get in the fast track example with a +modified buckets argument. But we would also like to record the size of all +requests and responses. + +```python +instrumentator.add( + metrics.request_size( + should_include_handler=True, + should_include_method=False, + should_include_status=True, + metric_namespace="a", + metric_subsystem="b", + ) +).add( + metrics.response_size( + should_include_handler=True, + should_include_method=False, + should_include_status=True, + metric_namespace="namespace", + metric_subsystem="subsystem", + ) +) +``` + +You can add as many metrics you like to the instrumentator. + +### Creating new metrics + +As already mentioned, it is possible to create custom functions to pass on to +`add()`. This is also how the default metrics are implemented. The documentation +and code +[here](https://trallnag.github.io/prometheus-fastapi-instrumentator/metrics.html) +is helpful to get an overview. + +The basic idea is that the instrumentator creates an `info` object that contains +everything necessary for instrumentation based on the configuration of the +instrumentator. This includes the raw request and response objects but also the +modified handler, grouped status code and duration. Next, all registered +instrumentation functions are called. They get `info` as their single argument. + +Let's say we want to count the number of times a certain language has been +requested. + +```python +from typing import Callable +from prometheus_fastapi_instrumentator.metrics import Info +from prometheus_client import Counter + +def http_requested_languages_total() -> Callable[[Info], None]: + METRIC = Counter( + "http_requested_languages_total", + "Number of times a certain language has been requested.", + labelnames=("langs",) + ) + + def instrumentation(info: Info) -> None: + langs = set() + lang_str = info.request.headers["Accept-Language"] + for element in lang_str.split(","): + element = element.split(";")[0].strip().lower() + langs.add(element) + for language in langs: + METRIC.labels(language).inc() + + return instrumentation +``` + +The function `http_requested_languages_total` is used for persistent elements +that are stored between all instrumentation executions (for example the metric +instance itself). Next comes the closure. This function must adhere to the shown +interface. It will always get an `Info` object that contains the request, +response and a few other modified informations. For example the (grouped) status +code or the handler. Finally, the closure is returned. + +**Important:** The response object inside `info` can either be the response +object or `None`. In addition, errors thrown in the handler are not caught by +the instrumentator. I recommend to check the documentation and/or the source +code before creating your own metrics. + +To use it, we hand over the closure to the instrumentator object. + +```python +instrumentator.add(http_requested_languages_total()) +``` + +### Perform instrumentation + +Up to this point, the FastAPI has not been touched at all. Everything has been +stored in the `instrumentator` only. To actually register the instrumentation +with FastAPI, the `instrument()` method has to be called. + +```python +instrumentator.instrument(app) +``` + +Notice that this will do nothing if `should_respect_env_var` has been set during +construction of the instrumentator object and the respective env var is not +found. + +### Specify namespace and subsystem + +You can specify the namespace and subsystem of the metrics by passing them in +the instrument method. + +```python +from prometheus_fastapi_instrumentator import Instrumentator + +@app.on_event("startup") +async def startup(): + Instrumentator().instrument(app, metric_namespace='myproject', metric_subsystem='myservice').expose(app) +``` + +Then your metrics will contain the namespace and subsystem in the metric name. + +```sh +# TYPE myproject_myservice_http_request_duration_highr_seconds histogram +myproject_myservice_http_request_duration_highr_seconds_bucket{le="0.01"} 0.0 +``` + +### Exposing endpoint + +To expose an endpoint for the metrics either follow +[Prometheus Python Client](https://github.com/prometheus/client_python) and add +the endpoint manually to the FastAPI or serve it on a separate server. You can +also use the included `expose` method. It will add an endpoint to the given +FastAPI. With `should_gzip` you can instruct the endpoint to compress the data +as long as the client accepts gzip encoding. Prometheus for example does by +default. Beware that network bandwith is often cheaper than CPU cycles. + +```python +instrumentator.expose(app, include_in_schema=False, should_gzip=True) +``` + +Notice that this will to nothing if `should_respect_env_var` has been set during +construction of the instrumentator object and the respective env var is not +found. + +## Contributing + +Please refer to [`CONTRIBUTING.md`](CONTRIBUTING). + +Consult [`DEVELOPMENT.md`](DEVELOPMENT.md) for guidance regarding development. + +Read [`RELEASE.md`](RELEASE.md) for details about the release process. + +## Licensing + +The default license for this project is the +[ISC License](https://choosealicense.com/licenses/isc). A permissive license +functionally equivalent to the BSD 2-Clause and MIT licenses, removing some +language that is no longer necessary. See [`LICENSE`](LICENSE) for the license +text. + +The [BSD 3-Clause License](https://choosealicense.com/licenses/bsd-3-clause) is +used as the license for the +[`routing`](src/prometheus_fastapi_instrumentator/routing.py) module. This is +due to it containing code from +[elastic/apm-agent-python](https://github.com/elastic/apm-agent-python). BSD +3-Clause is a permissive license similar to the BSD 2-Clause License, but with a +3rd clause that prohibits others from using the name of the copyright holder or +its contributors to promote derived products without written consent. The +license text is included in the module itself. + + +%package -n python3-prometheus-fastapi-instrumentator +Summary: Instrument your FastAPI with Prometheus metrics. +Provides: python-prometheus-fastapi-instrumentator +BuildRequires: python3-devel +BuildRequires: python3-setuptools +BuildRequires: python3-pip +%description -n python3-prometheus-fastapi-instrumentator +# Prometheus FastAPI Instrumentator <!-- omit in toc --> + +[](https://pypi.python.org/pypi/prometheus-fastapi-instrumentator) +[](https://pypi.python.org/pypi/prometheus-fastapi-instrumentator) +[](https://pepy.tech/project/prometheus-fastapi-instrumentator/month) +[](https://github.com/trallnag/kubestatus2cloudwatch/actions) +[](https://codecov.io/gh/trallnag/prometheus-fastapi-instrumentator) + +A configurable and modular Prometheus Instrumentator for your FastAPI. Install +`prometheus-fastapi-instrumentator` from +[PyPI](https://pypi.python.org/pypi/prometheus-fastapi-instrumentator/). Here is +the fast track to get started with a pre-configured instrumentator. Import the +instrumentator class: + +```python +from prometheus_fastapi_instrumentator import Instrumentator +``` + +Instrument your app with default metrics and expose the metrics: + +```python +Instrumentator().instrument(app).expose(app) +``` + +Depending on your code you might have to use the following instead: + +```python +instrumentator = Instrumentator().instrument(app) + +@app.on_event("startup") +async def _startup(): + instrumentator.expose(app) +``` + +With this, your FastAPI is instrumented and metrics are ready to be scraped. The +defaults give you: + +- Counter `http_requests_total` with `handler`, `status` and `method`. Total + number of requests. +- Summary `http_request_size_bytes` with `handler`. Added up total of the + content lengths of all incoming requests. +- Summary `http_response_size_bytes` with `handler`. Added up total of the + content lengths of all outgoing responses. +- Histogram `http_request_duration_seconds` with `handler`. Only a few buckets + to keep cardinality low. +- Histogram `http_request_duration_highr_seconds` without any labels. Large + number of buckets (>20). + +In addition, following behavior is active: + +- Status codes are grouped into `2xx`, `3xx` and so on. +- Requests without a matching template are grouped into the handler `none`. + +If one of these presets does not suit your needs you can do one of multiple +things: + +- Pick one of the already existing closures from + [`metrics`](./src/prometheus_fastapi_instrumentator/metrics.py) and pass it to + the instrumentator instance. See [here](#adding-metrics) how to do that. +- Create your own instrumentation function that you can pass to an + instrumentator instance. See [here](#creating-new-metrics) to learn how more. +- Don't use this package at all and just use the source code as inspiration on + how to instrument your FastAPI. + +Important: This package is not made for generic Prometheus instrumentation in +Python. Use the Prometheus client library for that. This packages uses it as +well. + +## Table of Contents <!-- omit in toc --> + +<!--TOC--> + +- [Features](#features) +- [Advanced Usage](#advanced-usage) + - [Creating the Instrumentator](#creating-the-instrumentator) + - [Adding metrics](#adding-metrics) + - [Creating new metrics](#creating-new-metrics) + - [Perform instrumentation](#perform-instrumentation) + - [Specify namespace and subsystem](#specify-namespace-and-subsystem) + - [Exposing endpoint](#exposing-endpoint) +- [Contributing](#contributing) +- [Licensing](#licensing) + +<!--TOC--> + +## Features + +Beyond the fast track, this instrumentator is **highly configurable** and it is +very easy to customize and adapt to your specific use case. Here is a list of +some of these options you may opt-in to: + +- Regex patterns to ignore certain routes. +- Completely ignore untemplated routes. +- Control instrumentation and exposition with an env var. +- Rounding of latencies to a certain decimal number. +- Renaming of labels and the metric. +- Metrics endpoint can compress data with gzip. +- Opt-in metric to monitor the number of requests in progress. + +It also features a **modular approach to metrics** that should instrument all +FastAPI endpoints. You can either choose from a set of already existing metrics +or create your own. And every metric function by itself can be configured as +well. You can see ready to use metrics +[here](https://trallnag.github.io/prometheus-fastapi-instrumentator/metrics.html). + +## Advanced Usage + +This chapter contains an example on the advanced usage of the Prometheus FastAPI +Instrumentator to showcase most of it's features. Fore more concrete info check +out the +[automatically generated documentation](https://trallnag.github.io/prometheus-fastapi-instrumentator/). + +### Creating the Instrumentator + +We start by creating an instance of the Instrumentator. Notice the additional +`metrics` import. This will come in handy later. + +```python +from prometheus_fastapi_instrumentator import Instrumentator, metrics + +instrumentator = Instrumentator( + should_group_status_codes=False, + should_ignore_untemplated=True, + should_respect_env_var=True, + should_instrument_requests_inprogress=True, + excluded_handlers=[".*admin.*", "/metrics"], + env_var_name="ENABLE_METRICS", + inprogress_name="inprogress", + inprogress_labels=True, +) +``` + +Unlike in the fast track example, now the instrumentation and exposition will +only take place if the environment variable `ENABLE_METRICS` is `true` at +run-time. This can be helpful in larger deployments with multiple services +depending on the same base FastAPI. + +### Adding metrics + +Let's say we also want to instrument the size of requests and responses. For +this we use the `add()` method. This method does nothing more than taking a +function and adding it to a list. Then during run-time every time FastAPI +handles a request all functions in this list will be called while giving them a +single argument that stores useful information like the request and response +objects. If no `add()` at all is used, the default metric gets added in the +background. This is what happens in the fast track example. + +All instrumentation functions are stored as closures in the `metrics` module. +Fore more concrete info check out the +[automatically generated documentation](https://trallnag.github.io/prometheus-fastapi-instrumentator/). + +Closures come in handy here because it allows us to configure the functions +within. + +```python +instrumentator.add(metrics.latency(buckets=(1, 2, 3,))) +``` + +This simply adds the metric you also get in the fast track example with a +modified buckets argument. But we would also like to record the size of all +requests and responses. + +```python +instrumentator.add( + metrics.request_size( + should_include_handler=True, + should_include_method=False, + should_include_status=True, + metric_namespace="a", + metric_subsystem="b", + ) +).add( + metrics.response_size( + should_include_handler=True, + should_include_method=False, + should_include_status=True, + metric_namespace="namespace", + metric_subsystem="subsystem", + ) +) +``` + +You can add as many metrics you like to the instrumentator. + +### Creating new metrics + +As already mentioned, it is possible to create custom functions to pass on to +`add()`. This is also how the default metrics are implemented. The documentation +and code +[here](https://trallnag.github.io/prometheus-fastapi-instrumentator/metrics.html) +is helpful to get an overview. + +The basic idea is that the instrumentator creates an `info` object that contains +everything necessary for instrumentation based on the configuration of the +instrumentator. This includes the raw request and response objects but also the +modified handler, grouped status code and duration. Next, all registered +instrumentation functions are called. They get `info` as their single argument. + +Let's say we want to count the number of times a certain language has been +requested. + +```python +from typing import Callable +from prometheus_fastapi_instrumentator.metrics import Info +from prometheus_client import Counter + +def http_requested_languages_total() -> Callable[[Info], None]: + METRIC = Counter( + "http_requested_languages_total", + "Number of times a certain language has been requested.", + labelnames=("langs",) + ) + + def instrumentation(info: Info) -> None: + langs = set() + lang_str = info.request.headers["Accept-Language"] + for element in lang_str.split(","): + element = element.split(";")[0].strip().lower() + langs.add(element) + for language in langs: + METRIC.labels(language).inc() + + return instrumentation +``` + +The function `http_requested_languages_total` is used for persistent elements +that are stored between all instrumentation executions (for example the metric +instance itself). Next comes the closure. This function must adhere to the shown +interface. It will always get an `Info` object that contains the request, +response and a few other modified informations. For example the (grouped) status +code or the handler. Finally, the closure is returned. + +**Important:** The response object inside `info` can either be the response +object or `None`. In addition, errors thrown in the handler are not caught by +the instrumentator. I recommend to check the documentation and/or the source +code before creating your own metrics. + +To use it, we hand over the closure to the instrumentator object. + +```python +instrumentator.add(http_requested_languages_total()) +``` + +### Perform instrumentation + +Up to this point, the FastAPI has not been touched at all. Everything has been +stored in the `instrumentator` only. To actually register the instrumentation +with FastAPI, the `instrument()` method has to be called. + +```python +instrumentator.instrument(app) +``` + +Notice that this will do nothing if `should_respect_env_var` has been set during +construction of the instrumentator object and the respective env var is not +found. + +### Specify namespace and subsystem + +You can specify the namespace and subsystem of the metrics by passing them in +the instrument method. + +```python +from prometheus_fastapi_instrumentator import Instrumentator + +@app.on_event("startup") +async def startup(): + Instrumentator().instrument(app, metric_namespace='myproject', metric_subsystem='myservice').expose(app) +``` + +Then your metrics will contain the namespace and subsystem in the metric name. + +```sh +# TYPE myproject_myservice_http_request_duration_highr_seconds histogram +myproject_myservice_http_request_duration_highr_seconds_bucket{le="0.01"} 0.0 +``` + +### Exposing endpoint + +To expose an endpoint for the metrics either follow +[Prometheus Python Client](https://github.com/prometheus/client_python) and add +the endpoint manually to the FastAPI or serve it on a separate server. You can +also use the included `expose` method. It will add an endpoint to the given +FastAPI. With `should_gzip` you can instruct the endpoint to compress the data +as long as the client accepts gzip encoding. Prometheus for example does by +default. Beware that network bandwith is often cheaper than CPU cycles. + +```python +instrumentator.expose(app, include_in_schema=False, should_gzip=True) +``` + +Notice that this will to nothing if `should_respect_env_var` has been set during +construction of the instrumentator object and the respective env var is not +found. + +## Contributing + +Please refer to [`CONTRIBUTING.md`](CONTRIBUTING). + +Consult [`DEVELOPMENT.md`](DEVELOPMENT.md) for guidance regarding development. + +Read [`RELEASE.md`](RELEASE.md) for details about the release process. + +## Licensing + +The default license for this project is the +[ISC License](https://choosealicense.com/licenses/isc). A permissive license +functionally equivalent to the BSD 2-Clause and MIT licenses, removing some +language that is no longer necessary. See [`LICENSE`](LICENSE) for the license +text. + +The [BSD 3-Clause License](https://choosealicense.com/licenses/bsd-3-clause) is +used as the license for the +[`routing`](src/prometheus_fastapi_instrumentator/routing.py) module. This is +due to it containing code from +[elastic/apm-agent-python](https://github.com/elastic/apm-agent-python). BSD +3-Clause is a permissive license similar to the BSD 2-Clause License, but with a +3rd clause that prohibits others from using the name of the copyright holder or +its contributors to promote derived products without written consent. The +license text is included in the module itself. + + +%package help +Summary: Development documents and examples for prometheus-fastapi-instrumentator +Provides: python3-prometheus-fastapi-instrumentator-doc +%description help +# Prometheus FastAPI Instrumentator <!-- omit in toc --> + +[](https://pypi.python.org/pypi/prometheus-fastapi-instrumentator) +[](https://pypi.python.org/pypi/prometheus-fastapi-instrumentator) +[](https://pepy.tech/project/prometheus-fastapi-instrumentator/month) +[](https://github.com/trallnag/kubestatus2cloudwatch/actions) +[](https://codecov.io/gh/trallnag/prometheus-fastapi-instrumentator) + +A configurable and modular Prometheus Instrumentator for your FastAPI. Install +`prometheus-fastapi-instrumentator` from +[PyPI](https://pypi.python.org/pypi/prometheus-fastapi-instrumentator/). Here is +the fast track to get started with a pre-configured instrumentator. Import the +instrumentator class: + +```python +from prometheus_fastapi_instrumentator import Instrumentator +``` + +Instrument your app with default metrics and expose the metrics: + +```python +Instrumentator().instrument(app).expose(app) +``` + +Depending on your code you might have to use the following instead: + +```python +instrumentator = Instrumentator().instrument(app) + +@app.on_event("startup") +async def _startup(): + instrumentator.expose(app) +``` + +With this, your FastAPI is instrumented and metrics are ready to be scraped. The +defaults give you: + +- Counter `http_requests_total` with `handler`, `status` and `method`. Total + number of requests. +- Summary `http_request_size_bytes` with `handler`. Added up total of the + content lengths of all incoming requests. +- Summary `http_response_size_bytes` with `handler`. Added up total of the + content lengths of all outgoing responses. +- Histogram `http_request_duration_seconds` with `handler`. Only a few buckets + to keep cardinality low. +- Histogram `http_request_duration_highr_seconds` without any labels. Large + number of buckets (>20). + +In addition, following behavior is active: + +- Status codes are grouped into `2xx`, `3xx` and so on. +- Requests without a matching template are grouped into the handler `none`. + +If one of these presets does not suit your needs you can do one of multiple +things: + +- Pick one of the already existing closures from + [`metrics`](./src/prometheus_fastapi_instrumentator/metrics.py) and pass it to + the instrumentator instance. See [here](#adding-metrics) how to do that. +- Create your own instrumentation function that you can pass to an + instrumentator instance. See [here](#creating-new-metrics) to learn how more. +- Don't use this package at all and just use the source code as inspiration on + how to instrument your FastAPI. + +Important: This package is not made for generic Prometheus instrumentation in +Python. Use the Prometheus client library for that. This packages uses it as +well. + +## Table of Contents <!-- omit in toc --> + +<!--TOC--> + +- [Features](#features) +- [Advanced Usage](#advanced-usage) + - [Creating the Instrumentator](#creating-the-instrumentator) + - [Adding metrics](#adding-metrics) + - [Creating new metrics](#creating-new-metrics) + - [Perform instrumentation](#perform-instrumentation) + - [Specify namespace and subsystem](#specify-namespace-and-subsystem) + - [Exposing endpoint](#exposing-endpoint) +- [Contributing](#contributing) +- [Licensing](#licensing) + +<!--TOC--> + +## Features + +Beyond the fast track, this instrumentator is **highly configurable** and it is +very easy to customize and adapt to your specific use case. Here is a list of +some of these options you may opt-in to: + +- Regex patterns to ignore certain routes. +- Completely ignore untemplated routes. +- Control instrumentation and exposition with an env var. +- Rounding of latencies to a certain decimal number. +- Renaming of labels and the metric. +- Metrics endpoint can compress data with gzip. +- Opt-in metric to monitor the number of requests in progress. + +It also features a **modular approach to metrics** that should instrument all +FastAPI endpoints. You can either choose from a set of already existing metrics +or create your own. And every metric function by itself can be configured as +well. You can see ready to use metrics +[here](https://trallnag.github.io/prometheus-fastapi-instrumentator/metrics.html). + +## Advanced Usage + +This chapter contains an example on the advanced usage of the Prometheus FastAPI +Instrumentator to showcase most of it's features. Fore more concrete info check +out the +[automatically generated documentation](https://trallnag.github.io/prometheus-fastapi-instrumentator/). + +### Creating the Instrumentator + +We start by creating an instance of the Instrumentator. Notice the additional +`metrics` import. This will come in handy later. + +```python +from prometheus_fastapi_instrumentator import Instrumentator, metrics + +instrumentator = Instrumentator( + should_group_status_codes=False, + should_ignore_untemplated=True, + should_respect_env_var=True, + should_instrument_requests_inprogress=True, + excluded_handlers=[".*admin.*", "/metrics"], + env_var_name="ENABLE_METRICS", + inprogress_name="inprogress", + inprogress_labels=True, +) +``` + +Unlike in the fast track example, now the instrumentation and exposition will +only take place if the environment variable `ENABLE_METRICS` is `true` at +run-time. This can be helpful in larger deployments with multiple services +depending on the same base FastAPI. + +### Adding metrics + +Let's say we also want to instrument the size of requests and responses. For +this we use the `add()` method. This method does nothing more than taking a +function and adding it to a list. Then during run-time every time FastAPI +handles a request all functions in this list will be called while giving them a +single argument that stores useful information like the request and response +objects. If no `add()` at all is used, the default metric gets added in the +background. This is what happens in the fast track example. + +All instrumentation functions are stored as closures in the `metrics` module. +Fore more concrete info check out the +[automatically generated documentation](https://trallnag.github.io/prometheus-fastapi-instrumentator/). + +Closures come in handy here because it allows us to configure the functions +within. + +```python +instrumentator.add(metrics.latency(buckets=(1, 2, 3,))) +``` + +This simply adds the metric you also get in the fast track example with a +modified buckets argument. But we would also like to record the size of all +requests and responses. + +```python +instrumentator.add( + metrics.request_size( + should_include_handler=True, + should_include_method=False, + should_include_status=True, + metric_namespace="a", + metric_subsystem="b", + ) +).add( + metrics.response_size( + should_include_handler=True, + should_include_method=False, + should_include_status=True, + metric_namespace="namespace", + metric_subsystem="subsystem", + ) +) +``` + +You can add as many metrics you like to the instrumentator. + +### Creating new metrics + +As already mentioned, it is possible to create custom functions to pass on to +`add()`. This is also how the default metrics are implemented. The documentation +and code +[here](https://trallnag.github.io/prometheus-fastapi-instrumentator/metrics.html) +is helpful to get an overview. + +The basic idea is that the instrumentator creates an `info` object that contains +everything necessary for instrumentation based on the configuration of the +instrumentator. This includes the raw request and response objects but also the +modified handler, grouped status code and duration. Next, all registered +instrumentation functions are called. They get `info` as their single argument. + +Let's say we want to count the number of times a certain language has been +requested. + +```python +from typing import Callable +from prometheus_fastapi_instrumentator.metrics import Info +from prometheus_client import Counter + +def http_requested_languages_total() -> Callable[[Info], None]: + METRIC = Counter( + "http_requested_languages_total", + "Number of times a certain language has been requested.", + labelnames=("langs",) + ) + + def instrumentation(info: Info) -> None: + langs = set() + lang_str = info.request.headers["Accept-Language"] + for element in lang_str.split(","): + element = element.split(";")[0].strip().lower() + langs.add(element) + for language in langs: + METRIC.labels(language).inc() + + return instrumentation +``` + +The function `http_requested_languages_total` is used for persistent elements +that are stored between all instrumentation executions (for example the metric +instance itself). Next comes the closure. This function must adhere to the shown +interface. It will always get an `Info` object that contains the request, +response and a few other modified informations. For example the (grouped) status +code or the handler. Finally, the closure is returned. + +**Important:** The response object inside `info` can either be the response +object or `None`. In addition, errors thrown in the handler are not caught by +the instrumentator. I recommend to check the documentation and/or the source +code before creating your own metrics. + +To use it, we hand over the closure to the instrumentator object. + +```python +instrumentator.add(http_requested_languages_total()) +``` + +### Perform instrumentation + +Up to this point, the FastAPI has not been touched at all. Everything has been +stored in the `instrumentator` only. To actually register the instrumentation +with FastAPI, the `instrument()` method has to be called. + +```python +instrumentator.instrument(app) +``` + +Notice that this will do nothing if `should_respect_env_var` has been set during +construction of the instrumentator object and the respective env var is not +found. + +### Specify namespace and subsystem + +You can specify the namespace and subsystem of the metrics by passing them in +the instrument method. + +```python +from prometheus_fastapi_instrumentator import Instrumentator + +@app.on_event("startup") +async def startup(): + Instrumentator().instrument(app, metric_namespace='myproject', metric_subsystem='myservice').expose(app) +``` + +Then your metrics will contain the namespace and subsystem in the metric name. + +```sh +# TYPE myproject_myservice_http_request_duration_highr_seconds histogram +myproject_myservice_http_request_duration_highr_seconds_bucket{le="0.01"} 0.0 +``` + +### Exposing endpoint + +To expose an endpoint for the metrics either follow +[Prometheus Python Client](https://github.com/prometheus/client_python) and add +the endpoint manually to the FastAPI or serve it on a separate server. You can +also use the included `expose` method. It will add an endpoint to the given +FastAPI. With `should_gzip` you can instruct the endpoint to compress the data +as long as the client accepts gzip encoding. Prometheus for example does by +default. Beware that network bandwith is often cheaper than CPU cycles. + +```python +instrumentator.expose(app, include_in_schema=False, should_gzip=True) +``` + +Notice that this will to nothing if `should_respect_env_var` has been set during +construction of the instrumentator object and the respective env var is not +found. + +## Contributing + +Please refer to [`CONTRIBUTING.md`](CONTRIBUTING). + +Consult [`DEVELOPMENT.md`](DEVELOPMENT.md) for guidance regarding development. + +Read [`RELEASE.md`](RELEASE.md) for details about the release process. + +## Licensing + +The default license for this project is the +[ISC License](https://choosealicense.com/licenses/isc). A permissive license +functionally equivalent to the BSD 2-Clause and MIT licenses, removing some +language that is no longer necessary. See [`LICENSE`](LICENSE) for the license +text. + +The [BSD 3-Clause License](https://choosealicense.com/licenses/bsd-3-clause) is +used as the license for the +[`routing`](src/prometheus_fastapi_instrumentator/routing.py) module. This is +due to it containing code from +[elastic/apm-agent-python](https://github.com/elastic/apm-agent-python). BSD +3-Clause is a permissive license similar to the BSD 2-Clause License, but with a +3rd clause that prohibits others from using the name of the copyright holder or +its contributors to promote derived products without written consent. The +license text is included in the module itself. + + +%prep +%autosetup -n prometheus-fastapi-instrumentator-6.0.0 + +%build +%py3_build + +%install +%py3_install +install -d -m755 %{buildroot}/%{_pkgdocdir} +if [ -d doc ]; then cp -arf doc %{buildroot}/%{_pkgdocdir}; fi +if [ -d docs ]; then cp -arf docs %{buildroot}/%{_pkgdocdir}; fi +if [ -d example ]; then cp -arf example %{buildroot}/%{_pkgdocdir}; fi +if [ -d examples ]; then cp -arf examples %{buildroot}/%{_pkgdocdir}; fi +pushd %{buildroot} +if [ -d usr/lib ]; then + find usr/lib -type f -printf "/%h/%f\n" >> filelist.lst +fi +if [ -d usr/lib64 ]; then + find usr/lib64 -type f -printf "/%h/%f\n" >> filelist.lst +fi +if [ -d usr/bin ]; then + find usr/bin -type f -printf "/%h/%f\n" >> filelist.lst +fi +if [ -d usr/sbin ]; then + find usr/sbin -type f -printf "/%h/%f\n" >> filelist.lst +fi +touch doclist.lst +if [ -d usr/share/man ]; then + find usr/share/man -type f -printf "/%h/%f.gz\n" >> doclist.lst +fi +popd +mv %{buildroot}/filelist.lst . +mv %{buildroot}/doclist.lst . + +%files -n python3-prometheus-fastapi-instrumentator -f filelist.lst +%dir %{python3_sitelib}/* + +%files help -f doclist.lst +%{_docdir}/* + +%changelog +* Tue Apr 11 2023 Python_Bot <Python_Bot@openeuler.org> - 6.0.0-1 +- Package Spec generated |
