authorgravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-05-01 16:01:08 -07:00
committergravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-05-01 16:01:08 -07:00
logfe7f00320d5c3f1839810a5be61f8dd570200c7e
treedb90ca6bfe9adaf917f1fc2b3560577b418ad940
parent6225cb6611796773c7016eba95dd5dd085f6bdb1
signaturebadge-check Signed by SSH key SHA256:4hHJbtBRU58AYXwjL7fkz2fnQHdiye8x1QpTCQ0sHNw

start otel


4 files changed, 304 insertions(+), 0 deletions(-)

build.zig+1
...@@ -12,6 +12,7 @@ pub fn build(b: *std.Build) void {...@@ -12,6 +12,7 @@ pub fn build(b: *std.Build) void {
12 addTest(b, target, mode, disable_llvm, mod, 1);12 addTest(b, target, mode, disable_llvm, mod, 1);
13 addTest(b, target, mode, disable_llvm, mod, 2);13 addTest(b, target, mode, disable_llvm, mod, 2);
14 addTest(b, target, mode, disable_llvm, mod, 3);14 addTest(b, target, mode, disable_llvm, mod, 3);
15 addTest(b, target, mode, disable_llvm, mod, 4);
1516
16 const test_step = b.step("test", "Run all library tests");17 const test_step = b.step("test", "Run all library tests");
17 test_step.dependOn(b.getInstallStep());18 test_step.dependOn(b.getInstallStep());
src/main.zig+1
...@@ -17,6 +17,7 @@ pub fn main() !void {...@@ -17,6 +17,7 @@ pub fn main() !void {
17 try tracer.init_thread(switch (build_options.backend) {17 try tracer.init_thread(switch (build_options.backend) {
18 0, 1 => .{},18 0, 1 => .{},
19 2, 3 => .{try nfs.mkdtemp()},19 2, 3 => .{try nfs.mkdtemp()},
20 4 => .{std.heap.c_allocator},
20 else => comptime unreachable,21 else => comptime unreachable,
21 });22 });
22 defer tracer.deinit_thread();23 defer tracer.deinit_thread();
src/mod.zig+3
...@@ -8,18 +8,21 @@ const impl = switch (backend) {...@@ -8,18 +8,21 @@ const impl = switch (backend) {
8 .log => log,8 .log => log,
9 .chrome => chrome,9 .chrome => chrome,
10 .spall => spall,10 .spall => spall,
11 .otel => otel,
11};12};
1213
13pub const none = @import("./none.zig");14pub const none = @import("./none.zig");
14pub const log = @import("./log.zig");15pub const log = @import("./log.zig");
15pub const chrome = @import("./chrome.zig");16pub const chrome = @import("./chrome.zig");
16pub const spall = @import("./spall.zig");17pub const spall = @import("./spall.zig");
18pub const otel = @import("./otel.zig");
1719
18pub const Backend = enum {20pub const Backend = enum {
19 none,21 none,
20 log,22 log,
21 chrome,23 chrome,
22 spall,24 spall,
25 otel,
23};26};
2427
25pub fn init(args: @typeInfo(@TypeOf(impl.init)).@"fn".params[0].type.?) !void {28pub fn init(args: @typeInfo(@TypeOf(impl.init)).@"fn".params[0].type.?) !void {
src/otel.zig created+299
...@@ -0,0 +1,299 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const tracer = @import("./mod.zig");
4const root = @import("root");
5const nfs = @import("nfs");
6const nio = @import("nio");
7const time = @import("time");
8const extras = @import("extras");
9
10const sys = switch (builtin.target.os.tag) {
11 .linux => @import("sys-linux"),
12 else => unreachable,
13};
14
15// export OTEL_EXPORTER_OTLP_PROTOCOL=grpc
16// export OTEL_EXPORTER_OTLP_PROTOCOL=http/json
17// export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
18// export OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:4318
19// export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:4318/v1/traces
20
21pub fn init(args: struct {}) !void {
22 _ = args;
23}
24
25pub fn deinit() void {
26 _ = {};
27}
28
29threadlocal var allocator: std.mem.Allocator = undefined;
30threadlocal var memfd: nfs.File = undefined;
31threadlocal var buffered_writer: nio.BufferedWriter(4096, nfs.File) = undefined;
32threadlocal var trace_id: [16]u8 = undefined;
33threadlocal var prev_span_id: ?[8]u8 = undefined;
34threadlocal var spans: std.ArrayListUnmanaged([]const u8) = .empty;
35
36pub fn init_thread(args: struct { std.mem.Allocator }) !void {
37 std.log.debug("called otel init_thread", .{});
38 allocator = args[0];
39 memfd = try nfs.memfd_create("zig-tracer", 0);
40 buffered_writer = .init(memfd);
41 trace_id = extras.randomBytes(16);
42 prev_span_id = null;
43
44 // try write_tag(1, .varint);
45 // try write_varint(150);
46 // 0896 01
47 // 1: 150
48
49 // try write_tag(2, .len);
50 // try write_varint(7);
51 // try buffered_writer.writeAll("testing");
52 // 1207 7465 7374 696e 67
53 // 2: {"testing"}
54
55 // try write_tag(3, .len);
56 // try write_varint(3);
57 // try write_tag(1, .varint);
58 // try write_varint(150);
59 // 1a03 0896 01
60 // 3: {1: 150}
61
62 // try write_tag(4, .len);
63 // try write_varint(5);
64 // try buffered_writer.writeAll("hello");
65 // try write_tag(6, .varint);
66 // try write_varint(3);
67 // try write_tag(6, .varint);
68 // try write_varint(270);
69 // try write_tag(6, .varint);
70 // try write_varint(86942);
71 // 2205 6865 6c6c 6f30 0330 8e02 309e a705
72 // 4: {"hello"}
73 // 6: 3
74 // 6: 270
75 // 6: 86942
76
77 // try write_tag(3, .sgroup);
78 // try write_tag(1, .varint);
79 // try write_varint(150);
80 // try write_tag(3, .egroup);
81 // 1b08 9601 1c
82 // 3: !{1: 150}
83
84 // try write_tag(4, .sgroup);
85 // try write_tag(3, .sgroup);
86 // try write_tag(1, .varint);
87 // try write_varint(150);
88 // try write_tag(3, .egroup);
89 // try write_tag(4, .egroup);
90 // 231b 0896 011c 24
91 // 4: !{3: !{1: 150}}
92
93}
94
95pub fn deinit_thread() void {
96 buffered_writer.flush() catch {};
97 memfd.close();
98}
99
100pub fn trace_begin(src: std.builtin.SourceLocation, comptime ifmt: []const u8, iargs: anytype) Data {
101 const span_id = extras.randomBytes(8);
102 const parent_id = prev_span_id;
103 prev_span_id = span_id;
104 const fmt = "{s}:{d}:{d} ({s})" ++ ifmt;
105 const args = .{ src.file, src.line, src.column, src.fn_name } ++ iargs;
106 const name = std.fmt.allocPrint(allocator, fmt, args) catch "";
107 const time_start_ns: u64 = @intCast(time.nanoTimestamp()); // this will overflow 2554 July 21 23:34:33.709 Z
108
109 return .{
110 .span_id = span_id,
111 .parent_id = parent_id,
112 .name = name,
113 .time_start_ns = time_start_ns,
114 };
115}
116
117pub fn trace_end(ctx: tracer.Ctx) void {
118 trace_end_inner(ctx) catch {};
119}
120fn trace_end_inner(ctx: tracer.Ctx) !void {
121 var temp: std.ArrayListUnmanaged(u8) = .empty;
122 defer temp.deinit(allocator);
123 const w = temp.writer(allocator);
124 try writef_bytes(w, 1, &trace_id);
125 try writef_bytes(w, 2, &ctx.data.span_id);
126 if (ctx.data.parent_id) |*pi| try writef_bytes(w, 4, pi);
127 try writef_string(w, 5, ctx.data.name);
128 if (ctx.data.name.len > 0) allocator.free(ctx.data.name);
129 try writef_varint(w, 6, 2);
130 try writef_varint(w, 7, ctx.data.time_start_ns);
131 try writef_varint(w, 8, @intCast(time.nanoTimestamp()));
132 try spans.append(allocator, temp.items);
133}
134
135pub const Data = struct {
136 span_id: [8]u8,
137 parent_id: ?[8]u8,
138 name: []const u8,
139 time_start_ns: u64,
140};
141
142//
143//
144
145// https://github.com/open-telemetry/opentelemetry-proto/tree/v1.10.0/opentelemetry/proto
146
147// message ExportTraceServiceRequest {
148// repeated opentelemetry.proto.trace.v1.ResourceSpans resource_spans = 1;
149// }
150
151// message ResourceSpans {
152// reserved 1000;
153// opentelemetry.proto.resource.v1.Resource resource = 1;
154// repeated ScopeSpans scope_spans = 2;
155// string schema_url = 3;
156// }
157
158// message Resource {
159// repeated opentelemetry.proto.common.v1.KeyValue attributes = 1;
160// uint32 dropped_attributes_count = 2;
161// }
162
163// message KeyValue {
164// string key = 1;
165// AnyValue value = 2;
166// }
167
168// message AnyValue {
169// oneof value {
170// string string_value = 1;
171// bool bool_value = 2;
172// int64 int_value = 3;
173// double double_value = 4;
174// ArrayValue array_value = 5;
175// KeyValueList kvlist_value = 6;
176// bytes bytes_value = 7;
177// }
178// }
179
180// message ArrayValue {
181// repeated AnyValue values = 1;
182// }
183
184// message KeyValueList {
185// repeated KeyValue values = 1;
186// }
187
188// message ScopeSpans {
189// opentelemetry.proto.common.v1.InstrumentationScope scope = 1;
190// repeated Span spans = 2;
191// string schema_url = 3;
192// }
193
194// message InstrumentationScope {
195// string name = 1;
196// string version = 2;
197// repeated KeyValue attributes = 3;
198// uint32 dropped_attributes_count = 4;
199// }
200
201// message Span {
202// bytes trace_id = 1;
203// bytes span_id = 2;
204// string trace_state = 3;
205// bytes parent_span_id = 4;
206// fixed32 flags = 16;
207// string name = 5;
208// SpanKind kind = 6;
209// fixed64 start_time_unix_nano = 7;
210// fixed64 end_time_unix_nano = 8;
211// repeated opentelemetry.proto.common.v1.KeyValue attributes = 9;
212// uint32 dropped_attributes_count = 10;
213// repeated Event events = 11;
214// uint32 dropped_events_count = 12;
215// repeated Link links = 13;
216// uint32 dropped_links_count = 14;
217// Status status = 15;
218// }
219
220// enum SpanKind {
221// SPAN_KIND_UNSPECIFIED = 0;
222// SPAN_KIND_INTERNAL = 1;
223// SPAN_KIND_SERVER = 2;
224// SPAN_KIND_CLIENT = 3;
225// SPAN_KIND_PRODUCER = 4;
226// SPAN_KIND_CONSUMER = 5;
227// }
228
229// message Event {
230// fixed64 time_unix_nano = 1;
231// string name = 2;
232// repeated opentelemetry.proto.common.v1.KeyValue attributes = 3;
233// uint32 dropped_attributes_count = 4;
234// }
235
236// message Link {
237// bytes trace_id = 1;
238// bytes span_id = 2;
239// string trace_state = 3;
240// repeated opentelemetry.proto.common.v1.KeyValue attributes = 4;
241// uint32 dropped_attributes_count = 5;
242// fixed32 flags = 6;
243// }
244
245//
246//
247
248const WireType = enum {
249 varint,
250 i64,
251 len,
252 sgroup,
253 egroup,
254 i32,
255};
256
257fn write_tag(w: anytype, nr: u64, ty: WireType) !void {
258 var _nr = nr;
259 _nr <<= 3;
260 _nr |= @intFromEnum(ty);
261 return write_varint(w, _nr);
262}
263
264fn write_varint(w: anytype, x: u64) !void {
265 var _x = x;
266 inline for (0..10) |i| {
267 if (x < std.math.powi(u64, 2, 7 * (i + 1)) catch unreachable) {
268 var b: [i + 1]u8 = @splat(0);
269 for (b[0..i]) |*n| {
270 n.* |= 128;
271 }
272 for (0..b.len) |j| {
273 b[j] |= @intCast(_x & 127);
274 _x >>= 7;
275 }
276 std.debug.assert(_x == 0);
277 try w.writeAll(&b);
278 return;
279 }
280 }
281 unreachable;
282}
283
284fn writef_bytes(w: anytype, nr: u64, bs: []const u8) !void {
285 try write_tag(w, nr, .len);
286 try write_varint(w, bs.len);
287 try w.writeAll(bs);
288}
289
290fn writef_string(w: anytype, nr: u64, bs: []const u8) !void {
291 try write_tag(w, nr, .len);
292 try write_varint(w, bs.len);
293 try w.writeAll(bs);
294}
295
296fn writef_varint(w: anytype, nr: u64, i: u64) !void {
297 try write_tag(w, nr, .varint);
298 try write_varint(w, i);
299}