

0 / 2 embers
0 / 3000 xp
click for more info
Complete a lesson to start your streak
click for more info
Difficulty: 3
click for more info
Not enough gems
Cost: 6 gems
1: Logging in Go
incomplete
2: Use the Logger
incomplete
3: Logging Requests
incomplete
4: Global Logger vs. Dependency Injection
incomplete
5: Logger Configuration
incomplete
6: Logger Failure
incomplete
7: Buffered Logging
incomplete
8: Logger Cleanup
incomplete
Back
ctrl+,
Next
ctrl+.
This lesson's interactive features are locked, please to keep using them
Here's the code I used to create Linko's logger:
func initializeLogger(logFile string) (*log.Logger, error) {
if logFile != "" {
file, err := os.OpenFile(logFile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0o644)
if err != nil {
return nil, fmt.Errorf("failed to open log file: %w", err)
}
multiWriter := io.MultiWriter(os.Stderr, file)
return log.New(multiWriter, "", log.LstdFlags), nil
}
return log.New(os.Stderr, "", log.LstdFlags), nil
}
func run(ctx context.Context, httpPort int, dataDir string) int {
logger, err := initializeLogger(os.Getenv("LINKO_LOG_FILE"))
if err != nil {
fmt.Fprintf(os.Stderr, "failed to initialize logger: %v\n", err)
return 1
}
// ...
}
Notice that if an error occurs when opening the file, I return an error from initializeLogger, and then in run() I write a message to os.Stderr and return a non-zero exit code. If you used log.Fatal or log.Panic instead, you might have a couple of problems in your code that would:
If you ask me, log.Fatal and log.Panic should be avoided... I don't even like that they're in the standard library, because they couple logging with control flow – but that's a different discussion.
Instead, I prefer to let the caller of the initializeLogger function decide how to behave in the event of a failure! Then, when it's time to handle the error (in the run function), this is one of the few times it's okay to log without a logger (by using fmt.Fprintf) because it was the logger itself that failed to initialize!