Python Integration Guide
Send logs from any Python application to logging.cheap using the OpenTelemetry SDK.
This guide bridges Python's built-in logging module to OpenTelemetry (OTLP),
so every logging.info() call automatically exports logs to logging.cheap.
How it works:
Your app → Python logging → LoggingHandler bridge → OTLP over HTTP → logging.cheap.
No agents to install, no collectors to manage, no YAML to configure.
Prerequisites
- Python 3.8 or later (3.10+ recommended)
- A logging.cheap account
Get an API Key
Create an API key from your Settings page.
Copy the key — it starts with cl_live_ and is only shown once.
If you just signed up, an API key prompt appears on your first login.
Install Dependencies
You need three packages: the OpenTelemetry API, the SDK, and the OTLP (OpenTelemetry Protocol) HTTP exporter. We recommend using a virtual environment to isolate dependencies:
python -m venv .venv && source .venv/bin/activate
Then install the packages:
pip install opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp-proto-http
The opentelemetry-exporter-otlp-proto-http package sends log data
over HTTP, which logging.cheap accepts natively.
Set Environment Variables
The OpenTelemetry SDK reads its configuration from standard
OTEL_* environment variables — no hardcoded endpoints or secrets in your code.
Set these before running your application:
# Required: where to send logs
export OTEL_EXPORTER_OTLP_ENDPOINT="https://logging.cheap"
# Required: authenticate with your API key
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer cl_live_YOUR_API_KEY"
# Required: name your service (appears in the log viewer)
export OTEL_SERVICE_NAME="my-python-app"
# Recommended: disable traces and metrics (logging.cheap only accepts logs)
export OTEL_TRACES_EXPORTER="none"
export OTEL_METRICS_EXPORTER="none"
Replace cl_live_YOUR_API_KEY with the key you copied earlier, and
my-python-app with a name that identifies your application in the log viewer
(e.g. api-server, checkout-service).
Make sure to include the Bearer prefix — without it, authentication will fail.
Initialize OpenTelemetry Logging
Create a file called app.py. The OTLP exporter reads configuration
from the environment variables above:
import logging
from opentelemetry._logs import set_logger_provider
from opentelemetry.exporter.otlp.proto.http._log_exporter import OTLPLogExporter
from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
from opentelemetry.sdk.resources import Resource
# 1. Create a resource describing your service
# (reads OTEL_SERVICE_NAME from the environment automatically)
resource = Resource.create()
# 2. Set up the logger provider with an OTLP exporter
logger_provider = LoggerProvider(resource=resource)
logger_provider.add_log_record_processor(
BatchLogRecordProcessor(OTLPLogExporter())
)
set_logger_provider(logger_provider)
# 3. Bridge Python's logging module to OpenTelemetry
handler = LoggingHandler(logger_provider=logger_provider)
logging.getLogger().addHandler(handler)
logging.getLogger().setLevel(logging.INFO)
# 4. Use Python's standard logging — it's automatically exported!
logging.info("Hello from logging.cheap!", extra={
"environment": "production",
"version": "1.0.0",
})
# 5. Flush logs before exiting (important for short-lived scripts)
logger_provider.shutdown()
Run It
python app.py
If everything is configured correctly, the script will run without errors.
Behind the scenes, the BatchLogRecordProcessor batches your log
and sends it to https://logging.cheap/v1/logs with your API key.
Understanding the Code
Here's what each piece does, if you're new to OpenTelemetry:
Resource.create()— creates metadata about your application. It automatically reads theOTEL_SERVICE_NAMEenvironment variable, which shows up as the service name in your log viewer.LoggerProvider— the central object that manages log processing. You attach exporters to it (in this case, the OTLP HTTP exporter).BatchLogRecordProcessor— collects log records and sends them in batches rather than one at a time. This is more efficient and reduces network overhead.OTLPLogExporter()— sends logs over HTTP using Protobuf encoding. It readsOTEL_EXPORTER_OTLP_ENDPOINTandOTEL_EXPORTER_OTLP_HEADERSfrom the environment automatically.LoggingHandler— a bridge between Python's built-inloggingmodule and OpenTelemetry. Once attached, everylogging.info(),logging.error(), etc. becomes an OpenTelemetry log record.
Note on _logs imports:
The underscore in opentelemetry._logs and opentelemetry.sdk._logs
indicates these modules are still marked as experimental in the OpenTelemetry Python SDK.
They are stable and widely used in practice — the underscore is a naming convention
that will be removed in a future release.
Using with Flask or Long-Running Apps
For web applications that run continuously, set up OpenTelemetry once at startup
and register a shutdown hook to flush logs when the process exits.
This example uses Flask (pip install flask):
import atexit
import logging
from flask import Flask
from opentelemetry._logs import set_logger_provider
from opentelemetry.exporter.otlp.proto.http._log_exporter import OTLPLogExporter
from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
from opentelemetry.sdk.resources import Resource
# Set up OpenTelemetry logging
resource = Resource.create()
logger_provider = LoggerProvider(resource=resource)
logger_provider.add_log_record_processor(
BatchLogRecordProcessor(OTLPLogExporter())
)
set_logger_provider(logger_provider)
# Bridge Python logging to OpenTelemetry
handler = LoggingHandler(logger_provider=logger_provider)
logging.getLogger().addHandler(handler)
logging.getLogger().setLevel(logging.INFO)
# Flush logs on shutdown
atexit.register(logger_provider.shutdown)
# Your Flask app
app = Flask(__name__)
@app.route("/")
def hello():
logging.info("Received a request")
return "Hello, world!"
if __name__ == "__main__":
app.run(port=8080)
The atexit.register(logger_provider.shutdown) call ensures that
all pending logs are flushed when your application stops. Without it, the last batch
of logs may be lost.
Verify in the Log Viewer
Navigate to the log viewer, select a time range that covers the last few minutes,
and search for your test message. You should see your "Hello from logging.cheap!" log entry
with the service name you set in OTEL_SERVICE_NAME and severity INFO.
If logs don't appear immediately, wait a few seconds for the batch processor to flush, then refresh the page.
Troubleshooting
- 401 Unauthorized — check that your
OTEL_EXPORTER_OTLP_HEADERSincludesBearerbefore your API key (with a space after "Bearer"). - No logs appearing — make sure you call
logger_provider.shutdown()before your script exits. The batch processor sends logs asynchronously, so without shutdown the process may exit before logs are flushed. - Connection errors — verify that
OTEL_EXPORTER_OTLP_ENDPOINTis set tohttps://logging.cheap(nothttp://). - Program exits before logs are sent — the batch processor buffers logs and sends them periodically. Always call
logger_provider.shutdown()before exit. For short-lived scripts, shutdown is essential. For long-running apps, useatexit.register()as shown in the Flask example above.
Next Steps
Your Python application is now sending logs to logging.cheap. In a production application,
initialize the logger provider once at startup and register a shutdown hook.
All logging.info(), logging.warning(), logging.error()
calls throughout your codebase will be exported automatically.
Head to the log viewer to search and explore your logs. For more on the OpenTelemetry Python SDK, see the official OpenTelemetry Python documentation.