# API logging experiment: reader data

Measured on October 29, 2026 with a dedicated Python 3.11.2 standard-library API over same-host loopback. Seven logging modes × two readers × three runs. Each run schedules 20 requests/sec for eight seconds, totaling 160 requests. Pilot measurements are excluded.

- `summary.csv`: 14 pooled conditions. Each p50/p95/p99 uses 480 requests and selects sorted observation `ceil(n*p)`. `run_p99_min_ms` / `max_ms` are the minimum and maximum of individual-run p99, not confidence intervals.
- `runs.csv`: results for all 42 runs, excluding nested internal environment information.
- `requests.csv`: relative timestamps, latency, and API instrumentation for 6,720 requests. No absolute timestamps, connection details, or log contents.
- `timeline.csv`: relative-time cumulative generated/read bytes. Generation is recorded just before calling the logger; reads are sampled about every 50ms. Between samples, `unread_bytes` can overestimate actual unread data and is not queue occupancy alone.

`latency_ms` runs from the scheduled send to receipt of the complete response body. `schedule_lag_ms` runs from the scheduled send to the start of the client's request handling. Latency includes connection setup and API waiting. `generate_us` covers the JSON-generation interval; `logging_us` includes logger calls, counting, and encoding. Neither is a pure per-log cost.

`completed_rps` is completed requests divided by max(eight seconds, time of the last HTTP response). `completed_and_drained_rps` also includes the time to finish reading every log. These describe how a fixed offered load finishes, not maximum capacity.

`api_cpu_cores` is API CPU seconds divided by the response-observation interval. `api_cpu_quota_percent` is relative to the 0.25-core cap. Instrumentation is included. `api_cpu_s_including_drain` also includes subsequent statistics retrieval and is a reference column, not pure logging CPU. One core means one logical host CPU.

`drain_after_last_response_s` runs from the last HTTP response to the final log read, not durable storage or acknowledgment by an external service. We did not test indefinite load or reaching the 256-message queue limit.

Short messages are 128 bytes including the newline. Large JSON lines are 16,384 bytes including the newline, fixed synthetic data consisting mostly of a long ASCII string. `json_only` generates the same JSON but emits zero bytes. All output was verified using byte count, newline count, and SHA-256. Missing output, queue drops, and HTTP errors were zero.

The output sink is a dedicated FIFO. `fast` has no artificial reading limit; `slow` waits after every 4,096-byte read, capped at 256KiB/sec. We did not benchmark disk durability or network delivery.
