logfire client for zig
1//! span tracking for distributed tracing
2//!
3//! spans measure the duration of operations.
4//! use defer to ensure spans are ended even on error paths.
5//!
6//! ## usage
7//!
8//! ```zig
9//! const s = logfire.span("db.query", .{});
10//! defer s.end();
11//! // ... do work
12//! ```
13
14const std = @import("std");
15const root = @import("root.zig");
16const Attribute = @import("attribute.zig").Attribute;
17
18pub const Span = struct {
19 logfire: ?*root.Logfire,
20 data: Data,
21 active: bool,
22
23 pub const max_attributes = 32;
24
25 pub const Data = struct {
26 name: []const u8,
27 trace_id: [16]u8,
28 span_id: [8]u8,
29 parent_span_id: ?[8]u8 = null,
30 start_time_ns: i128,
31 end_time_ns: i128,
32 attributes: [max_attributes]Attribute = undefined,
33 attribute_count: usize = 0,
34 };
35
36 /// create a span (called by Logfire.createSpan)
37 pub fn init(logfire: *root.Logfire, name: []const u8, span_id: [8]u8, trace_id: ?[16]u8, parent_span_id: ?[8]u8, attrs: anytype) Span {
38 var s = Span{
39 .logfire = logfire,
40 .data = .{
41 .name = name,
42 .trace_id = trace_id orelse [_]u8{0} ** 16,
43 .span_id = span_id,
44 .parent_span_id = parent_span_id,
45 .start_time_ns = std.time.nanoTimestamp(),
46 .end_time_ns = 0,
47 },
48 .active = true,
49 };
50
51 // store attributes
52 s.data.attribute_count = Attribute.fromStruct(attrs, &s.data.attributes);
53
54 return s;
55 }
56
57 /// create a no-op span (when logfire not configured)
58 pub fn noop() Span {
59 return .{
60 .logfire = null,
61 .data = .{
62 .name = "",
63 .trace_id = [_]u8{0} ** 16,
64 .span_id = [_]u8{0} ** 8,
65 .start_time_ns = 0,
66 .end_time_ns = 0,
67 },
68 .active = false,
69 };
70 }
71
72 /// end the span and record it
73 pub fn end(self: *const Span) void {
74 if (!self.active) return;
75 if (self.logfire) |lf| {
76 var data = self.data;
77 data.end_time_ns = std.time.nanoTimestamp();
78 lf.recordSpanEnd(data);
79 lf.spanEnded(self.data.parent_span_id);
80 }
81 }
82};
83
84// tests
85
86test "span lifecycle" {
87 const s = Span.noop();
88 defer s.end();
89
90 try std.testing.expect(!s.active);
91}