Repository navigation
Conversation
Add a `telemetry` mode, which the runner selects with `CODSPEED_RUNNER_MODE=telemetry`. It runs each benchmark once, sends the start and end of each round to the runner, and skips the perf map warm-up. When `CODSPEED_OTEL_ENDPOINT` is set, the plugin exports the spans of the global tracer provider and the `logging` records over OTLP/HTTP, and flushes them when pytest exits. `CODSPEED_TELEMETRY_FILTER` selects the records (`info` by default). Spans and records also get their times on the clock of the benchmark markers, so that the runner can find the benchmark of each one. The trace and span ids come from a random generator of their own: the plugin reseeds the global one before each benchmark. The OpenTelemetry SDK and exporter come with a new `telemetry` extra. Add an example benchmark that emits telemetry, and install the extra in the benchmarks workflow. Closes COD-4148 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Under the runner, the plugin wrote `results/<pid>.json` in every mode. Outside walltime mode, the file holds no benchmarks, and CodSpeed fails to read it as walltime results. The other integrations write this file only in walltime mode. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
[Medium impact] The changes affect telemetry collection for pytest-codspeed benchmark runs. The PR appears safe to merge, with a non-blocking fix recommended for exporter child loggers.
|
| def level_of(self, name: str) -> int: | ||
| while True: | ||
| level = self.levels.get(name) | ||
| if level is not None: | ||
| return level |
There was a problem hiding this comment.
Exporter logs bypass the filter
With CODSPEED_TELEMETRY_FILTER=info,urllib3.connectionpool=debug, level_of() returns the child directive before reaching the forced urllib3=off entry. This lets the exporter's own HTTP logs through. Exporting those messages can create more messages to export, causing repeated background requests and unrelated telemetry.
Reject EXPORTER_LOGGERS and their descendants before applying user directives.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/pytest_codspeed/telemetry.py
Line: 106-110
Comment:
**Exporter logs bypass the filter**
With `CODSPEED_TELEMETRY_FILTER=info,urllib3.connectionpool=debug`, `level_of()` returns the child directive before reaching the forced `urllib3=off` entry. This lets the exporter's own HTTP logs through. Exporting those messages can create more messages to export, causing repeated background requests and unrelated telemetry.
Reject `EXPORTER_LOGGERS` and their descendants before applying user directives.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
Merging this PR will degrade performance by 11.47%3 warnings, 1 note
Warning Please fix the performance issues or acknowledge them on CodSpeed. Performance Changes
Tip Investigate this regression by commenting Comparing |
Export the spans and logs emitted by the benchmarked code to the CodSpeed runner, so that they can be shown next to each benchmark.
Add a
telemetrymode, selected when the runner setsCODSPEED_RUNNER_MODE=telemetry. It runs each benchmark once, like the analysis modes, and:add_benchmark_timestamps). The start and stop commands of a benchmark reach the runner tens of microseconds away from the benchmarked code, so the rounds give tighter bounds to find the benchmark of each span and log;When
CODSPEED_OTEL_ENDPOINTis set, the plugin exports over OTLP/HTTP, with batch processors flushed when pytest exits:TracerProvider, the export is added to it;loggingrecords, filtered byCODSPEED_TELEMETRY_FILTER(infoby default, e.g.info,my_package=debug). The root logger level is lowered for the run so that the selected records reach the handler. The records of the exporter's own loggers (opentelemetry,urllib3,requests) are never exported.Spans get
codspeed.monotonic_start_ns/codspeed.monotonic_end_nsand log recordscodspeed.monotonic_time_ns, read from the instrument-hooks clock that the benchmark markers use.The OpenTelemetry SDK and exporter come with a new
telemetryextra (>=1.28.0). Without the extra, the plugin warns and exports nothing.Things worth a look
random.seed(0)before each benchmark, and the SDK's default id generator draws from the globalrandom. Every benchmark got the same trace and span ids, and the SDK's draws changed the random numbers the benchmarks get. The ids now come from a generator with its ownrandom.Random().LoggerProvider. Onlyloggingrecords are exported, through alogging.Filteron the SDKLoggingHandler. The SDK log processor API changed between 1.28 and 1.45, so stamping records emitted through the OpenTelemetry logs API would need version-specific code.LoggingHandleris deprecated in recent SDK versions; its warning is silenced.service.nameis the name of the pytest rootdir.Results file
The second commit writes
results/<pid>.jsonunder the runner only in walltime mode. In the other modes, the file held no benchmarks and failed to parse as walltime results on the CodSpeed side. The other integrations already write it only in walltime mode.Example and verification
tests/benchmarks/test_bench_telemetry.pyadds benchmarks that emit spans and logs, and the benchmarks workflow now installs thetelemetryextra. In simulation mode they show the cost of the calls without an exporter. Running them in telemetry mode in CI waits for the runner's telemetry mode to be released.Checked end to end with a local build of the runner (
codspeed run -m telemetry --skip-upload): it collects 3 spans and 3 logs from the example, thedebugrecord is filtered out, each span and log falls within the round markers of its benchmark, and each benchmark has its own trace id.Closes COD-4148