|
1 | 1 | # Changelog |
2 | 2 |
|
| 3 | +## 1.6.0 (2026-02-22) |
| 4 | + |
| 5 | +### Added |
| 6 | + |
| 7 | +- **Python Logging Integration** - Forward Python's `logging` module to Erlang's `logger` |
| 8 | + - `py:configure_logging/0,1` - Setup Python logging to forward to Erlang |
| 9 | + - `erlang.ErlangHandler` - Python logging handler that sends to Erlang |
| 10 | + - `erlang.setup_logging(level, format)` - Configure logging from Python |
| 11 | + - Fire-and-forget architecture using `enif_send()` for non-blocking messaging |
| 12 | + - Level filtering at NIF level for performance (skip message creation for filtered logs) |
| 13 | + - Log metadata includes module, line number, and function name |
| 14 | + - Thread-safe - works from any Python thread |
| 15 | + |
| 16 | +- **Distributed Tracing** - Collect trace spans from Python code |
| 17 | + - `py:enable_tracing/0`, `py:disable_tracing/0` - Enable/disable span collection |
| 18 | + - `py:get_traces/0` - Retrieve collected spans |
| 19 | + - `py:clear_traces/0` - Clear collected spans |
| 20 | + - `erlang.Span(name, **attrs)` - Context manager for creating spans |
| 21 | + - `erlang.trace(name)` - Decorator for tracing functions |
| 22 | + - Span events via `span.event(name, **attrs)` |
| 23 | + - Automatic parent/child span linking via thread-local storage |
| 24 | + - Error status capture with exception details |
| 25 | + - Duration tracking in microseconds |
| 26 | + |
| 27 | +- **New Erlang modules** |
| 28 | + - `py_logger` - gen_server receiving log messages from Python workers |
| 29 | + - `py_tracer` - gen_server collecting and managing trace spans |
| 30 | + |
| 31 | +- **New C source** |
| 32 | + - `c_src/py_logging.c` - NIF implementations for logging and tracing |
| 33 | + |
| 34 | +- **Documentation and examples** |
| 35 | + - `docs/logging.md` - Logging and tracing documentation |
| 36 | + - `examples/logging_example.erl` - Working escript example |
| 37 | + - Updated `docs/getting-started.md` with logging/tracing section |
| 38 | + |
| 39 | +- **New test suite** |
| 40 | + - `test/py_logging_SUITE.erl` - 9 tests for logging and tracing |
| 41 | + |
| 42 | +- `ATOM_NIL` for Elixir `nil` compatibility in type conversions |
| 43 | + |
| 44 | +### Performance |
| 45 | + |
| 46 | +- **Type conversion optimizations** - Faster Python ↔ Erlang marshalling |
| 47 | + - Use `enif_is_identical` for atom comparison instead of `strcmp` |
| 48 | + - Use `PyLong_AsLongLongAndOverflow` to avoid exception machinery |
| 49 | + - Cache `numpy.ndarray` type at init for fast isinstance checks |
| 50 | + - Stack allocate small tuples/maps (≤16 elements) to avoid heap allocation |
| 51 | + - Use `enif_make_map_from_arrays` for O(n) map building vs O(n²) puts |
| 52 | + - Reorder type checks for web workloads (strings/dicts first) |
| 53 | + - UTF-8 decode with bytes fallback for invalid sequences |
| 54 | + |
| 55 | +- **Fire-and-forget NIF architecture** - Log and trace calls never block Python execution |
| 56 | + - Uses `enif_send()` to dispatch messages asynchronously to Erlang processes |
| 57 | + - Python code continues immediately after sending, no round-trip wait |
| 58 | +- **NIF-level log filtering** - Messages below threshold are discarded before term creation |
| 59 | + - Volatile bool flags for O(1) receiver availability checks |
| 60 | + - Level threshold stored in C global, no Erlang callback needed |
| 61 | +- **Minimal term allocation** - Direct Erlang term building without intermediate structures |
| 62 | + - Timestamps captured at NIF level using `enif_monotonic_time()` |
| 63 | + |
3 | 64 | ## 1.5.0 (2026-02-18) |
4 | 65 |
|
5 | 66 | ### Added |
|
0 commit comments