Skip to main content

Crate tailtriage_core

Crate tailtriage_core 

Source
Expand description

§tailtriage-core

tailtriage-core is the framework-agnostic capture foundation for tailtriage.

Use it when you want explicit request lifecycle instrumentation and bounded JSON artifacts without controller, Axum, or Tokio runtime-sampler APIs unless you add them separately.

§What this crate does

tailtriage-core owns capture-side lifecycle semantics:

  • request admission
  • queue/stage/inflight instrumentation
  • explicit request completion
  • bounded in-memory retention
  • JSON run artifact writing

For in-process analysis/report generation, use tailtriage-analyzer. For command-line analysis of saved artifacts, use tailtriage-cli.

§Crate selection

Use tailtriage-core when you want the smallest framework-agnostic capture surface.

Use tailtriage when you want the recommended default entry point: an aggregator/re-export crate with optional integrations behind features.

§Installation

cargo add tailtriage-core

§Quick start

use tailtriage_core::Tailtriage;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let run = Tailtriage::builder("checkout-service")
        .output("tailtriage-run.json")
        .build()?;

    let started = run.begin_request("/checkout");
    started.completion.finish_ok();

    run.shutdown()?;
    Ok(())
}

§Request lifecycle

begin_request(...) / begin_request_with(...) returns StartedRequest with:

  • started.handle for queue/stage/inflight instrumentation
  • started.completion for explicit finish

For Arc<Tailtriage> flows that move request handles across spawned tasks or helper layers, use begin_request_owned(...) / begin_request_with_owned(...). Owned handles keep the same lifecycle rule: instrumentation does not finish the request, and the completion token must be finished exactly once.

use tailtriage_core::{RequestOptions, Tailtriage};

async fn demo() -> Result<(), Box<dyn std::error::Error>> {
    let run = Tailtriage::builder("checkout-service")
        .output("tailtriage-run.json")
        .build()?;

    let started = run.begin_request_with(
        "/checkout",
        RequestOptions::new().request_id("req-1").kind("http"),
    );
    let req = started.handle.clone();

    req.queue("ingress").await_on(async {}).await;
    req.stage("db")
        .await_on(async { Ok::<(), std::io::Error>(()) })
        .await?;

    started.completion.finish_ok();
    run.shutdown()?;
    Ok(())
}

§Output sinks

tailtriage-core captures run data and finalizes through a sink. It does not perform analysis/report generation.

  • LocalJsonSink (or builder .output(...)) writes Run artifact JSON to disk.
  • request_id is the per-run tailtriage identity of one completed logical request/work item. Explicit IDs should be unique among completed requests in one Run; queue and stage events should reuse an ID only for evidence from that same logical request. Duplicate retained completed IDs are allowed for backward compatibility but surface a lifecycle warning because request-scoped attribution can be ambiguous.
  • MemorySink stores finalized typed Run values in memory.
  • DiscardSink finalizes lifecycle and drops the finalized Run without persisting output.

MemorySink stores only the last finalized Run; each new finalized run replaces the previous stored value.

Use MemorySink when you want in-process analysis. DiscardSink drops finalized runs; use MemorySink instead when the finalized Run should be analyzed in process.

use tailtriage_core::{MemorySink, Tailtriage};

let sink = MemorySink::new();
let run = Tailtriage::builder("checkout-service")
    .sink(sink.clone())
    .build()?;

let started = run.begin_request("/checkout");
started.completion.finish_ok();
run.shutdown()?;

let finalized = sink.last_run();

§Two easy-to-miss helpers

For infallible async work, StageTimer::await_value(...) avoids a dummy Result:

let value = req.stage("cache").await_value(async { 42 }).await;

When queue depth is known at enqueue time, QueueTimer::with_depth_at_start(...) records it directly:

req.queue("ingress")
    .with_depth_at_start(12)
    .await_on(async {})
    .await;

§Lifecycle contract

  • queue(...), stage(...), and inflight(...) do not finish requests.
  • Every admitted request must be finished exactly once.
  • Dropping a completion token does not auto-finish.
  • Non-strict lifecycle: shutdown() writes the artifact and records unfinished-request warnings/metadata.
  • strict_lifecycle(true): unfinished requests cause shutdown() to return an error and no artifact is written.

Finalization timestamps:

  • Active snapshot() output is not finalized (metadata.finalized_at_unix_ms == None).
  • shutdown() writes final artifacts with both:
    • metadata.finished_at_unix_ms set to shutdown time
    • metadata.finalized_at_unix_ms set to that same timestamp
  • Older artifacts may deserialize with metadata.finalized_at_unix_ms == None.
  • When finalized_at_unix_ms is present, prefer that field as the finalization signal; finished_at_unix_ms remains for backward compatibility.

§Timing model

  • Duration fields in microseconds (latency_us, wait_us, stage latency_us) are authoritative for elapsed-time analysis.
  • Unix millisecond timestamps are wall-clock anchors for log correlation, artifact readability, and coarse temporal grouping.
  • Wall-clock timestamps can be coarse and can move if the system clock changes.
  • Analyzer scoring uses duration fields for latency, queue wait, and stage duration.
  • Temporal segmentation prefers run-relative monotonic offsets when present and falls back to Unix-ms wall-clock anchors for older or imported artifacts without complete run-relative timing.

§Capture modes

