DEV Community

Cover image for Opentelemetry for python
prumand
prumand

Posted on

Opentelemetry for python

I have a cli-app (see my previous post) and I want to collect some usage stats. Of course opentelemetry is the way to go and python support is pretty far.

I’m using jaeger for my traces. I don’t need live observability, but just some analytics from time to time. So I’d prefer starting a container and ingesting traces which are stored on disk on demand.

Unfortunately there is no to-disk exporter for traces out of the box, but it’s easy to set up. First for writing to files, I’ll just use the standard python logging:

import logging
from logging import LogRecord, Logger
from logging.handlers import RotatingFileHandler


handler = RotatingFileHandler(LOG_FILE_PATH, maxBytes=1024 * 1024, backupCount=5)
logging.basicConfig(
    level=logging.INFO,
    handlers=[handler],
    format='%(message)s',   
)
Enter fullscreen mode Exit fullscreen mode

It's ok to rotate logs from time to time. I can alway crank up the backupCount, if I think it’s important.

I’m using the standard otel-ConsoleExporter and change the formatter, since I want all traces to be written on one line. That makes it easier to ingest the file later. Further I change the output of the Exporter to my customer implementation.

class LogOut:
    def __init__(self, logger: Logger) -> None:
        self._logger = logger

    def write(self, msg):
        self._logger.info(msg)

    def flush(self):
        pass

provider = TracerProvider(resource=resource)
processor = BatchSpanProcessor(ConsoleSpanExporter(
    formatter=lambda span: span.to_json(None),
    out=LogOut(logging.getLogger('makit.cli'))
))

provider.add_span_processor(processor)
trace.set_tracer_provider(provider)
tracer = trace.get_tracer('makit.cli')

Enter fullscreen mode Exit fullscreen mode

So basically I just need to add the traces to my action-functions and I’ve all I need to know:

if __name__ == "__main__":
    with tracer.start_as_current_span("main") as span:
        get_app()()

Enter fullscreen mode Exit fullscreen mode

Sidenote

For me it’s fine to use span events as logging. Unfortunately those don’t show up in the jaeger-ui. But I tried something like the following:

class OtelLogger(logging.Logger):
    def handle(self, record: LogRecord) -> None:
        span = trace.get_current_span()
        span.add_event(record.msg)

Enter fullscreen mode Exit fullscreen mode

It gives me all the information I need and I thought about setting the span status based on the log-level.

Top comments (0)