# Python

> Install the justlog3 package, log to a file and to the cloud, levels, batching and shutdown behaviour.

The `justlog3` package writes log lines to a file in batches and, once you set an API token, sends them to JustLog3 Cloud from a background thread. `log()` never waits for the disk or the network.

## Install

```bash
pip install justlog3
```

Python 3.8 or newer. `httpx`, used for cloud sending, is installed with the package. Source and benchmarks: [github.com/OAzizjon/justlog3](https://github.com/OAzizjon/justlog3).

## Log to a file

```python
from justlog3 import Logger

logger = Logger('app.log')

logger.log('Hi...')
logger.log('debug', off_console=True)
logger.log('There is some error!', red=True)
logger.log('OK', green=True)
logger.log('There is critical error!', red=True, custom_prefix='CRITICAL!!')
```

The console shows colored lines; `app.log` gets one line per call with a prefix and a local timestamp with milliseconds:

```text
GREY:2026-10-05 14:03:12.517 - Hi...
RED:2026-10-05 14:03:12.518 - There is some error!
GREEN:2026-10-05 14:03:12.518 - OK
```

## Send to JustLog3 Cloud

```python
import os
from justlog3 import Logger, set_api_token

set_api_token(os.environ['JUSTLOG3_TOKEN'], url='https://jl3-cloud.site/api/logs/')
logger = Logger('app.log')
logger.log('This line goes to the file and to the cloud')
```

Call `set_api_token()` once, before or after creating loggers. Keep the token in an environment variable rather than in code.

- Lines are sent every `flush_interval` seconds (default 5), or sooner once `cloud_cycles` lines (default 75) are waiting.
- A request carries at most 5,000 lines and 512 KB of text; bodies from 1 KB are gzip-compressed.
- Each logger keeps up to 10,000 unsent lines; beyond that the oldest are dropped with a single warning.
- No answer or HTTP 5xx: the batch is resent with the same sequence number, so the server stores it once. The wait starts at 5 s and doubles up to 5 minutes.
- HTTP 429: nothing is sent until `Retry-After`; lines stay buffered.
- HTTP 401 / 403: cloud sending stops, files keep being written. Call `set_api_token()` again with a valid token.
- On exit the remaining lines are sent, waiting at most `timeout` seconds.

`set_api_token(token, *, url=..., flush_interval=5.0, timeout=5.0)` raises `ValueError` for an empty token.

## Levels

```python
from justlog3 import BasicLogger

logger = BasicLogger('app.log', console_level='INFO')
logger.debug('cache warmed')         # file and cloud only: below INFO
logger.info('server started')
logger.warning('disk 85% full')
logger.error('payment failed')
logger.critical('database is down')
```

`console_level` only filters the console. Every line still goes to the file and the cloud.

## Shutdown and signals

- Normal exit: every logger is flushed by an `atexit` hook.
- SIGTERM (`docker stop`, systemd, `kill`): JustLog3 turns the signal into a normal exit so the flush runs. Call `off_signal_handler()` before creating any logger to keep your own handler.
- `kill -9` or a power cut loses what is still in the buffer: at most `cycles - 1` lines (default 49).
- Call `shutdown()` yourself before `os._exit()`.

## Logger options

| Parameter | Default | Meaning |
|---|---|---|
| `filename` | `'app.log'` | `.log` is added if missing |
| `with_time` | `True` | timestamp in every line |
| `filemode` | `'a'` | `'w'` empties the file once, on creation |
| `cycles` | `50` | lines per disk write |
| `cloud_cycles` | `75` | waiting lines that wake the cloud sender |
| `off_sigmask` | `False` | don't hold back SIGTERM during writes (Linux, macOS) |
