Skip to main content

cells-log

cells-log is a lightweight structured logging kernel for Python services, CLIs, async workers and data pipelines.

It keeps Python's stdlib logging ecosystem underneath, but exposes a small bound-logger API with bootstrap-safe initialization, structured fields, ContextVar-based trace context, file routing and optional queue-based writing.

The import package path is cells.log.

Why this package exists

Most projects start with logging.basicConfig() and eventually grow a mixed set of console logs, JSON logs, module files, request IDs and ad-hoc wrappers. cells-log is designed to keep that layer small and stable:

  • Application code uses one bound logger API.
  • Infrastructure code controls sinks, formatters and lifecycle.
  • Project-specific policy, such as field serialization or redaction, is injected through hooks instead of hard-coded into the kernel.
  • Bootstrap logs work before full application settings are available.

Installation

From source:

python -m pip install -e .

From a built wheel:

python -m pip install dist/cells_log-*.whl

Quick start

from pathlib import Path

from cells.log import (
    LogConfig,
    configure_logging,
    get_logger,
    reset_trace_id,
    set_trace_id,
    shutdown_logging,
)


configure_logging(
    LogConfig(
        app_name="app",
        level="INFO",
        format="json",
        log_dir=Path("./logs"),
        enable_console=True,
        enable_module_file=True,
        enable_total_file=True,
        total_file="app.log",
        module_name_prefixes=("app",),
    )
)

trace_token = set_trace_id("trace-001")
try:
    log = get_logger("app.worker").bind(stage="worker")
    log.info("task completed", task_id="t1", duration_ms=12.3)
finally:
    reset_trace_id(trace_token)
    shutdown_logging()

Example JSON line:

{"timestamp":"2026-05-11T00:00:00+00:00","level":"INFO","logger":"app.worker","message":"task completed","trace_id":"trace-001","path":"/app/worker.py:42","func":"run","fields":{"channel":"app.worker","log_tag":"app_worker","stage":"worker","task_id":"t1","duration_ms":12.3}}

Public API

from cells.log import (
    LogConfig,
    bootstrap_logging,
    configure_logging,
    configure_logging_from_env,
    get_logger,
    get_queue_stats,
    set_trace_id,
    reset_trace_id,
    bind_log_context,
    reset_log_context,
    shutdown_logging,
)

bootstrap_logging()

Initializes console-only logging from environment variables. This is safe before application settings are loaded. File sinks and queue writing stay disabled during bootstrap, even when their environment flags are enabled.

from cells.log import bootstrap_logging, get_logger


bootstrap_logging(app_name="demo")
get_logger("demo.boot").info("bootstrap started")

configure_logging()

Configures the production handler graph from an explicit LogConfig.

from cells.log import LogConfig, configure_logging


configure_logging(LogConfig(app_name="demo", level="DEBUG", format="text"))

Reconfiguration prepares the replacement while the current configuration continues serving logs. Preparation failure leaves the current configuration active. Handover briefly gates delivery to drain accepted queue records and transfer file streams; activation failure restores the previous configuration. After a successful switch, old resources are cleaned up while the replacement serves logs.

Use force=False to keep an existing production configuration. Call shutdown_logging() before process exit to drain queued records and close managed handlers.

configure_logging_from_env()

Builds LogConfig from environment variables.

from pathlib import Path
from cells.log import configure_logging_from_env


configure_logging_from_env(
    app_name="demo",
    default_log_dir=Path("./logs"),
    env_prefix="CELLS_LOG",
)

Environment variables

When using configure_logging_from_env(env_prefix="CELLS_LOG"), supported variables include:

Variable Meaning
CELLS_LOG_LEVEL TRACE, DEBUG, INFO, WARNING, ERROR, CRITICAL
CELLS_LOG_FORMAT json or text
CELLS_LOG_DIR Log directory used by file sinks
CELLS_LOG_FILE Total log file path or file name
CELLS_LOG_ENABLE_CONSOLE Enable console sink
CELLS_LOG_ENABLE_MODULE_FILE Enable per-module file sink
CELLS_LOG_ENABLE_TOTAL_FILE Enable total file sink
CELLS_LOG_ROTATION daily or size
CELLS_LOG_RETENTION Number of rotated files to keep
CELLS_LOG_SIZE_LIMIT Size rotation limit, for example 100MB
CELLS_LOG_USE_QUEUE Enable in-process QueueHandler / QueueListener
CELLS_LOG_QUEUE_CAPACITY Maximum number of buffered log records
CELLS_LOG_QUEUE_FULL_POLICY drop, block, or sync_fallback
CELLS_LOG_CAPTURE_WARNINGS Route Python warnings into logging
CELLS_LOG_MANAGE_ROOT_LOGGER Route root logger records through cells-log

