Change the Default log Package Prefix, Flags, and Output in Go

Tech reviewed: Deepak Prasad
Change the Default log Package Prefix, Flags, and Output in Go

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:

go
package main

import "log"

func main() {
	log.Println("Hello, world!")
}
Output

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).

go
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")
}
Output

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).

go
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")
}
Output

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.

go
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")
}
Output

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.

go
package main

import "log"

func main() {
	log.SetFlags(log.LstdFlags)
	userID := 42
	log.Printf("user=%d logged in", userID)
}
Output

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.

go
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")
}
Output

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.

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).

go
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.


References


Frequently Asked Questions

1. How do I import the log package in Go?

Add import "log" and call log.Println, log.Printf, or related helpers on the default logger, or use log.New for a separate logger with its own prefix, flags, and io.Writer.

2. What is the difference between golang log format and a custom prefix?

SetFlags controls the automatic prefix fragment (date, time, file line). SetPrefix adds your own text in front; combine both for a standard logging format that still shows your service name or level.

3. When should I use log.New instead of the default logger?

Use log.New when you need multiple independent loggers, different outputs, or different prefixes in the same process without mutating the global default.

4. Does log.Printf work like fmt.Printf?

Yes, log.Printf uses the same verb rules as fmt.Printf, then writes the result through the logger with the configured prefix and flags.

5. How should I log errors in Go with the log package?

Pass err.Error() or use log.Printf with %v; for stack context wrap with fmt.Errorf and %w, then log the wrapped error string; the log package does not replace structured error handling.

6. Why should I not declare my own const named Ldate or Ltime?

Those identifiers already exist in package log; redeclaring them in your const block shadows the package constants and breaks flag values. Always use log.Ldate, log.Ltime, and the other log.L* names.
Antony Shikubu

Systems Integration Engineer

Highly skilled software developer with expertise in Python, Golang, and AWS cloud services.

  • Mastering Ansible Automation
  • Docker and Kubernetes: The Complete Guide
  • Go: Data Structures, Algorithms and Design Patterns With Go
  • Go (programming language)
  • Python (programming language)
  • Amazon Web Services