logfire client for zig
1# logfire-zig
2
3unofficial Zig SDK for [Pydantic Logfire](https://logfire.pydantic.dev/) - built on [otel-zig](https://github.com/open-telemetry/opentelemetry-zig) for OTLP/protobuf export.
4
5aiming for parity with [logfire-rust](https://github.com/pydantic/logfire-rust).
6
7see also:
8- [Logfire documentation](https://logfire.pydantic.dev/docs/)
9- [alternative clients guide](https://logfire.pydantic.dev/docs/how-to-guides/alternative-clients/) for OTLP protocol details
10
11## Using logfire-zig
12
13First [set up a Logfire project](https://logfire.pydantic.dev/docs/#logfire). Configure the SDK by [creating a write token](https://logfire.pydantic.dev/docs/how-to-guides/create-write-tokens/) and setting it as an environment variable (`LOGFIRE_WRITE_TOKEN` or `LOGFIRE_TOKEN`).
14
15Add to your `build.zig.zon`:
16
17```zig
18.dependencies = .{
19 .logfire = .{
20 .url = "https://tangled.sh/zzstoatzz.io/logfire-zig/archive/main",
21 .hash = "...", // zig build will tell you the hash
22 },
23},
24```
25
26Add to your `build.zig`:
27
28```zig
29const logfire = b.dependency("logfire", .{
30 .target = target,
31 .optimize = optimize,
32});
33exe.root_module.addImport("logfire", logfire.module("logfire"));
34```
35
36Then instrument your code:
37
38```zig
39const std = @import("std");
40const logfire = @import("logfire");
41
42pub fn main() !void {
43 const lf = try logfire.configure(.{
44 .service_name = "my-service",
45 });
46 defer lf.shutdown();
47
48 // structured logging (console output)
49 logfire.info("application started", .{});
50
51 // spans for timing operations (exported to Logfire)
52 {
53 const span = logfire.span("process.files", .{});
54 defer span.end();
55
56 // nested spans automatically link to parent
57 {
58 const child = logfire.span("process.single_file", .{ .filename = "data.csv" });
59 defer child.end();
60 std.time.sleep(10 * std.time.ns_per_ms);
61 }
62 }
63
64 // flush before exit
65 try lf.flush();
66}
67```
68
69Run with your token:
70
71```bash
72LOGFIRE_WRITE_TOKEN=pylf_v1_us_xxx zig build run
73```
74
75Without a token, output goes to console for local development.
76
77## Features
78
79- **Spans** - timing and tracing with attributes, automatic parent-child linking
80- **Instrumentation helpers** - `httpSpan()` and `sqlSpan()` for common patterns
81- **Async batched export** - BatchSpanProcessor exports every 500ms (matches Python/Rust)
82- **Logging** - trace, debug, info, warn, err (console output)
83- **OTLP Export** - HTTP/protobuf via otel-zig to Logfire or any OTLP-compatible backend
84- **Zero Config** - reads token and endpoint from environment
85- **Cross-platform** - works on macOS and Linux
86
87## API
88
89```zig
90// configuration
91const lf = try logfire.configure(.{
92 .service_name = "my-service",
93 .service_version = "1.0.0",
94});
95defer lf.shutdown();
96
97// spans (exported to Logfire via OTLP/protobuf)
98const span = logfire.span("operation.name", .{
99 .user_id = @as(i64, 123),
100 .request_path = "/api/search",
101});
102defer span.end();
103
104// add attributes after creation
105span.setAttribute("result_count", @as(i64, 42));
106
107// record errors (sets error status + adds exception event)
108span.recordError(error.ConnectionFailed);
109
110// instrumentation helpers for common patterns
111const http_span = logfire.httpSpan("GET", "/api/users", .{});
112defer http_span.end();
113// creates span named "HTTP GET /api/users" with http.request.method and url.path attrs
114
115const db_span = logfire.sqlSpan("SELECT * FROM users WHERE id = ?", "postgresql");
116defer db_span.end();
117// creates span with truncated SQL name and db.system attribute
118
119// logging (console output only for now)
120logfire.trace("detailed trace", .{});
121logfire.debug("debug info", .{});
122logfire.info("something happened", .{});
123logfire.warn("warning message", .{});
124logfire.err("error occurred", .{});
125
126// manual flush
127try lf.flush();
128```
129
130## Environment Variables
131
132| Variable | Description |
133|----------|-------------|
134| `LOGFIRE_WRITE_TOKEN` | Write token (preferred) |
135| `LOGFIRE_TOKEN` | Write token (fallback) |
136| `LOGFIRE_SERVICE_NAME` | Service name override |
137| `OTEL_EXPORTER_OTLP_ENDPOINT` | Custom OTLP endpoint |
138
139## Requirements
140
141- Zig 0.15+
142
143## Development
144
145```bash
146zig build test # run tests
147zig build example # run example
148```
149
150## Status
151
152This is an unofficial community SDK aiming for parity with [logfire-rust](https://github.com/pydantic/logfire-rust). Built on [otel-zig](https://github.com/open-telemetry/opentelemetry-zig).
153
154- [x] Spans with attributes
155- [x] Parent-child span linking (thread-local context)
156- [x] Error recording (`span.recordError()`)
157- [x] OTLP HTTP/protobuf export
158- [x] Async batched export (500ms interval, matches Python/Rust)
159- [x] Environment-based configuration
160- [x] Instrumentation helpers (`httpSpan`, `sqlSpan`)
161- [x] Cross-platform (macOS + Linux)
162- [ ] Structured logging export (console only, OTLP TODO)
163- [ ] Metrics (API stubs, implementation TODO)
164- [ ] W3C trace context propagation (cross-service)
165
166## License
167
168MIT