📜 simplibs-logger
A logger that works the moment you create it.
A ready-to-use logger built on the standard logging module, not instead of it: one constructor gives
you a colored console, an optional rotating file, optional JSON output and a short way to write —
logger.log_info("text %s", value). A wrong level, format or stream fails when you give it, not when the
first record is written, and a record always points at the line of your code.
from simplibs.logger import SimpleLogger
logger = SimpleLogger(name="my_app", file_path="logs/app.log")
logger.log_info("Application started")
logger.log_success("Import finished: %d rows", 120) # SUCCESS, level 25 — between INFO and WARNING
logger("Disk almost full", level="warning") # the logger is callable
Works with
simplibs-cli:resolve_verbosity(quiet=…, verbose=…)returns a level thatSimpleLogger(level=…)accepts as it is.
🧭 The Core Philosophy
The standard logging module is powerful and starts empty: a logger with no handler, a default level that
swallows info, a format that fails with a bare ValueError on the first record, a file that grows
until the disk is full. Every program configures the same things by hand — and every copy forgets one.
simplibs-logger is that configuration, written once and checked.
- It works at once. The default level is INFO, the console is colored, and nothing needs configuring
before
log_infowrites a line. - On top of
logging, never beside it. ASimpleLoggerwraps a reallogging.Logger(logger.logger), and the handlers and formatters are standard classes you can attach to any logger yourself. - A wrong value fails early. Levels, names, formats, output kinds and streams are validated when they are given — with the very function the standard library will call later.
- Build first, swap after. Every handler is built, and the file opened, before the existing logger is touched. A bad path leaves the previous setup in place, and handlers are closed, never leaked.
- The right place in the traceback. File, line and function are those of the code that called the
logger, for
log_info, forlogger("text")and for everything between. - Text for people, JSON for machines. A readable console and a JSON Lines file are one call.
- Layers that only look left.
definitionsandannotations←levels←formatting←handlers←lifecycle←SimpleLogger— every layer imports only from those before it.
SimpleLogger(level="loud") # -> raises: unknown level name
SimpleLogger(message_format="%(asctime)s %") # -> raises: not a valid message format
SimpleLogger(max_bytes=0) # -> raises: zero would mean "never rotate"
📦 Installation
pip install simplibs-logger
Requires Python 3.11+. The runtime dependencies (installed automatically) are other simplibs libraries:
simplibs-exception for the error card of a log file that
cannot be opened, simplibs-object for the validated atoms,
simplibs-validate for @validate_call and the validated
types, simplibs-path for the log file's path,
simplibs-style for the colors, and
simplibs-file for the type of the encoding.
🚀 Quick Start in 60 Seconds
Level 1: A logger with no setup
from simplibs.logger import SimpleLogger
logger = SimpleLogger()
logger.log_debug("Not shown — the default level is INFO")
logger.log_info("Application started")
logger.log_success("Import finished: %d rows", 120)
logger.log_warning("Disk almost full")
logger.log_error("Request failed")
2026-10-24 12:00:00 | INFO | simplibs | Application started
2026-10-24 12:00:00 | SUCCESS | simplibs | Import finished: 120 rows
2026-10-24 12:00:00 | WARNING | simplibs | Disk almost full
2026-10-24 12:00:00 | ERROR | simplibs | Request failed
The records go to standard error, and the level names are colored in a terminal. The columns stay straight, because the padding counts the visible name, not the color codes.
Level 2: A file that rotates, closed properly
from simplibs.logger import SimpleLogger
with SimpleLogger(name="my_app", level="debug", file_path="logs/app.log",
max_bytes=1_000_000, backup_count=5) as logger:
logger.log_debug("Connecting to %s", "db.local")
logger.log_info("Ready")
logs/ and the file are created when missing. The file is plain text, never colored, and at 1 MB it
becomes app.log.1 while five older files are kept. Leaving the with block closes the file.
Level 3: JSON for the machines
from simplibs.logger import SimpleLogger
logger = SimpleLogger(name="api", file_path="logs/api.jsonl", file_output="json", utc=True)
logger.log_info("request done", extra={"request_id": "r-7", "status": 200})
The console stays readable text; the file gets one JSON object per line:
{"time":"2026-10-24T12:34:56.789Z","level":"INFO","logger":"api","message":"request done","function":"<module>","file":"app.py","line":5,"request_id":"r-7","status":200}
Level 4: Exceptions, the switch and your own wiring
import logging
from simplibs.logger import SimpleLogger, build_console_handler
logger = SimpleLogger(name="importer", level="debug")
def import_rows():
raise ValueError("row 3 is broken")
try:
import_rows()
except ValueError:
logger.log_exception("Import failed") # ERROR + the traceback
if logger.is_enabled_for("debug"): # worth asking before an expensive message
logger.log_debug("state: %s", {"rows": 3})
logger.enabled = False # silence it, whatever the level
logger.log_error("not written")
# the pieces work on any standard logger
third_party = logging.getLogger("third_party")
third_party.addHandler(build_console_handler(level="warning"))
🛠️ Architecture & Package Structure
src/simplibs/logger/
├── annotations/ ◄── Validated types for signatures (BackupCount, MaxBytes, LogStream)
├── definitions/ ◄── Validated atoms and fixed values (LoggerLevel, LoggerName, ... SUCCESS_LEVEL)
├── levels/ ◄── A name to a number, and the name SUCCESS (resolve_level, register_success_level)
├── formatting/ ◄── How a record looks (build_formatter, ColoredFormatter, JsonFormatter)
├── handlers/ ◄── Where records go (build_console_handler, build_file_handler)
├── lifecycle/ ◄── Create and release (create_logger, close_logger)
└── SimpleLogger.py ◄── The ready-to-use logger class
The root of the package exports everything — the logger, the building blocks, the atoms, the constants and
the annotations — so one import line is all a program needs. The folders are plain packages for the code's
own organization; nothing has to be imported from them. The internal _helpers and _validations of each
folder are not exported.
from simplibs.logger import SimpleLogger, create_logger, close_logger
from simplibs.logger import build_console_handler, build_file_handler, build_formatter, JsonFormatter
📒 SimpleLogger — The Ready-to-Use Logger
The class most programs need: a configured logging.Logger behind a small calling interface. The
constructor builds the console, the file, the colors and the JSON output; log_info, log_success,
log_warning, … write the records, logger("text") writes at the default level, and log_exception
adds the traceback. It has an enabled switch, is_enabled_for(level), set_level, close() and works as
a context manager.
logger = SimpleLogger(file_path="logs/app.log")
logger.log_info("user %s logged in", name)
🔄 Lifecycle — Create and Release
Two functions underneath SimpleLogger, for a program that wants a plain logging.Logger:
create_logger builds one, validated, with no duplicate handlers however often it is called, and
close_logger detaches and closes every handler so the log file is released.
logger = create_logger(name="my_app", level="debug", file_path="logs/app.log", propagate=True)
close_logger(logger)
📮 Handlers — Where Records Go
Two builders of standard handlers: build_console_handler (standard error by default, any object with
write as a stream) and build_file_handler (creates its folder and file, rotates with max_bytes, and
turns a failed open into a structured card). A handler has no level of its own unless one is asked for, so
the logger decides.
console = build_console_handler(level="warning", output="json")
file = build_file_handler("logs/app.log", max_bytes=1_000_000)
🎨 Formatting — How a Record Looks
build_formatter is the one place a formatter is made and validated. ColoredFormatter colors the level
name and keeps the columns straight; JsonFormatter writes each record as one line of valid JSON, with the
fields of extra= as flat keys.
| Output | Formatter | For |
|---|---|---|
text |
logging.Formatter |
A file read by a person. |
text, colored |
ColoredFormatter |
The console. |
json |
JsonFormatter |
A log collector, jq, a container's stderr. |
📶 Levels — Numbers and Names
resolve_level turns a level written as a number or a name ("debug", "SUCCESS", 35) into the number,
and register_success_level gives the number 25 the name SUCCESS in logging, once, without overwriting
a name another library chose.
resolve_level("debug") # -> 10
resolve_level(None) # -> 20
🧬 Definitions — Validated Atoms and Fixed Values
The vocabulary of the library. Five validated atoms say what a value may be and what it is when nobody chose; three constants hold the fixed values behind them.
| Name | What it is |
|---|---|
LoggerLevel |
A positive number or a level name; default INFO. |
LoggerName |
A name that cannot reach the root logger; default "simplibs". |
DateFormat |
A time.strftime format; default "%Y-%m-%d %H:%M:%S". |
MessageFormat |
A %-style line format, syntax checked, field names free. |
OutputFormat |
"text" (default) or "json". |
SUCCESS_LEVEL, LEVEL_NAMES, OUTPUT_FORMATS |
The level 25, the six level names, the two output formats. |
🔖 Annotations — Types for Your Signatures
Three validated types that this library's signatures use and a caller can use in theirs, with
@validate_call:
| Type | Accepts |
|---|---|
BackupCount |
An int of at least one. |
MaxBytes |
An int greater than zero, or None — never rotate. |
LogStream |
Any object with write, or None — standard error. |
📖 Quick Reference
📒 SimpleLogger — The Ready-to-Use Logger
| Member | Description / Parameters |
|---|---|
SimpleLogger(...) |
Builds the logger (enabled, name, level, default_level, file_path, encoding, console, colored, console_level, file_level, stream, message_format, date_format, utc, max_bytes, backup_count, console_output, file_output, json_default). |
log, logger(...) |
Writes at level or at default_level (message, *args, level=None, **kwargs). |
log_debug … log_critical |
One method per level, incl. log_success (message, *args, **kwargs). |
log_exception |
ERROR with the traceback (message, *args, **kwargs). |
is_enabled_for |
Would a record of this level be written now (level=None). |
set_level |
Changes the level of the logger (level). |
enabled |
The switch; assignable. |
logger, name, level |
The underlying logger, its name and level. Read-only. |
close, with |
Closes every handler and stops writing. |
🔄 lifecycle/ — Create and Release
| Function | Description / Parameters |
|---|---|
create_logger |
Builds a validated logging.Logger (the arguments of SimpleLogger without enabled and default_level, plus propagate=False). |
close_logger |
Detaches and closes every handler of a logger (logger). |
📮 handlers/ — Where Records Go
| Function | Description / Parameters |
|---|---|
build_console_handler |
A stream handler (level, stream, message_format, date_format, colored=True, utc, output, json_default). |
build_file_handler |
A file handler, optionally rotating (file_path, level, encoding, message_format, date_format, utc, max_bytes, backup_count=3, output, json_default). |
🎨 formatting/ — How a Record Looks
| Name | Description / Parameters |
|---|---|
build_formatter |
A validated formatter (message_format, date_format, colored=False, utc, output, json_default). |
ColoredFormatter |
Colors the level name, padding included (fmt, datefmt). |
JsonFormatter |
One JSON object per line (utc=False, default=None). |
📶 levels/ — Numbers and Names
| Function | Description / Parameters |
|---|---|
resolve_level |
A level as a number (level=None). |
register_success_level |
Names the number 25 SUCCESS, once. |
🧬 definitions/ — Validated Atoms and Fixed Values
| Name | Description |
|---|---|
LoggerLevel, LoggerName |
A level (default INFO); a logger name (default "simplibs"). |
DateFormat, MessageFormat |
A strftime format; a %-style line format. |
OutputFormat |
"text" or "json". |
SUCCESS_LEVEL, LEVEL_NAMES, OUTPUT_FORMATS |
25; the six level names; ("text", "json"). |
🔖 annotations/ — Types for Your Signatures
| Type | Description |
|---|---|
BackupCount |
How many rotated files to keep; at least one. |
MaxBytes |
A size limit in bytes, or None for no rotation. |
LogStream |
A stream with write, or None for standard error. |
🔌 Streams, Colors and Files
How the logger behaves where the output goes somewhere other than a terminal:
- The console is standard error. Diagnostics belong there. A program that prints its log next to its own
output passes
stream=sys.stdout; any object withwriteworks. - The color follows the style switch, not the stream.
simplibs-styledecides — fromNO_COLOR,FORCE_COLOR,TERM=dumband from whether standard output is a terminal. A program that redirects only one of the two passescolored=False, or callsset_styles_enabled(False). - Files and JSON are never colored. ANSI codes do not belong in a file.
- The default level is INFO (the standard library starts at WARNING), and
SUCCESSis the number 25. - One name, one logger.
SimpleLogger(name="x")twice gives the same underlying logger and the second reconfigures the first. The default name"simplibs"is the parent of everysimplibs.*logger, so the default logger also receives what other simplibs libraries log through plainlogging. ASimpleLoggerorcreate_loggerlogger does not pass its records up unlesspropagate=True. - Close what you open.
close()andwithrelease the log file — on Windows an open handler blocks deleting or rotating it. On Windows, rotation also needs the file to be open in one process only.
⚠️ Exceptions
The library defines one error of its own: a structured
AccessError card, with a name that starts with LOGGER_.
Everything else is refused by the libraries it is built on, each with its own structured card: an invalid
level, name or format by the atoms (simplibs-object), a wrong argument type by @validate_call
(simplibs-validate), and a path that cannot be prepared by ensure_file (simplibs-path).
| Error name | Card | Wraps | Raised when |
|---|---|---|---|
LOGGER_FILE_OPEN_FAILED |
AccessError |
the caught OSError (its own type) |
The OS refuses to open the log file. |
Because the card keeps the original exception type, except PermissionError still works.
🔗 Related libraries
simplibs-exception— the structured card of a log file that cannot be opened.simplibs-object—SimpleConstant, the base of the validated atoms.simplibs-validate—@validate_calland the validated types behind the annotations.simplibs-path—ensure_fileandPathLikefor the log file.simplibs-style— the colors of the level names and the switch that turns them off.simplibs-file—EncodingName, the type of the file's encoding.simplibs-cli—resolve_verbosityturns--quietand-vinto a level for this logger.
☯️ About simplibs
All libraries in the simplibs (Simple Libraries) ecosystem share a common engineering philosophy:
- Dyslexia-friendly: We actively minimize cognitive load. Code is atomized into small, self-contained units, files are named directly after the logical task they perform, and explanations describe why something is designed, not just what it is.
- Programmer's Zen: Nothing should be missing, and nothing should be superfluous. We value clean execution paths and robust, understandable code architectures over rushed, messy feature sets.
- Defensive Style: We actively anticipate edge cases and failure modes so that only safe operational paths remain. Our code is built to degrade gracefully rather than crash unexpectedly.
- Minimalism: Find the most direct path to the goal in as few operational steps as possible without taking shortcuts on safety, readability, or completeness.
- Code as Craft: Code should be pleasant to look at, readable at a glance, and evoke structural harmony. We treat software engineering as a precision trade.
🤝 Contributing & Community
This is an open-source project built with love and care. We strongly believe in community collaboration and welcome any feedback, bug reports, or feature ideas!
- Want to contribute? Feel free to open an Issue or submit a Pull Request.
- Want to get in touch? If you'd like to discuss the project further, collaborate, or just say hello, feel free to open a GitHub Issue or start a Discussion.
📝 License
This library is released under the MIT License. Build great things!
Metadata
Release files for simplibs-logger 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| simplibs_logger-0.1.0.tar.gz | 52.2 kB | Without provenance | Details for simplibs_logger-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| simplibs_logger-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for simplibs_logger-0.1.0-py3-none-any.whl |
Total release size: 111.8 kB
Release files / simplibs_logger-0.1.0.tar.gz
| Download URL | simplibs_logger-0.1.0.tar.gz |
|---|---|
| Size | 52.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
821be5b122ee28c29e9fc8b7cb38e3ddeaea8523f95ce7f972e20b49ef108b73
|
|
BLAKE2b-256 checksum How to use checksums |
b947fe8885b672a2cffccef27f85f3047417a0290ce30f9a7dfcf939ff686378
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.9
|
Release files / simplibs_logger-0.1.0-py3-none-any.whl
| Download URL | simplibs_logger-0.1.0-py3-none-any.whl |
|---|---|
| Size | 59.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
29b346b453e86f7551d4a37c1a6c824ed20c5de8f6bca6a08b6f6dd2017522a7
|
|
BLAKE2b-256 checksum How to use checksums |
1230e1489f291170cc47e6d9177e4d50e9ef2aac63553f31a9d789a35f95e3d3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.9
|
Security for simplibs-logger 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.