Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 85 additions & 1 deletion build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,20 @@ pub fn build(b: *std.Build) void {
module_bls.linkLibrary(dep_blst.artifact("blst"));
b.modules.put(b.dupe("bls"), module_bls) catch @panic("OOM");

const module_ring_buffer = b.createModule(.{
.root_source_file = b.path("src/ring_buffer.zig"),
.target = target,
.optimize = optimize,
});
b.modules.put(b.dupe("ring_buffer"), module_ring_buffer) catch @panic("OOM");

const module_log = b.createModule(.{
.root_source_file = b.path("src/log/root.zig"),
.target = target,
.optimize = optimize,
});
b.modules.put(b.dupe("log"), module_log) catch @panic("OOM");

const module_state_transition = b.createModule(.{
.root_source_file = b.path("src/state_transition/root.zig"),
.target = target,
Expand Down Expand Up @@ -202,6 +216,29 @@ pub fn build(b: *std.Build) void {
const tls_run_exe_metrics_stf = b.step("run:metrics_stf", "Run the metrics_stf executable");
tls_run_exe_metrics_stf.dependOn(&run_exe_metrics_stf.step);

const module_log_smoke = b.createModule(.{
.root_source_file = b.path("examples/log_smoke.zig"),
.target = target,
.optimize = optimize,
});
b.modules.put(b.dupe("log_smoke"), module_log_smoke) catch @panic("OOM");

const exe_log_smoke = b.addExecutable(.{
.name = "log_smoke",
.root_module = module_log_smoke,
});

const install_exe_log_smoke = b.addInstallArtifact(exe_log_smoke, .{});

const tls_install_exe_log_smoke = b.step("build-exe:log_smoke", "Install the log_smoke executable");
tls_install_exe_log_smoke.dependOn(&install_exe_log_smoke.step);
b.getInstallStep().dependOn(&install_exe_log_smoke.step);

const run_exe_log_smoke = b.addRunArtifact(exe_log_smoke);
if (b.args) |args| run_exe_log_smoke.addArgs(args);
const tls_run_exe_log_smoke = b.step("run:log_smoke", "Run the log_smoke executable");
tls_run_exe_log_smoke.dependOn(&run_exe_log_smoke.step);

const module_download_spec_tests = b.createModule(.{
.root_source_file = b.path("test/spec/download_spec_tests.zig"),
.target = target,
Expand Down Expand Up @@ -681,6 +718,34 @@ pub fn build(b: *std.Build) void {
tls_run_test_bls.dependOn(&run_test_bls.step);
tls_run_test.dependOn(&run_test_bls.step);

const test_ring_buffer = b.addTest(.{
.name = "ring_buffer",
.root_module = module_ring_buffer,
.filters = b.option([][]const u8, "ring_buffer.filters", "ring_buffer test filters") orelse &[_][]const u8{},
});
const install_test_ring_buffer = b.addInstallArtifact(test_ring_buffer, .{});
const tls_install_test_ring_buffer = b.step("build-test:ring_buffer", "Install the ring_buffer test");
tls_install_test_ring_buffer.dependOn(&install_test_ring_buffer.step);

const run_test_ring_buffer = b.addRunArtifact(test_ring_buffer);
const tls_run_test_ring_buffer = b.step("test:ring_buffer", "Run the ring_buffer test");
tls_run_test_ring_buffer.dependOn(&run_test_ring_buffer.step);
tls_run_test.dependOn(&run_test_ring_buffer.step);

const test_log = b.addTest(.{
.name = "log",
.root_module = module_log,
.filters = b.option([][]const u8, "log.filters", "log test filters") orelse &[_][]const u8{},
});
const install_test_log = b.addInstallArtifact(test_log, .{});
const tls_install_test_log = b.step("build-test:log", "Install the log test");
tls_install_test_log.dependOn(&install_test_log.step);

const run_test_log = b.addRunArtifact(test_log);
const tls_run_test_log = b.step("test:log", "Run the log test");
tls_run_test_log.dependOn(&run_test_log.step);
tls_run_test.dependOn(&run_test_log.step);

const test_state_transition = b.addTest(.{
.name = "state_transition",
.root_module = module_state_transition,
Expand Down Expand Up @@ -723,6 +788,20 @@ pub fn build(b: *std.Build) void {
tls_run_test_metrics_stf.dependOn(&run_test_metrics_stf.step);
tls_run_test.dependOn(&run_test_metrics_stf.step);

const test_log_smoke = b.addTest(.{
.name = "log_smoke",
.root_module = module_log_smoke,
.filters = b.option([][]const u8, "log_smoke.filters", "log_smoke test filters") orelse &[_][]const u8{},
});
const install_test_log_smoke = b.addInstallArtifact(test_log_smoke, .{});
const tls_install_test_log_smoke = b.step("build-test:log_smoke", "Install the log_smoke test");
tls_install_test_log_smoke.dependOn(&install_test_log_smoke.step);

const run_test_log_smoke = b.addRunArtifact(test_log_smoke);
const tls_run_test_log_smoke = b.step("test:log_smoke", "Run the log_smoke test");
tls_run_test_log_smoke.dependOn(&run_test_log_smoke.step);
tls_run_test.dependOn(&run_test_log_smoke.step);

const test_download_spec_tests = b.addTest(.{
.name = "download_spec_tests",
.root_module = module_download_spec_tests,
Expand Down Expand Up @@ -1066,6 +1145,8 @@ pub fn build(b: *std.Build) void {
module_ssz.addImport("hashing", module_hashing);
module_ssz.addImport("persistent_merkle_tree", module_persistent_merkle_tree);

module_log.addImport("ring_buffer", module_ring_buffer);

module_state_transition.addImport("build_options", options_module_build_options);
module_state_transition.addImport("ssz", module_ssz);
module_state_transition.addImport("config", module_config);
Expand All @@ -1088,6 +1169,9 @@ pub fn build(b: *std.Build) void {
module_metrics_stf.addImport("config", module_config);
module_metrics_stf.addImport("preset", module_preset);
module_metrics_stf.addImport("httpz", dep_httpz.module("httpz"));
module_metrics_stf.addImport("log", module_log);

module_log_smoke.addImport("log", module_log);

module_download_spec_tests.addImport("spec_test_options", options_module_spec_test_options);

Expand Down Expand Up @@ -1161,7 +1245,7 @@ pub fn build(b: *std.Build) void {
module_bindings.addImport("config", module_config);
module_bindings.addImport("fork_types", module_fork_types);
module_bindings.addImport("state_transition", module_state_transition);
module_bindings.addImport("zapi:napi", dep_zapi.module("napi"));
module_bindings.addImport("zapi:zapi", dep_zapi.module("zapi"));

module_int.addImport("config", module_config);
module_int.addImport("download_era_options", options_module_download_era_options);
Expand Down
4 changes: 2 additions & 2 deletions build.zig.zon
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
.hash = "zig_yaml-0.1.0-C1161m2NAgDhth5OvjG1o1UNKcdo-XNfO82m10J1g4Cl",
},
.zapi = .{
.url = "git+https://github.com/chainsafe/zapi#cf013457cdf0612a62a6d008d65505ff79e85596",
.hash = "napi_z-0.1.0-iqJb7-4oAgBkIQFWFEDazD0-G9NRBQZaEY0fwTB7Aqbu",
.url = "git+https://github.com/chainsafe/zapi#5a51cf21121372451bd56af97db896f474bce384",
.hash = "zapi-0.1.0-rIqzUTM-AgDqeGAe3zmoh8_y6CtuBra5RE_BJVmUFMfX",
},
.zbench = .{
.url = "git+https://github.com/hendriknielaender/zBench#d8c7dd485306b88b757d52005614ebcb0a336942",
Expand Down
219 changes: 219 additions & 0 deletions examples/log_smoke.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
//! Log integration smoke example.
//!
//! Demonstrates the structured Logger API, std.log bridge,
//! caller-owned async pipelines, and all three layout formats.
//!
//! Run:
//! zig build run:log_smoke
//!
//! Expected output: structured log lines on stderr from each dispatcher/layout.

const std = @import("std");
const log_mod = @import("log");

/// Re-export std_options so std.log calls route through the pipeline.
pub const std_options = log_mod.std_options;

const Logger = log_mod.Logger;
const Attr = log_mod.Attr;
const Dispatch = log_mod.Dispatch;
const Dispatcher = log_mod.Dispatcher;
const LevelFilter = log_mod.LevelFilter;
const FlushableWriter = log_mod.FlushableWriter;
const WriterAppend = log_mod.WriterAppend;
const AsyncAppend = log_mod.AsyncAppend;
const TextLayout = log_mod.TextLayout;
const JsonLayout = log_mod.JsonLayout;
const LogfmtLayout = log_mod.LogfmtLayout;
const RollingFileWriter = log_mod.RollingFileWriter;

/// Helper: emit a standard set of log lines through `log`.
fn emitSample(log: anytype) void {
log.err("error msg", .{ .code = @as(u64, 500) });
log.warn("warn msg", .{});
log.info("info msg", .{ .slot = @as(u64, 42) });
log.debug("debug msg", .{ .flag = true });
}

pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();

// ── 1. Sync stderr pipeline (explicit TextLayout) ─────────────

std.debug.print("\n=== 1. Sync stderr pipeline (explicit TextLayout) ===\n", .{});
{
var filter = LevelFilter.init(.debug);
var sync_appender = WriterAppend(TextLayout).init(allocator, TextLayout{ .color = true }, FlushableWriter.stderrWriter());

var d = Dispatch.init();
d.addFilter(filter.any());
d.addAppend(sync_appender.any());

var dispatcher = Dispatcher.init();
dispatcher.addDispatch(d);
log_mod.setGlobalDispatcher(dispatcher.any());

const std_log = std.log.scoped(.smoke);
std_log.err("std.log bridge err", .{});
std_log.info("std.log bridge info", .{});

const log = Logger(4).init(.smoke, log_mod.getGlobalDispatcher());
emitSample(log);

const child = log.with(&[_]Attr{Attr.str("module", "fc")});
child.info("child log", .{ .epoch = @as(u64, 7) });

log_mod.resetGlobalDispatcher();
}

// ── 2. Async console pipeline (caller-owned) ────────────

std.debug.print("\n=== 2. Async console pipeline (caller-owned) ===\n", .{});
{
var filter = LevelFilter.init(.debug);
var appender = try AsyncAppend(TextLayout).init(
allocator,
1024,
FlushableWriter.stderrWriter(),
TextLayout{ .color = true },
.block,
);
try appender.start();

var d = Dispatch.init();
d.addFilter(filter.any());
d.addAppend(appender.any());

var dispatcher = Dispatcher.init();
dispatcher.addDispatch(d);
log_mod.setGlobalDispatcher(dispatcher.any());

const log = Logger(4).init(.smoke, log_mod.getGlobalDispatcher());
emitSample(log);
log_mod.flushGlobalDispatcher();

dispatcher.deinit();
log_mod.resetGlobalDispatcher();
}

// ── 3. Async file pipeline (caller-owned) ─────

std.debug.print("\n=== 3. Async file pipeline (caller-owned) ===\n", .{});
{
var rolling = try RollingFileWriter.init(std.fs.cwd(), "smoke.log", .{ .rotation = .never, .max_bytes = 1024 * 1024 });

var filter = LevelFilter.init(.debug);
var appender = try AsyncAppend(TextLayout).init(
allocator,
1024,
FlushableWriter.fromFlushable(&rolling),
TextLayout{},
.block,
);
try appender.start();

var d = Dispatch.init();
d.addFilter(filter.any());
d.addAppend(appender.any());

var dispatcher = Dispatcher.init();
dispatcher.addDispatch(d);
log_mod.setGlobalDispatcher(dispatcher.any());

const log = Logger(4).init(.smoke, log_mod.getGlobalDispatcher());
emitSample(log);
log_mod.flushGlobalDispatcher();

dispatcher.deinit();
rolling.deinit();
log_mod.resetGlobalDispatcher();
std.debug.print(" → wrote smoke.log\n", .{});
}

// ── 4. Combined pipeline: console + file (caller-owned) ─

std.debug.print("\n=== 4. Combined pipeline: console + file (caller-owned) ===\n", .{});
{
var rolling = try RollingFileWriter.init(std.fs.cwd(), "smoke_combined.log", .{ .rotation = .never, .max_bytes = 1024 * 1024 });

var filter = LevelFilter.init(.debug);
var console_appender = try AsyncAppend(TextLayout).init(
allocator,
1024,
FlushableWriter.stderrWriter(),
TextLayout{ .color = true },
.block,
);
var file_appender = try AsyncAppend(TextLayout).init(
allocator,
1024,
FlushableWriter.fromFlushable(&rolling),
TextLayout{},
.block,
);
try console_appender.start();
try file_appender.start();

var d = Dispatch.init();
d.addFilter(filter.any());
d.addAppend(console_appender.any());
d.addAppend(file_appender.any());

var dispatcher = Dispatcher.init();
dispatcher.addDispatch(d);
log_mod.setGlobalDispatcher(dispatcher.any());

const log = Logger(4).init(.smoke, log_mod.getGlobalDispatcher());
emitSample(log);
log_mod.flushGlobalDispatcher();

dispatcher.deinit();
rolling.deinit();
log_mod.resetGlobalDispatcher();
std.debug.print(" → wrote smoke_combined.log\n", .{});
}

// ── 5. Custom pipeline — JsonLayout (sync stderr) ────────

std.debug.print("\n=== 5. JsonLayout (sync stderr) ===\n", .{});
{
var debug_filter = LevelFilter.init(.debug);
var json_appender = WriterAppend(JsonLayout).init(allocator, JsonLayout{}, FlushableWriter.stderrWriter());

var d = Dispatch.init();
d.addFilter(debug_filter.any());
d.addAppend(json_appender.any());

var dispatcher = Dispatcher.init();
dispatcher.addDispatch(d);

const log = Logger(4).init(.smoke, dispatcher.any());
emitSample(log);
}

// ── 6. Custom pipeline — LogfmtLayout (sync stderr) ──────

std.debug.print("\n=== 6. LogfmtLayout (sync stderr) ===\n", .{});
{
var debug_filter = LevelFilter.init(.debug);
var logfmt_appender = WriterAppend(LogfmtLayout).init(allocator, LogfmtLayout{}, FlushableWriter.stderrWriter());

var d = Dispatch.init();
d.addFilter(debug_filter.any());
d.addAppend(logfmt_appender.any());

var dispatcher = Dispatcher.init();
dispatcher.addDispatch(d);

const log = Logger(4).init(.smoke, dispatcher.any());
emitSample(log);
}

// cleanup generated files
std.fs.cwd().deleteFile("smoke.log") catch {};
std.fs.cwd().deleteFile("smoke_combined.log") catch {};
Comment on lines +215 to +216

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The errors from deleteFile are being ignored with catch {}. While this is an example file, the style guide requires all errors to be handled (Rule 160). Silently ignoring file system errors can hide underlying problems. It would be better to at least print the error to stderr.

    std.fs.cwd().deleteFile("smoke.log") catch |err| std.debug.print("Failed to delete smoke.log: {s}\n", .{@errorName(err)});
    std.fs.cwd().deleteFile("smoke_combined.log") catch |err| std.debug.print("Failed to delete smoke_combined.log: {s}\n", .{@errorName(err)});

References
  1. Rule 160 states that all errors must be handled. The current code uses catch {} which silently ignores potential file system errors during cleanup. (link)


std.debug.print("\n=== All 6 configurations OK ===\n", .{});
}
Loading
Loading