Configuration model

LogConfig controls console and file sinks, rotation, queue backpressure, root/warnings capture, the fallback trace ID and serialization hooks. See cells/log/config.py for all fields and defaults.

See docs/architecture.md for the full design boundary.

Queue backpressure

Production logging uses a bounded queue by default, with a capacity of 10,000 records. Choose queue_full_policy according to the application's delivery and latency requirements:

Policy Behavior when the queue is full
drop (default) Discard the record and increment dropped_records
block Wait up to queue_block_timeout seconds, then discard and count the record
sync_fallback Write the record synchronously on the calling thread

get_queue_stats() returns enabled, capacity, depth and dropped_records for the active queue. Set use_queue=False for direct synchronous writing.

Context propagation

trace_id and log context are stored in ContextVar, so they work naturally in async pipelines and request-scoped code.

from cells.log import bind_log_context, reset_log_context, set_trace_id, reset_trace_id


trace_token = set_trace_id("request-001")
ctx_token = bind_log_context(request_id="request-001", user_id="u1")

try:
    ...
finally:
    reset_log_context(ctx_token)
    reset_trace_id(trace_token)

Hook model

cells-log intentionally does not hard-code business policy. Field serialization, redaction, pruning and normalization should be attached through hooks.

from cells.log import LogConfig, configure_logging


def field_processor(fields: dict) -> dict:
    fields = dict(fields)
    if "authorization" in fields:
        fields["authorization"] = "***"
    return fields


configure_logging(LogConfig(field_processor=field_processor))

Custom JSON serializer:

from cells import json
from cells.log import LogConfig, configure_logging


configure_logging(LogConfig(json_dumps=json.dumps))

File layout

With module and total logs enabled:

logs/
  app.log
  modules/
    app.log
    app_worker.log
    _external.log
    _overflow.log

Routing notes:

  • module_name_prefixes=("app",) keeps application logs in their own module files.
  • Loggers outside the allowed prefix are routed into _external.log.
  • If too many dynamic logger names are created, excess records are routed into _overflow.log instead of opening unlimited file descriptors.
  • Names that map to the same file share a rotating handler, including case variants on Windows; each record retains its original logger name.

GitHub Actions

This repository includes two workflows:

  • .github/workflows/build.yml: Python 3.11–3.14 tests on Linux and Windows, wheel artifact build, and GitHub Release publishing.
  • .github/workflows/publish-pypi.yml: release/manual wheel build, metadata check and PyPI publish.

After tests and wheel metadata checks pass, build.yml publishes the built wheel according to the triggering ref:

Trigger GitHub Release
Push to main Update the rolling tag to the tested commit and publish a prerelease titled rolling
Push a v* tag Publish a release using the tag as its title
Other pushes, pull requests, manual builds Build and upload workflow artifacts only

The rolling release replaces matching wheel assets and removes wheel assets from older versions. It is not marked as the latest stable release. Publishing uses the built-in GITHUB_TOKEN with contents: write permission limited to the release job.

Releases created with GITHUB_TOKEN do not trigger the separate PyPI workflow; use its manual trigger for those builds. See GitHub's workflow triggering documentation.

Expected repository secret:

PYPI_API_TOKEN

Optional R2/S3 artifact variables used by build.yml on v* tags:

AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY
S3_BUCKET
S3_ENDPOINT
S3_PREFIX
S3_REGION

Development

python -m pip install -e . pytest
python -m pytest -v

Build wheel locally:

python -m pip install build twine
python -m build --wheel
python -m twine check dist/*.whl

Design status

cells-log is intentionally small. The current package aims to cover the common 90% production logging cases without becoming a full observability platform. For OpenTelemetry, sampling, rate limiting, or redaction, prefer integration modules and hooks over changing the core logger API.

Metadata

Release files for cells-log 0.2.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distribution (wheel)

Table of built distributions (wheels) for cells-log 0.2.2
File Interpreter ABI Platform Actions
cells_log-0.2.2-py3-none-any.whl Python 3 none any Details for cells_log-0.2.2-py3-none-any.whl

Release files / cells_log-0.2.2-py3-none-any.whl

Download URL cells_log-0.2.2-py3-none-any.whl
Size 29.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d5303169d79198f701b726384595a7360a04d4215e13cd42a2ec2f6dae0ebbac
BLAKE2b-256 checksum
How to use checksums
153a4661a97ef59042d7380d946a799c408a47ed798657877d28b999277d29b3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.16

Security for cells-log 0.2.2

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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A 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.

Release history Release notifications | RSS feed

This release

0.2.2 This release

1 release file No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page