Skip to content

feat(log): structured logging#311

Closed
GrapeBaBa wants to merge 1 commit intomainfrom
gr/feature/logging
Closed

feat(log): structured logging#311
GrapeBaBa wants to merge 1 commit intomainfrom
gr/feature/logging

Conversation

@GrapeBaBa
Copy link
Copy Markdown
Contributor

@GrapeBaBa GrapeBaBa commented Apr 12, 2026

Summary

Add a full-featured structured logging module for lodestar-z

Key features

  • Comptime static dispatch pipeline: Filter → Diagnostic → Append, all resolved at compile time with zero runtime overhead for disabled log levels
  • Three layout formats: TextLayout (human-readable with ANSI color), JsonLayout, LogfmtLayout
  • Multiple appender types: WriterAppend (sync), AsyncAppend (lock-free ring buffer + background drain), RollingFileWriter (size/date rotation), OpenTelemetryAppend (OTLP JSON)
  • Composable filters: LevelFilter, ScopeFilter, EnvFilter (parses RUST_LOG-style directives like warn,fork_choice=debug)
  • Diagnostics: StaticDiagnostic (pre-bound attrs), ThreadLocalDiagnostic (per-thread context)
  • std.log bridge: Drop-in std_options so existing std.log.scoped() calls route through the pipeline
  • Global dispatcher builders: initConsoleDispatcher, initFileDispatcher, initCombinedDispatcher for common setups
  • Microsecond timestamps: ISO 8601 UTC format (2024-08-11T22:44:57.172105Z)
  • stderr by default: Following Unix convention (stdout = data, stderr = diagnostics)
  • Extracted ring_buffer.zig: Generic lock-free SPSC ring buffer, reusable outside logging

Architecture

Logger(N) → AnyDispatcher → Dispatcher(Dispatches)
                               ├─ Dispatch(Filters, Diagnostics, Appends)
                               │    ├─ LevelFilter / ScopeFilter / EnvFilter
                               │    ├─ StaticDiagnostic / ThreadLocalDiagnostic
                               │    └─ WriterAppend(Layout) / AsyncAppend(Layout)
                               └─ Dispatch(...)

Module structure

Consolidated from 20+ separate files into 6 focused modules:

  • record.zig — Scope, Level, Attr, Record, comptime interface checks
  • filter.zig — LevelFilter, ScopeFilter, EnvFilter
  • diagnostic.zig — StaticDiagnostic, ThreadLocalDiagnostic
  • layout.zig — TextLayout, JsonLayout, LogfmtLayout, ISO 8601 formatting
  • append.zig — WriterAppend, AsyncAppend, RollingFileWriter, OpenTelemetryAppend
  • dispatch.zig — Dispatch, Dispatcher, AnyDispatcher, Logger

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a new, highly performant, and flexible structured logging module for lodestar-z, drawing inspiration from the logforth architecture. The module focuses on compile-time optimizations, offering a variety of output formats and destinations, and advanced filtering mechanisms to provide a robust and configurable logging solution. While the core logging implementation files are not included in the provided diff, the changes primarily involve setting up development environment configurations and tooling related to the project.

Highlights

  • Comptime Static Dispatch Pipeline: Implemented a logging pipeline (Filter → Diagnostic → Append) with compile-time static dispatch, ensuring zero runtime overhead for disabled log levels.
  • Flexible Layout Formats: Introduced three distinct layout formats: TextLayout (human-readable with ANSI color), JsonLayout, and LogfmtLayout for diverse output needs.
  • Multiple Appender Types: Added various appender types, including synchronous WriterAppend, asynchronous AsyncAppend (using a lock-free ring buffer), RollingFileWriter for log rotation, and OpenTelemetryAppend for OTLP JSON export.
  • Composable Filters and Diagnostics: Provided composable filters (LevelFilter, ScopeFilter, EnvFilter supporting RUST_LOG-style directives) and diagnostics (StaticDiagnostic, ThreadLocalDiagnostic) for fine-grained control.
  • std.log Bridge and Global Dispatchers: Included a bridge to std.log to route existing std.log.scoped() calls and global dispatcher builders (initConsoleDispatcher, initFileDispatcher, initCombinedDispatcher) for easy setup.
  • Microsecond Timestamps and stderr Default: Configured microsecond timestamps in ISO 8601 UTC format and set stderr as the default output, adhering to Unix conventions.
  • Generic Lock-Free Ring Buffer: Extracted ring_buffer.zig as a generic lock-free SPSC ring buffer, making it reusable beyond the logging module.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a cross-implementation fork choice test comparator agent and updates subproject references. Feedback was provided regarding the inclusion of absolute local paths in the agent configuration, which hinders portability. Additionally, several local IDE state files and log directories were committed; these should be removed and ignored to adhere to the project's zero technical debt policy and prevent environment-specific noise.

