Skip to content

feat: per-component log levels#283

Merged
jason-lynch merged 1 commit intomainfrom
feat/PLAT-417/core-systemd-resources-pt3
Mar 6, 2026
Merged

feat: per-component log levels#283
jason-lynch merged 1 commit intomainfrom
feat/PLAT-417/core-systemd-resources-pt3

Conversation

@jason-lynch
Copy link
Member

Summary

Adds the ability to configure per-component log levels to make it easier to see, for example, debug logs for a single component.

Testing

Try setting a more verbose log level for a particular component in docker/control-plane-dev/config.json. For example:

{
  "profiling_enabled": true
  "logging": {
    "component_levels": {
      "election_candidate": "debug"
    }
  }
}

PLAT-417

@coderabbitai
Copy link

coderabbitai bot commented Mar 3, 2026

📝 Walkthrough

Walkthrough

This pull request implements per-component log level configuration by introducing a logging factory pattern. A new logging.Factory type enables components to create loggers with custom levels specified in configuration. Multiple internal services and providers were refactored to use the factory instead of direct logger injection.

Changes

Cohort / File(s) Summary
Changelog & Configuration
changes/unreleased/Added-20260303-092813.yaml, docs/installation/configuration.md, server/internal/config/config.go
Added new changelog entry, documented per-component logging options (api_server, election_candidate, embedded_etcd, remote_etcd, migration, migration_runner, scheduler_service, workflows_worker), and added ComponentLevels field to Logging struct with default value tracking.
Logging Factory
server/internal/logging/factory.go, server/internal/logging/provide.go
Introduced Factory type with NewFactory constructor that parses component-level configurations and Logger method that applies component-specific levels and adds component field to logs.
API Service
server/internal/api/provide.go, server/internal/api/server.go
Refactored to accept \*logging.Factory instead of zerolog.Logger; logger now created via loggerFactory.Logger("api_server").
Election Service
server/internal/election/candidate.go, server/internal/election/service.go, server/internal/election/provide.go, server/internal/election/candidate_test.go
Refactored to use factory pattern; NewCandidate and NewService updated to accept loggerFactory; test utilities updated accordingly.
ETCD Services
server/internal/etcd/embedded.go, server/internal/etcd/remote.go, server/internal/etcd/provide.go, server/internal/etcd/embedded_test.go, server/internal/etcd/remote_test.go
Refactored to accept loggerFactory; embedded and remote etcd constructors now use factory for logger creation with component-specific naming.
Migration Services
server/internal/migrate/migrations/add_task_scope.go, server/internal/migrate/runner.go, server/internal/migrate/provide.go, server/internal/migrate/runner_test.go
Refactored to use logging factory; NewRunner and migration initialization now use loggerFactory.Logger("migration_runner") and loggerFactory.Logger("migration").
Scheduler & Workflows Services
server/internal/scheduler/service.go, server/internal/scheduler/provide.go, server/internal/workflows/worker.go, server/internal/workflows/provide.go
Refactored to accept loggerFactory; scheduler service and workflows worker constructors updated to create loggers via factory.
Testing Utilities
server/internal/testutils/logger.go
Added LoggerFactory helper function for test support of factory-based logging.

Poem

🐰 Hops in circles 'round the logs,
Per-component levels cut the fog,
Factory patterns, clean and bright,
Each service glows with tailored light,
Logging unified, refactored right!

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.63% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The description includes a clear summary and testing instructions with an example, but lacks explicit documentation confirmation, changelog entry, and breaking changes callout as specified in the template. Confirm that documentation in docs/installation/configuration.md was updated for user-facing changes and verify changelog entry was properly added per repository standards.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: per-component log levels' directly and clearly summarizes the main change in the pull request, matching the primary objective of enabling per-component log level configuration.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/PLAT-417/core-systemd-resources-pt3

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@jason-lynch
Copy link
Member Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Mar 3, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@jason-lynch jason-lynch force-pushed the feat/PLAT-417/core-systemd-resources-pt2 branch from cce50f9 to 33e99fc Compare March 3, 2026 15:04
@jason-lynch jason-lynch force-pushed the feat/PLAT-417/core-systemd-resources-pt3 branch from 2e388a3 to 60e28d1 Compare March 3, 2026 15:04
@jason-lynch jason-lynch force-pushed the feat/PLAT-417/core-systemd-resources-pt2 branch from 33e99fc to 8c5edbb Compare March 3, 2026 15:06
@jason-lynch jason-lynch force-pushed the feat/PLAT-417/core-systemd-resources-pt3 branch from 60e28d1 to 4de1dc1 Compare March 3, 2026 15:06
@mmols
Copy link
Member

