Metadata-Version: 2.4
Name: lognest
Version: 0.1.3
Summary: A friendly, zero-config Python logger with pretty output, structured context, file rotation, and clean stdlib interop.
Author: lognest
License: MIT
Keywords: logging,logger,structured,color,console
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: System :: Logging
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# lognest

A friendly, zero-configuration Python logger.

One import. Beautiful colored output. Structured context. File rotation.
Pretty tracebacks. Thread-safe. Plays nicely with the standard `logging`
module. Pure Python, no dependencies.

```python
from lognest import log

log.info("hello world")
log.warning("careful now", user_id=42)
log.success("all done!")
```

```text
2026-05-21 12:34:56.789 | INFO     | __main__:<module>:3 | hello world
2026-05-21 12:34:56.790 | WARNING  | __main__:<module>:4 | careful now user_id=42
2026-05-21 12:34:56.790 | SUCCESS  | __main__:<module>:5 | all done!
```

## Install

```bash
pip install lognest
```

## Features at a glance

| Feature              | Example                                                              |
| -------------------- | -------------------------------------------------------------------- |
| Zero config          | `from lognest import log; log.info("hi")`                            |
| Levels               | `trace · debug · info · success · warning · error · critical`        |
| Structured context   | `log.info("done", request_id="abc", ms=42)`                          |
| Bound context        | `req_log = log.bind(request_id="abc")`                               |
| Scoped context       | `with log.contextualize(user="alice"): ...`                          |
| File output          | `log.add("app.log")`                                                 |
| Size rotation        | `log.add("app.log", rotate="10 MB", keep=5)`                         |
| Time rotation        | `log.add("app.log", rotate="daily", keep="7 days", compress=True)`   |
| JSON output          | `log.add("app.jsonl", serialize=True)`                               |
| Custom format        | `log.add(sys.stderr, format="{time} {level} {message}")`             |
| Exception catching   | `@log.catch` / `with log.catch(): ...`                               |
| Exception with TB    | `try: ... except: log.exception("oops")`                             |
| stdlib interop       | `log.intercept_stdlib()`                                             |
| Custom sink          | `log.add(my_callable)`                                               |
| Per-sink filter      | `log.add(..., filter=lambda r: "secret" not in r["message"])`        |
| Per-sink level       | `log.add(..., level="WARNING")`                                      |

## Logging

```python
from lognest import log

log.trace("very fine detail")
log.debug("debug detail")
log.info("informational")
log.success("operation succeeded")
log.warning("watch out")
log.error("something failed")
log.critical("crashing now")
```

Pass keyword arguments to attach **structured context** to a record:

```python
log.info("payment processed", order_id=1234, amount=29.99, currency="USD")
# ... payment processed order_id=1234 amount=29.99 currency=USD
```

The message itself can also reference kwargs by name:

```python
log.info("user {name} logged in from {ip}", name="alice", ip="10.0.0.1")
```

## Bound and scoped context

```python
req_log = log.bind(request_id="abc-123", user="alice")
req_log.info("started")
req_log.info("finished", ms=42)
```

```python
with log.contextualize(job="cleanup"):
    log.info("removed temp files")     # job=cleanup appears in the record
```

## Files and rotation

`log.add(...)` registers a new sink. The default `stderr` sink stays in
place; you do not need to remove it.

```python
# Plain file
log.add("app.log")

# Rotate every 10 megabytes, keep the 5 most recent
log.add("app.log", rotate="10 MB", keep=5)

# Rotate every day, keep a week, gzip the rotated files
log.add("app.log", rotate="daily", keep="7 days", compress=True)

# Errors only
log.add("errors.log", level="ERROR")

# JSON lines (great for log shippers and analytics)
log.add("events.jsonl", serialize=True)

# Custom format string
log.add("simple.log", format="{time} [{level}] {message}")

# Custom callable sink — receives the fully-rendered line
log.add(lambda line: my_queue.put(line))
```

`log.add(...)` returns an integer id. Use it to remove a single sink, or
call `log.remove()` to remove all sinks.

```python
sid = log.add("app.log")
log.remove(sid)
log.remove()           # remove everything (incl. the default stderr sink)
```

Rotation accepts:

- A size: `"10 MB"`, `"500 KB"`, `"1 GB"`, or a raw byte count `10_000_000`
- A schedule: `"hourly"`, `"daily"`, `"weekly"`, `"monthly"`

Retention accepts:

- A count: `keep=5` keeps the five most recent rotated files
- An age: `keep="7 days"` removes anything older

## Catching exceptions

As a context manager:

```python
with log.catch():
    risky_call()
```

As a decorator:

```python
@log.catch
def task():
    1 / 0

@log.catch(reraise=True, message="task crashed")
def task():
    ...
```

Inside an `except` block:

```python
try:
    risky_call()
except Exception:
    log.exception("call failed", endpoint="/api/x")
```

All three produce a clean, color-coded traceback.

## Standard library interop

If a third-party library uses `logging.getLogger(...)`, just reroute it:

```python
from lognest import log
log.intercept_stdlib()         # everything now flows through lognest

import some_library
some_library.do_thing()
```

## Custom format strings

The default `format` may be a `str` template or a callable.

Available placeholders for string templates:

```
{time} {level} {name} {function} {line} {file}
{message} {thread} {process}
{extra.key}            # any key attached via bind / contextualize / kwargs
```

For complete control, pass a callable that receives the record dict and
returns a string:

```python
def my_format(record):
    return f"[{record['level']}] {record['message']}"

log.add(sys.stderr, format=my_format)
```

## Why lognest?

There are good loggers in the Python ecosystem already. `lognest` aims for
the **shortest path** from "I want logs" to "I have nice logs":

- One import, one object, sensible defaults — no factories, no handlers
  to wire up.
- Structured context is just `kwargs` on every call.
- Configuration uses plain words: `rotate="10 MB"`, `keep="7 days"`.
- Catching exceptions and intercepting the stdlib is a one-liner.

## License

MIT.