Modes change retention defaults only. They do not change lifecycle semantics and do not auto-start runtime sampling.

  • CaptureMode::Light
  • CaptureMode::Investigation

Override limits with:

  • capture_limits(...) (full override)
  • capture_limits_override(...) (field-level override)

§Advanced: assembling completed run artifacts

Most users should use Tailtriage::builder(...) for live request instrumentation. Use RunBuilder only when you already have completed request, stage, queue, in-flight, or runtime evidence and need to assemble a standard Run artifact.

RunBuilder is intended for import/conversion paths. It does not perform live lifecycle tracking. RunBuilder::new validates top-level run timestamp ordering. Each push_* call validates required event/snapshot shape and timestamp ordering before retention is applied. Completed request latency, stage latency, and queue wait fields are authoritative evidence; RunBuilder does not synthesize, repair, or reject those durations based on wall-clock timestamp deltas. It applies the same bounded retention/truncation semantics as live core capture, so events beyond configured CaptureLimits are dropped without error and Run.truncation counters are updated. RunBuilder does not validate cross-event correlation (for example a stage without a matching request) and does not synthesize lifecycle completions. It surfaces a lifecycle warning when retained completed requests contain duplicate request IDs. For assembled/imported artifacts, host and pid default to None, and generated run IDs use the same core run-id semantics as live capture.

use tailtriage_core::{RequestEvent, RunBuilder, RunBuilderOptions};

fn assemble_run() -> Result<(), Box<dyn std::error::Error>> {
    let mut builder = RunBuilder::new(RunBuilderOptions::new("checkout-service"))?;
    builder.push_request(RequestEvent {
        request_id: "req-1".into(),
        route: "/test".into(),
        kind: Some("http".into()),
        started_at_unix_ms: 1,
        started_at_run_us: None,
        finished_at_unix_ms: 2,
        finished_at_run_us: None,
        latency_us: 1_000,
        outcome: "ok".into(),
    })?;

    let run = builder.finish();
    assert_eq!(run.requests.len(), 1);
    Ok(())
}

§What this crate does not do

This crate does not provide:

  • repeated arm/disarm controller windows
  • Tokio runtime sampling
  • Axum middleware/extractors
  • analysis/report generation

Use sibling crates for those surfaces: tailtriage-controller, tailtriage-tokio, tailtriage-axum, tailtriage-analyzer, and tailtriage-cli.

Structs§

CaptureLimits
Limits that bound in-memory capture growth for one run.
CaptureLimitsOverride
Field-level capture limit overrides applied on top of mode defaults.
DiscardSink
Sink that finalizes capture lifecycle without writing a run artifact.
EffectiveCoreConfig
Stable, resolved core configuration used by one capture run.
EffectiveTokioSamplerConfig
Stable, resolved Tokio runtime sampler configuration used by one run.
InFlightSnapshot
Point-in-time in-flight gauge reading.
InflightGuard
RAII guard tracking one in-flight unit for a named gauge.
LocalJsonSink
Local file sink that writes one JSON document per run at shutdown.
MemorySink
In-memory sink that stores only the last finalized run.
OwnedRequestCompletion
Completion-facing request token backed by Arc<Tailtriage>.
OwnedRequestHandle
Instrumentation-facing request handle backed by Arc<Tailtriage>.
OwnedStartedRequest
Split request lifecycle start result backed by Arc<Tailtriage>.
QueueEvent
Queue wait measurement for a request waiting on a queue/permit.
QueueTimer
Thin wrapper for recording queue-wait latency around one await point.
RequestCompletion
Completion-facing request token.
RequestEvent
Per-request timing and status.
RequestHandle
Instrumentation-facing request handle.
RequestOptions
Optional request start settings used by crate::Tailtriage::begin_request_with.
Run
A full output artifact for one tailtriage capture run.
RunBuilder
Advanced/import API for completed-run artifact assembly.
RunBuilderOptions
Options for assembling a completed Run artifact.
RunMetadata
Top-level metadata for one capture run.
RuntimeSnapshot
Point-in-time runtime metrics sample.
StageEvent
Timing record for one named stage.
StageTimer
Thin wrapper for recording stage latency around one await point.
StartedRequest
Split request lifecycle start result.
Tailtriage
Per-run collector that records request events and writes the final artifact.
TailtriageBuilder
Builder for constructing a crate::Tailtriage run.
TruncationSummary
Per-section counters indicating dropped samples due to capture limits.
UnfinishedRequestSample
One unfinished request sample captured for lifecycle warnings.
UnfinishedRequests
Summary of unfinished requests detected at shutdown.

Enums§

BuildError
Errors emitted while building one tailtriage capture instance.
CaptureMode
Capture mode used during a run.
Outcome
Logical request outcome categories used by the public API.
RunBuilderEventError
Validation error returned when a pushed event or snapshot has invalid shape.
RunEndReason
Run lifecycle end reason recorded in artifact metadata.
RuntimeSamplerRegistrationError
Error returned when registering Tokio runtime sampler metadata.
SinkError
Errors emitted while writing run artifacts.

Constants§

SCHEMA_VERSION
Current schema version for Run JSON artifacts.

Traits§

RunSink
A sink that persists the final run artifact produced at shutdown.

Functions§

system_time_to_unix_ms
Converts a SystemTime to unix epoch milliseconds.
unix_time_ms
Returns the current unix epoch timestamp in milliseconds.