Skip to content

build: add context-based logging infrastructure - #24

Merged
Roasbeef merged 6 commits into
mainfrom
logging-infrastructure
Nov 26, 2025
Merged

build: add context-based logging infrastructure#24
Roasbeef merged 6 commits into
mainfrom
logging-infrastructure

Conversation

@Roasbeef

Copy link
Copy Markdown
Member

PR #7 moved us away from global logging toward per-instance loggers, which is the right direction for testability and avoiding shared mutable state. However, threading a logger through every function parameter can become tedious, especially in deep call stacks where most functions just pass the logger along without using it.

This PR introduces a compromise: context-based logging utilities that let us attach a logger to a context and extract it later. Functions that already take a context can access the logger without additional parameters, while still maintaining explicit control over which logger instance is used. The pattern works naturally with request-scoped contexts, where each request can carry its own logger with relevant metadata.

The PR also ports lnd's build-tag system for controlling log levels and output destinations at compile time. This makes it easy to enable verbose logging during test debugging with make unit log="stdlog trace" pkg=db while keeping tests quiet by default.

@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello @Roasbeef, 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 significantly enhances the project's logging infrastructure by introducing a context-based logger propagation mechanism. This change aims to improve code cleanliness and maintainability by allowing loggers to be implicitly passed through context.Context, avoiding verbose function signatures. Additionally, it integrates a flexible build-tag system for compile-time configuration of logging behavior, enabling fine-grained control over log verbosity and output, which is particularly beneficial for debugging and testing scenarios.

Highlights

  • Context-based Logging: Implements a system to attach and retrieve btclog.Logger instances from context.Context, reducing the need to pass loggers explicitly through function parameters, especially in deep call stacks.
  • Compile-time Logging Control: Introduces a build-tag system, similar to lnd, allowing developers to control logging levels (e.g., trace, debug, info) and output destinations (e.g., stdout, none, default) at compile time using make parameters.
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.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

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.

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

Copy link
Copy Markdown

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 robust context-based logging infrastructure, which is a great step towards better testability and cleaner code by avoiding passing loggers through every function. The implementation of context-based loggers and the build-tag system for controlling log levels are well-executed. I have one suggestion to refactor the NewSubLogger function in build/log.go to improve its clarity, remove some code duplication, and make it more robust by handling a potential error. Overall, this is a solid contribution that improves the project's logging capabilities.

Comment thread build/log.go

@bhandras bhandras left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nice! LGTM, pending linter fixes 🎉

Comment thread build/context_logger_test.go Outdated

@sputn1ck sputn1ck left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM!

#NoGlobals

This commit introduces the core logging infrastructure ported from
lnd's build package. The LogType enum defines the three logging modes
supported: LogTypeNone (no logging), LogTypeStdOut (stdout only), and
LogTypeDefault (stdout plus optional log rotator).

The NewSubLogger function serves as a factory for creating subsystem
loggers. It examines the current deployment mode (Production vs
Development) and logging type to determine how to construct the
logger. For production builds, it delegates to an optional generator
function. For development builds with stdlog enabled, it creates a
stdout logger with the build-tag-configured log level. This design
allows tests to easily enable logging while keeping logs disabled by
default.
This commit adds eight files that use Go build tags to control the
default log level at compile time. The mechanism allows selecting
different verbosity levels without runtime configuration changes.

The loglevel_default.go file activates when no specific level tag is
set, providing "info" as the default. The remaining files each handle
their respective level: trace, debug, info, warn, error, critical,
and off. Each level-specific file requires both the "dev" tag and its
level tag (e.g., "dev && trace").

This pattern, borrowed from lnd, enables testing with verbose output
by simply adding build tags: "go test -tags='dev stdlog trace' ./..."
will enable trace-level logging to stdout.
This commit adds three files that control where log output is directed
based on build tags.

The log_default.go file sets LoggingType to LogTypeDefault when neither
stdlog nor nolog tags are present, suitable for production daemon use
where logs go to both stdout and a log file rotator.

The log_stdlog.go file activates with the "stdlog" tag and directs all
output to stdout only, which is ideal for unit tests where you want to
see log output in the test output.

The log_nolog.go file activates with the "nolog" tag and disables all
logging entirely, which is the default for tests to avoid noise.
This commit introduces utilities for passing loggers through the call
stack via context.Context, providing a middle ground between global
loggers and explicit parameter passing.

