Index
๐ค AI-Generated Content
This documentation was generated with AI assistance and is still being audited. Some, or potentially a lot, of this information may be inaccurate. Learn more.
provide.foundation.logger.otlp
¶
Generic OTLP (OpenTelemetry Protocol) support for Foundation logger.
This package provides generic OpenTelemetry Protocol support that can be used with any OTLP-compatible backend (OpenObserve, Datadog, Honeycomb, etc.).
Key Components: - OTLPLogClient: Generic client for sending logs via OTLP - OTLPCircuitBreaker: Reliability pattern for handling endpoint failures - Helper functions for resource creation, trace context, severity mapping
Example
from provide.foundation.logger.otlp import OTLPLogClient
client = OTLPLogClient( ... endpoint="https://api.example.com/v1/logs", ... headers={"Authorization": "Bearer token"}, ... service_name="my-service", ... )
client.send_log("Hello from OTLP", level="INFO")
Classes¶
OTLPCircuitBreaker
¶
OTLPCircuitBreaker(
failure_threshold: int = 5,
timeout: float = 60.0,
half_open_timeout: float = 10.0,
)
Circuit breaker for OTLP connections with exponential backoff.
States
- closed: Normal operation, requests allowed
- open: Too many failures, requests blocked
- half_open: Testing if service recovered
Examples:
>>> breaker = OTLPCircuitBreaker(failure_threshold=3, timeout=60.0)
>>> if breaker.can_attempt():
... success = send_otlp_log()
... if success:
... breaker.record_success()
... else:
... breaker.record_failure()
Initialize circuit breaker.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
failure_threshold
|
int
|
Number of failures before opening circuit |
5
|
timeout
|
float
|
Seconds to wait before attempting half-open (doubles each time) |
60.0
|
half_open_timeout
|
float
|
Seconds to wait in half-open before trying again |
10.0
|
Source code in provide/foundation/logger/otlp/circuit.py
Attributes¶
Functions¶
can_attempt
¶
Check if we can attempt an OTLP operation.
Returns:
| Type | Description |
|---|---|
bool
|
True if operation should be attempted, False if circuit is open |
Source code in provide/foundation/logger/otlp/circuit.py
get_stats
¶
Get circuit breaker statistics.
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Dictionary with current state and statistics |
Source code in provide/foundation/logger/otlp/circuit.py
record_failure
¶
Record a failed operation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
error
|
Exception | None
|
Optional exception that caused the failure |
None
|
Source code in provide/foundation/logger/otlp/circuit.py
record_success
¶
Record a successful operation.
Source code in provide/foundation/logger/otlp/circuit.py
reset
¶
Manually reset the circuit breaker to closed state.
Source code in provide/foundation/logger/otlp/circuit.py
OTLPLogClient
¶
OTLPLogClient(
endpoint: str,
headers: dict[str, str] | None = None,
service_name: str = "foundation",
service_version: str | None = None,
environment: str | None = None,
timeout: float = 30.0,
use_circuit_breaker: bool = True,
)
Generic OTLP client for any OpenTelemetry-compatible backend.
This client works with any OTLP-compatible backend and provides: - Single log sending with automatic flushing - Persistent LoggerProvider for continuous logging - Circuit breaker pattern for reliability - Automatic trace context extraction - Attribute normalization for OTLP compatibility
Examples:
>>> client = OTLPLogClient(
... endpoint="https://api.honeycomb.io/v1/logs",
... headers={"x-honeycomb-team": "YOUR_API_KEY"},
... service_name="my-service",
... )
>>> client.send_log("Hello OTLP!", level="INFO")
True
>>> # Use with persistent logger provider
>>> provider = client.create_logger_provider()
>>> # Configure structlog to use provider
Initialize OTLP client.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
endpoint
|
str
|
OTLP endpoint URL (e.g., "https://api.example.com/v1/logs") |
required |
headers
|
dict[str, str] | None
|
Optional custom headers (auth, organization, etc.) |
None
|
service_name
|
str
|
Service name for resource attributes |
'foundation'
|
service_version
|
str | None
|
Optional service version |
None
|
environment
|
str | None
|
Optional environment (dev, staging, prod) |
None
|
timeout
|
float
|
Request timeout in seconds |
30.0
|
use_circuit_breaker
|
bool
|
Enable circuit breaker pattern |
True
|
Source code in provide/foundation/logger/otlp/client.py
Functions¶
create_logger_provider
¶
Create persistent LoggerProvider for continuous logging.
Returns:
| Type | Description |
|---|---|
Any | None
|
LoggerProvider if OpenTelemetry SDK available, None otherwise |
Use this for long-running applications that need persistent OTLP logging. The provider can be used with structlog processors for automatic OTLP export.
Circuit breaker: - Returns None if circuit is open - Records success if provider created - Records failure if exception occurs
Examples:
>>> provider = client.create_logger_provider()
>>> if provider:
... # Configure structlog with provider
... pass
Source code in provide/foundation/logger/otlp/client.py
from_config
classmethod
¶
Create client from TelemetryConfig.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
Any
|
TelemetryConfig instance |
required |
additional_headers
|
dict[str, str] | None
|
Additional headers to merge with config headers |
None
|
Returns:
| Type | Description |
|---|---|
OTLPLogClient
|
Configured OTLPLogClient instance |
Raises:
| Type | Description |
|---|---|
ValueError
|
If config.otlp_endpoint is not set |
Examples:
>>> from provide.foundation.logger.config.telemetry import TelemetryConfig
>>> config = TelemetryConfig.from_env()
>>> client = OTLPLogClient.from_config(config)
Source code in provide/foundation/logger/otlp/client.py
get_stats
¶
Get client statistics including circuit breaker state.
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Dictionary with client and circuit breaker statistics |
Examples:
Source code in provide/foundation/logger/otlp/client.py
is_available
¶
Check if OTLP is available (SDK installed and circuit not open).
Returns:
| Type | Description |
|---|---|
bool
|
True if OTLP is available and circuit is closed |
Examples:
Source code in provide/foundation/logger/otlp/client.py
send_log
¶
Send single log via OTLP.
Creates a temporary LoggerProvider, sends the log, and flushes immediately. This ensures delivery for single log sends but is less efficient for bulk logging.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
message
|
str
|
Log message |
required |
level
|
str
|
Log level (DEBUG, INFO, WARN, ERROR, FATAL) |
'INFO'
|
attributes
|
dict[str, Any] | None
|
Optional log attributes |
None
|
Returns:
| Type | Description |
|---|---|
bool
|
True if sent successfully, False otherwise |
Circuit breaker pattern: - Checks circuit before attempting - Records success/failure - Automatically disables after threshold failures - Auto-recovers with exponential backoff
Examples:
Source code in provide/foundation/logger/otlp/client.py
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 | |
Functions¶
add_trace_context_to_attributes
¶
Add trace context to attributes dict (modifies in place).
Extracts trace context and adds trace_id/span_id to attributes. Safe to call even if no trace context is available (no-op).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
attributes
|
dict[str, Any]
|
Dictionary to add trace context to (modified in place) |
required |
Examples:
>>> attrs = {"key": "value"}
>>> add_trace_context_to_attributes(attrs)
>>> # attrs may now include 'trace_id' and 'span_id' if context available
Source code in provide/foundation/logger/otlp/helpers.py
build_otlp_endpoint
¶
Build OTLP endpoint URL for specific signal type.
Constructs the full OTLP endpoint URL for the given signal type. Handles trailing slashes and is idempotent (won't double-add paths).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
base_endpoint
|
str
|
Base OTLP endpoint (e.g., "https://api.example.com") |
required |
signal_type
|
str
|
"logs", "traces", or "metrics" |
'logs'
|
Returns:
| Type | Description |
|---|---|
str
|
Full endpoint URL (e.g., "https://api.example.com/v1/logs") |
Examples:
Source code in provide/foundation/logger/otlp/helpers.py
build_otlp_headers
¶
build_otlp_headers(
base_headers: dict[str, str] | None = None,
auth_token: str | None = None,
) -> dict[str, str]
Build OTLP headers with optional authentication.
Creates headers dictionary with OTLP-required headers and optional auth.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
base_headers
|
dict[str, str] | None
|
Base headers to include |
None
|
auth_token
|
str | None
|
Optional bearer token for authentication |
None
|
Returns:
| Type | Description |
|---|---|
dict[str, str]
|
Complete headers dict with Content-Type and auth |
Examples:
>>> build_otlp_headers(auth_token="secret123")
{'Content-Type': 'application/x-protobuf', 'Authorization': 'Bearer secret123'}
>>> build_otlp_headers(base_headers={"X-Custom": "value"})
{'X-Custom': 'value', 'Content-Type': 'application/x-protobuf'}
Source code in provide/foundation/logger/otlp/helpers.py
build_resource_attributes
¶
build_resource_attributes(
service_name: str,
service_version: str | None = None,
environment: str | None = None,
additional_attrs: dict[str, Any] | None = None,
) -> dict[str, Any]
Build resource attributes dictionary.
Creates a dictionary with standard OpenTelemetry resource attributes: - service.name (required) - service.version (optional) - deployment.environment (optional) - Any additional custom attributes
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
service_name
|
str
|
Service name (required) |
required |
service_version
|
str | None
|
Service version (optional) |
None
|
environment
|
str | None
|
Deployment environment (dev, staging, prod, etc.) |
None
|
additional_attrs
|
dict[str, Any] | None
|
Additional custom resource attributes |
None
|
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Dictionary of resource attributes |
Examples:
>>> build_resource_attributes(
... "my-service",
... service_version="1.2.3",
... environment="production",
... )
{'service.name': 'my-service', 'service.version': '1.2.3', 'deployment.environment': 'production'}
Source code in provide/foundation/logger/otlp/resource.py
create_otlp_resource
¶
create_otlp_resource(
service_name: str,
service_version: str | None = None,
environment: str | None = None,
additional_attrs: dict[str, Any] | None = None,
) -> Any | None
Create OpenTelemetry Resource instance.
Attempts to create an OpenTelemetry SDK Resource with the provided attributes. Returns None if the OpenTelemetry SDK is not available (optional dependency).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
service_name
|
str
|
Service name (required) |
required |
service_version
|
str | None
|
Service version (optional) |
None
|
environment
|
str | None
|
Deployment environment (optional) |
None
|
additional_attrs
|
dict[str, Any] | None
|
Additional custom resource attributes |
None
|
Returns:
| Type | Description |
|---|---|
Any | None
|
Resource instance if OpenTelemetry SDK available, None otherwise |
Examples:
>>> resource = create_otlp_resource("my-service", service_version="1.0.0")
>>> # Returns Resource instance or None if SDK not installed
>>> resource = create_otlp_resource(
... "my-service",
... environment="production",
... additional_attrs={"team": "platform"},
... )
Source code in provide/foundation/logger/otlp/resource.py
extract_trace_context
¶
Extract current trace context from OpenTelemetry.
Extracts trace context from OpenTelemetry if SDK is available and a valid span is recording.
Returns:
| Type | Description |
|---|---|
dict[str, str] | None
|
Dict with 'trace_id' and 'span_id', or None if not available |
Examples:
Source code in provide/foundation/logger/otlp/helpers.py
get_otlp_circuit_breaker
¶
Get the global OTLP circuit breaker instance.
Returns:
| Type | Description |
|---|---|
OTLPCircuitBreaker
|
Shared OTLPCircuitBreaker instance |
Source code in provide/foundation/logger/otlp/circuit.py
map_level_to_severity
¶
Map log level string to OTLP severity number.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
level
|
str
|
Log level string (e.g., "INFO", "ERROR", "WARN") |
required |
Returns:
| Type | Description |
|---|---|
int
|
OTLP severity number (1-24) |
int
|
Falls back to 9 (INFO) for unknown levels |
Examples:
>>> map_level_to_severity("INFO")
9
>>> map_level_to_severity("ERROR")
17
>>> map_level_to_severity("warning")
13
>>> map_level_to_severity("unknown")
9
Source code in provide/foundation/logger/otlp/severity.py
map_severity_to_level
¶
Map OTLP severity number to log level string.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
severity
|
int
|
OTLP severity number (1-24) |
required |
Returns:
| Type | Description |
|---|---|
str
|
Log level string (TRACE, DEBUG, INFO, WARN, ERROR, FATAL) |
str
|
Falls back to "INFO" for unknown severity numbers |
Examples:
>>> map_severity_to_level(9)
'INFO'
>>> map_severity_to_level(17)
'ERROR'
>>> map_severity_to_level(100)
'INFO'
Source code in provide/foundation/logger/otlp/severity.py
normalize_attributes
¶
Normalize attribute values for OTLP compatibility.
Converts non-serializable types to OTLP-compatible values: - Non-serializable types โ strings - Nested dicts โ JSON strings - Lists โ JSON strings - None values โ empty strings
Returns new dict (doesn't modify input).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
attributes
|
dict[str, Any]
|
Dictionary of attributes to normalize |
required |
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
New dictionary with normalized values |
Examples:
Source code in provide/foundation/logger/otlp/helpers.py
reset_otlp_circuit_breaker
¶
Reset the global circuit breaker (primarily for testing).