logfire client for zig

add httpSpan() and sqlSpan() instrumentation helpers

framework integrations for common span patterns:
- httpSpan(method, path) -> "HTTP GET /api/..." with standard attrs
- sqlSpan(sql, db_system) -> truncated SQL with db.system attr

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

+50
+50
src/otel_wrapper.zig
··· 378 378 } 379 379 380 380 // ============================================================================ 381 + // instrumentation helpers (HTTP, SQL) 382 + // ============================================================================ 383 + 384 + /// create an HTTP span with formatted name: "HTTP {method} {path}" 385 + /// adds standard http.request.method and url.path attributes 386 + pub fn httpSpan(method: []const u8, path: []const u8, attrs: anytype) Span { 387 + var name_buf: [256]u8 = undefined; 388 + const span_name = std.fmt.bufPrint(&name_buf, "HTTP {s} {s}", .{ method, path }) catch "HTTP request"; 389 + 390 + // merge standard HTTP attrs with user attrs 391 + const T = @TypeOf(attrs); 392 + if (@typeInfo(T) == .@"struct" and @typeInfo(T).@"struct".fields.len > 0) { 393 + // user provided extra attrs - for now just use standard ones 394 + // TODO: merge attrs properly 395 + return span(span_name, .{ 396 + .@"http.request.method" = method, 397 + .@"url.path" = path, 398 + }); 399 + } else { 400 + return span(span_name, .{ 401 + .@"http.request.method" = method, 402 + .@"url.path" = path, 403 + }); 404 + } 405 + } 406 + 407 + /// create a SQL span with truncated query as name 408 + /// adds db.system attribute 409 + pub fn sqlSpan(sql: []const u8, db_system: []const u8) Span { 410 + var name_buf: [128]u8 = undefined; 411 + const span_name = truncateSql(&name_buf, sql); 412 + return span(span_name, .{ .@"db.system" = db_system }); 413 + } 414 + 415 + /// truncate SQL for display (max 60 chars, break at word boundary) 416 + fn truncateSql(buf: []u8, sql: []const u8) []const u8 { 417 + const max_len: usize = 60; 418 + if (sql.len <= max_len) { 419 + return std.fmt.bufPrint(buf, "{s}", .{sql}) catch sql[0..@min(sql.len, buf.len)]; 420 + } 421 + 422 + // find a good break point (space) 423 + var end: usize = max_len; 424 + while (end > 40 and sql[end] != ' ') : (end -= 1) {} 425 + if (end <= 40) end = max_len; 426 + 427 + return std.fmt.bufPrint(buf, "{s}...", .{sql[0..end]}) catch sql[0..@min(sql.len, buf.len)]; 428 + } 429 + 430 + // ============================================================================ 381 431 // helpers 382 432 // ============================================================================ 383 433