Metadata-Version: 2.4
Name: humanlog
Version: 0.1.0
Summary: One-line progress + logging for humans.
Author-email: srichs <srichs@pm.me>
License: MIT
Project-URL: Homepage, https://github.com/srichs/humanlog
Project-URL: Repository, https://github.com/srichs/humanlog
Project-URL: Issues, https://github.com/srichs/humanlog/issues
Project-URL: Changelog, https://github.com/srichs/humanlog/blob/main/CHANGELOG.md
Keywords: logging,cli,progress,terminal,developer-tools
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.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Logging
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: black>=24.0; extra == "dev"
Requires-Dist: mypy>=1.0; extra == "dev"
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-cov>=5.0; extra == "dev"
Requires-Dist: ruff>=0.5; extra == "dev"
Requires-Dist: sphinx>=7.0; extra == "dev"
Dynamic: license-file

# humanlog

[![CI](https://github.com/srichs/humanlog/actions/workflows/ci.yml/badge.svg)](https://github.com/srichs/humanlog/actions/workflows/ci.yml)
[![PyPI](https://img.shields.io/pypi/v/humanlog.svg)](https://pypi.org/project/humanlog/)

One-line logging + progress for humans.  
Works in terminals, CI, and Jupyter — no setup, no configuration.

```python
from humanlog import log

log.step("Downloading data")
...
log.done(rows=120_000)

log.info("Cleaning finished")
log.warn("Missing values detected", cols=3)
```

## Why humanlog?

Most logging tools are either:

- too low-level (stdlib logging),
- too heavy (structured logging frameworks),
- or great at one thing (progress bars) but awkward for everything else.

`humanlog` is opinionated and small:

- readable output
- automatic timing
- graceful behavior everywhere
- zero configuration

It’s for applications, scripts, CLIs, and data workflows.

## Install

```bash
pip install humanlog
```

Python 3.9+ (no dependencies).

## The core idea

### Steps feel like steps

```python
log.step("Training model")
train()
log.done(loss=0.031)
```

Output (terminal):

```text
→ Training model …
✓ Training model (loss=0.031, time=2.41s)
```

No manual timers. No formatting. No state.

### Informational messages

```python
log.info("Loading dataset", rows=120_000)
log.warn("Missing values detected", cols=3)
log.error("Failed to connect to database")
```

Output:

```text
[12:40:03] ℹ Loading dataset (rows=120000)
[12:40:04] ⚠ Missing values detected (cols=3)
[12:40:05] ✖ Failed to connect to database
```

## Works everywhere

### Terminal (TTY)

- Animated steps
- Clean overwrite
- Human-friendly symbols

### CI (GitHub Actions, GitLab, etc.)

- No carriage-return garbage
- Timestamped, line-by-line logs

### Jupyter notebooks

- No broken progress bars
- Clean output cells

`humanlog` automatically detects where it’s running and adapts.

Set `HUMANLOG_NO_ANIMATE=1` (or `NO_COLOR=1`) to force non-animated output.

## Zero configuration

No handlers.  
No log levels to wire up.  
No global logging side effects.

Just import and go.

## API

### `log.step(message: str)`

Start a timed step.

If another step is already active, `humanlog` automatically finishes it first so
you never end up with overlapping step state.

You can also use it as a context manager to auto-complete the step:

```python
with log.step("Training model"):
    train()
```

If an exception escapes the block, the step is automatically closed as failed:

```text
[12:40:05] ✖ Training model (error=RuntimeError, time=0.73s)
```

### `log.done(**info)`

Finish the current step and print timing + optional key/value info.

### `log.info(message: str, **info)`

Print an informational message.

If a step is active, it is automatically completed before the info line is
printed.

### `log.warn(message: str, **info)`

Print a warning (to stderr).

If a step is active, it is automatically completed before the warning is
printed.

### `log.error(message: str, **info)`

Print an error (to stderr).

If a step is active, it is automatically completed before the error is printed.

Key/value info is always optional and rendered inline:

```python
log.info("Loaded batch", batch=3, rows=1024)
```

## Example: a real script

```python
from humanlog import log

log.step("Downloading data")
download()
log.done(rows=120_000)

log.step("Cleaning data")
clean()
log.done(dropped=341)

log.step("Training model")
train()
log.done(epochs=10, loss=0.031)

log.info("All done")
```

Readable. Intentional. Calm.

## Non-goals (by design)

`humanlog` is not:

- a replacement for structured logging
- a metrics system
- a tracing framework
- a progress-bar DSL

If you need JSON logs or OpenTelemetry, use those tools.

If you want pleasant, readable output, use `humanlog`.

## Roadmap (likely, but optional)

- `log.track(iterable, label="Processing")`
- optional Rich-powered rendering
- stdlib logging bridge (opt-in)

The core will stay small.

## Philosophy

`humanlog` optimizes for:

- clarity over flexibility
- defaults over configuration
- humans over machines

Logs are for people first.

## Contributing

Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for local setup and quality checks.

## Changelog

Release history is tracked in [CHANGELOG.md](CHANGELOG.md).

## Releasing

Maintainers can follow [RELEASE.md](RELEASE.md) for packaging, verification, and publish steps.

## License

MIT