mmols commented Mar 5, 2026

Worked for me when tested, just had to add a missing comma here compared to the PR description.

{
  "profiling_enabled": true,
  "logging": {
    "component_levels": {
      "election_candidate": "debug"
    }
  }
}

@jason-lynch jason-lynch force-pushed the feat/PLAT-417/core-systemd-resources-pt2 branch from 8c5edbb to 62341c7 Compare March 6, 2026 14:50
Base automatically changed from feat/PLAT-417/core-systemd-resources-pt2 to main March 6, 2026 17:22
Adds the ability to configure per-component log levels to make it easier
to see, for example, debug logs for a single component.

PLAT-417
@jason-lynch jason-lynch force-pushed the feat/PLAT-417/core-systemd-resources-pt3 branch from 4de1dc1 to d902a69 Compare March 6, 2026 17:23
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
server/internal/config/config.go (1)

42-48: Consider validating ComponentLevels entries in Logging.validate().

The validate() method checks that Level is a valid zerolog level, but ComponentLevels entries are not validated here. While NewFactory does catch invalid levels at construction time, adding validation here would surface configuration errors earlier during Config.Validate().

♻️ Proposed enhancement
 func (l Logging) validate() []error {
 	var errs []error
 	if _, err := zerolog.ParseLevel(l.Level); err != nil {
 		errs = append(errs, fmt.Errorf("log_level: invalid log level %q: %w", l.Level, err))
 	}
+	for component, level := range l.ComponentLevels {
+		if _, err := zerolog.ParseLevel(level); err != nil {
+			errs = append(errs, fmt.Errorf("component_levels.%s: invalid log level %q: %w", component, level, err))
+		}
+	}
 	return errs
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/internal/config/config.go` around lines 42 - 48, Logging.validate
currently only checks l.Level and ignores entries in l.ComponentLevels; update
Logging.validate to iterate over the l.ComponentLevels map and validate each
value with zerolog.ParseLevel, appending formatted errors (including the
component key and invalid value) to the errs slice so Config.Validate surfaces
these issues early (this complements the checks done in NewFactory).
server/internal/migrate/runner.go (1)

43-52: Promote the component key to a shared constant.

"migration_runner" is now part of the logging.component_levels contract. Keeping it inline makes future renames easy to miss in config docs or tests; a package-level const (or shared component-name list) would make that surface safer as more components adopt the factory.

♻️ Suggested shape
+const componentMigrationRunner = "migration_runner"
+
 func NewRunner(
 	hostID string,
 	store *Store,
 	injector *do.Injector,
 	loggerFactory *logging.Factory,
 	migrations []Migration,
 	candidate *election.Candidate,
 ) *Runner {
 	return &Runner{
 		hostID:     hostID,
 		store:      store,
 		injector:   injector,
-		logger:     loggerFactory.Logger("migration_runner"),
+		logger:     loggerFactory.Logger(componentMigrationRunner),
 		migrations: migrations,
 		candidate:  candidate,
 		errCh:      make(chan error, 1),
 		doneCh:     make(chan struct{}),
 	}
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/internal/migrate/runner.go` around lines 43 - 52, The literal
component name "migration_runner" used in the Runner constructor
(loggerFactory.Logger("migration_runner")) should be promoted to a package-level
constant to avoid drifting config/tests; add a const (e.g., const
componentMigrationRunner = "migration_runner") near the top of the package and
replace the inline literal in the Runner creation and any other usages (Runner,
loggerFactory.Logger) with that constant so renames are centralized and less
error-prone.
server/internal/etcd/provide.go (1)

55-79: Use a component logger for the etcd reconfiguration path.

Now that this provider already resolves *logging.Factory, the debug block at Lines 66-71 is the main etcd startup path still bypassing logging.component_levels. If those messages are part of etcd troubleshooting, derive this logger from loggerFactory too so a per-component debug override actually surfaces them.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/internal/etcd/provide.go` around lines 55 - 79, The debug messages in
the etcd reconfiguration path currently use the package-level `logger` and
bypass per-component levels; resolve and use a component logger from the
already-invoked `loggerFactory` instead: obtain a component logger (via
`loggerFactory.Component` or equivalent) before the debug block and replace uses
of `logger.Debug()` in this path with that component logger, and ensure
`newEtcdForMode(newMode, cfg, loggerFactory)` (or its call-site) receives or
uses this component logger so all etcd startup/reconfiguration logs respect
`logging.component_levels`.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@server/internal/config/config.go`:
- Around line 42-48: Logging.validate currently only checks l.Level and ignores
entries in l.ComponentLevels; update Logging.validate to iterate over the
l.ComponentLevels map and validate each value with zerolog.ParseLevel, appending
formatted errors (including the component key and invalid value) to the errs
slice so Config.Validate surfaces these issues early (this complements the
checks done in NewFactory).

In `@server/internal/etcd/provide.go`:
- Around line 55-79: The debug messages in the etcd reconfiguration path
currently use the package-level `logger` and bypass per-component levels;
resolve and use a component logger from the already-invoked `loggerFactory`
instead: obtain a component logger (via `loggerFactory.Component` or equivalent)
before the debug block and replace uses of `logger.Debug()` in this path with
that component logger, and ensure `newEtcdForMode(newMode, cfg, loggerFactory)`
(or its call-site) receives or uses this component logger so all etcd
startup/reconfiguration logs respect `logging.component_levels`.

In `@server/internal/migrate/runner.go`:
- Around line 43-52: The literal component name "migration_runner" used in the
Runner constructor (loggerFactory.Logger("migration_runner")) should be promoted
to a package-level constant to avoid drifting config/tests; add a const (e.g.,
const componentMigrationRunner = "migration_runner") near the top of the package
and replace the inline literal in the Runner creation and any other usages
(Runner, loggerFactory.Logger) with that constant so renames are centralized and
less error-prone.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ba433b3e-b75d-4eec-8e0a-818d988dd9d2

📥 Commits

Reviewing files that changed from the base of the PR and between 72b01b5 and d902a69.

📒 Files selected for processing (25)
  • changes/unreleased/Added-20260303-092813.yaml
  • docs/installation/configuration.md
  • server/internal/api/provide.go
  • server/internal/api/server.go
  • server/internal/config/config.go
  • server/internal/election/candidate.go
  • server/internal/election/candidate_test.go
  • server/internal/election/provide.go
  • server/internal/election/service.go
  • server/internal/etcd/embedded.go
  • server/internal/etcd/embedded_test.go
  • server/internal/etcd/provide.go
  • server/internal/etcd/remote.go
  • server/internal/etcd/remote_test.go
  • server/internal/logging/factory.go
  • server/internal/logging/provide.go
  • server/internal/migrate/migrations/add_task_scope.go
  • server/internal/migrate/provide.go
  • server/internal/migrate/runner.go
  • server/internal/migrate/runner_test.go
  • server/internal/scheduler/provide.go
  • server/internal/scheduler/service.go
  • server/internal/testutils/logger.go
  • server/internal/workflows/provide.go
  • server/internal/workflows/worker.go

@jason-lynch jason-lynch merged commit eec724e into main Mar 6, 2026
3 checks passed
@jason-lynch jason-lynch deleted the feat/PLAT-417/core-systemd-resources-pt3 branch March 6, 2026 18:17
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.

2 participants