Skip to main content

saluki_core/data_model/event/trace_stats/
mod.rs

1//! Trace stats.
2
3use saluki_context::tags::SharedTagSet;
4use stringtheory::MetaString;
5
6/// Trace statistics output from the APM Stats transform.
7///
8/// Contains pre-aggregated trace statistics grouped by client/tracer. The encoder wraps this
9/// in a `StatsPayload` protobuf and adds agent-level metadata (agentHostname, agentEnv,
10/// agentVersion, clientComputed, splitPayload) from ADP configuration.
11#[derive(Clone, Debug, PartialEq, Default)]
12pub struct TraceStats {
13    /// Multiple client payloads, one per PayloadAggregationKey (hostname/env/version/container).
14    stats: Vec<ClientStatsPayload>,
15}
16
17impl TraceStats {
18    /// Creates a new `TraceStats` with the given client stats payloads.
19    pub fn new(stats: Vec<ClientStatsPayload>) -> Self {
20        Self { stats }
21    }
22
23    /// Returns a reference to the client stats payloads.
24    pub fn stats(&self) -> &[ClientStatsPayload] {
25        &self.stats
26    }
27
28    /// Returns a mutable reference to the client stats payloads.
29    pub fn stats_mut(&mut self) -> &mut Vec<ClientStatsPayload> {
30        &mut self.stats
31    }
32}
33
34/// Tracer-level stats payload.
35///
36/// Groups stats by tracer/container identity (hostname, env, version, container_id).
37#[derive(Clone, Debug, PartialEq, Default)]
38pub struct ClientStatsPayload {
39    hostname: MetaString,
40    env: MetaString,
41    version: MetaString,
42    stats: Vec<ClientStatsBucket>,
43    lang: MetaString,
44    tracer_version: MetaString,
45    runtime_id: MetaString,
46    sequence: u64,
47    agent_aggregation: MetaString,
48    service: MetaString,
49    container_id: MetaString,
50    tags: SharedTagSet,
51    git_commit_sha: MetaString,
52    image_tag: MetaString,
53    process_tags_hash: u64,
54    process_tags: MetaString,
55}
56
57impl ClientStatsPayload {
58    /// Creates a new `ClientStatsPayload` with the required identity fields.
59    pub fn new(hostname: impl Into<MetaString>, env: impl Into<MetaString>, version: impl Into<MetaString>) -> Self {
60        Self {
61            hostname: hostname.into(),
62            env: env.into(),
63            version: version.into(),
64            ..Self::default()
65        }
66    }
67
68    /// Sets the stats buckets.
69    pub fn with_stats(mut self, stats: Vec<ClientStatsBucket>) -> Self {
70        self.stats = stats;
71        self
72    }
73
74    /// Sets the tracer language.
75    pub fn with_lang(mut self, lang: impl Into<MetaString>) -> Self {
76        self.lang = lang.into();
77        self
78    }
79
80    /// Sets the tracer version.
81    pub fn with_tracer_version(mut self, tracer_version: impl Into<MetaString>) -> Self {
82        self.tracer_version = tracer_version.into();
83        self
84    }
85
86    /// Sets the runtime identifier.
87    pub fn with_runtime_id(mut self, runtime_id: impl Into<MetaString>) -> Self {
88        self.runtime_id = runtime_id.into();
89        self
90    }
91
92    /// Sets the message sequence number.
93    pub fn with_sequence(mut self, sequence: u64) -> Self {
94        self.sequence = sequence;
95        self
96    }
97
98    /// Sets the agent aggregation key.
99    pub fn with_agent_aggregation(mut self, agent_aggregation: impl Into<MetaString>) -> Self {
100        self.agent_aggregation = agent_aggregation.into();
101        self
102    }
103
104    /// Sets the main service name.
105    pub fn with_service(mut self, service: impl Into<MetaString>) -> Self {
106        self.service = service.into();
107        self
108    }
109
110    /// Sets the container identifier.
111    pub fn with_container_id(mut self, container_id: impl Into<MetaString>) -> Self {
112        self.container_id = container_id.into();
113        self
114    }
115
116    /// Sets the orchestrator tags.
117    pub fn with_tags(mut self, tags: impl Into<SharedTagSet>) -> Self {
118        self.tags = tags.into();
119        self
120    }
121
122    /// Sets the git commit SHA.
123    pub fn with_git_commit_sha(mut self, git_commit_sha: impl Into<MetaString>) -> Self {
124        self.git_commit_sha = git_commit_sha.into();
125        self
126    }
127
128    /// Sets the container image tag.
129    pub fn with_image_tag(mut self, image_tag: impl Into<MetaString>) -> Self {
130        self.image_tag = image_tag.into();
131        self
132    }
133
134    /// Sets the process tags hash.
135    pub fn with_process_tags_hash(mut self, process_tags_hash: u64) -> Self {
136        self.process_tags_hash = process_tags_hash;
137        self
138    }
139
140    /// Sets the process tags.
141    pub fn with_process_tags(mut self, process_tags: impl Into<MetaString>) -> Self {
142        self.process_tags = process_tags.into();
143        self
144    }
145
146    /// Returns the hostname.
147    pub fn hostname(&self) -> &str {
148        &self.hostname
149    }
150
151    /// Returns the environment.
152    pub fn env(&self) -> &str {
153        &self.env
154    }
155
156    /// Returns the version.
157    pub fn version(&self) -> &str {
158        &self.version
159    }
160
161    /// Returns the stats buckets.
162    pub fn stats(&self) -> &[ClientStatsBucket] {
163        &self.stats
164    }
165
166    /// Returns the tracer language.
167    pub fn lang(&self) -> &str {
168        &self.lang
169    }
170
171    /// Returns the tracer version.
172    pub fn tracer_version(&self) -> &str {
173        &self.tracer_version
174    }
175
176    /// Returns the runtime identifier.
177    pub fn runtime_id(&self) -> &str {
178        &self.runtime_id
179    }
180
181    /// Returns the message sequence number.
182    pub fn sequence(&self) -> u64 {
183        self.sequence
184    }
185
186    /// Returns the agent aggregation key.
187    pub fn agent_aggregation(&self) -> &str {
188        &self.agent_aggregation
189    }
190
191    /// Returns the main service name.
192    pub fn service(&self) -> &str {
193        &self.service
194    }
195
196    /// Returns the container identifier.
197    pub fn container_id(&self) -> &str {
198        &self.container_id
199    }
200
201    /// Returns the orchestrator tags.
202    pub fn tags(&self) -> &SharedTagSet {
203        &self.tags
204    }
205
206    /// Returns the git commit SHA.
207    pub fn git_commit_sha(&self) -> &str {
208        &self.git_commit_sha
209    }
210
211    /// Returns the container image tag.
212    pub fn image_tag(&self) -> &str {
213        &self.image_tag
214    }
215
216    /// Returns the process tags hash.
217    pub fn process_tags_hash(&self) -> u64 {
218        self.process_tags_hash
219    }
220
221    /// Returns the process tags.
222    pub fn process_tags(&self) -> &str {
223        &self.process_tags
224    }
225
226    /// Adds a new client statistics bucket to this payload.
227    pub fn add_stats(&mut self, stats: ClientStatsBucket) {
228        self.stats.push(stats);
229    }
230
231    /// Consumes the statistics buckets and returns them.
232    ///
233    /// No statistics buckets will remain in `self`.
234    pub fn take_stats(&mut self) -> Vec<ClientStatsBucket> {
235        std::mem::take(&mut self.stats)
236    }
237}
238
239/// A time bucket containing aggregated stats.
240///
241/// Stats are grouped into fixed-duration buckets (typically 10 seconds).
242#[derive(Clone, Debug, PartialEq, Default)]
243pub struct ClientStatsBucket {
244    /// Bucket start timestamp in nanoseconds since Unix epoch.
245    start: u64,
246    /// Bucket duration in nanoseconds.
247    duration: u64,
248    /// Grouped stats within this bucket.
249    stats: Vec<ClientGroupedStats>,
250    /// Time shift applied by the agent.
251    agent_time_shift: i64,
252}
253
254impl ClientStatsBucket {
255    /// Creates a new `ClientStatsBucket` with the given time range and stats.
256    pub fn new(start: u64, duration: u64, stats: Vec<ClientGroupedStats>) -> Self {
257        Self {
258            start,
259            duration,
260            stats,
261            agent_time_shift: 0,
262        }
263    }
264
265    /// Sets the grouped stats.
266    pub fn with_stats(mut self, stats: Vec<ClientGroupedStats>) -> Self {
267        self.stats = stats;
268        self
269    }
270
271    /// Sets the agent time shift.
272    pub fn with_agent_time_shift(mut self, agent_time_shift: i64) -> Self {
273        self.agent_time_shift = agent_time_shift;
274        self
275    }
276
277    /// Returns the bucket start timestamp in nanoseconds.
278    pub fn start(&self) -> u64 {
279        self.start
280    }
281
282    /// Returns the bucket duration in nanoseconds.
283    pub fn duration(&self) -> u64 {
284        self.duration
285    }
286
287    /// Returns the grouped stats within this bucket.
288    pub fn stats(&self) -> &[ClientGroupedStats] {
289        &self.stats
290    }
291
292    /// Returns a mutable reference to the grouped stats within this bucket.
293    pub fn stats_mut(&mut self) -> &mut Vec<ClientGroupedStats> {
294        &mut self.stats
295    }
296
297    /// Returns the agent time shift.
298    pub fn agent_time_shift(&self) -> i64 {
299        self.agent_time_shift
300    }
301
302    /// Consumes the grouped statistics and returns them.
303    ///
304    /// No statistics groups will remain in `self`.
305    pub fn take_stats(&mut self) -> Vec<ClientGroupedStats> {
306        std::mem::take(&mut self.stats)
307    }
308}
309
310/// Aggregated stats for spans grouped by aggregation key.
311///
312/// Contains both the aggregation key fields (service, name, resource, etc.) and
313/// the aggregated values (hits, errors, duration, latency distributions).
314#[derive(Clone, Debug, PartialEq, Default)]
315pub struct ClientGroupedStats {
316    // Aggregation key fields
317    service: MetaString,
318    name: MetaString,
319    resource: MetaString,
320    http_status_code: u32,
321    span_type: MetaString,
322    db_type: MetaString,
323    span_kind: MetaString,
324    peer_tags: Vec<MetaString>,
325    is_trace_root: Option<bool>,
326    grpc_status_code: MetaString,
327    http_method: MetaString,
328    http_endpoint: MetaString,
329
330    // Aggregated values
331    hits: u64,
332    errors: u64,
333    duration: u64,
334    ok_summary: Vec<u8>,
335    error_summary: Vec<u8>,
336    synthetics: bool,
337    top_level_hits: u64,
338}
339
340impl ClientGroupedStats {
341    /// Creates a new `ClientGroupedStats` with the required aggregation key fields.
342    pub fn new(service: impl Into<MetaString>, name: impl Into<MetaString>, resource: impl Into<MetaString>) -> Self {
343        Self {
344            service: service.into(),
345            name: name.into(),
346            resource: resource.into(),
347            ..Self::default()
348        }
349    }
350
351    // Builder methods for aggregation key fields
352
353    /// Sets the HTTP status code.
354    pub fn with_http_status_code(mut self, http_status_code: u32) -> Self {
355        self.http_status_code = http_status_code;
356        self
357    }
358
359    /// Sets the span type.
360    pub fn with_span_type(mut self, span_type: impl Into<MetaString>) -> Self {
361        self.span_type = span_type.into();
362        self
363    }
364
365    /// Sets the database type.
366    pub fn with_db_type(mut self, db_type: impl Into<MetaString>) -> Self {
367        self.db_type = db_type.into();
368        self
369    }
370
371    /// Sets the span kind.
372    pub fn with_span_kind(mut self, span_kind: impl Into<MetaString>) -> Self {
373        self.span_kind = span_kind.into();
374        self
375    }
376
377    /// Sets the peer tags.
378    pub fn with_peer_tags(mut self, peer_tags: Vec<MetaString>) -> Self {
379        self.peer_tags = peer_tags;
380        self
381    }
382
383    /// Sets whether this is a trace root.
384    pub fn with_is_trace_root(mut self, is_trace_root: Option<bool>) -> Self {
385        self.is_trace_root = is_trace_root;
386        self
387    }
388
389    /// Sets the gRPC status code.
390    pub fn with_grpc_status_code(mut self, grpc_status_code: impl Into<MetaString>) -> Self {
391        self.grpc_status_code = grpc_status_code.into();
392        self
393    }
394
395    /// Sets the HTTP method.
396    pub fn with_http_method(mut self, http_method: impl Into<MetaString>) -> Self {
397        self.http_method = http_method.into();
398        self
399    }
400
401    /// Sets the HTTP endpoint.
402    pub fn with_http_endpoint(mut self, http_endpoint: impl Into<MetaString>) -> Self {
403        self.http_endpoint = http_endpoint.into();
404        self
405    }
406
407    // Builder methods for aggregated values
408
409    /// Sets the hit count.
410    pub fn with_hits(mut self, hits: u64) -> Self {
411        self.hits = hits;
412        self
413    }
414
415    /// Sets the error count.
416    pub fn with_errors(mut self, errors: u64) -> Self {
417        self.errors = errors;
418        self
419    }
420
421    /// Sets the total duration in nanoseconds.
422    pub fn with_duration(mut self, duration: u64) -> Self {
423        self.duration = duration;
424        self
425    }
426
427    /// Sets the DDSketch summary for successful spans.
428    pub fn with_ok_summary(mut self, ok_summary: Vec<u8>) -> Self {
429        self.ok_summary = ok_summary;
430        self
431    }
432
433    /// Sets the DDSketch summary for error spans.
434    pub fn with_error_summary(mut self, error_summary: Vec<u8>) -> Self {
435        self.error_summary = error_summary;
436        self
437    }
438
439    /// Sets the synthetics traffic flag.
440    pub fn with_synthetics(mut self, synthetics: bool) -> Self {
441        self.synthetics = synthetics;
442        self
443    }
444
445    /// Sets the top-level hit count.
446    pub fn with_top_level_hits(mut self, top_level_hits: u64) -> Self {
447        self.top_level_hits = top_level_hits;
448        self
449    }
450
451    // Getters for aggregation key fields
452
453    /// Returns the service name.
454    pub fn service(&self) -> &str {
455        &self.service
456    }
457
458    /// Returns the operation name.
459    pub fn name(&self) -> &str {
460        &self.name
461    }
462
463    /// Returns the resource name.
464    pub fn resource(&self) -> &str {
465        &self.resource
466    }
467
468    /// Returns the HTTP status code.
469    pub fn http_status_code(&self) -> u32 {
470        self.http_status_code
471    }
472
473    /// Returns the span type.
474    pub fn span_type(&self) -> &str {
475        &self.span_type
476    }
477
478    /// Returns the database type.
479    pub fn db_type(&self) -> &str {
480        &self.db_type
481    }
482
483    /// Returns the span kind.
484    pub fn span_kind(&self) -> &str {
485        &self.span_kind
486    }
487
488    /// Returns the peer tags.
489    pub fn peer_tags(&self) -> &[MetaString] {
490        &self.peer_tags
491    }
492
493    /// Returns whether this is a trace root.
494    pub fn is_trace_root(&self) -> Option<bool> {
495        self.is_trace_root
496    }
497
498    /// Returns the gRPC status code.
499    pub fn grpc_status_code(&self) -> &str {
500        &self.grpc_status_code
501    }
502
503    /// Returns the HTTP method.
504    pub fn http_method(&self) -> &str {
505        &self.http_method
506    }
507
508    /// Returns the HTTP endpoint.
509    pub fn http_endpoint(&self) -> &str {
510        &self.http_endpoint
511    }
512
513    // Getters for aggregated values
514
515    /// Returns the hit count.
516    pub fn hits(&self) -> u64 {
517        self.hits
518    }
519
520    /// Returns the error count.
521    pub fn errors(&self) -> u64 {
522        self.errors
523    }
524
525    /// Returns the total duration in nanoseconds.
526    pub fn duration(&self) -> u64 {
527        self.duration
528    }
529
530    /// Returns the DDSketch summary for successful spans.
531    pub fn ok_summary(&self) -> &[u8] {
532        &self.ok_summary
533    }
534
535    /// Returns the DDSketch summary for error spans.
536    pub fn error_summary(&self) -> &[u8] {
537        &self.error_summary
538    }
539
540    /// Returns the synthetics traffic flag.
541    pub fn synthetics(&self) -> bool {
542        self.synthetics
543    }
544
545    /// Returns the top-level hit count.
546    pub fn top_level_hits(&self) -> u64 {
547        self.top_level_hits
548    }
549}