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',
)
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')
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()()
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)
It gives me all the information I need and I thought about setting the span status based on the log-level.
Top comments (0)