The ContextWithLogger function attaches a logger to a context, while
LoggerFromContext extracts it. If no logger is present, it returns
btclog.Disabled which safely no-ops all log calls, making it safe to
use without nil checks.

A MustLoggerFromContext variant is also provided for code paths where
a logger must be present, panicking if one is not found.

This approach reduces the need to thread loggers through every function
parameter while maintaining explicit control over which logger is used.
The pattern works well with the existing per-instance logging from PR
#7, allowing subsystem loggers to be attached to request contexts.
This commit adds a "log" parameter to the Makefile that allows
overriding the default LOG_TAGS value. By default, tests run with
"nolog" to keep output clean, but developers can enable logging for
debugging with: make unit log="stdlog trace" pkg=db

The parameter accepts any combination of log type and level tags,
making it easy to toggle verbose logging during test development
without modifying the Makefile itself.
@Roasbeef
Roasbeef force-pushed the logging-infrastructure branch from 411b1d8 to 7240858 Compare November 26, 2025 01:25
Update CLAUDE.md and AGENTS.md to document the log= parameter for
enabling debug logging during unit tests. This replaces the previously
documented unit-debug target with the more flexible parameter approach.
@Roasbeef
Roasbeef force-pushed the logging-infrastructure branch from 7240858 to d7cf785 Compare November 26, 2025 01:25
@Roasbeef
Roasbeef enabled auto-merge November 26, 2025 01:26
@claude

claude Bot commented Nov 26, 2025

Copy link
Copy Markdown

PR Review: Context-Based Logging Infrastructure

Summary

This PR introduces a well-designed context-based logging system ported from lnd, providing a practical middle ground between global loggers and explicit parameter passing. The implementation is solid overall with good test coverage.

✅ Strengths

Architecture & Design

  • Excellent compromise: Context-based logging reduces parameter threading while maintaining explicit control
  • Build tag system: Clean separation of concerns with compile-time log level control
  • Safe defaults: LoggerFromContext returns btclog.Disabled instead of nil, eliminating nil-check boilerplate
  • Well-documented: Clear function comments that explain WHY and HOW, following project guidelines

Code Quality

  • Proper context key isolation: Using empty struct loggerKey{} prevents collisions
  • Comprehensive test coverage: Six test cases covering all edge cases (round-trip, missing logger, nil logger, panics)
  • Follows project conventions: 8-space tabs, literate comments, good code organization

Makefile Integration

  • Developer-friendly: log="stdlog trace" parameter makes debugging straightforward
  • Good documentation: Updated both CLAUDE.md and AGENTS.md with clear examples

🔍 Issues Found

1. Typo in log.go:9 comment

// LogType is an indicating the type of logging specified by the build flag.

Should be: "LogType is an enum indicating..." or "LogType indicates..."

Severity: Low (documentation clarity)
Location: build/log.go:9

2. Unused code: Log compressor utilities (lines 37-61)

The constants Gzip, Zstd, map logCompressors, and function SupportedLogCompressor are defined but never used in this PR or codebase.

Reasoning: Since this PR focuses on context-based logging and test log control, the compressor code appears to be forward-looking infrastructure. If it's not immediately needed:

  • Consider removing it (YAGNI principle)
  • OR add a comment explaining it's for future log rotation support
  • OR include it in a subsequent PR that actually uses it

Severity: Medium (code cleanliness)
Location: build/log.go:37-61

3. Error ignored in log.go:101

level, _ := btclog.LevelFromString(LogLevel)

If LevelFromString returns an error (invalid log level), it's silently ignored and level gets a zero value. This could lead to unexpected logging behavior.

Recommendation: Add error handling or at least a comment explaining why errors are safe to ignore (e.g., if LogLevel is guaranteed valid by build tags).

level, err := btclog.LevelFromString(LogLevel)
if err != nil {
        // LogLevel is guaranteed valid by build tags, but log if something
        // goes wrong in case build system is misconfigured.
        panic(fmt.Sprintf("invalid log level from build tags: %v", err))
}

Severity: Medium (potential runtime issues)
Location: build/log.go:101

4. Missing test: NewSubLogger behavior

While context_logger_test.go has excellent coverage, there are no tests for NewSubLogger. Consider adding tests for:

  • Behavior with different Deployment types
  • Behavior with different LoggingType values
  • Nil genSubLogger handling
  • Stdout logger creation with build tags

