How can I set a variable value with dot in names using Python logs' formatter?

Viewed 37

Now I'm trying to adopt the official example from DataDog docs. In the example, there is a logs' format with custom variables that contain a dot . in their names. How can I set such variables in PYthon Logging library? I have tried to use Python ContextFilter to achieve this goal, but with it, I can only set variables without dots in their names. And it's logically, as in Python dots . indicates an access to an object's attribute. Maybe you know a workaround? Here is my try that doesn't work:

import logging
from ddtrace import tracer

from ddtrace import patch

patch(logging=True)


class LogServiceMetaData:
    def __init__(self, service="bi_app_log_service", env="DEV", version="20220624"):
        self.service = service
        self.env = env
        self.version = version


class ContextFilter(logging.Filter):
    """
    This is a filter which injects contextual information into the log.
    """

    def filter(self, record):
        dd = LogServiceMetaData()
        record.dd = dd
        return True


FORMAT = ('%(asctime)s %(levelname)s [%(name)s] [%(filename)s:%(lineno)d] '
          '[dd.service=%(dd.service)s dd.env=%(dd.env)s '
          'dd.version=%(dd.version)s '
          'dd.trace_id=%(dd.trace_id)s dd.span_id=%(dd.span_id)s]'
          '- %(message)s')


@tracer.wrap()
def hello():
    logging.basicConfig(format=FORMAT)
    logger = logging.getLogger(__name__)
    logger.addFilter(ContextFilter())
    logger.level = logging.DEBUG
    logger.info('Hello, World!')


if __name__ == '__main__':
    hello()

Incorrect output:

test.py
2022-07-24 13:54:58,779 INFO [__main__] [test.py:40] [dd.service= dd.env= dd.version= dd.trace_id=8205295872395558531 dd.span_id=4783080736929852481]- Hello, World!

Process finished with exit code 0

In this output, instead of dd.service= I expect to see dd.service=bi_app_log_service

1 Answers

You can do this using the approach in this example:

import logging

class DDFilter(logging.Filter):
    def filter(self, record):
        d = record.__dict__
        d['dd.foo'] = 'foo'
        d['dd.bar'] = 'bar'
        return True

logging.basicConfig(level=logging.DEBUG, format='%(dd.foo)s %(dd.bar)s %(message)s')
logging.getLogger().handlers[0].addFilter(DDFilter())
logging.debug('A debug message')

When run, this will print

foo bar A debug message
Related