API Reference#
This page documents every public class and function in the thinlog package.
Core#
Thinlog – a lightweight, fully-typed logging toolkit built on Python’s standard logging.
Thinlog extends logging with advanced filtering, structured JSON formatting,
and remote log delivery (HTTP and Telegram) while staying thin and transparent.
Public API:
configure_logging()– set up logging from aLoggingSettingsor dict.get_logger()– create aKeywordFriendlyLoggerand register it.KeywordFriendlyLogger– logger adapter that forwards keyword arguments as extra fields.LoggingSettings– typed dataclass matchinglogging.config.dictConfig()schema.RegisteredLoggers– global registry of logger names used by the wildcard feature.
- class thinlog.KeywordFriendlyLogger(logger: Logger, extra: dict[str, Any] | None = None)[source]#
Bases:
LoggerAdapter[Logger]A
logging.LoggerAdapterthat passes arbitrary keyword arguments as extra fields.Standard
loggingrequires extra data to be wrapped in anextradict.KeywordFriendlyLoggerlets callers pass keyword arguments directly – they are automatically moved into extra so that filters, formatters, and handlers can access them as record attributes.- Parameters:
logger – The underlying
logging.Loggerto adapt.extra – Optional default extra fields attached to every record.
- ignored_meta_keys = ('exc_info', 'stack_info', 'stacklevel')#
- process(msg: Any, metadata: MutableMapping[str, Any]) tuple[Any, MutableMapping[str, Any]][source]#
Move keyword arguments into the extra dict.
Keys listed in
ignored_meta_keysare left in metadata so that the standard logging machinery can handle them.- Parameters:
msg – The log message.
metadata – Keyword arguments passed to the logging call.
- Returns:
A
(msg, metadata)tuple ready for the underlying logger.
- class thinlog.LoggingSettings(version: int = 1, formatters: dict[str, dict[str, ~typing.Any]]=<factory>, filters: dict[str, dict[str, ~typing.Any]]=<factory>, handlers: dict[str, dict[str, ~typing.Any]]=<factory>, loggers: dict[str, dict[str, ~typing.Any]]=<factory>, incremental: bool = False, disable_existing_loggers: bool = False, root: dict[str, ~typing.Any]=<factory>)[source]#
Bases:
objectConfiguration container for
logging.config.dictConfig().All fields mirror the keys accepted by
logging.config.dictConfig(). Convert to a plain dict withdataclasses.asdict()before passing todictConfig(this is handled automatically byconfigure_logging()).- Parameters:
version – Schema version – must be
1.formatters – Formatter definitions keyed by name.
filters – Filter definitions keyed by name.
handlers – Handler definitions keyed by name.
loggers – Logger definitions keyed by name.
incremental – If
True, merge into the existing configuration.disable_existing_loggers – If
True, disable loggers not in loggers.root – Configuration for the root logger.
- disable_existing_loggers: bool = False#
- filters: dict[str, dict[str, Any]]#
- formatters: dict[str, dict[str, Any]]#
- handlers: dict[str, dict[str, Any]]#
- incremental: bool = False#
- loggers: dict[str, dict[str, Any]]#
- root: dict[str, Any]#
- version: int = 1#
- class thinlog.RegisteredLoggers[source]#
Bases:
objectStores logger names so
configure_logging()can apply wildcard configs to them.Loggers are registered automatically when created via
get_logger().- loggers: ClassVar[set[str]] = {}#
- thinlog.configure_logging(name: str, config: LoggingSettings | dict[str, Any], extra: dict[str, Any] | None = None, more_loggers: list[str] | None = None, include_default_logger: bool = False, include_registered_loggers: bool = False, include_root_logger: bool = False, disable_log_errors: bool = True) KeywordFriendlyLogger[source]#
Set up the logging system and return a ready-to-use logger.
Applies the configuration, starts any
QueueHandlerlistener, and registers anatexit()handler that stops the listener and callslogging.shutdown()on interpreter exit.Wildcard loggers: If the config contains a logger named
"*", its settings are copied to every logger listed in more_loggers (and those gathered fromRegisteredLoggers). A specific logger can set"merge": trueto extend rather than replace the wildcard config.- Parameters:
name – Name of the primary logger to return.
config – A
LoggingSettingsinstance or a raw dict suitable forlogging.config.dictConfig().extra – Default extra fields for the returned logger.
more_loggers – Additional logger names to configure via the wildcard.
include_default_logger – If
True, add name to more_loggers.include_registered_loggers – If
True, include all names fromRegisteredLoggers.include_root_logger – If
True, apply the wildcard config to the root logger as well.disable_log_errors – If
True(default), setlogging.raiseExceptionstoFalse.
- Returns:
A
KeywordFriendlyLoggerinstance.
- thinlog.get_logger(name: str, extra: dict[str, Any] | None, register: bool = True) KeywordFriendlyLogger[source]#
Create a
KeywordFriendlyLoggerand optionally register it.- Parameters:
name – Logger name (passed to
logging.getLogger()).extra – Default extra fields attached to every record.
register – If
True(default), add name toRegisteredLoggersso it is picked up by the wildcard feature inconfigure_logging().
- Returns:
A
LoggerAdapterwrapping the named logger.
Logger adapter that converts keyword arguments into extra fields.
- class thinlog.log.KeywordFriendlyLogger(logger: Logger, extra: dict[str, Any] | None = None)[source]#
Bases:
LoggerAdapter[Logger]A
logging.LoggerAdapterthat passes arbitrary keyword arguments as extra fields.Standard
loggingrequires extra data to be wrapped in anextradict.KeywordFriendlyLoggerlets callers pass keyword arguments directly – they are automatically moved into extra so that filters, formatters, and handlers can access them as record attributes.- Parameters:
logger – The underlying
logging.Loggerto adapt.extra – Optional default extra fields attached to every record.
- ignored_meta_keys = ('exc_info', 'stack_info', 'stacklevel')#
- process(msg: Any, metadata: MutableMapping[str, Any]) tuple[Any, MutableMapping[str, Any]][source]#
Move keyword arguments into the extra dict.
Keys listed in
ignored_meta_keysare left in metadata so that the standard logging machinery can handle them.- Parameters:
msg – The log message.
metadata – Keyword arguments passed to the logging call.
- Returns:
A
(msg, metadata)tuple ready for the underlying logger.
Bootstrap logging configuration via logging.config.dictConfig().
- thinlog.bootstraping.configure_logging(name: str, config: LoggingSettings | dict[str, Any], extra: dict[str, Any] | None = None, more_loggers: list[str] | None = None, include_default_logger: bool = False, include_registered_loggers: bool = False, include_root_logger: bool = False, disable_log_errors: bool = True) KeywordFriendlyLogger[source]#
Set up the logging system and return a ready-to-use logger.
Applies the configuration, starts any
QueueHandlerlistener, and registers anatexit()handler that stops the listener and callslogging.shutdown()on interpreter exit.Wildcard loggers: If the config contains a logger named
"*", its settings are copied to every logger listed in more_loggers (and those gathered fromRegisteredLoggers). A specific logger can set"merge": trueto extend rather than replace the wildcard config.- Parameters:
name – Name of the primary logger to return.
config – A
LoggingSettingsinstance or a raw dict suitable forlogging.config.dictConfig().extra – Default extra fields for the returned logger.
more_loggers – Additional logger names to configure via the wildcard.
include_default_logger – If
True, add name to more_loggers.include_registered_loggers – If
True, include all names fromRegisteredLoggers.include_root_logger – If
True, apply the wildcard config to the root logger as well.disable_log_errors – If
True(default), setlogging.raiseExceptionstoFalse.
- Returns:
A
KeywordFriendlyLoggerinstance.
Typed dataclass matching the logging.config.dictConfig() schema.
- class thinlog.settings.LoggingSettings(version: int = 1, formatters: dict[str, dict[str, ~typing.Any]]=<factory>, filters: dict[str, dict[str, ~typing.Any]]=<factory>, handlers: dict[str, dict[str, ~typing.Any]]=<factory>, loggers: dict[str, dict[str, ~typing.Any]]=<factory>, incremental: bool = False, disable_existing_loggers: bool = False, root: dict[str, ~typing.Any]=<factory>)[source]#
Bases:
objectConfiguration container for
logging.config.dictConfig().All fields mirror the keys accepted by
logging.config.dictConfig(). Convert to a plain dict withdataclasses.asdict()before passing todictConfig(this is handled automatically byconfigure_logging()).- Parameters:
version – Schema version – must be
1.formatters – Formatter definitions keyed by name.
filters – Filter definitions keyed by name.
handlers – Handler definitions keyed by name.
loggers – Logger definitions keyed by name.
incremental – If
True, merge into the existing configuration.disable_existing_loggers – If
True, disable loggers not in loggers.root – Configuration for the root logger.
- disable_existing_loggers: bool = False#
- filters: dict[str, dict[str, Any]]#
- formatters: dict[str, dict[str, Any]]#
- handlers: dict[str, dict[str, Any]]#
- incremental: bool = False#
- loggers: dict[str, dict[str, Any]]#
- root: dict[str, Any]#
- version: int = 1#
Factory for creating and registering loggers.
- thinlog.util.get_logger(name: str, extra: dict[str, Any] | None, register: bool = True) KeywordFriendlyLogger[source]#
Create a
KeywordFriendlyLoggerand optionally register it.- Parameters:
name – Logger name (passed to
logging.getLogger()).extra – Default extra fields attached to every record.
register – If
True(default), add name toRegisteredLoggersso it is picked up by the wildcard feature inconfigure_logging().
- Returns:
A
LoggerAdapterwrapping the named logger.
Global registry of logger names for the wildcard configuration feature.
Filters#
Log filters for whitelisting, blocklisting, and conditional attribute assignment.
- class thinlog.filters.AssignerFilter(assignments: dict[str, Any], **kwargs: Any)[source]#
Bases:
WhitelistFilterAssign attributes to matching records without blocking any.
Extends
WhitelistFilter– if a record matches the whitelist criteria, the assignments dict is applied to the record as attributes. The filter always returnsTrueso no records are dropped.- Parameters:
assignments – Mapping of attribute names to values that will be set on matching records via
setattr().
- class thinlog.filters.BlocklistFilter(by_name: list[str] | None = None, by_msg: list[str] | None = None, by_attr: dict[str, Any] | None = None, **kwargs: Any)[source]#
Bases:
FilterBlock records whose name, message, or attributes match a blocklist.
This is the inverse of
WhitelistFilter. Any record that matches is rejected; all others pass through.- Parameters:
by_name – Logger names to block.
by_msg – Messages to block.
by_attr – Mapping of attribute names to values that trigger blocking. Use
"_any_"to block on any value for a given attribute.
- class thinlog.filters.WhitelistFilter(by_name: list[str] | None = None, by_msg: list[str] | None = None, by_attr: dict[str, Any] | None = None, **kwargs: Any)[source]#
Bases:
FilterAllow records whose name, message, or attributes match a whitelist.
Records are checked against three criteria (any match is sufficient):
by_name – the record’s logger name.
by_msg – the record’s message (also checked inside JSON-encoded messages).
by_attr – arbitrary record attributes; use the special value
"_any_"to match any value for a given attribute.
- Parameters:
by_name – Logger names to allow.
by_msg – Messages to allow.
by_attr – Mapping of attribute names to expected values.
Whitelist filter – allow log records that match specific criteria.
- class thinlog.filters.whitelist.WhitelistFilter(by_name: list[str] | None = None, by_msg: list[str] | None = None, by_attr: dict[str, Any] | None = None, **kwargs: Any)[source]#
Bases:
FilterAllow records whose name, message, or attributes match a whitelist.
Records are checked against three criteria (any match is sufficient):
by_name – the record’s logger name.
by_msg – the record’s message (also checked inside JSON-encoded messages).
by_attr – arbitrary record attributes; use the special value
"_any_"to match any value for a given attribute.
- Parameters:
by_name – Logger names to allow.
by_msg – Messages to allow.
by_attr – Mapping of attribute names to expected values.
Blocklist filter – reject log records that match specific criteria.
- class thinlog.filters.blocklist.BlocklistFilter(by_name: list[str] | None = None, by_msg: list[str] | None = None, by_attr: dict[str, Any] | None = None, **kwargs: Any)[source]#
Bases:
FilterBlock records whose name, message, or attributes match a blocklist.
This is the inverse of
WhitelistFilter. Any record that matches is rejected; all others pass through.- Parameters:
by_name – Logger names to block.
by_msg – Messages to block.
by_attr – Mapping of attribute names to values that trigger blocking. Use
"_any_"to block on any value for a given attribute.
Assigner filter – conditionally attach attributes to log records.
- class thinlog.filters.assigner.AssignerFilter(assignments: dict[str, Any], **kwargs: Any)[source]#
Bases:
WhitelistFilterAssign attributes to matching records without blocking any.
Extends
WhitelistFilter– if a record matches the whitelist criteria, the assignments dict is applied to the record as attributes. The filter always returnsTrueso no records are dropped.- Parameters:
assignments – Mapping of attribute names to values that will be set on matching records via
setattr().
Formatters#
Log formatters for JSON, plain-text, and Telegram output.
- class thinlog.formatters.JsonFormatter(show_locals: bool = False, **kwargs: Any)[source]#
Bases:
FormatterSerialize the full log record as a JSON string.
Exception tracebacks are converted to structured dicts using
structlog.tracebacks.ExceptionDictTransformer, and stack-info frames are extracted viaparse_stack_info().- Parameters:
show_locals – If
True, local variables are included in exception tracebacks.
- formatException(ei: tuple[type[BaseException], BaseException, TracebackType | None] | tuple[None, None, None]) str[source]#
Format exception info as a JSON string, falling back to the standard formatter.
- classmethod format_exception(ei: tuple[type[BaseException], BaseException, TracebackType | None] | tuple[None, None, None]) Any[source]#
Transform exception info into a structured dict via structlog.
- classmethod format_record(record: LogRecord) dict[str, Any][source]#
Convert a
LogRecordto a plain dict.Handles
exc_info(via structlog transformer) andstack_info(viaparse_stack_info()).
- transformer: ClassVar[ExceptionDictTransformer]#
- class thinlog.formatters.MsgFormatter(fmt=None, datefmt=None, style='%', validate=True, *, defaults=None)[source]#
Bases:
FormatterReturn just the formatted message string, discarding all other record fields.
- class thinlog.formatters.TelegramFormatter(fmt=None, datefmt=None, style='%', validate=True, *, defaults=None)[source]#
Bases:
FormatterFormat log records as HTML
<code>blocks for the Telegram Bot API.Messages that fit within
MAX_MESSAGE_LEN(4096 chars) are sent as regular messages. Longer messages are split: a truncated caption (up toMAX_CAPTION_LENchars) is sent alongside the full text as a document attachment.- MAX_CAPTION_LEN = 1024#
- MAX_MESSAGE_LEN = 4096#
- format_advanced(record: LogRecord) tuple[str | None, str][source]#
Return a
(caption, text)tuple for Telegram delivery.If the message fits in a single Telegram message, caption is
Noneand text contains the full HTML-wrapped content.If the message is too long, caption is a truncated HTML preview and text is the raw full-length message (sent as a document).
- parse_mode = 'HTML'#
JSON formatter with rich exception context via structlog.
- class thinlog.formatters.json.JsonFormatter(show_locals: bool = False, **kwargs: Any)[source]#
Bases:
FormatterSerialize the full log record as a JSON string.
Exception tracebacks are converted to structured dicts using
structlog.tracebacks.ExceptionDictTransformer, and stack-info frames are extracted viaparse_stack_info().- Parameters:
show_locals – If
True, local variables are included in exception tracebacks.
- formatException(ei: tuple[type[BaseException], BaseException, TracebackType | None] | tuple[None, None, None]) str[source]#
Format exception info as a JSON string, falling back to the standard formatter.
- classmethod format_exception(ei: tuple[type[BaseException], BaseException, TracebackType | None] | tuple[None, None, None]) Any[source]#
Transform exception info into a structured dict via structlog.
- classmethod format_record(record: LogRecord) dict[str, Any][source]#
Convert a
LogRecordto a plain dict.Handles
exc_info(via structlog transformer) andstack_info(viaparse_stack_info()).
- transformer: ClassVar[ExceptionDictTransformer]#
Minimal formatter that returns only the log message.
- class thinlog.formatters.msg.MsgFormatter(fmt=None, datefmt=None, style='%', validate=True, *, defaults=None)[source]#
Bases:
FormatterReturn just the formatted message string, discarding all other record fields.
HTML formatter for Telegram messages with length-aware splitting.
- class thinlog.formatters.telegram.TelegramFormatter(fmt=None, datefmt=None, style='%', validate=True, *, defaults=None)[source]#
Bases:
FormatterFormat log records as HTML
<code>blocks for the Telegram Bot API.Messages that fit within
MAX_MESSAGE_LEN(4096 chars) are sent as regular messages. Longer messages are split: a truncated caption (up toMAX_CAPTION_LENchars) is sent alongside the full text as a document attachment.- MAX_CAPTION_LEN = 1024#
- MAX_MESSAGE_LEN = 4096#
- format_advanced(record: LogRecord) tuple[str | None, str][source]#
Return a
(caption, text)tuple for Telegram delivery.If the message fits in a single Telegram message, caption is
Noneand text contains the full HTML-wrapped content.If the message is too long, caption is a truncated HTML preview and text is the raw full-length message (sent as a document).
- parse_mode = 'HTML'#
Handlers#
Log handlers for stdout, HTTP, and Telegram delivery.
- class thinlog.handlers.CtxPrintHandler(level=0)[source]#
Bases:
HandlerPrint the
contextattribute of a log record as JSON to stdout.If the record has no
contextattribute the handler silently does nothing. Useful for debugging context filters during development.
- class thinlog.handlers.JsonHTTPHandler(settings: JsonHttpHandlerSettings | dict[str, Any])[source]#
Bases:
HandlerSend formatted log records as JSON via HTTP POST using
httpx.The HTTP client is lazily initialised on first use. Per-record overrides (URL, headers, disable) can be provided via a
handlers_context.json_httpdict on the log record.- Parameters:
settings – A
JsonHttpHandlerSettingsor a dict of its fields.
- property client: Client#
Lazily initialised
httpx.Client.
- emit(record: LogRecord) None[source]#
Format and POST the record as JSON.
Supports per-record overrides via
record.handlers_context["json_http"]:disable– skip this record entirely.url– override the target URL.headers– replace the default headers.append_headers– merge additional headers.
- class thinlog.handlers.TelegramHandler(settings: TelegramHandlerSettings | dict[str, Any])[source]#
Bases:
HandlerSend log records to a Telegram chat via the Bot API.
Short messages are sent as regular Telegram messages. Messages exceeding
MAX_MESSAGE_LENare uploaded as document attachments with a truncated caption.Per-record overrides (chat_id, token, topic_id, disable) can be provided via a
handlers_context.telegramdict on the log record.- Parameters:
settings – A
TelegramHandlerSettingsor a dict of its fields.
- API_ENDPOINT = 'https://api.telegram.org'#
- property client: Client#
Lazily initialised
httpx.Client.
- emit(record: LogRecord) None[source]#
Format the record and send it to Telegram.
Short messages are sent via
sendMessage; long messages are uploaded as a document viasendDocument. Supports per-record overrides viarecord.handlers_context["telegram"].
- classmethod format_url(token: str, method: str) str[source]#
Build a Telegram Bot API URL for the given method.
- request(method: str, bot_token: str | None = None, **kwargs: Any) Any[source]#
Call a Telegram Bot API method and return the JSON response.
Handler that prints a record’s context attribute as JSON to stdout.
- class thinlog.handlers.ctx_print.CtxPrintHandler(level=0)[source]#
Bases:
HandlerPrint the
contextattribute of a log record as JSON to stdout.If the record has no
contextattribute the handler silently does nothing. Useful for debugging context filters during development.
Handler that sends JSON log records via HTTP POST.
- class thinlog.handlers.json_http.JsonHTTPHandler(settings: JsonHttpHandlerSettings | dict[str, Any])[source]#
Bases:
HandlerSend formatted log records as JSON via HTTP POST using
httpx.The HTTP client is lazily initialised on first use. Per-record overrides (URL, headers, disable) can be provided via a
handlers_context.json_httpdict on the log record.- Parameters:
settings – A
JsonHttpHandlerSettingsor a dict of its fields.
- property client: Client#
Lazily initialised
httpx.Client.
- emit(record: LogRecord) None[source]#
Format and POST the record as JSON.
Supports per-record overrides via
record.handlers_context["json_http"]:disable– skip this record entirely.url– override the target URL.headers– replace the default headers.append_headers– merge additional headers.
- class thinlog.handlers.json_http.JsonHttpHandlerSettings(url: str, headers: dict[str, str], level: str | int = 0, timeout: int = 5, proxy: str | None = None)[source]#
Bases:
objectSettings for
JsonHTTPHandler.The first instance created is stored as a global singleton accessible via
global_instance().- Parameters:
url – Target URL for HTTP POST requests.
headers – Default HTTP headers sent with every request.
level – Minimum log level (name or int).
timeout – HTTP request timeout in seconds.
proxy – Optional HTTP proxy URL.
- classmethod global_instance() JsonHttpHandlerSettings | None[source]#
Return the first-created settings instance, or
None.
- headers: dict[str, str]#
- level: str | int = 0#
- proxy: str | None = None#
- timeout: int = 5#
- url: str#
Handler that sends log records to Telegram via the Bot API.
- class thinlog.handlers.telegram.TelegramHandler(settings: TelegramHandlerSettings | dict[str, Any])[source]#
Bases:
HandlerSend log records to a Telegram chat via the Bot API.
Short messages are sent as regular Telegram messages. Messages exceeding
MAX_MESSAGE_LENare uploaded as document attachments with a truncated caption.Per-record overrides (chat_id, token, topic_id, disable) can be provided via a
handlers_context.telegramdict on the log record.- Parameters:
settings – A
TelegramHandlerSettingsor a dict of its fields.
- API_ENDPOINT = 'https://api.telegram.org'#
- property client: Client#
Lazily initialised
httpx.Client.
- emit(record: LogRecord) None[source]#
Format the record and send it to Telegram.
Short messages are sent via
sendMessage; long messages are uploaded as a document viasendDocument. Supports per-record overrides viarecord.handlers_context["telegram"].
- classmethod format_url(token: str, method: str) str[source]#
Build a Telegram Bot API URL for the given method.
- request(method: str, bot_token: str | None = None, **kwargs: Any) Any[source]#
Call a Telegram Bot API method and return the JSON response.
- class thinlog.handlers.telegram.TelegramHandlerSettings(token: str, chat_id: int | str, topic_id: int | str | None = None, level: str | int = 0, timeout: int = 5, disable_notification: bool = False, disable_web_page_preview: bool = True, proxy: str | None = None)[source]#
Bases:
objectSettings for
TelegramHandler.The first instance created is stored as a global singleton accessible via
global_instance().- Parameters:
token – Telegram Bot API token.
chat_id – Target chat ID (group, channel, or user).
topic_id – Optional forum topic (message thread) ID.
level – Minimum log level (name or int).
timeout – HTTP request timeout in seconds.
disable_notification – Send silently.
disable_web_page_preview – Disable link previews in messages.
proxy – Optional HTTP proxy URL.
- chat_id: int | str#
- disable_notification: bool = False#
- disable_web_page_preview: bool = True#
- classmethod global_instance() TelegramHandlerSettings | None[source]#
Return the first-created settings instance, or
None.
- level: str | int = 0#
- proxy: str | None = None#
- timeout: int = 5#
- token: str#
- topic_id: int | str | None = None#