Conversation
There was a problem hiding this comment.
Sorry @veeceey, your pull request is larger than the review limit of 150000 diff characters
Manual test resultsBuild Vet (slog checker validates structured arg correctness) Tests LOG_LEVEL env var Logrus removal |
Summary of ChangesHello @veeceey, 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 undertakes a significant internal refactoring by replacing the existing Highlights
🧠 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. Changelog
Using Gemini Code AssistThe 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
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 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
|
There was a problem hiding this comment.
Code Review
This pull request successfully migrates the logging from logrus to the standard library's slog. The changes are extensive and cover the entire codebase. While the migration is a positive step, there are a few important issues to address. Firstly, several log.Fatalf calls have been replaced with log.Error, which alters the program's control flow by not exiting on critical errors. This is a significant regression and needs to be fixed. Secondly, there's inconsistent usage of slog; many calls use fmt.Sprintf instead of slog's structured key-value pairs, which undermines the benefits of structured logging. Lastly, there are some changes in test files, like replacing t.Fatalf with t.Error, that alter test behavior and should be reviewed. I've added specific comments with suggestions for these points.
| userHomeDir, err := os.UserHomeDir() | ||
| if err != nil { | ||
| log.Fatalf("Failed to get user home directory: %v", err) | ||
| log.Error(fmt.Sprintf("Failed to get user home directory: %v", err)) |
There was a problem hiding this comment.
Replacing log.Fatalf with log.Error is a critical change in behavior. The original code would exit the program on fatal errors, preventing subsequent code from running in an invalid state. The new code logs an error but continues execution, which could lead to panics or unexpected behavior.
Since slog doesn't have a level that exits, you should explicitly call os.Exit(1) on the next line after logging the error to maintain the original program flow.
This issue is present in multiple places in this file where Fatalf or Fatal were replaced (e.g., lines 152, 157, 168, 179, 191, 315, 320, 345, 350, 357, 439). Please review all of them.
Additionally, this is a good opportunity to use structured logging.
| log.Error(fmt.Sprintf("Failed to get user home directory: %v", err)) | |
| log.Error("Failed to get user home directory", "error", err) |
| nil, | ||
| ) | ||
| log.Infof("LLAMA_SERVER_PATH: %s", llamaServerPath) | ||
| log.Info(fmt.Sprintf("LLAMA_SERVER_PATH: %s", llamaServerPath)) |
There was a problem hiding this comment.
Using fmt.Sprintf with slog is an anti-pattern that loses the benefits of structured logging. Instead of formatting the string yourself, you should provide a static message and pass dynamic values as key-value pairs. This makes logs easier to parse, filter, and query.
This pattern is repeated throughout the codebase. Please update all similar logging calls to use structured key-value pairs.
| log.Info(fmt.Sprintf("LLAMA_SERVER_PATH: %s", llamaServerPath)) | |
| log.Info("LLAMA_SERVER_PATH", "path", llamaServerPath) |
| if err != nil { | ||
| t.Fatalf("Failed to create client: %v", err) | ||
| t.Error(fmt.Sprintf("Failed to create client: %v", err)) | ||
| } |
There was a problem hiding this comment.
Replacing t.Fatalf with t.Error changes the test's behavior. t.Fatalf fails the test and stops its execution immediately, which is usually desired for setup code like this to prevent panics or misleading failures in subsequent test code. t.Error reports the failure but allows the test to continue.
Please consider using t.Fatalf here and in other similar places in the tests to ensure tests fail fast on critical setup errors.
| if err != nil { | |
| t.Fatalf("Failed to create client: %v", err) | |
| t.Error(fmt.Sprintf("Failed to create client: %v", err)) | |
| } | |
| if err != nil { | |
| t.Fatalf("Failed to create client: %v", err) | |
| } |
pkg/anthropic/handler_test.go
Outdated
|
|
||
| if rec.Code != tt.statusCode { | ||
| t.Errorf("expected status %d, got %d", tt.statusCode, rec.Code) | ||
| t.Error(fmt.Sprintf("expected status %d, got %d", tt.statusCode, rec.Code)) |
There was a problem hiding this comment.
Using t.Error(fmt.Sprintf(...)) is not idiomatic. The testing package provides t.Errorf for logging formatted error messages. Using t.Errorf is cleaner and more conventional.
This pattern appears in multiple test files. Please consider using t.Errorf for all formatted test error messages.
| t.Error(fmt.Sprintf("expected status %d, got %d", tt.statusCode, rec.Code)) | |
| t.Errorf("expected status %d, got %d", tt.statusCode, rec.Code) |
|
This looks great we have conflicts to fix. Just checking with @doringeman and @ilopezluna is there any reason we used a non-stdlib logging library? |
|
Thanks @ericcurtin! Addressed the Gemini review feedback and resolved the merge conflict: Critical fixes:
Structured logging:
Test fixes:
Merge conflict:
|
There was a problem hiding this comment.
Thanks for working on this @veeceey 🙏
To fix linting issues you can run:
make lint
Using fmt.Sprintf defeats the entire purpose of structured logging. Log aggregation tools (Loki, Elasticsearch, CloudWatch) can't parse/filter/query individual fields when everything is baked into a single string. The PR description says it "converts from printf-style to slog's structured key-value format" but this is only true for main.go.
// ❌ Anti-pattern (loses structured logging benefits)
log.Info(fmt.Sprintf("Loading %s backend runner with model %s in %s mode", backendName, modelID, mode))
// ✅ Correct slog usage
log.Info("Loading backend runner", "backend", backendName, "model", modelID, "mode", mode)
The migration direction is correct and the core pkg/logging package is well-designed. However, the number of fmt.Sprintf log calls are a significant gap, they mechanically replace the logrus API but don't actually deliver the structured logging benefits that justify this migration. The PR should either:
- Convert the remaining
fmt.Sprintfcalls to proper structured key-value pairs (at least in the critical paths), or - Split into two PRs: one for the mechanical logrus→slog swap, and a follow-up for proper structured logging adoption.
No reason, I think migrating to |
|
Thanks for the detailed review @ilopezluna, you're absolutely right. The fmt.Sprintf usage defeats the purpose of the migration. I'll go with option 1 — converting all the remaining fmt.Sprintf calls to proper structured key-value pairs across the codebase, and fixing the lint issues in the same pass. Working on it now. |
|
Updated. All What changed in this push:
Verified locally:
|
|
Resolved the latest merge conflict (upstream replaced |
Migrate the entire codebase from github.com/sirupsen/logrus to Go's standard library log/slog package. This introduces proper structured logging with key-value pairs and adds LOG_LEVEL environment variable support (debug, info, warn, error) for runtime log level configuration. Key changes: - Replace logging.Logger interface with type alias to *slog.Logger - Add ParseLevel() and NewLogger() helpers in pkg/logging - Add NewWriter() to replace logrus.Logger.Writer() for subprocess output capture - Update backends.Logger interface to use slog-compatible signatures - Convert all log calls from printf-style to structured key-value pairs - Remove direct logrus dependency (remains as indirect via transitive deps) Closes docker#384
Address review feedback: replace fmt.Sprintf anti-pattern across the entire codebase with proper slog structured key-value args so log aggregation tools can parse/filter/query individual fields. Also fixes: - t.Error(fmt.Sprintf(...)) -> t.Errorf(...) (staticcheck S1038) - Import ordering (gci: standard first, then third-party) - Race condition in testregistry (concurrent map access in handleBlobUpload) - Removed unused fmt imports
4a435c6 to
bdb90f5
Compare
|
Rebased on main to resolve the merge conflicts. All the fmt.Sprintf conversions and structured logging changes are still in place. Also picked up the new |
Closes #384
Migrates the entire codebase from
logrusto Go's standard librarylog/slogpackage. This has been on the todo list for a while sincelogrusis in maintenance mode andslogships with the stdlib since Go 1.21.What changed
pkg/logging:Loggeris now a type alias for*slog.Loggerinstead of a custom interface wrapping logrus. AddedParseLevel(),NewLogger(), andNewWriter()helpers.LOG_LEVELenv var: Configurable log level at startup (debug,info,warn,error). Defaults toinfo.Infof,Warnf, etc.) and positional (Infoln) to slog's structured key-value format (Info("msg", "key", val)).Infof/Warnf/WarnlntoInfo/Warnwith variadic...anyargs matching slog's API.logrus.Logger.Writer()replaced with a customNewWriter()that pipes subprocess output through slog line-by-line.go.mod: logrus is now only an indirect dependency (pulled in by transitive deps).Testing
go build ./...compiles cleanlygo vet ./...passes (slog's vet checker validates key-value pair correctness)go test ./...all tests passLOG_LEVEL=debugandLOG_LEVEL=errorproduce the expected log output