Skip to content

fix(logging): port upstream slog-leveler fixes - #14536

Merged
puertomontt merged 4 commits into
kgateway-dev:mainfrom
puertomontt:logging-upstream-sync
Aug 11, 2026
Merged

puertomontt merged 4 commits into
kgateway-dev:mainfrom
puertomontt:logging-upstream-sync

Conversation

@puertomontt

Copy link
Copy Markdown
Contributor

Description

pkg/logging is derived from slog-leveler. Upstream PR #6 (0752d55) is the only change since we forked; this ports the parts that apply to us.

The bug: HTTPLevelHandler mutated component levels as it iterated the parsed request, so a request naming an unknown component was applied partially. Because Go randomizes map iteration order, the same request returned 200 or 400 nondeterministically — the same input, eight times:

attempt 0: status=400 level(probe-a)=INFO  body="logger not found for component: does-not-exist\n"
attempt 1: status=200 level(probe-a)=DEBUG body="component probe-a log level set to: debug\nlogger not found for component: does-not-exist\n"
attempt 6: status=400 level(probe-a)=INFO  body="logger not found for component: does-not-exist\n"
attempt 7: status=200 level(probe-a)=DEBUG body="component probe-a log level set to: debug\nlogger not found for component: does-not-exist\n"

On the 200 path the known component's level is changed despite the request failing, and http.Error fires after w.Write has already committed the status, so the response carries both a success line and an error line. Note that the bad-level case was already atomic (parsed in an earlier loop); only unknown components leaked. Fixed by validating every requested component up front, then mutating.

Also in this PR:

  • Remove the exported GlobalLevel. It was documented as "the slog.LevelVar for the default logger" but was never stored in componentLeveler and never read outside a test that compared it against its own zero value. Upstream deleted it in the same PR. Verified zero references repo-wide.
  • Reset the componentLeveler map between tests rather than only resetting levels, so components no longer leak across cases.
  • Fix the HTTPLelevelHandler doc typo, and document that DeleteLeveler leaves existing loggers working but disconnected from SetLevel/GetLevel.

Not ported: upstream replaced init() with an explicit SetDefault() that main must call. That model assumes component loggers are not created as package-level vars, since those run before main and would find no registered default to inherit from — which is exactly our convention. init() stays.

Change Type

/kind fix

Changelog

NONE

Additional Notes

GlobalLevel is an exported symbol, so its removal is technically breaking for anything importing pkg/logging from outside the repo. It was inert, and upstream removed it, but happy to split it out if reviewers would rather keep the bug fix isolated.

Validation:

  • go build -tags e2e ./... clean.
  • go test ./pkg/logging/... -race -shuffle=on and ./pkg/kgateway/admin/... (the handler's only consumer) pass.
  • Custom golangci-lint build over ./pkg/logging/...: 0 issues.
  • The atomicity fix was confirmed against a temporary regression test that failed on the unpatched handler (level must be left untouched, expected: 0, actual: -4) and passed with the fix; the test was dropped from the final diff.

Follow-up, not in this PR: NewWithOptions computes AddSource: opts.AddSource || level.Level() <= slog.LevelDebug once at construction. Because nearly every logger in the repo is a package-level var initialized before SetupLogging applies the configured level, that clause resolves to false for all but controller-runtime and klog — so first-party component loggers never emit source even at debug. That clause is a kgateway-local addition; upstream has always used plain opts.AddSource.

pkg/logging is derived from github.com/shashankram/slog-leveler. Upstream
PR kgateway-dev#6 (commit 0752d55) is the only change since we forked; port the parts
that apply to us.

HTTPLevelHandler mutated levels as it iterated the requested components,
so a request naming an unknown component was applied partially. Because
Go randomizes map iteration order, the same request returned 200 or 400
nondeterministically: on the 200 path the known component's level was
changed despite the request failing, and http.Error fired after w.Write
had already committed the status, producing a body with both a success
line and an error line. Validate every component up front, then mutate.

Also:
- Remove the exported GlobalLevel. It was documented as the LevelVar for
  the default logger but was never stored in componentLeveler and never
  read outside a test that compared it against its own zero value.
- Reset the componentLeveler map between tests instead of only resetting
  levels, so components no longer leak across cases.
- Fix the HTTPLelevelHandler doc typo and document that DeleteLeveler
  leaves existing loggers working but disconnected from SetLevel/GetLevel.

Not ported: upstream replaced init() with an explicit SetDefault(). That
model assumes component loggers are not created as package-level vars,
which is exactly our convention, so init() stays.

Signed-off-by: omar <omar.hammami@solo.io>
Copilot AI lite review requested due to automatic review settings August 7, 2026 15:12
@gateway-bot gateway-bot added kind/fix Categorizes issue or PR as related to a bug. release-note-none labels Aug 7, 2026

Copilot AI left a comment

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.

Pull request overview

This PR ports key fixes from upstream slog-leveler into kgateway’s pkg/logging, primarily to make HTTPLevelHandler’s component-level updates atomic (validate all requested components before mutating any levels) and to clean up related test and API surface issues.

Changes:

  • Fix HTTPLevelHandler to validate all requested components up-front to avoid partial updates and nondeterministic success/failure responses.
  • Remove the exported (but inert) GlobalLevel and adjust tests accordingly.
  • Improve test isolation by fully resetting the componentLeveler map between test cases; update docs/typos.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
pkg/logging/logger.go Clarifies DeleteLeveler behavior in docs (loggers remain usable but become disconnected from Set/Get).
pkg/logging/logger_test.go Adds resetComponentLeveler() helper and uses it to prevent cross-test pollution.
pkg/logging/level.go Removes GlobalLevel, fixes handler comment typo, and makes HTTPLevelHandler validate components before mutating levels.
pkg/logging/level_test.go Updates expectations for GlobalLevel removal and uses resetComponentLeveler() for stronger test isolation.

Comment thread pkg/logging/level.go Outdated
name = DefaultComponent
}
if _, ok := componentLeveler.Load(name); !ok {
http.Error(w, fmt.Sprintf("logger not found for component: %s", component), http.StatusBadRequest)
Comment thread pkg/logging/level.go
@puertomontt
puertomontt enabled auto-merge August 7, 2026 15:49
@puertomontt
puertomontt added this pull request to the merge queue Aug 11, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 11, 2026
Signed-off-by: omar <omar.hammami@solo.io>
Signed-off-by: omar <omar.hammami@solo.io>
@puertomontt
puertomontt enabled auto-merge August 11, 2026 14:30
@puertomontt
puertomontt added this pull request to the merge queue Aug 11, 2026
Merged via the queue into kgateway-dev:main with commit 72ff6ac Aug 11, 2026
36 checks passed
@puertomontt
puertomontt deleted the logging-upstream-sync branch August 11, 2026 16:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind/fix Categorizes issue or PR as related to a bug. release-note-none

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants