Go
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions and Methods
-
Section 4: Concurrency
-
Section 5: Packages and Tooling
-
Section 6: More Standard Library
-
Section 7: Building Services
-
Section 8: Advanced Go
-
Section 9: Go in the Cloud-Native Ecosystem
-
Section 10: Data Structures and Algorithms in Go
-
Section 11: Testing and Deployment
-
Section 12: Practical Projects
-
Section 13: More Standard Library Practice
-
Section 14: More Practice Projects
-
Section 15: Design Patterns in Go
-
Section 16: Interview Practice
-
Section 17: Package fmt In Depth
-
Section 18: Package strings and strconv
-
Section 19: Package os and io
-
Section 20: Package time
-
Section 21: Package sort and container
-
Section 22: Package encoding
-
Section 23: Package net/http In Depth
-
Section 24: Package context
-
Section 25: Package regexp and bytes
-
Section 26: Package errors In Depth
-
Section 27: Package crypto and hash
-
Section 28: Package flag and log
-
Section 29: Package sync In Depth
-
Section 30: More Practice Exercises
-
Section 31: Go Modules and Workspaces In Depth
-
Section 32: Generics Deep Dive (Go 1.18+)
-
Section 33: Testing Package In Depth
-
Section 34: More Interview and Whiteboard Practice
-
Section 35: Package math and unicode
-
Section 36: Package path and filepath
-
Section 37: Package database/sql
-
Section 38: Package text/template and html/template
-
Section 39: Package archive and compress
-
Section 40: Lower-Level net Package
-
Section 41: Package runtime and reflect
-
Section 42: Package embed and io/fs
-
Section 43: Go Assembly and CGO Basics
-
Section 44: Building CLIs and TUIs
-
Section 45: Go Performance Tuning
-
Section 46: More Real-World Projects
-
Section 47: Go in Production
-
Section 48: Go Design Patterns
-
Section 49: Go Interfaces Deep Dive
-
Section 50: Final Practice and Review
93: Working with the log Package
A few years ago, I had a colleague who relied entirely on fmt.Println for debugging a background worker. It worked great on his local machine. But once it hit production, we encountered a race condition that only triggered once every few thousand requests. He spent three days trying to correlate the "something went wrong" printouts with the server's system logs, but since fmt doesn't provide timestamps by default, he was basically guessing when the errors actually occurred. That's the moment you realize that "printing" isn't "logging."
The standard log package in Go is your first line of defense against this kind of frustration. While it's simple, it provides the essential metadata—like the date and time—that transforms a random string of text into a useful diagnostic record. More importantly, it writes to standard error (stderr) by default, which is the convention for logs in most cloud and container environments.
Moving Beyond Simple Printing
When you use log.Println or log.Printf, you're getting more than just a line of text. By default, the logger prefixes every entry with the current date and time. This seems trivial until you're staring at a log file with 10,000 lines trying to find exactly when a database connection timed out.
package main
import (
"log"
)
func main() {
log.Println("Starting the server on port 8080...")
// Simulating an error
err := doSomething()
if err != nil {
log.Printf("Critical error encountered: %v", err)
}
}
func doSomething() error {
return someError // imagine a real error here
}
I usually recommend log.Printf for most production scenarios because it allows you to inject variables into your log messages cleanly. It's a small habit, but it makes your logs much more readable than concatenating strings with plus signs.
Adding Context with Custom Flags
The default timestamp is a start, but when your project grows to dozens of files, knowing when something happened isn't enough—you need to know where it happened. This is where log flags come in. I almost always add log.Lshortfile to my projects; it appends the filename and line number to every log entry.
package main
import (
"log"
)
func main() {
// Ldate: the date in the local time zone: 2009/01/23
// Ltime: the time in the local time zone: 01:23:23
// Lshortfile: final file name element and line number: main.go:23
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
log.Println("This log now tells me exactly which line of code produced it.")
}
Be careful not to overdo it. Adding too much metadata to every single line can bloat your logs and make them harder to read for humans. Use Lshortfile for errors and warnings, but maybe stick to basic timestamps for general info logs.
Persisting Logs to a File
Writing to the console is great for development, but in a real-world app, you want those logs stored somewhere permanent. The log package allows you to redirect the output to any object that implements the io.Writer interface, which includes files.
package main
import (
"log"
"os"
)
func main() {
// Open a file for writing.
// O_APPEND keeps old logs, O_CREATE makes the file if it's missing,
// O_WRONLY opens it for writing only.
file, err := os.OpenFile("app.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
if err != nil {
log.Fatal(err)
}
defer file.Close()
// Set the output of the standard logger to the file
log.SetOutput(file)
log.Println("This message will be saved to app.log instead of the console.")
}
One quick tip: notice the use of log.Fatal in the error check. log.Fatal is a powerful tool—it logs the message and then immediately calls os.Exit(1). Use it sparingly, typically only during the application startup phase. If you call it inside a request handler in a web server, you'll crash the entire server just because one request failed, which is definitely not what you want.
📋 Practical Task
Implementing a Persistent Application Audit Log
Build a small Go program that simulates a user authentication system. Your task is to implement a logging system that records security events to a local file.
- Create a file named
auth.logto store the logs. - Configure the
logpackage to include the date, time, and the short file name/line number (Lshortfile). - Create a function called
logEvent(event string, userID int)that writes a formatted message to the log file (e.g., "User 123: Login Successful"). - In your
mainfunction, simulate three different events: a successful login, a failed password attempt, and a password reset. - Ensure that if the log file cannot be opened, the program uses
log.Fatalto exit immediately.
There are no comments for now.