# log-io v1.0.0 - Stable API

**Date:** 2026-05-18
**Compare:** `v0.10.0...v1.0.0`

## Headline

The public surface is frozen. Backwards-incompatible changes after
`1.0.0` require a major version bump per Semantic Versioning. See
[`REPS.md`](../../REPS.md) section 8 for the binding stability policy
and [`docs/API.md`](../API.md) for the complete public-symbol
reference.

No functional code changes from `v0.10.0`. The 1.0.0 release is a
freeze, a documentation polish, and the CI gate that protects the
guarantee going forward.

Behind the line: the journey from `v0.1.0` (scaffold) to `v1.0.0`
landed three formats (JSON / logfmt / human), five sinks, per-target
filtering with directive parsing, thread-local context propagation
with RAII guards, per-logger default fields, a multi-sink fanout
with continue-on-error dispatch, RFC 3339 timestamps with no
runtime dependency, an allocation-free steady-state hot path, and a
112-test suite covering unit, integration, stress, property, and
doctest paths plus four `cargo-fuzz` harnesses.

## What changed since `v0.10.0`

### New - `cargo-semver-checks` CI job

A dedicated CI job runs
[`cargo-semver-checks`](https://github.com/obi1kenobi/cargo-semver-checks)
against the published baseline on every pull request and every push
to `main`. Backwards-incompatible additions to the public surface
fail CI before they can ship.

### New - `docs/API.md` rewritten as the complete reference

Every public item now has a dedicated section in
[`docs/API.md`](../API.md) with description, signature, parameter
breakdown, and runnable examples. The document mirrors the rustdoc
on docs.rs and serves as the offline reference.

### Changed - `README.md` polished for production

Removed pre-release status banner and dev-facing notes; added an
overview, installation, performance summary, examples index, and a
direct link to the API reference.

### Changed - `REPS.md` section 8 declares stability

The stability section was previously "0.x.y releases are not
API-stable". It now declares the public API frozen and enumerates
what is and isn't covered by the SemVer guarantee.

### Changed - `CHANGELOG.md` consolidated

The 1.0.0 entry summarises the complete public surface and the
features / hardening / performance work that landed across the
`0.9` and `0.10` pre-release lines. The historical entries remain
as brief pointers to their git tags.

There are no source-level changes to the crate code between
`v0.10.0` and `v1.0.0`. The package contents on crates.io are
functionally identical; the version bump signals API stability.

## Frozen public surface

Full listing in [`docs/API.md`](../API.md). Summary by module:

### Always available (data model, `no_std`-compatible)

- `log_io::{Level, ParseLevelError}`
- `log_io::{Value, Field, Metadata, Record}`
- `log_io::format::{Format, JsonFormat, LogfmtFormat, HumanFormat}`
  (each formatter behind its own feature flag, all on by default)
- `log_io::VERSION`

### `std` feature (default)

- `log_io::{Filter, FilterRule, ParseFilterError}`
- `log_io::{Logger, LoggerBuilder, ErrorHandler}`
- `log_io::{Error, Result, Sink}`
- `log_io::sink::{StdoutSink, StderrSink, FileSink, WriterSink, NullSink}`
- `log_io::context::{ContextGuard, MAX_CONTEXT_SLOTS, with_field,
  with_trace_id, with_request_id, with_snapshot, clear, len}`
- Macros: `log_at!`, `trace!`, `debug!`, `info!`, `warn!`, `error!`

### Feature flags

| Flag     | Default | Effect                                          |
|----------|---------|-------------------------------------------------|
| `std`    | yes     | Logger, sinks, context, timestamps.             |
| `json`   | yes     | `JsonFormat`.                                   |
| `logfmt` | yes     | `LogfmtFormat`.                                 |
| `human`  | yes     | `HumanFormat` (no_std-compatible).              |

### MSRV

`1.75`, unchanged from `v0.10.0`.

### Runtime dependencies

Zero. The crate depends only on `core` and (with `std` enabled)
`std`. `proptest` is a dev-dependency only, used in
`tests/property.rs`.

## Verification

Local matrix (Windows x86_64-pc-windows-msvc, stable 1.95):

- `cargo build` (default features) [OK]
- `cargo build --no-default-features` [OK]
- `cargo build --all-features` [OK]
- `cargo +1.75 build --all-features` (MSRV) [OK]
- `cargo fmt --all -- --check` [OK]
- `cargo clippy --all-targets --all-features -- -D warnings` [OK]
- `cargo clippy --all-targets --no-default-features -- -D warnings` [OK]
- `cargo test --all-features` - **112 tests pass, 0 ignored**
  (61 unit + 18 integration + 16 stress + 11 property + 6 doctest)
- `RUSTDOCFLAGS="-D warnings -D rustdoc::broken-intra-doc-links" cargo doc --all-features --no-deps` [OK]
- `cargo package` [OK] (50 files, 224.6 KiB; 59.4 KiB compressed)
- Banned-word and em-dash scan across `src/`, `tests/`, `examples/`,
  `benches/`, `docs/`, `README.md`, `CHANGELOG.md`: 0 hits

CI matrix runs on ubuntu-latest, macos-latest, windows-latest for
the `test` job plus dedicated `clippy`, `fmt`, `docs`, `msrv`, and
`semver-checks` jobs.

## Performance

Measured on a developer laptop (release, `lto = "thin"`); full
methodology in [`BENCH.md`](../../BENCH.md).

### Format-only (ns / record, 5 fields)

| Format          | ns / record |
|-----------------|------------:|
| JSON            | ~131        |
| logfmt          | ~159        |
| human           | ~210        |
| JSON, no fields | ~12         |

### Full pipeline (ns / call, 3 fields)

| Path                            | ns / call |
|---------------------------------|----------:|
| `dispatch_only` (null sink)     | ~6        |
| `below_threshold` (filtered)    | ~1        |
| `json + writer`                 | ~59       |
| `logfmt + writer`               | ~63       |
| `json + writer`, no fields      | ~25       |

### Concurrent throughput (records / second)

| Threads | Total      | Wall time | Throughput   |
|--------:|-----------:|----------:|-------------:|
| 1       | 100 K      | 6 ms      | ~15 M rec/s  |
| 2       | 200 K      | 8 ms      | ~22 M rec/s  |
| 4       | 400 K      | 14 ms     | ~28 M rec/s  |
| 8       | 800 K      | 82 ms     | ~10 M rec/s  |
| 16      | 1.6 M      | 211 ms    | ~7.6 M rec/s |

Filtered-out records cost ~1 ns; put coarse target-based filtering
up front rather than guarding at the call site.

## Migration

### From `v0.10.0`

No source changes required. The public API surface is unchanged.

### From `v0.9.0`

`v0.10.0` introduced breaking changes to the builder API; see the
[v0.9.0 to v0.10.0 migration notes](https://github.com/jamesgober/log-io/blob/v0.10.0/CHANGELOG.md#0100---2026-05-18)
in the `0.10.0` changelog. Key changes:

- `.stdout().json().build()` is now `.stdout_json().build()` or
  `.stdout(JsonFormat::new()).build()`.
- Builder methods `.file()`, `.file_append()`, `.null()`, and the
  factory-style `.writer(closure)` are removed. Use
  `.with_sink(FileSink::create(...)?)` for files,
  `.with_sink(NullSink::new())` for null, and `.writer(W, F)`
  passing the writer directly.
- `Logger::try_log_with_target` is now `Logger::try_log`.
- The `warn_!` macro is now `warn!`.
- `Logger` dispatch now continues across sink failures: the first
  error is surfaced but every sink still receives the record.
- `HumanFormat` no longer requires the `std` feature.
- The `NoFormat` placeholder is gone; `build()` panics if no sink
  has been installed (the panic is a clear programmer-error
  signal, not a runtime failure mode).

## Stability commitment

From `1.0.0` forward (full policy in
[`REPS.md`](../../REPS.md) section 8):

- **Patch (`1.0.x`)** - bug fixes, doc improvements, internal
  performance work, test additions. No new public items.
- **Minor (`1.x.0`)** - pure additions to the public surface, new
  opt-in features, new variants on enums that are reserved for
  growth, MSRV bumps.
- **Major (`2.0.0`)** - anything that removes, renames, or changes
  the signature of a public symbol, or that adds a non-opt-in
  runtime dependency.

The CI `semver-checks` job enforces this for the public API
surface; the policy itself binds documentation and dependency
posture as well.

## Release ceremony

```bash
# After the version bump + CHANGELOG + verification:
git tag -a v1.0.0 -m "Release v1.0.0 - Stable API"
git push origin main
git push origin v1.0.0

# Publish.
cargo publish

# Verify the index updated.
cargo search log-io | head -3
```

GitHub release title: `v1.0.0 - Stable API`. Not tagged as
pre-release.

## Acknowledgements

`log-io` reached `1.0.0` through `0.1.0` (scaffold), `0.9.0` (first
implementation), `0.10.0` (hardening), and this freeze. Pre-release
work was extensive but the project never had external dependencies
and never carried `unsafe` code; the public-API surface declared
here is the same one that compiled and tested green for the
preceding twelve hours of development.

---

**Full Changelog:** https://github.com/jamesgober/log-io/compare/v0.10.0...v1.0.0
