lumber-cloud
Python client for Lumber: cloud logging with end-to-end encryption. Every log is encrypted with AES-256-GCM before it leaves your process, so Lumber's servers store only ciphertext plus each log's level (kept readable for filtering and push alerts). You read your logs in the Lumber app, which decrypts them on your device.
Its one dependency is cryptography. Logs are sent from a background thread over Python's own HTTP client, so logging never waits on the network.
Installation
pip install lumber-cloud
Python 3.9 or later.
Quick start
import os
import lumber_cloud as lumber
lumber.config(api_key=os.environ["LUMBER_API_KEY"])
lumber.info("Server started", port=8000)
lumber.warn("Rate limit approaching", request_id="req-123", user_id=456)
lumber.error("Payment failed", trace_id="trace-abc", order_id=789)
Call config() once at startup; every module that imports lumber_cloud then logs with the same client. Logs still queued when your program exits get up to 5 seconds to send.
Getting your API key
Create an app in the Lumber app and it takes you straight to adding an API key. The key is shown only once, so copy it right away. Any device holding the app's key can add more from the app's Secrets screen, one per deployment if you like.
An API key is two values joined by a dot: a token, then the app's encryption key. The client sends only the token to Lumber and encrypts every log with the rest, so the encryption key never leaves your process. Keep the API key in an environment variable or your secrets manager, never in source control.
With logging
LumberHandler sends records from Python's logging to Lumber, so the loggers your code and its libraries already use go there too.
import logging
import os
from lumber_cloud import LumberHandler
logging.basicConfig(level=logging.INFO) # keep printing to the console
logging.getLogger().addHandler(LumberHandler(os.environ["LUMBER_API_KEY"]))
log = logging.getLogger(__name__)
log.info("Report ready in %.1fs", 2.4, extra={"request_id": "req-123", "rows": 1500})
try:
charge(order)
except PaymentError:
log.exception("Payment failed") # sent at ERROR with the traceback
Each record becomes a log at the matching level (CRITICAL is FATAL). Its message is the record's message with its arguments filled in. extra= keys go into the log's metadata, except request_id and trace_id, which are the log's own fields, and context, which goes into its context. The logger's name, file, line and function go into the context too.
In Django, add it to LOGGING:
LOGGING = {
"version": 1,
"handlers": {
"lumber": {"class": "lumber_cloud.LumberHandler", "api_key": os.environ["LUMBER_API_KEY"], "level": "INFO"},
},
"root": {"handlers": ["lumber"], "level": "INFO"},
}
To share one client between a handler and your own calls, pass it: LumberHandler(client=lumber.config(api_key=...)).
Log methods
| Method | Level |
|---|---|
debug(message, **data) |
DEBUG |
info(message, **data) |
INFO |
warn(message, **data), warning(...) |
WARN |
error(message, **data) |
ERROR |
exception(message, **data) |
ERROR, with the exception being handled |
fatal(message, **data), critical(...) |
FATAL |
flush(timeout=None) |
Waits for queued logs to send; False if timeout seconds ran out first |
The module's functions log with the client config() made. LumberClient has the same methods, for when you need more than one.
Data
Keyword arguments become the log's fields:
lumber.info(
"User signed in",
request_id="req-abc", # the log's request ID
trace_id="trace-xyz", # the log's trace ID
context={"region": "us-east"}, # merged into its context
metadata={"plan": "pro"}, # merged into its metadata
user_id=123, # any other keyword goes into metadata
exc_info=err, # an exception (or True for the one being handled)
)
An explicit metadata key wins over a colliding keyword. An exception goes into metadata as exception (ValueError: bad amount) and traceback. Values JSON can't hold are sent as text (a datetime as ISO 8601, a set as a list), and a NaN as null. Everything, message included, is encrypted.
Options
lumber.config(
api_key=os.environ["LUMBER_API_KEY"],
environment="production", # sent as `env` in every log's context
auto_context=True, # send the process id and hostname with every log
caller=False, # also send the file, line and function that logged
caller_depth=0, # frames to skip for `caller`, one per wrapper around Lumber
timeout=10.0, # seconds to wait for Lumber to answer one log
max_queue=10_000, # logs waiting to send before new ones are dropped
shutdown_timeout=5.0, # seconds queued logs get when the program exits
)
Delivery
Logs are sent in order, one POST /logs per log, from a daemon thread over a connection kept open between logs. A network error or a 5xx counts as a failed attempt: each log gets 5, waiting 1, 2, 4, then 8 seconds between them, and is then dropped with a warning on stderr. A log Lumber refuses (a 4xx, such as an invalid API key) is dropped at once with a warning, since sending it again won't help. When your team has sent its plan's logs for the month, Lumber answers 429: the client warns once and drops logs until the month turns. While Lumber can't be reached, up to max_queue logs wait; past that, new ones are dropped.
Forking servers (gunicorn, uWSGI) are handled: a worker process starts its own sending thread and connection with its first log.
Development
python -m venv .venv && .venv/bin/pip install -e '.[dev]'
.venv/bin/pytest && .venv/bin/ruff check . && .venv/bin/mypy
tests/contract holds Lumber-Kit's contract at the version in tests/contract/VERSION: a JSON Schema of the wire format and known answers for API keys, encryption, payloads and delivery, which Lumber's Node and Go clients are tested against too. python scripts/sync_contract.py 0.8.0 moves it to a new kit version.
License
MIT. See LICENSE.
Metadata
Release files for lumber-cloud 2.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| lumber_cloud-2.0.0.tar.gz | 29.5 kB | With provenance | Details for lumber_cloud-2.0.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| lumber_cloud-2.0.0-py3-none-any.whl | Python 3 | none | any | Details for lumber_cloud-2.0.0-py3-none-any.whl |
Total release size: 46.6 kB
Release files / lumber_cloud-2.0.0.tar.gz
| Download URL | lumber_cloud-2.0.0.tar.gz |
|---|---|
| Size | 29.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
56a8be91996b0223dca056f80705ac3c79bfd9dbdfc4e33125802a327bdd19ad
|
|
BLAKE2b-256 checksum How to use checksums |
ec2c513fc7a74369dbdef9dbbf1839915f93c96a838e7002b0e48ceb628bc642
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 9, 2026.
Transparency logRelease files / lumber_cloud-2.0.0-py3-none-any.whl
| Download URL | lumber_cloud-2.0.0-py3-none-any.whl |
|---|---|
| Size | 17.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
366919cccf9b2e9fddc111d0259f8f4db05a1e0d9f039c101347d1621d3370b5
|
|
BLAKE2b-256 checksum How to use checksums |
c6dce1dcd0d33f4becde2d1a90bdc434a51675807e8a3191cab7c51eb21d8f06
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 9, 2026.
Transparency logSecurity for lumber-cloud 2.0.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for lumber_cloud-2.0.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 9, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.