fix: preserve literal '%' in non-format log messages#1334
Conversation
The structured logger built every log body with fmt.Sprintf(message, args...)
in (*sentryLogger).log. Emit is the non-format method and calls log() with no
trailing args, and Write goes through Emit, so a literal % in those messages was
treated as a format verb and corrupted (e.g. Emit("disk usage at 95% capacity")
produced "disk usage at 95%!c(MISSING)apacity").
Only run fmt.Sprintf when args are present; otherwise use the message as-is. The
resolved body is also used for the debug logger output.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Semver Impact of This PR🟢 Patch (bug fixes) 📋 Changelog PreviewThis is how your changes will appear in the changelog. Bug Fixes 🐛
🤖 This preview updates automatically when you update the PR. |
Codecov Report✅ All modified and coverable lines are covered by tests.
Additional details and impacted files@@ Coverage Diff @@
## master #1334 +/- ##
==========================================
- Coverage 86.04% 78.16% -7.88%
==========================================
Files 62 86 +24
Lines 6090 8024 +1934
==========================================
+ Hits 5240 6272 +1032
- Misses 635 1453 +818
- Partials 215 299 +84 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
| { | ||
| name: "Emit with format-like verbs", | ||
| logFunc: func(ctx context.Context, l Logger) { | ||
| l.Warn().WithCtx(ctx).Emit("got %s and %d, 100% done") | ||
| }, | ||
| wantBody: "got %s and %d, 100% done", | ||
| }, |
There was a problem hiding this comment.
This makes more sense here, since users should escape % in Emitf cases. Also the first case already guarantees that % literals are not skipped.
| { | |
| name: "Emit with format-like verbs", | |
| logFunc: func(ctx context.Context, l Logger) { | |
| l.Warn().WithCtx(ctx).Emit("got %s and %d, 100% done") | |
| }, | |
| wantBody: "got %s and %d, 100% done", | |
| }, | |
| { | |
| name: "Emit with format-like verbs", | |
| logFunc: func(ctx context.Context, l Logger) { | |
| l.Warn().WithCtx(ctx).Emitf("got %s and %d, 100%% done", "string", 10) | |
| }, | |
| wantBody: "got %s and %d, 100% done", | |
| }, |
The structured logger formats every log body with
fmt.Sprintf(message, args...)in(*sentryLogger).log, including the non-format methods.Emitbuilds its message withfmt.Sprint(args...)and callslogwith no remaining args, andWritegoes throughEmit, so a literal%in those messages is treated as a format verb.This also affects messages routed through the logging integrations, which forward the user's message via
Emit(...)with no args (e.g.slog,logrus,zap).Only run
fmt.Sprintfwhen there are args; otherwise use the message as-is. The same resolved body is used for the debug logger output.Added
Test_sentryLogger_EmitPreservesLiteralPercentcoveringEmit,Write, and a message with format-like verbs. It fails before the change and passes after.Tested:
go test -race .,go vet .,gofmt -s, andgolangci-lint run ./all clean.