Add OTLPHandler for standard library logging module#1903
Add OTLPHandler for standard library logging module#1903lzchen merged 13 commits intoopen-telemetry:logsfrom
Conversation
codeboten
left a comment
There was a problem hiding this comment.
Code looks good, just a typo and a question about naming. Ant thoughts on whether the OTLPHandler should be part of the SDK or a separate OTLP logging package?
| """ | ||
|
|
||
|
|
||
| class OTLPHandler(logging.Handler): |
There was a problem hiding this comment.
Should this be called an emitter rather than a handler? Or is this a convenience handler to allow users to add this handler to their python root handler?
There was a problem hiding this comment.
Yes, this is one of the the handlers users can attach to root logger. I believe the name Handler makes more sense here.
| self._resource = resource | ||
| self._instrumentation_info = instrumentation_info | ||
|
|
||
| @property |
There was a problem hiding this comment.
Shouldn't this be on the provider level?
There was a problem hiding this comment.
Yes, this should be on provider as well.
Co-authored-by: alrex <alrex.boten@gmail.com>
My initial thoughts were since this is Handler for logging module maybe we can provide this as a part of SDK and other third party libs as separate packages. No strong opinions. |
|
One other argument to provide OTLPHandler as part of the SDK so that the users doesn’t need to find yet another separate package for something common. |
…lemetry-python into logging-otlp-handler
|
|
||
|
|
||
| _STD_TO_OTLP = { | ||
| 10: SeverityNumber.DEBUG, |
There was a problem hiding this comment.
How come the interim levels defined like this? (11-19, 21-29, etc.)
There was a problem hiding this comment.
Curious will numbers in "std" ever be anything other than 0, 10, 20, 30, 40, 50?
There was a problem hiding this comment.
This follows the log data model specification for mapping source format https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/data-model.md#mapping-of-severitynumber. The step value from one level to another level in logging module is 10 but the it is 5 in log data model, so it is clearly many-to-one mapping for some levels. Higher the value, higher the severity. In general these intermediary levels don't come into play but logging library allows programmers to define the custom log levels if they need, so this addresses that case. For example I would define some scary b/w error and critical level like below
>>> import logging
>>> SCARY = 45
>>> logging.addLevelName(SCARY, "SCARY")
>>> def scary(self, message, *args, **kws):
... self._log(SCARY, message, args, **kws)
...
>>> logging.Logger.scary = scary
>>> logger = logging.getLogger(__name__)
>>> logger.scary("Boo")
Boo| def _translate(self, record: logging.LogRecord) -> LogRecord: | ||
| timestamp = int(record.created * 1e9) | ||
| span_context = get_current_span().get_span_context() | ||
| # TODO: attributes (or resource attributes?) from record metadata |
There was a problem hiding this comment.
FYI, I believe attributes can be populated from both custom and resources.
There was a problem hiding this comment.
So logging.LogRecord https://docs.python.org/3/library/logging.html#logrecord-attributes has a number of attributes like funcName, filename, and processName etc.. I was wondering if we should just make these as OTLP log record attributes https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/data-model.md#field-attributes or log record resource attributes.
There was a problem hiding this comment.
For logging.LogRecord attributes, it makes more sense to be part of the OTLP log record attributes.
When I was saying "custom" attributes, I meant the ability for users to add custom attributes to the LogRecord themselves (it is a feature in OpenCensus as well), but may not be defined yet in the specs.
Not any concern for now, just something to be mindful of.
There was a problem hiding this comment.
logging.LogRecord has an (optional) extra attribute that holds arbitrary user-injected attributes.
This is not super well documented, but the docs can be found a couple paragraphs down from here.
From a users perspective, it looks like this:
logging.debug("msg", extra={...})Would it be possible to map this to OTLP log record attributes? You could take the top-level attributes of extra (which would be my preference), or require them to be nested under a specific key (e.g. logging.debug("msg", extra={"oltp-attributes": {...}})).
| self._log_emitter = log_emitter or get_log_emitter(__name__) | ||
|
|
||
| def _translate(self, record: logging.LogRecord) -> LogRecord: | ||
| timestamp = int(record.created * 1e9) |
There was a problem hiding this comment.
Just for your information, this PEP may be relevant here.
* Add initial overall structure and classes for logs sdk (#1894) * Add global LogEmitterProvider and convenience function get_log_emitter (#1901) * Add OTLPHandler for standard library logging module (#1903) * Add LogProcessors implementation (#1916) * Fix typos in test_handler.py (#1953) * Add support for OTLP Log exporter (#1943) * Add support for user defined attributes in OTLPHandler (#1952) * use timeout in force_flush (#2118) * use timeout in force_flush * fix lint * Update opentelemetry-sdk/src/opentelemetry/sdk/logs/export/__init__.py Co-authored-by: Srikanth Chekuri <srikanth.chekuri92@gmail.com> * fix lint Co-authored-by: Srikanth Chekuri <srikanth.chekuri92@gmail.com> * add a ConsoleExporter for logging (#2099) Co-authored-by: Srikanth Chekuri <srikanth.chekuri92@gmail.com> * Update SDK docs and Add example with OTEL collector logging (debug) exporter (#2050) * Fix exception in severity number transformation (#2208) * Fix exception with warning message transformation * Fix lint * Fix lint * fstring * Demonstrate how to set the Resource for LogEmitterProvider (#2209) * Demonstrate how to set the Resource for LogEmitterProvider Added a Resource to the logs example to make it more complete. Previously it was using the built-in Resource. Now it adds the service.name and service.instance.id attributes. The resulting emitted log records look like this: ``` Resource labels: -> telemetry.sdk.language: STRING(python) -> telemetry.sdk.name: STRING(opentelemetry) -> telemetry.sdk.version: STRING(1.5.0) -> service.name: STRING(shoppingcart) -> service.instance.id: STRING(instance-12) InstrumentationLibraryLogs #0 InstrumentationLibrary __main__ 0.1 LogRecord #0 Timestamp: 2021-10-14 18:33:43.425820928 +0000 UTC Severity: ERROR ShortName: Body: Hyderabad, we have a major problem. Trace ID: ce1577e4a703f42d569e72593ad71888 Span ID: f8908ac4258ceff6 Flags: 1 ``` * Fix linting * Use batch processor in example (#2225) * move logs to _logs (#2240) * move logs to _logs * fix lint * move log_exporter to _log_exporter as it's still experimental (#2252) Co-authored-by: Srikanth Chekuri <srikanth.chekuri92@gmail.com> Co-authored-by: Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com> Co-authored-by: Leighton Chen <lechen@microsoft.com> Co-authored-by: Tigran Najaryan <4194920+tigrannajaryan@users.noreply.github.com> Co-authored-by: Owais Lone <owais@users.noreply.github.com>
Description
Part-3 of #1890. Builds on top of #1901. This adds OTLPHandler for logging module. Python logging module provides a way to send the log records to the one or more appropriate destinations. More info about handler https://docs.python.org/3/library/logging.html#handler-objects