radar_trace 0.3.0
radar_trace: ^0.3.0 copied to clipboard
Pure-Dart tracer framework with monotonic spans, log-linear latency histograms, Zone-based async nesting, and a lossless outlier ring.
0.3.0 #
Integration-facing additions from the first third-party integration.
Additive except for one documented behaviour change: record() no longer
samples (see below). Existing call sites, subclasses and JSON readers keep
working.
A minor bump rather than a patch, so a ^0.2.0 dependency does not pick the
behaviour change up on pub upgrade — taking it is a deliberate move to
^0.3.0.
- Span exporters.
TraceRecorder(exporters: [...])mirrors every accepted span into host-supplied callbacks, so teeing spans into an app's own logging pipeline no longer needs aTraceRecordersubclass. Exporters see spans that pass theenabledgate andexportFilter, including ones whose key is later dropped bymaxKeys. A throwing exporter is swallowed and counted inexporterErrorCount— recording never breaks. exportFilter. Abool Function(Span)in front of the exporters, consulted on the export path only. This is the volume control for a chatty key: it quietens a mirror-style exporter without moving a percentile, which loweringsampleRatecannot do. A throwing filter skips the export and counts intoexporterErrorCount.- Outcome tagging at stop time.
SpanHandle.stop({attributes})andSpanHandle.fail(error, [attributes])merge attributes over the start-time ones, so an outcome-dependent dimension (cache hit vs miss, error class) can be recorded. Attributes are not part of theTraceKey, so aggregation is unchanged. - Error type on failed spans.
SpanHandle.fail,Tracer.traceandTracer.traceAsyncrecord the error's runtime type as theerror.typeattribute (kErrorTypeAttribute) — the type name only, never the message. - Cross-isolate merge.
TraceSnapshot.fromJsonandTraceSnapshot.mergecombine snapshots from separate isolates into one view. Histograms add bucket-wise, so merged percentiles stay as accurate as single-isolate ones. Each snapshot now carriesclockOriginWallMicros, and merge rebases later-started isolates onto the earliest origin — otherwise the merged window andcallsPerSecondwould mix incomparable per-isolate clocks.traceClockOriginWallMicrosexposes the current isolate's origin. TraceSnapshot.toJson/SpanKeyStatsSnapshot.toJsonnow include the latency distribution (sparse — non-empty buckets only), without which a restored snapshot could not report percentiles. Outlier spans are opt-in viaincludeOutliers, keeping the default payload aggregate-sized.Span.toJson/Span.fromJsonare public for that path; attribute values that are not JSON primitives are serialised viatoString().- Per-trace capture.
TraceRecorder(slowTraceCapacity: n)retains the slowestncomplete traces asTraceTrees — root span plus its full subtree — reachable viaTraceSnapshot.slowTraces, withTraceTree.describe()for an indented waterfall. Aggregates answer "how long does this usually take"; this answers "why did this one take 8 seconds". Off by default (capacity 0 is the switch). Bounded bymaxSpansPerTrace(marks the treetruncated) andmaxInFlightTraces(evicts the oldest buffer intoTraceSnapshot.traceDropCount). A trace whose root never arrives is dropped and counted, never reported as a partial waterfall; a span that arrives once its trace has been evicted or already reported marks that treetruncatedrather than rebuilding a second one that would look whole. Trees serialise viaincludeSlowTracesand merge slowest-first across isolates, rebased like everything else. - Sampling moved to the start of a span.
TraceRecorder.shouldSample()is now the draw, andTracer.trace/traceAsync/startcall it before building anything — a sampled-out span costs one call and one branch instead of aSpan, an attribute map and aZone.fork.Tracer.startreturns the newSpanHandle.inert()when sampled out.- Behaviour change:
record()no longer samples. A caller handing hand-built spans torecordwithsampleRate < 1.0now gets all of them; callshouldSample()first if you want the old behaviour. Sampling belongs to the start of a span — by the time one is finished the work has been paid for.Tracerstill calls the publicrecord, soTraceRecordersubclasses that override it keep seeing every span. - A sampled-out span forks no zone, so it is invisible to its descendants:
a sampled-in child under it attaches to the nearest sampled-in ancestor,
or becomes its own root when there is none, rather than pointing at a
span nobody recorded. A trace holds only sampled-in spans, so below
sampleRate: 1.0a retained waterfall can be missing a middle level without being marked truncated.
- Behaviour change:
- Docs:
traceAsyncnotes thatTshould be passed explicitly when wrapping an existing assignment — inference reads the assignment context, which is the promoted type when the target is type-promoted, so a nullable body can stop compiling once it is wrapped.
0.2.0 #
- New
seriesmodule:MetricSample,SeriesGap, andMetricSeries(JSON withschemaVersion: 1), plusassessSeriesproducing aSeriesAssessmentwith aSeriesVerdict(monotonicGrowth/plateau/noisy/insufficientData). Implements the field-proven growth methodology: settle-window trim, gap-aware region selection (gaps are never bridged), init-free batch2-minus-batch1 delta, Theil-Sen robust slope, degrees-of-freedom-preserving noise (first-difference sigma combined with residual sigma), Mann-Kendall certification of growth verdicts (bounds the false-growth rate under pure noise at 0.5% one-sided; measured 0.07% over 12k seeded trials), and end-shift checks so late crashes cannot read as growth and late rises cannot read as a bounded plateau. Details claim only measured facts and quantify the detection floor. Honest degradation throughout: a signal that cannot be truthfully computed readsinsufficientData/ null — never a plausible number.
0.1.2 #
- Optional
dedupKeyonTracer.trace/traceAsync/start— a caller-supplied signature (e.g. arguments) marking repeated invocations of the same operation.SpanKeyStatsSnapshot.duplicateCountreports how many spans repeated a previously-seen signature for a key (bounded to ~1024 signatures), distinct from any statistical "hot" heuristic.Span.dedupKeycarries the joined signature.
0.1.1 #
- Docs only (no code change): document the per-key
SpanKeyStatsSnapshotmetrics —callsPerSecond,avgInterCallIntervalMicros,meanMicros,maxMicros,totalMicros,firstStartMicros,lastStartMicros— in the README.
0.1.0 #
- Initial release: Span model, LatencyHistogram, OutlierRing, TraceRecorder, TraceSnapshot, Tracer façade with Zone-based nesting.
- Added per-key call metrics to
SpanKeyStatsSnapshot:firstStartMicros,lastStartMicros,avgInterCallIntervalMicros(null when count < 2),callsPerSecond(null when count < 2 or window is zero),meanMicros,maxMicros,totalMicros— all exact (no bucket approximation).