Searches for golang log to file, go log to file, golang logging to file, golang log file, golang log to stdout, golang log to file and stdout, golang log, golang log example, golang log syslog, and go import log all lead to the same two packages: log for line-oriented messages and log/syslog on Unix when you want the OS logger. This guide shows go import log, writing to a custom file, teeing to stdout and a file with io.MultiWriter, optional syslog, and a short goroutine example. For heavier pipelines, see logrus or zap.
Tested with Go 1.24 on Linux.
go import log and the default logger
import "log"The log package exposes Print, Printf, Println, Fatal, and Panic variants on a default Logger. log.SetOutput chooses the io.Writer (defaults to standard error). log.SetFlags adds timestamps, file names, and other prefixes.
golang log to file (os.OpenFile)
Open the destination file, then point the default logger at it:
package main
import (
"log"
"os"
)
func main() {
f, err := os.OpenFile("logFile.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Panic(err)
}
defer f.Close()
log.SetOutput(f)
log.SetFlags(log.LstdFlags | log.Lshortfile)
log.Println("This is my first log")
}Flags commonly used with OpenFile here:
os.O_APPEND— append instead of truncating on each open.os.O_CREATE— create the file if it does not exist.os.O_WRONLY— open for writing only (enough for logging).
Mode 0644 gives the owner read/write and group/others read-only; tighten if your threat model requires it.
golang log to file and stdout (io.MultiWriter)
io.MultiWriter duplicates each write to several writers. That is the usual answer for golang log to stdout and a file together:
package main
import (
"io"
"log"
"os"
)
func main() {
f, err := os.OpenFile("logFile.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Panic(err)
}
defer f.Close()
log.SetOutput(io.MultiWriter(os.Stdout, f))
log.SetFlags(log.LstdFlags | log.Lshortfile)
log.Println("This is my first log")
}Running the program prints one line to the terminal and the same line is appended to logFile.log.
golang log syslog (log/syslog)
On many Linux systems you can send application messages through log/syslog. You need a working syslog socket and permission to use it; failures are common in containers or minimal installs, so treat this as environment-specific:
package main
import (
"log"
"log/syslog"
)
func main() {
w, err := syslog.New(syslog.LOG_INFO|syslog.LOG_LOCAL0, "myapp")
if err != nil {
log.Fatal(err)
}
log.SetOutput(w)
log.SetFlags(log.Lshortfile)
log.Println("hello from Go via syslog")
}Use journalctl or tail -F /var/log/syslog (distribution-dependent) to confirm delivery when you have host access.
Concurrency and the default logger
The default Logger serializes writes to its Writer, so calling log.Println from many goroutines is safe without extra mutexes—as long as they share one configured output:
package main
import (
"log"
"os"
"sync"
)
func main() {
f, err := os.OpenFile("logFile.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Fatal(err)
}
defer f.Close()
log.SetOutput(f)
log.SetFlags(log.LstdFlags)
var wg sync.WaitGroup
for w := 0; w < 3; w++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
for i := 0; i < 3; i++ {
log.Printf("worker %d i=%d", id, i)
}
}(w)
}
wg.Wait()
}2026/06/17 22:34:16 worker 2 i=0
2026/06/17 22:34:16 worker 2 i=1
2026/06/17 22:34:16 worker 2 i=2
2026/06/17 22:34:16 worker 0 i=0
...For structured JSON logs or sampling, prefer zap or logrus.
Summary
Golang log to file is usually os.OpenFile plus log.SetOutput. Golang log to file and stdout adds io.MultiWriter so one log line hits both places. Golang log syslog uses log/syslog on Unix when the daemon and permissions allow it. go import log is enough for small services; combine with log.SetFlags, always defer file.Close(), and remember the default logger is safe for concurrent use but still a single global configuration.