Severity: Low (test coverage gap)
Location: build/log_test.go (doesn't exist)

🔒 Security

No security concerns identified. Context keys are properly isolated and logger propagation is safe.

⚡ Performance

No performance concerns. Context value lookups are O(1) and logger creation is lightweight.

📋 Recommendations

Must Fix

  1. Fix the typo in log.go:9 comment
  2. Address the ignored error in log.go:101

Should Consider

  1. Remove or document the unused compressor code (lines 37-61)
  2. Add tests for NewSubLogger function

Minor Improvements

  1. Consider adding a package-level doc comment to build/context_logger.go explaining the context-based logging pattern
  2. The Makefile comment (lines 60-62) is good, but could mention that nolog is the default for quiet test output

📝 Commit Quality

Commits follow project guidelines well:

  • Proper prefix format (build:)
  • Clear present-tense subjects
  • Detailed bodies explaining WHY
  • Logical separation (one concept per commit)
  • Appropriate granularity

Verdict

LGTM with minor fixes recommended

The PR delivers solid infrastructure that will improve codebase ergonomics. The issues identified are minor and don't block merging, but addressing them would increase code quality. The architecture is sound and follows lnd patterns successfully.

@claude

claude Bot commented Nov 26, 2025

Copy link
Copy Markdown

PR Review: Context-Based Logging Infrastructure

Summary

This PR introduces context-based logging utilities and a build-tag system for controlling log levels during development. The implementation is well-designed and follows Go best practices. Overall, this is a solid addition that will improve testability and debugging workflows.


Strengths

  1. Excellent Design Pattern

    • Context-based logging is idiomatic Go and integrates well with existing context-passing patterns
    • The fallback to btclog.Disabled in LoggerFromContext prevents nil panics
    • Providing both LoggerFromContext (safe) and MustLoggerFromContext (panics) gives flexibility
  2. Strong Test Coverage

    • All edge cases are tested: round-trip, missing logger, nil logger, panic behavior
    • Tests use t.Parallel() correctly
    • Tests follow the repository's style guidelines with clear comments
  3. Build Tag System

    • Clean separation of concerns with build tags for log types and levels
    • The Makefile integration (make unit log="stdlog trace") is intuitive
    • Documentation updates in AGENTS.md and CLAUDE.md are helpful
  4. Code Quality

    • Comments follow the style guide (function name prefix, explain how/why)
    • Proper use of unexported loggerKey struct{} prevents context key collisions
    • 80-character line limit respected throughout

Issues and Concerns

1. Comment Typo in build/log.go:9

  • Current: "LogType is an indicating the type of logging specified by the build flag."
  • Should be: "LogType is an enum indicating the type of logging specified by the build flag."

2. Unused Variables in build/log.go:39-60

  • The logCompressors map and related constants (Gzip, Zstd) are exported but never used in this PR
  • Per CLAUDE.md: "Don't design for hypothetical future requirements"
  • Recommendation: Remove the unused compression-related code, or add a comment explaining why it's here with a tracking issue reference

3. Ignored Error in build/log.go:101

  • The error from btclog.LevelFromString is silently ignored
  • While LogLevel is controlled by build tags and should always be valid, it would be safer to handle the error case with a fallback to btclog.LevelInfo

4. Makefile: LOG_TAGS Default Value

  • The default is nolog, which is quiet-by-default for tests
  • This is fine, but consider adding a note in CLAUDE.md about why tests are quiet by default (to avoid log spam in CI)

Suggestions for Improvement

  1. Add example usage documentation in build/context_logger.go
  2. Consider adding helpers that combine logger with metadata for request-scoped fields
  3. Verify that make unit log="stdlog trace" pkg=build works as expected

Security and Performance

  • No security concerns identified
  • Performance impact is minimal: O(1) context lookups, btclog.Disabled singleton avoids allocations, build tags allow zero-cost logging in production

Checklist Before Merge

  • Fix typo in build/log.go:9
  • Decide whether to keep or remove unused logCompressors code
  • Consider handling error from btclog.LevelFromString
  • Verify make unit log works as expected
  • Run make lint

Overall Assessment

Verdict: Approve with Minor Changes

This is a well-implemented feature that aligns with the project's goals. The code quality is high, test coverage is excellent, and the design is idiomatic. The identified issues are minor and easily addressed. Once the typo is fixed and the unused code is resolved, this is ready to merge.

Great work!

@Roasbeef
Roasbeef disabled auto-merge November 26, 2025 01:36
@Roasbeef
Roasbeef merged commit 10e5707 into main Nov 26, 2025
14 checks passed
@bhandras
bhandras deleted the logging-infrastructure branch December 18, 2025 17:56
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.

3 participants