Production apps need a standard logging format on stderr or in files. After import "log", the default logger already applies the usual date and time prefix. You tune it with log.SetFlags, add a service or level prefix with log.SetPrefix, create separate pipelines with log.New, and emit formatted lines with log.Printf. This guide stays on the standard library log package (listing packages); for CLI flags in general see Go flags examples and time layouts.
Tested with Go 1.24 on Linux.
Default logger after import "log"
Create a module and main.go (modules; packages), then:
package main
import "log"
func main() {
log.Println("Hello, world!")
}Run locally: you should see one line on stderr with the default date, time, and message.
log.SetFlags — built-in prefix fields
Flags choose which automatic fragments appear before your text. Use the names from package log (never shadow Ldate / Ltime with your own const).
package main
import "log"
func main() {
log.Println("default flags")
log.SetFlags(log.Ldate)
log.Println("date only")
log.SetFlags(log.Ltime)
log.Println("time only")
log.SetFlags(log.Ldate | log.Ltime)
log.Println("date and time")
log.SetFlags(log.Lshortfile)
log.Println("short file")
log.SetFlags(log.LstdFlags | log.Lshortfile)
log.Println("std plus short file")
}Run locally: each line’s prefix should reflect the flag set active for that call. Llongfile and Lshortfile relate to file paths (line numbers in logs).
log.SetPrefix and custom timestamps
SetPrefix is fixed until you change it again. For a layout that updates each line, refresh the prefix when needed (or set a static service name).
package main
import (
"log"
"time"
)
func main() {
log.SetFlags(log.Lshortfile)
log.SetPrefix(time.Now().UTC().Format("2006-01-02") + " ")
log.Println("with ISO date prefix")
log.SetPrefix(time.Now().UTC().Format("15:04:05") + " ")
log.Println("with time-only prefix")
}You should see your message after the prefix you chose.
log.New — separate loggers
log.New(w io.Writer, prefix string, flag int) returns *log.Logger with its own writer, golang log prefix, and golang log format flags—useful for components or tests without touching the default.
package main
import (
"log"
"os"
)
func main() {
info := log.New(os.Stdout, "INFO: ", log.LstdFlags)
errL := log.New(os.Stderr, "ERROR: ", log.LstdFlags|log.Lshortfile)
info.Println("started")
errL.Println("something went wrong")
}You should see INFO: on stdout and ERROR: with file context on stderr.
log.Printf — formatted lines (go log printf)
log.Printf mirrors fmt.Printf: same verbs, then the logger adds prefix and flags.
package main
import "log"
func main() {
log.SetFlags(log.LstdFlags)
userID := 42
log.Printf("user=%d logged in", userID)
}You should see user=42 logged in after the standard prefix.
Simple level-style prefixes and error text
The log package has no typed levels; teams often encode go error logging and other severities in the prefix string.
package main
import (
"errors"
"fmt"
"log"
)
func work() error {
return fmt.Errorf("parse: %w", errors.New("bad input"))
}
func main() {
log.SetFlags(log.LstdFlags | log.Lshortfile)
if err := work(); err != nil {
log.SetPrefix("ERROR: ")
log.Println(err)
return
}
log.SetPrefix("INFO: ")
log.Println("ok")
}Run locally: you should see an ERROR: line containing parse: bad input.
Log to a file
Open the file first; only call defer file.Close() when OpenFile succeeded. More patterns: log to a file in Go.
package main
import (
"log"
"os"
)
func main() {
file, err := os.OpenFile("logs.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Fatal(err)
}
defer file.Close()
logger := log.New(file, "INFO: ", log.LstdFlags|log.Lshortfile)
logger.Println("Log custom date and time in logs.log")
}Run in your project directory and inspect logs.log.
Log to console and file with io.MultiWriter
io.MultiWriter duplicates each write to several destinations; attach it with logger.SetOutput (or pass it as the writer to log.New).
package main
import (
"io"
"log"
"os"
)
func main() {
file, err := os.OpenFile("logs.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Fatal(err)
}
defer file.Close()
mw := io.MultiWriter(os.Stdout, file)
logger := log.New(mw, "INFO: ", log.LstdFlags|log.Lshortfile)
logger.Println("Log to console and file")
}Run locally: one line should appear on stdout and the same line should append to logs.log.
Summary
After import "log", you control the usual line format with log.SetFlags, add a prefix with log.SetPrefix, and use log.Printf wherever you need printf-style formatting. Use log.New for separate writers or prefixes, make error lines clear with explicit prefixes and wrapped errors, and duplicate output with io.MultiWriter when logs must hit both the console and a file.

