Metadata-Version: 2.4
Name: simplibs-logger
Version: 0.1.0
Summary: A ready-to-use logger on top of the standard logging module: a console, an optional rotating file, colored level names, optional JSON output and a short calling interface.
Author-email: "Dalibor Sova (Sudip2708)" <daliborsova@seznam.cz>
License-Expression: MIT
Project-URL: Homepage, https://github.com/simplibs/simplibs-logger
Project-URL: Repository, https://github.com/simplibs/simplibs-logger
Project-URL: Issues, https://github.com/simplibs/simplibs-logger/issues
Project-URL: Changelog, https://github.com/simplibs/simplibs-logger/blob/main/CHANGELOG.md
Keywords: logger,logging,log,console,json,jsonl,rotating-file,colors,simplibs,simplibs-logger,developer-tools
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Logging
Classifier: Typing :: Typed
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: simplibs-exception>=1.1.4
Requires-Dist: simplibs-object>=0.1.0
Requires-Dist: simplibs-validate>=0.3.2
Requires-Dist: simplibs-path>=0.1.1
Requires-Dist: simplibs-style>=0.1.0
Requires-Dist: simplibs-file>=0.1.0
Provides-Extra: dev
Requires-Dist: pytest>=9.0; extra == "dev"
Requires-Dist: pytest-cov>=5.0; extra == "dev"
Dynamic: license-file

# 📜 `simplibs-logger`

