Skip to content

Laxyny/logthisx

Repository files navigation

logthisx

A modern, friendly Python logging library with sensible defaults, colored console output, rotating files, structured JSON, context binding, and decorators. Built on top of the standard logging module so it composes naturally with the rest of the Python ecosystem.

PyPI Python versions License: MIT CI


Table of contents

Why logthisx

  • Looks good in your terminal out of the box, without forcing you to know the logging.config DSL.
  • Plays nicely with the standard library: every logthisx logger is a logging.Logger, so third-party libraries that already use logging inherit your configuration.
  • No mandatory third-party dependency. rich support is available as an optional extra.
  • Structured logging built in: JSON formatter with a stable schema for Docker, CI, log aggregators and observability tools.
  • Practical features that are tedious to wire up by hand: rotation, context binding, decorators, redaction.

Installation

pip install logthisx

With the optional rich extra for fancier console output:

pip install "logthisx[rich]"

logthisx supports Python 3.9 to 3.13.

Quickstart

from logthisx import configure, logger

configure(level="INFO", console=True, file="logs/app.log")

logger.info("Application started")
logger.success("Operation completed")
logger.warning("Something looks wrong")
logger.error("Something failed")
logger.debug("Debug value", extra={"value": 42})

You can also retrieve named loggers:

from logthisx import configure, get_logger

configure(level="DEBUG", console=True)

log = get_logger("my.app")
log.info("Ready")

The package is side-effect free on import: it does not touch the root logger unless you ask for it explicitly (logger_name="root").

Configuration

configure() is the single entry point that wires handlers and formatters together. It is idempotent: calling it again replaces the handlers it previously installed, so duplicated log lines are not a concern.

from logthisx import configure

configure(
    level="INFO",
    console=True,
    console_json=False,
    detailed=False,
    show_time=True,
    time_format="%Y-%m-%d %H:%M:%S",
    use_color=None,
    file="logs/app.log",
    file_json=False,
    max_bytes=5 * 1024 * 1024,
    backup_count=5,
    redact=True,
    custom_levels=False,
    logger_name="logthisx",
    propagate=False,
)

Alternatively, build a Config and pass it in:

from logthisx import Config, configure

cfg = Config(level="DEBUG", file="logs/app.log", file_json=True)
configure(config=cfg)

Or read it from the environment:

from logthisx import Config, configure

configure(config=Config.from_env())

Recognized environment variables (default prefix LOGTHISX_):

Variable Description
LOGTHISX_LEVEL Log level (DEBUG, INFO, WARNING, ...)
LOGTHISX_CONSOLE Enable console handler (1/0)
LOGTHISX_JSON Use JSON console output (1/0)
LOGTHISX_DETAILED Include module/function/line in console (1/0)
LOGTHISX_FILE Path to the log file
LOGTHISX_MAX_BYTES Rotation threshold in bytes
LOGTHISX_BACKUP_COUNT Number of rotated files to keep
LOGTHISX_REDACT Enable redaction of sensitive keys (1/0)

Console logging

The default console formatter is a colorized, compact one-liner. Colors are auto-detected based on whether stderr is a TTY; set use_color=True/False to force the behavior, or set NO_COLOR=1 / FORCE_COLOR=1 in the environment.

Enable the detailed mode to include module.function:line:

configure(level="DEBUG", console=True, detailed=True)

File logging

configure(file="logs/app.log") installs a RotatingFileHandler that:

  • Creates the parent directory if it does not exist.
  • Uses UTF-8 encoding by default.
  • Rotates the file once it reaches max_bytes (default 5 MiB).
  • Keeps backup_count historical files (default 5).
  • Returns a no-op handler and prints a warning if the file cannot be opened, so logging never crashes the host application.

JSON logging

Set console_json=True or file_json=True to switch to the JSON formatter. The schema is stable across releases and looks like:

{
  "timestamp": "2026-01-02T15:04:05.123456+00:00",
  "level": "INFO",
  "logger": "my.app",
  "module": "api",
  "function": "handle_request",
  "line": 42,
  "message": "Request received",
  "extra": {"request_id": "abc"},
  "exception": {
    "type": "ValueError",
    "message": "boom",
    "traceback": "..."
  }
}

extra is always present (possibly empty). exception is only present when the record carries exception information. Timestamps are ISO 8601 with explicit UTC offset.

Context binding

Attach structured context to every record without repeating yourself:

from logthisx import get_logger

log = get_logger("api").bind(request_id="abc123", user_id=42)
log.info("Request received")
log.info("Cache hit", extra={"cache_key": "users:42"})

bind() returns a new logger; the parent is not mutated. Chain calls to extend the context, and use unbind("user_id") to drop a key.

The context shows up in console output, in files and in JSON payloads.

Decorators

from logthisx.decorators import log_call, log_errors, log_time

@log_call(level="DEBUG", include_args=True)
def add(a, b):
    return a + b

@log_time(threshold_ms=50.0)
def slow_path():
    ...

@log_errors()
def risky():
    raise RuntimeError("boom")

All three decorators support both def and async def functions. They are cheap when the relevant level is disabled (a single isEnabledFor check).

Custom levels

logthisx ships with three extra levels:

Constant Value Default name
SUCCESS 25 SUCCESS
ALERT 35 ALERT
SPECIAL 45 SPECIAL

They are registered automatically when configure(custom_levels=True) is called, and exposed as methods (logger.success, logger.alert, logger.special).

Optional short aliases (II, DD, SS, WW, AA, EE, AZ, CC) can be turned on with custom_level_aliases=True. They are off by default to avoid polluting the global namespace for users who prefer standard names.

Exceptions

Use logger.exception("message") from inside an except: block. The traceback is rendered cleanly in the console and stored verbatim in files and inside the exception.traceback field of JSON records.

try:
    risky()
except Exception:
    logger.exception("risky failed")

Redaction

extra dictionaries are scanned for sensitive keys (password, token, api_key, secret, authorization, ...) and their values are replaced by *** before being written. Set configure(redact=False) to disable, or pass a custom set of keys to the formatters directly if you need a tighter or looser policy.

The redaction does not inspect the free-form message text. Replacing arbitrary substrings in messages is unreliable and gives a false sense of security. Pass secrets through extra so the redaction can do its job.

CLI

logthisx --version
logthisx demo            # Emit one record per level so you can preview output
logthisx demo --json     # Same, but in JSON
logthisx doctor          # End-to-end self-check (console, file, rotation, JSON)

logthisx demo is also handy for verifying that your terminal renders the colors correctly.

Migration from 0.1.x

logthisx 0.1 exposed four module-level functions:

from logthis import log_info, log_warn, log_error, enable_file_logging

enable_file_logging("logs/app.log")
log_info("hello")

These keep working in 0.2.0 but emit DeprecationWarning. The recommended new API is:

from logthisx import configure, logger

configure(level="INFO", console=True, file="logs/app.log")
logger.info("hello")
logger.warning("careful")
logger.error("broken")

See MIGRATION.md for the full guide.

Development

git clone https://github.com/laxyny/logthis.git
cd logthis
python -m pip install -e ".[dev]"
python -m ruff check src tests
python -m ruff format --check src tests
python -m pytest
python -m build

License

Released under the MIT License.

Releases

No releases published

Packages

 
 
 

Contributors

Languages