Comment on lines +12 to +14
| lodestar-z | Zig | `/Users/grapebaba/Documents/projects/lodestar-z/src/fork_choice/` |
| Lodestar | TypeScript | `/Users/grapebaba/Documents/projects/lodestar/packages/fork-choice/` |
| Prysm | Go | `/Users/grapebaba/Documents/projects/prysm/beacon-chain/forkchoice/` |
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.

high

The agent configuration contains absolute local paths (e.g., /Users/grapebaba/...). These paths are specific to your local machine and will not work for other contributors or in CI environments. Please use relative paths or placeholders to ensure the agent is portable.

Suggested change
| lodestar-z | Zig | `/Users/grapebaba/Documents/projects/lodestar-z/src/fork_choice/` |
| Lodestar | TypeScript | `/Users/grapebaba/Documents/projects/lodestar/packages/fork-choice/` |
| Prysm | Go | `/Users/grapebaba/Documents/projects/prysm/beacon-chain/forkchoice/` |
| lodestar-z | Zig | `./src/fork_choice/` |
| Lodestar | TypeScript | `../lodestar/packages/fork-choice/` |
| Prysm | Go | `../prysm/beacon-chain/forkchoice/` |

Comment thread .idea/workspace.xml Outdated
Comment on lines +1 to +139
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="BackendCodeEditorMiscSettings">
<option name="/Default/Housekeeping/FeatureSuggestion/FeatureSuggestionManager/DisabledSuggesters/=SwitchToGoToActionSuggester/@EntryIndexedValue" value="true" type="bool" />
<option name="/Default/Housekeeping/GlobalSettingsUpgraded/IsUpgraded/@EntryValue" value="true" type="bool" />
<option name="/Default/RiderDebugger/RiderRestoreDecompile/RestoreDecompileSetting/@EntryValue" value="false" type="bool" />
</component>
<component name="CMakeProjectFlavorService">
<option name="flavorId" value="CMakePlainProjectFlavor" />
</component>
<component name="CMakeSettings">
<configurations>
<configuration PROFILE_NAME="Debug" ENABLED="true" CONFIG_NAME="Debug" />
</configurations>
</component>
<component name="ChangeListManager">
<list default="true" id="6e3484a0-38f9-468e-a49d-da0b4388c34c" name="Changes" comment="">
<change beforePath="$PROJECT_DIR$/.claude/settings.json" beforeDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="ClangdSettings">
<option name="formatViaClangd" value="false" />
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="MacroExpansionManager">
<option name="directoryName" value="NMibdqr0" />
</component>
<component name="ProjectColorInfo">{
&quot;customColor&quot;: &quot;&quot;,
&quot;associatedIndex&quot;: 6
}</component>
<component name="ProjectId" id="3BZJOZXF9BoXCRtRGozb6COoXoz" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;ModuleVcsDetector.initialDetectionPerformed&quot;: &quot;true&quot;,
&quot;RunOnceActivity.RadMigrateCodeStyle&quot;: &quot;true&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252&quot;: &quot;true&quot;,
&quot;RunOnceActivity.cidr.known.project.marker&quot;: &quot;true&quot;,
&quot;RunOnceActivity.git.unshallow&quot;: &quot;true&quot;,
&quot;RunOnceActivity.readMode.enableVisualFormatting&quot;: &quot;true&quot;,
&quot;RunOnceActivity.typescript.service.memoryLimit.init&quot;: &quot;true&quot;,
&quot;cf.first.check.clang-format&quot;: &quot;false&quot;,
&quot;cidr.known.project.marker&quot;: &quot;true&quot;,
&quot;git-widget-placeholder&quot;: &quot;gr/feature/forkchoice-z&quot;,
&quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
&quot;node.js.detected.package.tslint&quot;: &quot;true&quot;,
&quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
&quot;node.js.selected.package.tslint&quot;: &quot;(autodetect)&quot;,
&quot;nodejs_package_manager_path&quot;: &quot;pnpm&quot;,
&quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
}
}</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="6e3484a0-38f9-468e-a49d-da0b4388c34c" name="Changes" comment="" />
<created>1774690629408</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1774690629408</updated>
<workItem from="1774690631355" duration="3238000" />
<workItem from="1774788828048" duration="711000" />
</task>
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" />
</component>
<component name="Vcs.Log.Tabs.Properties">
<option name="RECENT_FILTERS">
<map>
<entry key="Branch">
<value>
<list>
<RecentGroup>
<option name="FILTER_VALUES">
<option value="gr/feature/forkchoice-z" />
</option>
</RecentGroup>
</list>
</value>
</entry>
<entry key="User">
<value>
<list>
<RecentGroup>
<option name="FILTER_VALUES">
<option value="*" />
</option>
</RecentGroup>
</list>
</value>
</entry>
</map>
</option>
<option name="TAB_STATES">
<map>
<entry key="MAIN">
<value>
<State>
<option name="FILTERS">
<map>
<entry key="user">
<value>
<list>
<option value="*" />
</list>
</value>
</entry>
</map>
</option>
</State>
</value>
</entry>
</map>
</option>
</component>
<component name="github-copilot-workspace">
<instructionFileLocations>
<option value=".github/instructions" />
</instructionFileLocations>
<promptFileLocations>
<option value=".github/prompts" />
</promptFileLocations>
</component>
</project> No newline at end of file
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 workspace.xml file contains local IDE state, including absolute paths and window configurations. This file should not be committed to the repository as it is user-specific and can lead to merge conflicts. It should be added to the project's .gitignore file.

