Skip to main content

Opinionated structured logging library for Python with a fluent interface

Project description

Fluentlog

Opinionated structured logging for Python with a fluent API.

  • API inspired by zerolog
  • JSON output format
  • OpenTelemetry naming conventions when relevant
  • Near zero-cost for disabled log levels

Installation

pip install fluentlog

Getting Started

Simple example

import fluentlog

log = fluentlog.Logger().bind().int("request_id", 1).logger()

log.info().str("user", "jmcs").int("uid", 42).msg("user logged in")
# {"level":"INFO","request_id":1,"user":"jmcs","uid":42,"message":"user logged in"}

# Disabled levels have near-zero overhead
log.debug().func( expensive_func).msg("debug info")  # expensive_func is never called

Log Levels

fluentlog supports the following log levels, from more to less critical:

  • FATAL: Errors the application can't recover from
  • ERROR: Errors that make the current context fail, but not the entire application
  • WARNING: Recoverable errors
  • INFO: Expected lifecycle events and relevant business signals
  • DEBUG: Internal details useful while diagnosing behavior during development
  • TRACE: Very fine-grained execution details, usually only useful for deep debugging

You can set the log level for your logger either in the constructor or using a fluent method:

import fluentlog

log = fluentlog.Logger(level=fluentlog.Level.DEBUG)

# or 

log = fluentlog.Logger().set_level(fluentlog.Level.DEBUG)

Logging context

import fluentlog

def some_func():
    log = fluentlog.context()
    log.info().msg("From func")

def main():
    log = fluentlog.context().bind().str("context", "example").logger()
    some_func()
    # {"level":"INFO", "message": "From func"}
    with fluentlog.context_logger(log):
        some_func()
        # {"level":"INFO", "context": "example", "message": "From func"}

main()

Performance

Benchmarks show ~2-3x faster than stdlib logging with formatted output, with greater advantages when log levels are filtered.

Design decisions

Why use different methods for different types?

Using different methods for different types allows for optimising serialization strategies for mutable and immutable types. For example, dict() and list() deep-copy their arguments to prevent mutations after the event is logged from affecting the output, while int() and str() can safely reference immutable values directly without copying.

Why dummy events for disabled log levels?

Having dummy events achieves near-zero overhead, as we can avoid unnecessary processing without having to check the log level everywhere.

Why OpenTelemetry naming conventions?

I use OpenTelemetry for distributed tracing, and like consistent and precise naming, even when it comes at the cost of verbosity.

Why no formatted messages?

Formatted messages are familiar because that's how traditional logging usually works. But for structured logs they are a trap, as important data gets buried in strings instead of proper fields, which makes filtering and querying harder.

Why context-based logger passing?

Preserving logging context across boundaries is essential in complex applications, but having bound context inside a function is useful too. Context-based logger passing allows for both options and keeps things purposeful while avoiding cluttering application APIs.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fluentlog-0.1.0.tar.gz (14.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fluentlog-0.1.0-py3-none-any.whl (13.5 kB view details)

Uploaded Python 3

File details

Details for the file fluentlog-0.1.0.tar.gz.

File metadata

  • Download URL: fluentlog-0.1.0.tar.gz
  • Upload date:
  • Size: 14.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.5.9

File hashes

Hashes for fluentlog-0.1.0.tar.gz
Algorithm Hash digest
SHA256 f3c789846e4a2f8c7de247ba89729e0c3c9b389df8ea23fbb6c173a594880ae6
MD5 77c846f5afee4ba130dbdab091f2da08
BLAKE2b-256 899d33a42a4f35f8d434ed76ac05e5950bf858770d340fc8ede18de3c7657269

See more details on using hashes here.

File details

Details for the file fluentlog-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: fluentlog-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 13.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.5.9

File hashes

Hashes for fluentlog-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e9ad44274e640815c45f46c119849dadfdbe6571af1428491bd57e286dd834d9
MD5 dcf26f14ba958443543f6a5c3f17f29c
BLAKE2b-256 bc0936864fdc9a9996cc65ae4321294d9d79a2c9795d7d44b4a19f47c112d809

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page