opentelemetry-python icon indicating copy to clipboard operation
opentelemetry-python copied to clipboard

how to get "body" to use what's specified in logging setformatters?

Open jesumyip opened this issue 2 years ago • 5 comments

Using these python modules:

opentelemetry-instrumentation-fastapi==0.39b0
opentelemetry-api==1.18.0
opentelemetry-sdk==1.18.0
opentelemetry-exporter-otlp==1.18.0
opentelemetry-exporter-otlp-proto-grpc==1.18.0
opentelemetry-exporter-otlp-proto-http==1.18.0
opentelemetry-instrumentation==0.39b0
opentelemetry-proto==1.18.0
opentelemetry-semantic-conventions==0.39b0
opentelemetry-util-http==0.39b0
opentelemetry-instrumentation-logging==0.39b0

I have the following code snippet:

console_log_exporter = ConsoleLogExporter()
logger_provider = LoggerProvider(
    resource=Resource.create({})
)

set_logger_provider(logger_provider)
logger_provider.add_log_record_processor(SimpleLogRecordProcessor(console_log_exporter))
otel_log_handler = LoggingHandler(logger_provider=logger_provider)
LoggingInstrumentor().instrument()
logging.getLogger().addHandler(otel_log_handler)

I have the following env vars set:

OTEL_PYTHON_LOG_CORRELATION=true
OTEL_PYTHON_LOG_FORMAT=%(pathname)s:%(funcName)s:%(lineno)d:%(levelname)s:%(message)s
OTEL_PYTHON_LOG_LEVEL=info

Whenever I do something like logging.info("Hi there, Mr. Pineapple!"), I see that the output JSON has something that looks like this:

{
    "body": "Hi there, Mr. Pineapple!",
    "severity_number": "<SeverityNumber.INFO: 9>",
    "severity_text": "INFO",
    "attributes": {
        "otelSpanID": "2e94f2400eb2f2c9",
        "otelTraceID": "1b63d84a5a4faec3b1dd77477d1d52df",
        "otelTraceSampled": true,
        "otelServiceName": "my-fruit-service"
    },
    "timestamp": "2023-06-19T06:48:28.605903Z",
    "trace_id": "0x1b63d84a5a4faec3b1dd77477d1d52df",
    "span_id": "0x2e94f2400eb2f2c9",
    "trace_flags": 1,
    "resource": "BoundedAttributes({'telemetry.sdk.language': 'python', 'telemetry.sdk.name': 'opentelemetry', 'telemetry.sdk.version': '1.18.0', 'service.instance.id': 'dev', 'service.name': 'my-fruit-service'}, maxlen=None)"
}

I am expecting "body" to have the format dictated by OTEL_PYTHON_LOG_FORMAT, but it does not. How can I get "body" to also contain for example lineno, pathname, etc? @srikanthccv

jesumyip avatar Jun 19 '23 06:06 jesumyip

I guess the problem is in https://github.com/open-telemetry/opentelemetry-python/blob/6b9f389940ec0123d5dafc7cb400fc23c6f691c6/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/init.py#L474 it is only doing body=record.getMessage(). So, it is completely ignoring any formatters that has been configured.

so a possible fix would be to change https://github.com/open-telemetry/opentelemetry-python/blob/6b9f389940ec0123d5dafc7cb400fc23c6f691c6/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/init.py#LL497C9-L497C51 to:

msg = self.format(record)
record.msg = msg
self._logger.emit(self._translate(record))

For those who can't wait for the update, you can easily create a new class and use it instead of LoggingHandler.

class FormattedLoggingHandler(LoggingHandler):
    def emit(self, record: logging.LogRecord) -> None:
        """
        Emit a record.

        The record is translated to OTel format, and then sent across the pipeline.
        """
        msg = self.format(record)
        record.msg = msg
        self._logger.emit(self._translate(record))

.
.
.
.
    # Something along these lines (but with better error handling!!)
    otel_log_handler = FormattedLoggingHandler(logger_provider=logger_provider)
    LoggingInstrumentor().instrument()
    logFormatter = logging.Formatter(fmt=os.getenv("OTEL_PYTHON_LOG_FORMAT", None))
    otel_log_handler.setFormatter(logFormatter)
    logging.getLogger().addHandler(otel_log_handler)

jesumyip avatar Jun 19 '23 13:06 jesumyip

small update, since I don't use args, this code:

       msg = self.format(record)
       record.msg = msg
       self._logger.emit(self._translate(record))

should be changed to:

       msg = self.format(record)
       record.msg = msg
       record.args = None
       self._logger.emit(self._translate(record))

or you will hit this error:

  File "/usr/local/lib/python3.11/logging/__init__.py", line 377, in getMessage
    msg = msg % self.args
          ~~~~^~~~~~~~~~~
TypeError: not all arguments converted during string formatting

jesumyip avatar Jun 20 '23 04:06 jesumyip

@jesumyip thanks for reporting this, would you mind submitting a PR?

aabmass avatar Jun 22 '23 16:06 aabmass

I believe this pr would address this issue

lzchen avatar Jul 01 '24 17:07 lzchen