[![PyPI](https://img.shields.io/pypi/v/simplibs-logger)](https://pypi.org/project/simplibs-logger/)
[![Python](https://img.shields.io/badge/python-3.11%2B-blue)](https://www.python.org/downloads/)
[![Licence](https://img.shields.io/badge/licence-MIT-green)](https://github.com/simplibs/simplibs-logger/blob/main/LICENSE)
[![tests](https://github.com/simplibs/simplibs-logger/actions/workflows/tests.yml/badge.svg)](https://github.com/simplibs/simplibs-logger/actions)

**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.

```python
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`](https://pypi.org/project/simplibs-cli/): `resolve_verbosity(quiet=…, verbose=…)`
> returns a level that `SimpleLogger(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_info` writes a line.
* **On top of `logging`, never beside it.** A `SimpleLogger` wraps a real `logging.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`, for `logger("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.** `definitions` and `annotations` ← `levels` ← `formatting` ← `handlers` ←
  `lifecycle` ← `SimpleLogger` — every layer imports only from those before it.

```python
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

```bash
pip install simplibs-logger
```

Requires Python 3.11+. The runtime dependencies (installed automatically) are other simplibs libraries:
[`simplibs-exception`](https://pypi.org/project/simplibs-exception/) for the error card of a log file that
cannot be opened, [`simplibs-object`](https://pypi.org/project/simplibs-object/) for the validated atoms,
[`simplibs-validate`](https://pypi.org/project/simplibs-validate/) for `@validate_call` and the validated
types, [`simplibs-path`](https://pypi.org/project/simplibs-path/) for the log file's path,
[`simplibs-style`](https://pypi.org/project/simplibs-style/) for the colors, and
[`simplibs-file`](https://pypi.org/project/simplibs-file/) for the type of the encoding.

---

## 🚀 Quick Start in 60 Seconds

### Level 1: A logger with no setup

```python
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")
```

```text
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

```python
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

```python
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:

```json
{"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

```python
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.

```python
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.

```python
logger = SimpleLogger(file_path="logs/app.log")
logger.log_info("user %s logged in", name)
```

➡️ [README_LOGGER_SIMPLELOGGER](https://github.com/simplibs/simplibs-logger/blob/main/docs/README_LOGGER_SIMPLELOGGER.md)

---

## 🔄 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.

```python
logger = create_logger(name="my_app", level="debug", file_path="logs/app.log", propagate=True)
close_logger(logger)
```

➡️ [README_LOGGER_LIFECYCLE](https://github.com/simplibs/simplibs-logger/blob/main/docs/README_LOGGER_LIFECYCLE.md)

---

## 📮 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.

```python
console = build_console_handler(level="warning", output="json")
file = build_file_handler("logs/app.log", max_bytes=1_000_000)
```

➡️ [README_LOGGER_HANDLERS](https://github.com/simplibs/simplibs-logger/blob/main/docs/README_LOGGER_HANDLERS.md)

---

## 🎨 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.   |

➡️ [README_LOGGER_FORMATTING](https://github.com/simplibs/simplibs-logger/blob/main/docs/README_LOGGER_FORMATTING.md)

---

## 📶 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.

```python
resolve_level("debug")        # -> 10
resolve_level(None)           # -> 20
```

➡️ [README_LOGGER_LEVELS](https://github.com/simplibs/simplibs-logger/blob/main/docs/README_LOGGER_LEVELS.md)

---

## 🧬 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. |

➡️ [README_LOGGER_DEFINITIONS](https://github.com/simplibs/simplibs-logger/blob/main/docs/README_LOGGER_DEFINITIONS.md)

---

## 🔖 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.     |

➡️ [README_LOGGER_ANNOTATIONS](https://github.com/simplibs/simplibs-logger/blob/main/docs/README_LOGGER_ANNOTATIONS.md)

---

## 📖 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.                                                    |

➡️ [README_LOGGER_SIMPLELOGGER](https://github.com/simplibs/simplibs-logger/blob/main/docs/README_LOGGER_SIMPLELOGGER.md)

---

### 🔄 `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`).                                |

➡️ [README_LOGGER_LIFECYCLE](https://github.com/simplibs/simplibs-logger/blob/main/docs/README_LOGGER_LIFECYCLE.md)

---

### 📮 `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`). |

➡️ [README_LOGGER_HANDLERS](https://github.com/simplibs/simplibs-logger/blob/main/docs/README_LOGGER_HANDLERS.md)

---

### 🎨 `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`).                               |

➡️ [README_LOGGER_FORMATTING](https://github.com/simplibs/simplibs-logger/blob/main/docs/README_LOGGER_FORMATTING.md)

---

### 📶 `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.                             |

➡️ [README_LOGGER_LEVELS](https://github.com/simplibs/simplibs-logger/blob/main/docs/README_LOGGER_LEVELS.md)

---

### 🧬 `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")`.      |

➡️ [README_LOGGER_DEFINITIONS](https://github.com/simplibs/simplibs-logger/blob/main/docs/README_LOGGER_DEFINITIONS.md)

---

### 🔖 `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.     |

➡️ [README_LOGGER_ANNOTATIONS](https://github.com/simplibs/simplibs-logger/blob/main/docs/README_LOGGER_ANNOTATIONS.md)

---

## 🔌 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 with `write` works.
* **The color follows the style switch, not the stream.** `simplibs-style` decides — from `NO_COLOR`,
  `FORCE_COLOR`, `TERM=dumb` and from whether *standard output* is a terminal. A program that redirects
  only one of the two passes `colored=False`, or calls `set_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 `SUCCESS` is 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 every `simplibs.*` logger, so the default logger also
  receives what other simplibs libraries log through plain `logging`. A `SimpleLogger` or `create_logger`
  logger does not pass its records up unless `propagate=True`.
* **Close what you open.** `close()` and `with` release 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`](https://pypi.org/project/simplibs-exception/) 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`](https://pypi.org/project/simplibs-exception/)** — the structured card of a log file that cannot be opened.
* **[`simplibs-object`](https://pypi.org/project/simplibs-object/)** — `SimpleConstant`, the base of the validated atoms.
* **[`simplibs-validate`](https://pypi.org/project/simplibs-validate/)** — `@validate_call` and the validated types behind the annotations.
* **[`simplibs-path`](https://pypi.org/project/simplibs-path/)** — `ensure_file` and `PathLike` for the log file.
* **[`simplibs-style`](https://pypi.org/project/simplibs-style/)** — the colors of the level names and the switch that turns them off.
* **[`simplibs-file`](https://pypi.org/project/simplibs-file/)** — `EncodingName`, the type of the file's encoding.
* **[`simplibs-cli`](https://pypi.org/project/simplibs-cli/)** — `resolve_verbosity` turns `--quiet` and `-v` into 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!

---
