Skip to content

Commit 5579b02

Browse files
authored
Merge pull request #89 from autometrics-dev/otlp-exporter
Add otlp exporters
2 parents 7e42201 + c0f6ad2 commit 5579b02

31 files changed

+1673
-631
lines changed

.github/workflows/main.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,12 @@ jobs:
2323
with:
2424
python-version: ${{ matrix.python-version }}
2525
cache: poetry
26-
- name: Install dependencies
27-
run: poetry install --no-interaction --no-root --with dev,examples
26+
- name: Install dependencies (cpython)
27+
if: ${{ matrix.python-version != 'pypy3.10' }}
28+
run: poetry install --no-interaction --no-root --with dev,examples --all-extras
29+
- name: Install dependencies (pypy)
30+
if: ${{ matrix.python-version == 'pypy3.10' }}
31+
run: poetry install --no-interaction --no-root --with dev,examples --extras=exporter-otlp-proto-http
2832
- name: Check code formatting
2933
run: poetry run black .
3034
- name: Lint lib code

CHANGELOG.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
1313
### Added
1414

1515
- Added support for `record_error_if` and `record_success_if`
16+
- Added OTLP exporters for OpenTelemetry tracker (#89)
1617

1718
### Changed
1819

19-
-
20+
- [💥 Breaking change] `init` function is now required to be called before using autometrics (#89)
21+
- Prometheus exporters are now configured via `init` function (#89)
2022

2123
### Deprecated
2224

@@ -32,7 +34,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
3234

3335
### Security
3436

35-
-
37+
- Updated FastAPI and Pydantic dependencies in the examples group (#89)
3638

3739
## [0.9](https://github.com/autometrics-dev/autometrics-py/releases/tag/0.8) - 2023-07-24
3840

README.md

Lines changed: 113 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -29,65 +29,76 @@ See [Why Autometrics?](https://github.com/autometrics-dev#why-autometrics) for m
2929
## Quickstart
3030

3131
1. Add `autometrics` to your project's dependencies:
32-
```shell
33-
pip install autometrics
34-
```
32+
33+
```shell
34+
pip install autometrics
35+
```
3536

3637
2. Instrument your functions with the `@autometrics` decorator
3738

38-
```python
39-
from autometrics import autometrics
40-
41-
@autometrics
42-
def my_function():
43-
# ...
44-
```
45-
46-
3. Export the metrics for Prometheus
47-
```python
48-
# This example uses FastAPI, but you can use any web framework
49-
from fastapi import FastAPI, Response
50-
from prometheus_client import generate_latest
51-
52-
# Set up a metrics endpoint for Prometheus to scrape
53-
# `generate_latest` returns metrics data in the Prometheus text format
54-
@app.get("/metrics")
55-
def metrics():
56-
return Response(generate_latest())
57-
```
58-
59-
4. Run Prometheus locally with the [Autometrics CLI](https://docs.autometrics.dev/local-development#getting-started-with-am) or [configure it manually](https://github.com/autometrics-dev#5-configuring-prometheus) to scrape your metrics endpoint
60-
```sh
61-
# Replace `8080` with the port that your app runs on
62-
am start :8080
63-
```
64-
65-
5. (Optional) If you have Grafana, import the [Autometrics dashboards](https://github.com/autometrics-dev/autometrics-shared#dashboards) for an overview and detailed view of all the function metrics you've collected
39+
```python
40+
from autometrics import autometrics
41+
42+
@autometrics
43+
def my_function():
44+
# ...
45+
```
46+
47+
3. Configure autometrics by calling the `init` function:
48+
49+
```python
50+
from autometrics import init
51+
52+
init(tracker="prometheus", service_name="my-service")
53+
```
54+
55+
4. Export the metrics for Prometheus
56+
57+
```python
58+
# This example uses FastAPI, but you can use any web framework
59+
from fastapi import FastAPI, Response
60+
from prometheus_client import generate_latest
61+
62+
# Set up a metrics endpoint for Prometheus to scrape
63+
# `generate_latest` returns metrics data in the Prometheus text format
64+
@app.get("/metrics")
65+
def metrics():
66+
return Response(generate_latest())
67+
```
68+
69+
5. Run Prometheus locally with the [Autometrics CLI](https://docs.autometrics.dev/local-development#getting-started-with-am) or [configure it manually](https://github.com/autometrics-dev#5-configuring-prometheus) to scrape your metrics endpoint
70+
71+
```sh
72+
# Replace `8080` with the port that your app runs on
73+
am start :8080
74+
```
75+
76+
6. (Optional) If you have Grafana, import the [Autometrics dashboards](https://github.com/autometrics-dev/autometrics-shared#dashboards) for an overview and detailed view of all the function metrics you've collected
6677

6778
## Using `autometrics-py`
6879

6980
- You can import the library in your code and use the decorator for any function:
7081

71-
```py
72-
from autometrics import autometrics
82+
```python
83+
from autometrics import autometrics
7384

74-
@autometrics
75-
def sayHello:
76-
return "hello"
85+
@autometrics
86+
def sayHello:
87+
return "hello"
7788

78-
```
89+
```
7990

8091
- To show tooltips over decorated functions in VSCode, with links to Prometheus queries, try installing [the VSCode extension](https://marketplace.visualstudio.com/items?itemName=Fiberplane.autometrics).
8192

82-
> **Note**: We cannot support tooltips without a VSCode extension due to behavior of the [static analyzer](https://github.com/davidhalter/jedi/issues/1921) used in VSCode.
93+
> **Note**: We cannot support tooltips without a VSCode extension due to behavior of the [static analyzer](https://github.com/davidhalter/jedi/issues/1921) used in VSCode.
8394
84-
- You can also track the number of concurrent calls to a function by using the `track_concurrency` argument: `@autometrics(track_concurrency=True)`.
95+
- You can also track the number of concurrent calls to a function by using the `track_concurrency` argument: `@autometrics(track_concurrency=True)`.
8596

86-
> **Note**: Concurrency tracking is only supported when you set with the environment variable `AUTOMETRICS_TRACKER=prometheus`.
97+
> **Note**: Concurrency tracking is only supported when you set with the environment variable `AUTOMETRICS_TRACKER=prometheus`.
8798
8899
- To access the PromQL queries for your decorated functions, run `help(yourfunction)` or `print(yourfunction.__doc__)`.
89100

90-
> For these queries to work, include a `.env` file in your project with your prometheus endpoint `PROMETHEUS_URL=your endpoint`. If this is not defined, the default endpoint will be `http://localhost:9090/`
101+
> For these queries to work, include a `.env` file in your project with your prometheus endpoint `PROMETHEUS_URL=your endpoint`. If this is not defined, the default endpoint will be `http://localhost:9090/`
91102
92103
## Dashboards
93104

@@ -119,15 +130,15 @@ The library uses the concept of Service-Level Objectives (SLOs) to define the ac
119130
120131
In order to receive alerts, **you need to add a special set of rules to your Prometheus setup**. These are configured automatically when you use the [Autometrics CLI](https://docs.autometrics.dev/local-development#getting-started-with-am) to run Prometheus.
121132

122-
> Already running Prometheus yourself? [Read about how to load the autometrics alerting rules into Prometheus here](https://github.com/autometrics-dev/autometrics-shared#prometheus-recording--alerting-rules).
133+
> Already running Prometheus yourself? [Read about how to load the autometrics alerting rules into Prometheus here](https://github.com/autometrics-dev/autometrics-shared#prometheus-recording--alerting-rules).
123134
124135
Once the alerting rules are in Prometheus, you're ready to go.
125136

126-
To use autometrics SLOs and alerts, create one or multiple `Objective`s based on the function(s) success rate and/or latency, as shown above.
137+
To use autometrics SLOs and alerts, create one or multiple `Objective`s based on the function(s) success rate and/or latency, as shown above.
127138

128139
The `Objective` can be passed as an argument to the `autometrics` decorator, which will include the given function in that objective.
129140

130-
The example above used a success rate objective. (I.e., we wanted to be alerted when the error rate started to increase.)
141+
The example above used a success rate objective. (I.e., we wanted to be alerted when the error rate started to increase.)
131142

132143
You can also create an objective for the latency of your functions like so:
133144

@@ -191,8 +202,7 @@ Autometrics makes it easy to identify if a specific version or commit introduced
191202
>
192203
> autometrics-py will track support for build_info using the OpenTelemetry tracker via [this issue](https://github.com/autometrics-dev/autometrics-py/issues/38)
193204
194-
195-
The library uses a separate metric (`build_info`) to track the version and, optionally, the git commit of your service.
205+
The library uses a separate metric (`build_info`) to track the version and, optionally, the git commit of your service.
196206

197207
It then writes queries that group metrics by the `version`, `commit` and `branch` labels so you can spot correlations between code changes and potential issues.
198208

@@ -230,7 +240,62 @@ exemplar collection by setting `AUTOMETRICS_EXEMPLARS=true`. You also need to en
230240

231241
## Exporting metrics
232242

233-
After collecting metrics with Autometrics, you need to export them to Prometheus. You can either add a separate route to your server and use the `generate_latest` function from the `prometheus_client` package, or you can use the `start_http_server` function from the same package to start a separate server that will expose the metrics. Autometrics also re-exports the `start_http_server` function with a preselected port 9464 for compatibility with other Autometrics packages.
243+
There are multiple ways to export metrics from your application, depending on your setup. You can see examples of how to do this in the [examples/export_metrics](https://github.com/autometrics-dev/autometrics-py/tree/main/examples/export_metrics) directory.
244+
245+
If you want to export metrics to Prometheus, you have two options in case of both `opentelemetry` and `prometheus` trackers:
246+
247+
1. Create a route inside your app and respond with `generate_latest()`
248+
249+
```python
250+
# This example uses FastAPI, but you can use any web framework
251+
from fastapi import FastAPI, Response
252+
from prometheus_client import generate_latest
253+
254+
# Set up a metrics endpoint for Prometheus to scrape
255+
@app.get("/metrics")
256+
def metrics():
257+
return Response(generate_latest())
258+
```
259+
260+
2. Specify `prometheus` as the exporter type, and a separate server will be started to expose metrics from your app:
261+
262+
```python
263+
exporter = {
264+
"type": "prometheus",
265+
"address": "localhost",
266+
"port": 9464
267+
}
268+
init(tracker="prometheus", service_name="my-service", exporter=exporter)
269+
```
270+
271+
For the OpenTelemetry tracker, you have more options, including a custom metric reader. You can specify the exporter type to be `otlp-proto-http` or `otlp-proto-grpc`, and metrics will be exported to a remote OpenTelemetry collector via the specified protocol. You will need to install the respective extra dependency in order for this to work, which you can do when you install autometrics:
272+
273+
```sh
274+
pip install autometrics[exporter-otlp-proto-http]
275+
pip install autometrics[exporter-otlp-proto-grpc]
276+
```
277+
278+
After installing it you can configure the exporter as follows:
279+
280+
```python
281+
exporter = {
282+
"type": "otlp-proto-grpc",
283+
"address": "http://localhost:4317",
284+
"insecure": True
285+
}
286+
init(tracker="opentelemetry", service_name="my-service", exporter=exporter)
287+
```
288+
289+
To use a custom metric reader you can specify the exporter type to be `otel-custom` and provide a custom metric reader:
290+
291+
```python
292+
my_custom_metric_reader = PrometheusMetricReader("")
293+
exporter = {
294+
"type": "otel-custom",
295+
"reader": my_custom_metric_reader
296+
}
297+
init(tracker="opentelemetry", service_name="my-service", exporter=exporter)
298+
```
234299

235300
## Development of the package
236301

@@ -255,7 +320,7 @@ Code in this repository is:
255320
In order to run these tools locally you have to install them, you can install them using poetry:
256321

257322
```sh
258-
poetry install --with dev
323+
poetry install --with dev --all-extras
259324
```
260325

261326
After that you can run the tools individually

Tiltfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
docker_compose('docker-compose.yaml')

configs/otel-collector-config.yaml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
receivers:
2+
otlp:
3+
protocols:
4+
grpc:
5+
http:
6+
7+
exporters:
8+
logging:
9+
loglevel: debug
10+
prometheus:
11+
endpoint: "0.0.0.0:9464" # This is where Prometheus will scrape the metrics from.
12+
# namespace: <namespace> # Replace with your namespace.
13+
14+
15+
processors:
16+
batch:
17+
18+
service:
19+
pipelines:
20+
metrics:
21+
receivers: [otlp]
22+
processors: []
23+
exporters: [logging, prometheus]

docker-compose.yaml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
version: "3.9"
2+
3+
volumes:
4+
app-logs:
5+
6+
services:
7+
am:
8+
image: autometrics/am:latest
9+
extra_hosts:
10+
- host.docker.internal:host-gateway
11+
ports:
12+
- "6789:6789"
13+
- "9090:9090"
14+
container_name: am
15+
command: "start http://otel-collector:9464/metrics host.docker.internal:9464"
16+
environment:
17+
- LISTEN_ADDRESS=0.0.0.0:6789
18+
restart: unless-stopped
19+
volumes:
20+
- app-logs:/var/log
21+
otel-collector:
22+
image: otel/opentelemetry-collector-contrib:latest
23+
container_name: otel-collector
24+
command: ["--config=/etc/otel-collector-config.yaml"]
25+
volumes:
26+
- ./configs/otel-collector-config.yaml:/etc/otel-collector-config.yaml
27+
ports:
28+
- "4317:4317"
29+
- "4318:4318"
30+
- "8888:8888" # expose container metrics in prometheus format
31+
- "55680:55680"
32+
- "55679:55679"
33+
restart: unless-stopped
34+
push-gateway:
35+
image: ghcr.io/zapier/prom-aggregation-gateway:latest
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import time
2+
from autometrics import autometrics, init
3+
4+
# Autometrics supports exporting metrics to Prometheus via the OpenTelemetry.
5+
# This example uses the Prometheus Python client, available settings are same as the
6+
# Prometheus Python client. By default, the Prometheus exporter will expose metrics
7+
# on port 9464. If you don't have a Prometheus server running, you can run Tilt or
8+
# Docker Compose from the root of this repo to start one up.
9+
10+
init(
11+
tracker="opentelemetry",
12+
exporter={
13+
"type": "prometheus",
14+
"port": 9464,
15+
},
16+
service_name="my-service",
17+
)
18+
19+
20+
@autometrics
21+
def my_function():
22+
pass
23+
24+
25+
while True:
26+
my_function()
27+
time.sleep(1)

examples/export_metrics/otlp.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import time
2+
from autometrics import autometrics, init
3+
4+
# Autometrics supports exporting metrics to OTLP collectors via gRPC and HTTP transports.
5+
# This example uses the gRPC transport, available settings are similar to the OpenTelemetry
6+
# Python SDK. By default, the OTLP exporter will send metrics to localhost:4317.
7+
# If you don't have an OTLP collector running, you can run Tilt or Docker Compose
8+
# to start one up.
9+
10+
init(
11+
exporter={
12+
"type": "otlp-proto-grpc",
13+
"push_interval": 1000,
14+
},
15+
service_name="my-service",
16+
)
17+
18+
19+
@autometrics
20+
def my_function():
21+
pass
22+
23+
24+
while True:
25+
my_function()
26+
time.sleep(1)

0 commit comments

Comments
 (0)