OpenTelemetry log handler and tracing plugin for simple_log_factory
Project description
simple-log-factory-ext-otel
OpenTelemetry log handler and tracing plugin for simple_log_factory.
Ship your log messages and traces to any OpenTelemetry-compatible backend (Tempo, Jaeger, Grafana Cloud, SigNoz, etc.) without changing any existing logging code.
Installation
pip install simple-log-factory-ext-otel
Or with UV:
uv add simple-log-factory-ext-otel
Quick Start
Pattern 1 — Logs Only
from simple_log_factory import log_factory
from simple_log_factory_ext_otel import OtelLogHandler
# 1. Initialize the handler
otel_handler = OtelLogHandler(
service_name="my-service",
endpoint="http://localhost:4317",
)
# 2. Pass it to log_factory — done
logger = log_factory(
__name__,
custom_handlers=[otel_handler],
)
logger.info("This goes to console AND to your OTel backend")
# 3. Clean shutdown (optional but recommended)
otel_handler.shutdown()
Pattern 2 — All-in-One with otel_log_factory()
from simple_log_factory_ext_otel import otel_log_factory
# One call — creates logger, OTel handler, and tracer
traced = otel_log_factory(
service_name="my-service",
otel_exporter_endpoint="http://localhost:4318",
)
# Logging works immediately
traced.info("Service started")
# Spans are ready too
with traced.span("process-order", attributes={"order.id": "123"}):
traced.info("Processing order")
# Subsequent calls with the same endpoint/service/log_name return the
# cached instance (cache_logger=True by default)
same_traced = otel_log_factory(
service_name="my-service",
otel_exporter_endpoint="http://localhost:4318",
)
assert same_traced is traced
# Different service names or endpoints get independent loggers
payments_traced = otel_log_factory(
service_name="payment-service",
otel_exporter_endpoint="http://localhost:4318",
)
Pattern 3 — Logs + Tracing with setup_otel()
from simple_log_factory import log_factory
from simple_log_factory_ext_otel import setup_otel, TracedLogger
# 1. One-call setup — creates both log handler and tracer
# with a shared Resource; registers TracerProvider globally
handler, otel_tracer = setup_otel(
service_name="my-service",
endpoint="http://localhost:4317",
)
# 2. Create a logger with the OTel handler
logger = log_factory(__name__, custom_handlers=[handler])
# 3. Wrap with TracedLogger for span support
traced = TracedLogger(logger=logger, tracer=otel_tracer.tracer)
# 4. Use span() to create correlated spans
with traced.span("process-order", attributes={"order.id": "123"}):
traced.info("Processing order") # auto-correlated with the span
# ... your business logic ...
# 5. Or use @trace() as a decorator
@traced.trace("fetch-user")
def fetch_user(user_id: int) -> dict:
traced.info("Fetching user %d", user_id)
return {"id": user_id, "name": "Alice"}
fetch_user(42)
Configuration
OtelLogHandler Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
service_name |
str |
(required) | Logical name of the service emitting logs |
endpoint |
str |
http://localhost:4317 |
OTLP receiver endpoint |
protocol |
str |
"grpc" |
Transport protocol — "grpc" or "http" |
insecure |
bool |
True |
Use plaintext (insecure) connection |
headers |
Dict[str, str] |
None |
Metadata headers sent with every export request |
resource_attributes |
Dict[str, str] |
None |
Extra OTel Resource attributes |
log_level |
int |
logging.NOTSET |
Minimum severity forwarded to the OTel pipeline |
export_timeout_millis |
int |
30000 |
Timeout in ms for each export batch |
Using HTTP Instead of gRPC
from simple_log_factory_ext_otel import OtelLogHandler
handler = OtelLogHandler(
service_name="my-service",
endpoint="http://localhost:4318/v1/logs",
protocol="http",
)
Custom Resource Attributes
from simple_log_factory_ext_otel import OtelLogHandler
handler = OtelLogHandler(
service_name="my-service",
resource_attributes={
"deployment.environment": "production",
"service.version": "1.2.3",
},
)
Authenticated Endpoints
from simple_log_factory_ext_otel import OtelLogHandler
handler = OtelLogHandler(
service_name="my-service",
endpoint="https://otel.example.com:4317",
insecure=False,
headers={"Authorization": "Bearer <token>"},
)
Level Filtering
Only export warnings and above to your OTel backend:
import logging
from simple_log_factory_ext_otel import OtelLogHandler
handler = OtelLogHandler(
service_name="my-service",
log_level=logging.WARNING,
)
TracedLogger API
TracedLogger wraps a standard logging.Logger and an OTel Tracer:
span(name, attributes)— context manager that creates an OTel span. Logs inside the block are auto-correlated.trace(name, attributes)— decorator that wraps a function in a span. Exceptions are recorded on the span and re-raised.debug/info/warning/error/exception/critical/log— proxy to the underlying logger.logger/tracerproperties — escape hatches for direct access.
setup_otel() Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
service_name |
str |
(required) | Logical name of the service |
endpoint |
str |
http://localhost:4317 |
OTLP receiver endpoint |
protocol |
str |
"grpc" |
Transport protocol — "grpc" or "http" |
insecure |
bool |
True |
Use plaintext (insecure) connection |
headers |
Dict[str, str] |
None |
Metadata headers sent with every export request |
resource_attributes |
Dict[str, str] |
None |
Extra OTel Resource attributes |
export_timeout_millis |
int |
30000 |
Timeout in ms for each export batch |
log_level |
int |
logging.NOTSET |
Minimum severity forwarded to the OTel pipeline |
Returns a (OtelLogHandler, OtelTracer) tuple. The TracerProvider is registered globally so auto-instrumentation libraries (FastAPI, psycopg2, etc.) share the same provider.
otel_log_factory() Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
service_name |
str |
(required) | Logical name of the service |
otel_exporter_endpoint |
str |
(required) | Base URL of the OTel collector (e.g. http://localhost:4318) |
log_name |
str |
service_name |
Name passed to log_factory. Defaults to service_name when None |
cache_logger |
bool |
True |
Cache and reuse the logger for the same endpoint/service/log-name combo |
use_http_protocol |
bool |
True |
True for HTTP (appends /v1/logs and /v1/traces), False for gRPC |
**kwargs |
Extra keyword arguments forwarded to simple_log_factory.log_factory |
Returns a TracedLogger with both logging and tracing configured. The TracerProvider is registered globally. Loggers are cached by the composite key (otel_exporter_endpoint, service_name, log_name), so different services or endpoints get independent loggers.
How It Works
OtelLogHandler extends logging.Handler directly (not OTel's LoggingHandler) and internally composes the OTel pipeline:
Your code
└─► log_factory (attaches handler, sets formatter & level)
└─► OtelLogHandler.emit(record)
└─► Internal OTel LoggingHandler (LogRecord → OTel translation)
└─► BatchLogRecordProcessor
└─► OTLPLogExporter (gRPC or HTTP)
└─► OTel Collector / Backend
This composition pattern is critical: log_factory calls setFormatter() and setLevel() on every handler it receives. If we inherited from OTel's LoggingHandler, the factory's formatter would overwrite OTel's internal translation. By wrapping it, both pipelines stay independent.
Trace Context Correlation
When using setup_otel() or sharing a Resource between OtelLogHandler and OtelTracer, span and trace IDs are automatically attached to log records emitted inside active spans. No extra configuration needed.
Lifecycle Management
The handler registers an atexit hook to flush and shut down on interpreter exit. For explicit control:
# Force-flush buffered records
otel_handler.flush()
# Graceful shutdown (idempotent, safe to call multiple times)
otel_handler.shutdown()
Local Development
Create a virtual environment with UV:
uv venv
uv pip install -e ".[dev]"
Run tests:
pytest --cov=simple_log_factory_ext_otel --cov-report=term-missing
Run linters:
black --check .
isort --check .
ruff check .
mypy simple_log_factory_ext_otel
License
GPL-3.0 — see LICENSE.md for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file simple_log_factory_ext_otel-1.3.0.tar.gz.
File metadata
- Download URL: simple_log_factory_ext_otel-1.3.0.tar.gz
- Upload date:
- Size: 31.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cf2a2d9ac238e195cf3fdfe5d6db0f00294a1eee61c76d173005e31a129480a6
|
|
| MD5 |
c37d43550d2466298a1cf6a20968b710
|
|
| BLAKE2b-256 |
85e20db51f4a54bfb76416197efacc8ffe211479cbaaa91e88d64b767d7272c2
|
Provenance
The following attestation bundles were made for simple_log_factory_ext_otel-1.3.0.tar.gz:
Publisher:
publish.yml on brenordv/log-factory-package-ext-otel
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
simple_log_factory_ext_otel-1.3.0.tar.gz -
Subject digest:
cf2a2d9ac238e195cf3fdfe5d6db0f00294a1eee61c76d173005e31a129480a6 - Sigstore transparency entry: 953409679
- Sigstore integration time:
-
Permalink:
brenordv/log-factory-package-ext-otel@223e0be5c18a3f22ec402c7e011e68b314895b47 -
Branch / Tag:
refs/heads/master - Owner: https://github.com/brenordv
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@223e0be5c18a3f22ec402c7e011e68b314895b47 -
Trigger Event:
push
-
Statement type:
File details
Details for the file simple_log_factory_ext_otel-1.3.0-py3-none-any.whl.
File metadata
- Download URL: simple_log_factory_ext_otel-1.3.0-py3-none-any.whl
- Upload date:
- Size: 26.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
97be5f3c00f41f473db667d4f1fc57685cfe59ba237dc7a45caceaa6b55a679b
|
|
| MD5 |
705c68eea5f1bbad179ee3e427bf5e0f
|
|
| BLAKE2b-256 |
3b03d1a5688540d74df2d7c6269b1c0021ab861301fdd5273c11da555e46280c
|
Provenance
The following attestation bundles were made for simple_log_factory_ext_otel-1.3.0-py3-none-any.whl:
Publisher:
publish.yml on brenordv/log-factory-package-ext-otel
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
simple_log_factory_ext_otel-1.3.0-py3-none-any.whl -
Subject digest:
97be5f3c00f41f473db667d4f1fc57685cfe59ba237dc7a45caceaa6b55a679b - Sigstore transparency entry: 953409683
- Sigstore integration time:
-
Permalink:
brenordv/log-factory-package-ext-otel@223e0be5c18a3f22ec402c7e011e68b314895b47 -
Branch / Tag:
refs/heads/master - Owner: https://github.com/brenordv
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@223e0be5c18a3f22ec402c7e011e68b314895b47 -
Trigger Event:
push
-
Statement type: