| ... | @@ -16,14 +16,38 @@ const sys = switch (builtin.target.os.tag) { | ... | @@ -16,14 +16,38 @@ const sys = switch (builtin.target.os.tag) { |
| 16 | // export OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:4318 | 16 | // export OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:4318 |
| 17 | // export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:4318/v1/traces | 17 | // export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:4318/v1/traces |
| 18 | | 18 | |
| | 19 | // https://opentelemetry.io/docs/specs/otlp/ |
| | 20 | // https://protobuf.dev/programming-guides/encoding/ |
| 19 | // https://github.com/open-telemetry/opentelemetry-proto/tree/v1.10.0/opentelemetry/proto | 21 | // https://github.com/open-telemetry/opentelemetry-proto/tree/v1.10.0/opentelemetry/proto |
| 20 | | 22 | |
| | 23 | var etc_os_release: []const u8 = ""; |
| | 24 | var @"os.version": ?[]const u8 = null; |
| | 25 | var @"os.name": ?[]const u8 = null; |
| | 26 | |
| 21 | pub fn init(args: struct {}) !void { | 27 | pub fn init(args: struct {}) !void { |
| 22 | _ = args; | 28 | _ = args; |
| | 29 | const file = try nfs.cwd().openFile("/etc/os-release", .{}); |
| | 30 | defer file.close(); |
| | 31 | etc_os_release = try file.mmap(); |
| | 32 | var iter = std.mem.splitScalar(u8, etc_os_release, '\n'); |
| | 33 | iter.index = 0; |
| | 34 | while (iter.next()) |line| { |
| | 35 | if (extras.trimPrefixEnsure(line, "VERSION_ID=")) |val| { |
| | 36 | @"os.version" = std.mem.trim(u8, val, &.{'"'}); |
| | 37 | break; |
| | 38 | } |
| | 39 | } |
| | 40 | iter.index = 0; |
| | 41 | while (iter.next()) |line| { |
| | 42 | if (extras.trimPrefixEnsure(line, "NAME=")) |val| { |
| | 43 | @"os.name" = std.mem.trim(u8, val, &.{'"'}); |
| | 44 | break; |
| | 45 | } |
| | 46 | } |
| 23 | } | 47 | } |
| 24 | | 48 | |
| 25 | pub fn deinit() void { | 49 | pub fn deinit() void { |
| 26 | _ = {}; | 50 | nfs.munmap(etc_os_release); |
| 27 | } | 51 | } |
| 28 | | 52 | |
| 29 | threadlocal var allocator: std.mem.Allocator = undefined; | 53 | threadlocal var allocator: std.mem.Allocator = undefined; |
| ... | @@ -32,6 +56,16 @@ threadlocal var trace_id: [16]u8 = undefined; | ... | @@ -32,6 +56,16 @@ threadlocal var trace_id: [16]u8 = undefined; |
| 32 | threadlocal var prev_span_id: ?[8]u8 = undefined; | 56 | threadlocal var prev_span_id: ?[8]u8 = undefined; |
| 33 | threadlocal var spans: std.ArrayListUnmanaged([]const u8) = .empty; | 57 | threadlocal var spans: std.ArrayListUnmanaged([]const u8) = .empty; |
| 34 | | 58 | |
| | 59 | pub var @"service.version": ?[]const u8 = null; |
| | 60 | pub var @"server.address": ?[]const u8 = null; |
| | 61 | pub var @"server.port": ?u16 = null; |
| | 62 | pub threadlocal var @"http.request.method": ?[]const u8 = null; |
| | 63 | pub threadlocal var @"http.response.status_code": ?u16 = null; |
| | 64 | pub threadlocal var @"http.route": ?[]const u8 = null; |
| | 65 | |
| | 66 | pub threadlocal var @"url.path": ?[]const u8 = null; |
| | 67 | pub threadlocal var @"url.query": ?[]const u8 = null; |
| | 68 | |
| 35 | pub fn init_thread(args: struct { std.mem.Allocator, std.Uri }) !void { | 69 | pub fn init_thread(args: struct { std.mem.Allocator, std.Uri }) !void { |
| 36 | allocator, traces_endpoint = args; | 70 | allocator, traces_endpoint = args; |
| 37 | trace_id = extras.randomBytes(16); | 71 | trace_id = extras.randomBytes(16); |
| ... | @@ -73,7 +107,15 @@ fn deinit_thread_inner() !void { | ... | @@ -73,7 +107,15 @@ fn deinit_thread_inner() !void { |
| 73 | .@"telemetry.sdk.version" = "(hash)", | 107 | .@"telemetry.sdk.version" = "(hash)", |
| 74 | .@"telemetry.sdk.language" = "zig", | 108 | .@"telemetry.sdk.language" = "zig", |
| 75 | .@"service.name" = root.otel_service_name, | 109 | .@"service.name" = root.otel_service_name, |
| | 110 | .@"service.version" = @"service.version", |
| 76 | .@"os.type" = "linux", | 111 | .@"os.type" = "linux", |
| | 112 | .@"os.version" = @"os.version", |
| | 113 | .@"os.name" = @"os.name", |
| | 114 | .@"server.address" = @"server.address", |
| | 115 | .@"server.port" = @"server.port", |
| | 116 | .@"http.request.method" = @"http.request.method", |
| | 117 | .@"http.response.status_code" = @"http.response.status_code", |
| | 118 | .@"http.route" = @"http.route", |
| 77 | }); | 119 | }); |
| 78 | try writef_varint(w, 2, 0); | 120 | try writef_varint(w, 2, 0); |
| 79 | } | 121 | } |
| ... | @@ -149,6 +191,7 @@ fn trace_end_inner(ctx: tracer.Ctx) !void { | ... | @@ -149,6 +191,7 @@ fn trace_end_inner(ctx: tracer.Ctx) !void { |
| 149 | try writef_varint(w, 6, 2); | 191 | try writef_varint(w, 6, 2); |
| 150 | try writef_i64(w, 7, (ctx.data.time_start_ns)); | 192 | try writef_i64(w, 7, (ctx.data.time_start_ns)); |
| 151 | try writef_i64(w, 8, @intCast(time.nanoTimestamp())); | 193 | try writef_i64(w, 8, @intCast(time.nanoTimestamp())); |
| | 194 | if (ctx.data.parent_id == null) try writef_kv(w, 9, .{ .@"url.path" = @"url.path", .@"url.query" = @"url.query" }); |
| 152 | try spans.append(allocator, temp.items); | 195 | try spans.append(allocator, temp.items); |
| 153 | } | 196 | } |
| 154 | | 197 | |
| ... | @@ -227,10 +270,13 @@ fn writef_kv(w: anytype, nr: u64, kvs: anytype) !void { | ... | @@ -227,10 +270,13 @@ fn writef_kv(w: anytype, nr: u64, kvs: anytype) !void { |
| 227 | defer temp2.deinit(allocator); | 270 | defer temp2.deinit(allocator); |
| 228 | const y = temp2.writer(allocator); | 271 | const y = temp2.writer(allocator); |
| 229 | | 272 | |
| 230 | inline for (comptime std.meta.fields(@TypeOf(kvs))) |field| { | 273 | inline for (comptime std.meta.fields(@TypeOf(kvs))) |field| blk: { |
| | 274 | const value = @field(kvs, field.name); |
| | 275 | if (@typeInfo(@TypeOf(value)) == .optional and value == null) break :blk; |
| | 276 | |
| 231 | try writef_string(x, 1, field.name); | 277 | try writef_string(x, 1, field.name); |
| 232 | | 278 | |
| 233 | try writef_string(y, 1, @field(kvs, field.name)); | 279 | try writef_kv_v(y, value); |
| 234 | try writef_len(x, 2, temp2.items.len); | 280 | try writef_len(x, 2, temp2.items.len); |
| 235 | try x.writeAll(temp2.items); | 281 | try x.writeAll(temp2.items); |
| 236 | temp2.clearRetainingCapacity(); | 282 | temp2.clearRetainingCapacity(); |
| ... | @@ -240,6 +286,28 @@ fn writef_kv(w: anytype, nr: u64, kvs: anytype) !void { | ... | @@ -240,6 +286,28 @@ fn writef_kv(w: anytype, nr: u64, kvs: anytype) !void { |
| 240 | temp.clearRetainingCapacity(); | 286 | temp.clearRetainingCapacity(); |
| 241 | } | 287 | } |
| 242 | } | 288 | } |
| | 289 | fn writef_kv_v(w: anytype, v: anytype) !void { |
| | 290 | const V = @TypeOf(v); |
| | 291 | const info = @typeInfo(V); |
| | 292 | if (info == .optional) { |
| | 293 | if (v == null) return; |
| | 294 | return writef_kv_v(w, v.?); |
| | 295 | } |
| | 296 | if (comptime extras.isZigString(V)) { |
| | 297 | return writef_string(w, 1, v); |
| | 298 | } |
| | 299 | if (info == .bool) { |
| | 300 | return write_varint(w, 2, @intFromBool(v)); |
| | 301 | } |
| | 302 | if (info == .int) { |
| | 303 | return writef_varint(w, 3, v); |
| | 304 | } |
| | 305 | if (info == .float) { |
| | 306 | return writef_i64(w, 4, @bitCast(v)); |
| | 307 | } |
| | 308 | @compileLog(v); |
| | 309 | comptime unreachable; |
| | 310 | } |
| 243 | | 311 | |
| 244 | fn writef_i64(w: anytype, nr: u64, i: u64) !void { | 312 | fn writef_i64(w: anytype, nr: u64, i: u64) !void { |
| 245 | try write_tag(w, nr, .i64); | 313 | try write_tag(w, nr, .i64); |