References
  1. TigerBeetle has a 'zero technical debt' policy. Committing local IDE configuration files adds unnecessary noise and maintenance burden to the repository. (link)

Comment on lines +1 to +4
/Users/grapebaba/.claude/plugins/cache/claude-plugins-official/remember/0.1.0/scripts/save-session.sh: line 135: cd: /Users/grapebaba/.claude/plugins/cache/claude-plugins-official/.claude/remember: No such file or directory
/Users/grapebaba/.claude/plugins/cache/claude-plugins-official/remember/0.1.0/scripts/save-session.sh: line 140: [: : integer expression expected
/Users/grapebaba/.claude/plugins/cache/claude-plugins-official/remember/0.1.0/scripts/save-session.sh: line 142: [: : integer expression expected
/Users/grapebaba/.claude/plugins/cache/claude-plugins-official/remember/0.1.0/scripts/save-session.sh: line 164: cd: /Users/grapebaba/.claude/plugins/cache/claude-plugins-official/.claude/remember: No such file or directory
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

This log file (and the .remember/ directory) appears to be local noise from a script execution. It contains shell errors and environment-specific paths that are not relevant to the codebase. It should be removed from the pull request.

References
  1. TigerBeetle has a 'zero technical debt' policy. Avoid committing temporary files or logs that are not part of the application's functional code or documentation. (link)

@GrapeBaBa GrapeBaBa force-pushed the gr/feature/logging branch from 2d7517a to 750a08c Compare April 12, 2026 02:24
@GrapeBaBa GrapeBaBa changed the title feat(log): structured logging with comptime dispatch, aligned with logforth feat(log): structured logging Apr 12, 2026
@GrapeBaBa GrapeBaBa force-pushed the gr/feature/logging branch 4 times, most recently from e69d667 to 786d479 Compare April 12, 2026 02:57
@GrapeBaBa GrapeBaBa force-pushed the gr/feature/logging branch from 786d479 to 2f474a2 Compare April 12, 2026 02:58
@GrapeBaBa GrapeBaBa closed this Apr 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant