logfire client for zig

make otel wrapper the primary module

- `logfire` module now points to otel_wrapper.zig (recommended)
- `logfire-legacy` module available for transition period
- example renamed to use main module
- both modules tested in `zig build test`

leaflet-search can now adopt without import changes.

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

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

+53 -35
+48 -30
build.zig
··· 10 10 .optimize = optimize, 11 11 }); 12 12 13 - // library module (legacy API) 14 - const mod = b.addModule("logfire", .{ 15 - .root_source_file = b.path("src/root.zig"), 13 + // config module (shared between modules) 14 + const config_mod = b.createModule(.{ 15 + .root_source_file = b.path("src/config.zig"), 16 + .target = target, 17 + .optimize = optimize, 18 + }); 19 + 20 + // main module: otel-backed wrapper (recommended) 21 + const logfire_mod = b.addModule("logfire", .{ 22 + .root_source_file = b.path("src/otel_wrapper.zig"), 16 23 .target = target, 17 24 .optimize = optimize, 18 25 .imports = &.{ 19 - .{ .name = "otel-api", .module = otel_dep.module("otel-api") }, 20 - .{ .name = "otel-sdk", .module = otel_dep.module("otel-sdk") }, 21 - .{ .name = "otel-exporters", .module = otel_dep.module("otel-exporters") }, 22 - .{ .name = "otel-semconv", .module = otel_dep.module("otel-semconv") }, 26 + .{ .name = "otel", .module = otel_dep.module("otel") }, 27 + .{ .name = "config", .module = config_mod }, 23 28 }, 24 29 }); 25 30 26 - // otel-based wrapper module (new API) 27 - const otel_mod = b.addModule("logfire-otel", .{ 28 - .root_source_file = b.path("src/otel_wrapper.zig"), 31 + // legacy module: direct OTLP/JSON implementation (deprecated) 32 + const legacy_mod = b.addModule("logfire-legacy", .{ 33 + .root_source_file = b.path("src/root.zig"), 29 34 .target = target, 30 35 .optimize = optimize, 31 36 .imports = &.{ 32 - .{ .name = "otel", .module = otel_dep.module("otel") }, 33 - .{ .name = "config", .module = b.createModule(.{ 34 - .root_source_file = b.path("src/config.zig"), 35 - .target = target, 36 - .optimize = optimize, 37 - }) }, 37 + .{ .name = "otel-api", .module = otel_dep.module("otel-api") }, 38 + .{ .name = "otel-sdk", .module = otel_dep.module("otel-sdk") }, 39 + .{ .name = "otel-exporters", .module = otel_dep.module("otel-exporters") }, 40 + .{ .name = "otel-semconv", .module = otel_dep.module("otel-semconv") }, 38 41 }, 39 42 }); 40 43 41 - // tests 42 - const tests = b.addTest(.{ .root_module = mod }); 44 + // tests (run against main module) 45 + const tests = b.addTest(.{ 46 + .root_module = b.createModule(.{ 47 + .root_source_file = b.path("src/otel_wrapper.zig"), 48 + .target = target, 49 + .optimize = optimize, 50 + .imports = &.{ 51 + .{ .name = "otel", .module = otel_dep.module("otel") }, 52 + .{ .name = "config", .module = config_mod }, 53 + }, 54 + }), 55 + }); 43 56 const run_tests = b.addRunArtifact(tests); 44 57 58 + // legacy tests 59 + const legacy_tests = b.addTest(.{ .root_module = legacy_mod }); 60 + const run_legacy_tests = b.addRunArtifact(legacy_tests); 61 + 45 62 const test_step = b.step("test", "run unit tests"); 46 63 test_step.dependOn(&run_tests.step); 64 + test_step.dependOn(&run_legacy_tests.step); 47 65 48 - // example executable 66 + // example executable (uses new otel-backed module) 49 67 const example = b.addExecutable(.{ 50 68 .name = "example", 51 69 .root_module = b.createModule(.{ 52 - .root_source_file = b.path("examples/basic.zig"), 70 + .root_source_file = b.path("examples/otel_basic.zig"), 53 71 .target = target, 54 72 .optimize = optimize, 55 - .imports = &.{.{ .name = "logfire", .module = mod }}, 73 + .imports = &.{.{ .name = "logfire", .module = logfire_mod }}, 56 74 }), 57 75 }); 58 76 59 77 const run_example = b.addRunArtifact(example); 60 - const example_step = b.step("example", "run the basic example"); 78 + const example_step = b.step("example", "run the otel-based example"); 61 79 example_step.dependOn(&run_example.step); 62 80 63 - // otel-based example 64 - const otel_example = b.addExecutable(.{ 65 - .name = "otel-example", 81 + // legacy example (for testing old API during transition) 82 + const legacy_example = b.addExecutable(.{ 83 + .name = "legacy-example", 66 84 .root_module = b.createModule(.{ 67 - .root_source_file = b.path("examples/otel_basic.zig"), 85 + .root_source_file = b.path("examples/basic.zig"), 68 86 .target = target, 69 87 .optimize = optimize, 70 - .imports = &.{.{ .name = "logfire-otel", .module = otel_mod }}, 88 + .imports = &.{.{ .name = "logfire", .module = legacy_mod }}, 71 89 }), 72 90 }); 73 91 74 - const run_otel_example = b.addRunArtifact(otel_example); 75 - const otel_example_step = b.step("otel-example", "run the otel-based example"); 76 - otel_example_step.dependOn(&run_otel_example.step); 92 + const run_legacy_example = b.addRunArtifact(legacy_example); 93 + const legacy_example_step = b.step("legacy-example", "run the legacy example"); 94 + legacy_example_step.dependOn(&run_legacy_example.step); 77 95 78 96 // validation script for otel-zig OTLP export (not in CI, requires LOGFIRE_TOKEN) 79 97 const validate_otel = b.addExecutable(.{
+5 -5
examples/otel_basic.zig
··· 1 - //! otel-based logfire example 1 + //! logfire example 2 2 //! 3 - //! demonstrates the new otel-zig backed API. 3 + //! demonstrates the otel-zig backed API. 4 4 //! API matches current leaflet-search usage patterns. 5 5 //! 6 6 //! run with: 7 - //! LOGFIRE_TOKEN=your_token zig build otel-example 7 + //! LOGFIRE_TOKEN=your_token zig build example 8 8 //! 9 9 //! or without token for console-only output: 10 - //! zig build otel-example 10 + //! zig build example 11 11 12 12 const std = @import("std"); 13 - const logfire = @import("logfire-otel"); 13 + const logfire = @import("logfire"); 14 14 15 15 pub fn main() !void { 16 16 // configure logfire (reads LOGFIRE_TOKEN from env)