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.1.tar.gz (15.3 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.1-py3-none-any.whl (14.1 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: fluentlog-0.1.1.tar.gz
  • Upload date:
  • Size: 15.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.6 {"installer":{"name":"uv","version":"0.11.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"25.10","id":"questing","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for fluentlog-0.1.1.tar.gz
Algorithm Hash digest
SHA256 4e97d45ca68a2340133b0681b4310e36f397f59d9259fbc1083e23e22590e629
MD5 e857e4396a83f3611fddfd5536732381
BLAKE2b-256 7a2e74f3a17e4953c9258ee09995f9c4d84a9807c52fe20b86011f148b7ea6d5

See more details on using hashes here.

File details

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

File metadata

  • Download URL: fluentlog-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 14.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.6 {"installer":{"name":"uv","version":"0.11.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"25.10","id":"questing","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for fluentlog-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 922265f1420547cefe23600b06fdfc844e3065ad805f3ea66db50a17cb35fbc2
MD5 1f8cba60467149d2a25c81bf0d08a118
BLAKE2b-256 81c2b1d3811fd295fae8ce84ef0c1836d0550cf503da10eea2407b34446c2566

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