Skip to content

Commit 52dd117

Browse files
committed
PB-2183: implement otel instrumentation
1 parent 7ee4bba commit 52dd117

10 files changed

Lines changed: 1072 additions & 428 deletions

File tree

.env.test

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# OTEL
2+
OTEL_SDK_DISABLED=false
3+
OTEL_ENABLE_FLASK=true
4+
OTEL_ENABLE_LOGGING=true
5+
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
6+
OTEL_EXPORTER_OTLP_INSECURE=true
7+
OTEL_RESOURCE_ATTRIBUTES=service.name=service-alti
8+
OTEL_PYTHON_EXCLUDED_URLS="checker"
9+
OTEL_PYTHON_LOG_CORRELATION=true

.pylintrc

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,6 @@ load-plugins=pylint_flask
3636
# Pickle collected data for later comparisons.
3737
persistent=yes
3838

39-
# When enabled, pylint would attempt to guess common misconfiguration and emit
40-
# user-friendly hints instead of false-positive error messages.
41-
suggestion-mode=yes
42-
4339
# Allow loading of arbitrary C extensions. Extensions are imported into the
4440
# active Python interpreter and may run arbitrary code.
4541
unsafe-load-any-extension=no

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ ci:
9494
# Create virtual env with all packages for development using the Pipfile.lock
9595
pipenv sync --dev
9696

97+
.PHONY: otelrequirements
98+
otelrequirements:
99+
edot-bootstrap --action=requirements
97100

98101
# linting target, calls upon yapf to make sure your code is easier to read and respects some conventions.
99102

Pipfile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ logging-utilities = "~=5.0"
1212
Flask = "~=3.1"
1313
PyYAML = "~=6.0"
1414

15+
# OpenTelemetry packages
16+
opentelemetry-sdk = "*"
17+
opentelemetry-exporter-otlp = "*"
18+
opentelemetry-instrumentation-flask = "*"
19+
opentelemetry-instrumentation-logging = "*"
20+
1521
[dev-packages]
1622
mock = "*"
1723
nose2 = "*"

Pipfile.lock

Lines changed: 952 additions & 423 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/helpers/otel.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
from os import getenv
2+
3+
from opentelemetry import trace
4+
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import \
5+
OTLPSpanExporter
6+
from opentelemetry.instrumentation.flask import FlaskInstrumentor
7+
from opentelemetry.instrumentation.logging import LoggingInstrumentor
8+
from opentelemetry.sdk.resources import Resource
9+
from opentelemetry.sdk.trace import TracerProvider
10+
from opentelemetry.sdk.trace.export import BatchSpanProcessor
11+
12+
from app.helpers.utils import strtobool
13+
14+
15+
def initialize() -> None:
16+
if not strtobool(getenv("OTEL_SDK_DISABLED", "false")):
17+
if strtobool(getenv("OTEL_ENABLE_LOGGING", "false")):
18+
LoggingInstrumentor().instrument()
19+
20+
21+
def initialize_flask(app):
22+
if not strtobool(getenv("OTEL_SDK_DISABLED", "false")):
23+
if strtobool(getenv("OTEL_ENABLE_FLASK", "false")):
24+
FlaskInstrumentor().instrument_app(app)
25+
26+
27+
def setup_trace_provider():
28+
if not strtobool(getenv("OTEL_SDK_DISABLED", "false")):
29+
# Since we created a new tracer, the default span processor is gone. We need to
30+
# create a new one using the default OTEL env variables and ad it to the tracer.
31+
span_processor = BatchSpanProcessor(
32+
OTLPSpanExporter(
33+
endpoint=getenv('OTEL_EXPORTER_OTLP_ENDPOINT', "http://localhost:4317"),
34+
headers=getenv('OTEL_EXPORTER_OTLP_HEADERS'),
35+
insecure=strtobool(getenv('OTEL_EXPORTER_OTLP_INSECURE', "false"))
36+
)
37+
)
38+
39+
provider = TracerProvider(resource=Resource.create())
40+
provider.add_span_processor(span_processor)
41+
trace.set_tracer_provider(provider)

docker-compose-otel.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
version: "2"
2+
services:
3+
otel-collector:
4+
image: otel/opentelemetry-collector-contrib:latest
5+
command: --config otel-local-config.yaml
6+
env_file:
7+
- .env.test
8+
volumes:
9+
- ./otel-local-config.yaml:/otel-local-config.yaml
10+
ports:
11+
- "4317:4317"
12+
13+
zipkin:
14+
image: openzipkin/zipkin:latest
15+
ports:
16+
- "9411:9411"

logging-cfg-local.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ formatters:
8282
duration: "%(duration)s"
8383
payload: "%(response.json).128s"
8484
message: message
85+
otelSpanID: otelSpanID
86+
otelTraceID: otelTraceID
87+
otelTraceSampled: otelTraceSampled
8588

8689
handlers:
8790
console:

otel-local-config.yaml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
receivers:
2+
otlp:
3+
protocols:
4+
grpc:
5+
endpoint: 0.0.0.0:4317
6+
7+
processors:
8+
batch:
9+
10+
exporters:
11+
debug:
12+
verbosity: detailed
13+
zipkin:
14+
endpoint: http://zipkin:9411/api/v2/spans
15+
16+
service:
17+
pipelines:
18+
traces:
19+
receivers: [otlp]
20+
processors: [batch]
21+
exporters: [debug, zipkin]

wsgi.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
# Initialize OTEL.
2+
# Initialize should be called as early as possible, but at least before the app is imported
3+
# The order has a impact on how the libraries are instrumented. If called after app import,
4+
# e.g. the flask instrumentation has no effect. See:
5+
# https://github.com/open-telemetry/opentelemetry.io/blob/main/content/en/docs/zero-code/python/troubleshooting.md#use-programmatic-auto-instrumentation
6+
7+
from app.helpers.otel import initialize
8+
from app.helpers.otel import initialize_flask
9+
from app.helpers.otel import setup_trace_provider
10+
11+
initialize()
12+
113
import multiprocessing
214

315
from gunicorn.app.base import BaseApplication
@@ -9,6 +21,13 @@
921
from app.settings import GUNICORN_WORKER_TMP_DIR
1022
from app.settings import HTTP_PORT
1123

24+
initialize_flask(application)
25+
26+
def post_fork(server, worker):
27+
server.log.info("Worker spawned (pid: %s)", worker.pid)
28+
29+
# Setup OTEL providers for this worker
30+
setup_trace_provider()
1231

1332
class StandaloneApplication(BaseApplication): # pylint: disable=abstract-method
1433

@@ -44,6 +63,7 @@ def load(self):
4463
'worker_tmp_dir': GUNICORN_WORKER_TMP_DIR,
4564
'timeout': 60,
4665
'keepalive': GUNICORN_KEEPALIVE,
47-
'logconfig_dict': get_logging_cfg()
66+
'logconfig_dict': get_logging_cfg(),
67+
'post_fork': post_fork
4868
}
4969
StandaloneApplication(application, options).run()

0 commit comments

Comments
 (0)