Configurable Logging

Logs are only useful in a distributed system if they can be collected, searched, and correlated in a log aggregation system — and that works best when every service logs in the same shape and is configured in the same way. If each service hand-rolled its own logging setup, formats and configuration options would inevitably drift apart across the deployment. hexkit therefore centralizes logging in the hexkit.log module: every hexkit-based service draws its settings from the same LoggingConfig fields and emits the same output shape, so operators deal with a single format and a single set of configuration options, no matter which service a log line comes from.

Concretely, the module sets up Python’s standard logging machinery so that every log line comes out as structured JSON, enriched with the service name, instance ID, and the current correlation ID — everything needed to attribute log lines to their origin and to the request chain they belong to, without any per-service parsing rules.

The module is part of hexkit’s core, so no extra is needed. Services keep using the standard library as usual, i.e. logging.getLogger(__name__) and the familiar logging calls, and only call hexkit once at startup to configure the output.

Configuration

All settings are collected in LoggingConfig. Inherit your service’s config class from it (see Configuration), or instantiate it directly:

from hexkit.log import LoggingConfig

config = LoggingConfig(
    log_level="INFO",
    service_name="my-cool-special-service",
    service_instance_id="germany-bw-instance-001",
)
Parameter Description
log_level The minimum log level to capture: CRITICAL, ERROR, WARNING, INFO (the default), DEBUG, or TRACE.
service_name The name of the (micro-)service, included in every log message. Required.
service_instance_id A string uniquely identifying this instance among all instances of the service, included in every log message. Required.
log_format If set, replaces the JSON output with a classic format string (see Plain-Text Output). Defaults to None.
log_traceback Whether to include exception tracebacks in log messages. Defaults to True.

TRACE is a custom level that hexkit registers below DEBUG (numeric value 5). Since importing hexkit.log registers the level name, getLevelName("TRACE") and level-based filtering work with it like with the built-in levels. hexkit itself does not log at this level — it is offered for services that want a tier below DEBUG for particularly fine-grained output, so setting log_level to TRACE only adds records that the service or a library emits.

Setting Up Logging

Call configure_logging once at service startup:

from hexkit.log import configure_logging

configure_logging(config=config)

This attaches a handler that writes to standard error, applying the configured level and format. By default the root logger is configured, so records from all loggers in the service (including those of libraries) are captured; pass an explicit logger argument to configure only a specific logger instead.

Right after setup, one INFO message (“Logging configured, complete configuration in details”) is emitted with the full dump of the passed config object in its details. Since services typically pass their complete service config (which inherits from LoggingConfig), this documents the exact configuration each instance is running with. Fields declared with Pydantic’s Secret types are masked in this dump — which covers hexkit’s own credential parameters, and any a service declares the same way (see Secrets).

Note that configure_logging adds a handler on each call, so calling it twice would duplicate every log line — call it exactly once per process.

JSON Output Format

With the default configuration, each log record is written as a single-line JSON object (pretty-printed here for readability):

{
  "timestamp": "2026-07-31T12:34:56.789Z",
  "service": "my-cool-special-service",
  "instance": "germany-bw-instance-001",
  "level": "WARNING",
  "name": "my_service.core.books",
  "correlation_id": "0b8ecf6a-799a-4b54-98cc-de1b571246f7",
  "message": "Book with ID 42 not found",
  "details": {}
}

The fields are:

Field Description
timestamp Time of logging in ISO 8601 format with millisecond precision, always in UTC (Z suffix), regardless of the host’s timezone.
service The configured service_name.
instance The configured service_instance_id.
level The log level name, e.g. INFO or ERROR.
name The name of the logger that emitted the record, conventionally the module path.
correlation_id The correlation ID set in the current context, or null if none is set.
message The logged message with any printf-style arguments already interpolated.
details Any additional values passed via extra (see below).
exception Only present when an exception was logged (see below).

Adding Details

Anything passed to a logging call via the standard extra mechanism ends up in the details object instead of being discarded:

log.info("Book checked out", extra={"book_id": book.id, "user": user.name})

Values that are not JSON-serializable (UUIDs, datetimes, arbitrary objects) are converted with repr(), so logging them never raises.

Exceptions

When an exception is logged — via log.exception(...) or exc_info=True — the output gains an exception object with the exception type and message, plus the formatted traceback unless log_traceback is disabled. Turning log_traceback off is useful where tracebacks are considered too verbose or too revealing for the log sink at hand.

Plain-Text Output

Setting log_format switches from JSON to plain-text output using the standard logging.Formatter syntax. In addition to the standard record attributes, the hexkit-specific attributes timestamp, service, instance, level, correlation_id, and details are available:

config = LoggingConfig(
    service_name="my-cool-special-service",
    service_instance_id="germany-bw-instance-001",
    log_format="%(timestamp)s - %(service)s - %(level)s - %(message)s",
)

This is mainly intended for local development, where a compact human-readable line beats JSON; deployed services should stick with the default JSON output.

API Reference