OpenTelemetry Logging Instrumentation
raw JSON → 0.61b0 verified Tue May 12 auth: no python install: verified
The `opentelemetry-instrumentation-logging` library provides automatic instrumentation for Python's standard `logging` module. It converts native Python log messages into OpenTelemetry logs, enabling correlation with traces and metrics, and facilitates their export to an observability backend. Part of the `opentelemetry-python-contrib` project, this library is currently in beta (version 0.61b0) and undergoes regular releases as part of the broader OpenTelemetry Python ecosystem.
pip install opentelemetry-instrumentation-logging opentelemetry-sdk opentelemetry-exporter-otlp cli
opentelemetry-instrument Common errors
error ModuleNotFoundError: No module named 'opentelemetry.instrumentation.logging' ↓
cause This error occurs when the `opentelemetry-instrumentation-logging` package or its dependencies are not installed, or the Python environment where the application is run does not have access to the installed package.
fix
Ensure the library is installed in the correct Python environment:
pip install opentelemetry-instrumentation-logging error AttributeError: 'ProxyTracerProvider' object has no attribute 'resource' ↓
cause This error typically arises when an instrumentation attempts to access the `resource` attribute of the global `TracerProvider` (or `LoggerProvider`) before a concrete SDK provider (e.g., `TracerProvider(resource=...)`) has been set globally using `trace.set_tracer_provider()` or `set_logger_provider()`.
fix
Initialize and set the
TracerProvider and LoggerProvider with a Resource instance at the very beginning of your application's lifecycle, before any instrumentation or logging calls. For example:
from opentelemetry import trace
from opentelemetry._logs import set_logger_provider
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk._logs import LoggerProvider
from opentelemetry.sdk.resources import Resource
# Configure resource
resource = Resource(attributes={
"service.name": "my-python-app",
"service.version": "1.0.0"
})
# Set global TracerProvider
tracer_provider = TracerProvider(resource=resource)
trace.set_tracer_provider(tracer_provider)
# Set global LoggerProvider
logger_provider = LoggerProvider(resource=resource)
set_logger_provider(logger_provider)
# Now instrument logging
from opentelemetry.instrumentation.logging import LoggingInstrumentor
LoggingInstrumentor().instrument() error OpenTelemetry logs not appearing in console or backend ↓
cause Logs may not appear due to several reasons, including the Python root logger's level being set too high, an OpenTelemetry log exporter not being configured or started, incorrect initialization order where `logging.basicConfig()` overrides the OpenTelemetry handler, or the `OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED` environment variable not being set to `true` when using auto-instrumentation.
fix
1. Ensure a
LoggerProvider and a LogRecordProcessor with an OTLPLogExporter (or ConsoleLogRecordExporter for local debugging) are configured and started.
2. Set the Python root logger's level to logging.NOTSET or a sufficiently low level to capture all desired messages. Call logging.basicConfig(level=logging.NOTSET, handlers=[LoggingHandler(logger_provider=logger_provider)]) *after* setting the LoggerProvider.
3. If using auto-instrumentation, ensure OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED=true is set as an environment variable or passed to LoggingInstrumentor().instrument().
Example setup:
import logging
import os
from opentelemetry._logs import set_logger_provider
from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor, ConsoleLogRecordExporter # or OTLPLogExporter
from opentelemetry.sdk.resources import Resource
from opentelemetry.instrumentation.logging import LoggingInstrumentor
resource = Resource(attributes={
"service.name": "my-app-logs"
})
logger_provider = LoggerProvider(resource=resource)
# Use ConsoleLogRecordExporter for debugging, OTLPLogExporter for sending to a collector
log_exporter = ConsoleLogRecordExporter() # or OTLPLogExporter()
processor = BatchLogRecordProcessor(log_exporter)
logger_provider.add_log_record_processor(processor)
set_logger_provider(logger_provider)
# Instrument the standard logging module
LoggingInstrumentor().instrument(set_logging_format=True)
# Configure the root logger to use the OpenTelemetry handler
# Ensure its level is low enough to capture desired logs
logging.basicConfig(level=logging.INFO, handlers=[LoggingHandler(logger_provider=logger_provider)])
logger = logging.getLogger(__name__)
logger.info("This log should appear in OpenTelemetry.") error KeyError: 'otelTraceID' ↓
cause This error occurs when the logging format string includes placeholders like `%(otelTraceID)s` or `%(otelSpanID)s`, but the `opentelemetry-instrumentation-logging` has not yet injected these attributes into the log record, often because `logging.basicConfig()` (or a similar manual logger configuration) was called *before* the `LoggingInstrumentor` was enabled.
fix
Ensure that the
LoggingInstrumentor().instrument() call (or setting OTEL_PYTHON_LOG_CORRELATION=true for auto-instrumentation) happens *before* any calls to logging.basicConfig() or any custom formatter that attempts to access otelTraceID or otelSpanID. The LoggingInstrumentor registers a custom log record factory that adds these fields. If set_logging_format=True is passed to instrument(), it will handle basicConfig() for you.
import logging
from opentelemetry.instrumentation.logging import LoggingInstrumentor
from opentelemetry.sdk._logs import LoggerProvider # assuming LoggerProvider is set up globally
from opentelemetry.sdk.resources import Resource
from opentelemetry._logs import set_logger_provider
# ... (setup resource and logger_provider as in previous example)
resource = Resource(attributes={"service.name": "my-app"})
logger_provider = LoggerProvider(resource=resource)
set_logger_provider(logger_provider)
# Instrument logging FIRST, and let it handle basicConfig if desired
LoggingInstrumentor().instrument(set_logging_format=True) # This will call basicConfig with trace context format
# If you need a custom format, enable instrumentation first, then configure basicConfig without trace_id/span_id fields in format string or provide them via custom formatter after instrumentation is active.
# Example with custom format, ensuring instrumentation runs first
# LoggingInstrumentor().instrument()
# logging.basicConfig(format='%(asctime)s [%(levelname)s] [%(name)s] - %(message)s', level=logging.INFO)
logger = logging.getLogger(__name__)
logger.info("Log with trace context.") Warnings
breaking The library is currently in beta (`0.x.y` versioning). While stable for many use cases, its API is subject to change, and breaking changes may occur in minor or patch releases. ↓
fix Review release notes for each update. Pin dependencies to specific versions (`==0.x.y`) in production to control updates.
gotcha Calling `LoggingInstrumentor().uninstrument()` can corrupt the standard Python `logging` module's log factory linked list, potentially breaking other logging handlers or custom log record factories that were set after `LoggingInstrumentor`. ↓
fix Avoid calling `uninstrument()` if possible. If dynamic instrumentation/uninstrumentation is strictly necessary, carefully evaluate its impact on other logging components and consider alternative approaches like logging filters or custom loggers that do not rely on modifying the global log factory. Refer to GitHub Issue #3808 for context.
gotcha If you manually call `logging.basicConfig()` and are not relying on the `OTEL_PYTHON_LOG_CORRELATION` environment variable to configure the logging format, you *must* ensure `LoggingInstrumentor().instrument()` is called *before* `logging.basicConfig()` to avoid `KeyError` exceptions when attempting to inject trace context variables. ↓
fix Ensure `LoggingInstrumentor().instrument(set_logging_format=True)` is called early in your application's startup, prior to any custom `logging.basicConfig()` calls that use trace context variables (e.g., `%(otelTraceID)s`).
deprecated This package provides a logging handler to replace a deprecated `LoggingHandler` previously available in `opentelemetry-sdk`. If `opentelemetry-instrumentation-logging` is installed, you should *not* set the `OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED` environment variable to `true`, as this variable is intended for the *deprecated* SDK handler and can cause confusion or conflicts. ↓
fix Install `opentelemetry-instrumentation-logging` and call `LoggingInstrumentor().instrument()` (or rely on auto-instrumentation). Avoid using the `OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED` environment variable if you are using this package.
gotcha Simply installing `opentelemetry-instrumentation-logging` and calling `instrument()` is not sufficient to export logs. You must also configure a `LoggerProvider`, add a `LogRecordProcessor`, and specify a `LogExporter` (e.g., `OTLPLogExporter`) to direct your OpenTelemetry logs to an observability backend. ↓
fix Always follow the OpenTelemetry logging setup by creating a `Resource`, `LoggerProvider`, `LogRecordProcessor`, and `LogExporter`, and setting the global logger provider via `set_logger_provider()`.
Install compatibility verified last tested: 2026-05-12 v0.62b1 (up to date)
python os / libc status wheel install import disk mem side effects
3.10 alpine (musl) wheel - 0.32s 52.0M 9.5M clean
3.10 alpine (musl) - - 0.34s 51.8M 9.4M -
3.10 slim (glibc) wheel 5.6s 0.23s 50M 9.5M clean
3.10 slim (glibc) - - 0.26s 50M 9.4M -
3.11 alpine (musl) wheel - 0.46s 55.5M 10.8M clean
3.11 alpine (musl) - - 0.56s 55.3M 10.7M -
3.11 slim (glibc) wheel 4.8s 0.42s 53M 10.8M clean
3.11 slim (glibc) - - 0.45s 53M 10.7M -
3.12 alpine (musl) wheel - 0.59s 47.1M 10.8M clean
3.12 alpine (musl) - - 0.67s 46.8M 10.8M -
3.12 slim (glibc) wheel 4.0s 0.62s 45M 10.8M clean
3.12 slim (glibc) - - 0.66s 45M 10.8M -
3.13 alpine (musl) wheel - 0.29s 46.8M 8.3M clean
3.13 alpine (musl) - - 0.32s 46.5M 8.2M -
3.13 slim (glibc) wheel 4.0s 0.30s 45M 8.3M clean
3.13 slim (glibc) - - 0.33s 44M 8.2M -
3.9 alpine (musl) wheel - 0.26s 51.3M 8.7M clean
3.9 alpine (musl) - - 0.29s 51.1M 8.6M -
3.9 slim (glibc) wheel 6.4s 0.25s 49M 8.7M clean
3.9 slim (glibc) - - 0.27s 49M 8.6M -
Imports
- LoggingInstrumentor
from opentelemetry.instrumentation.logging import LoggingInstrumentor - LoggerProvider
from opentelemetry.sdk._logs import LoggerProvider - BatchLogRecordProcessor
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor - OTLPLogExporter
from opentelemetry.exporter.otlp.proto.grpc._log_exporter import OTLPLogExporter
Quickstart last tested: 2026-04-24
import logging
from opentelemetry import trace
from opentelemetry._logs import set_logger_provider
from opentelemetry.sdk._logs import LoggerProvider
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor, ConsoleLogRecordExporter
from opentelemetry.sdk.resources import Resource
from opentelemetry.instrumentation.logging import LoggingInstrumentor
# Configure a basic logger
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# 1. Configure OpenTelemetry Resource
resource = Resource.create({
"service.name": "my-python-logging-app",
"service.instance.id": "instance-1"
})
# 2. Configure LoggerProvider with a processor and exporter
# For demonstration, using ConsoleLogRecordExporter to print to stdout
# In a real application, you'd use OTLPLogExporter or another backend exporter.
log_exporter = ConsoleLogRecordExporter()
log_processor = BatchLogRecordProcessor(log_exporter)
logger_provider = LoggerProvider(resource=resource)
logger_provider.add_log_record_processor(log_processor)
set_logger_provider(logger_provider)
# 3. Instrument the standard logging module
# set_logging_format=True enables trace context injection into log format
LoggingInstrumentor().instrument(set_logging_format=True)
# 4. Use standard logging, which will now be captured by OpenTelemetry
logger.info("This is a regular Python log message.")
# 5. Demonstrate log correlation with an active trace span
tracer = trace.get_tracer("my-tracer")
with tracer.start_as_current_span("my-operation") as span:
span.set_attribute("operation.id", "xyz123")
logger.warning("This log message is inside a span, so it should be correlated.")
# 6. Shut down the logger provider to ensure all logs are exported
logger_provider.shutdown()
print("Logs sent to console (or configured exporter).")