Golang log to file, stdout, and syslog with the standard log package

Golang log to file with os.OpenFile and log.SetOutput; golang log to file and stdout using io.MultiWriter; golang log syslog on Linux; go import log and golang log example; concurrency-safe default log.Logger.

Published

Updated

Read time 3 min read

Reviewed byDeepak Prasad

Golang log to file, stdout, and syslog with the standard log package

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

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

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

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

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

go
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()
}
text
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.


References


Frequently Asked Questions

1. How do I golang log to file from the standard library?

Open a file with os.OpenFile using O_APPEND|O_CREATE|os.O_WRONLY (or O_RDWR), pass the file to log.SetOutput, then use log.Println and friends. Call Close when the program exits, typically with defer.

2. How do I golang log to file and stdout at the same time?

Use io.MultiWriter(os.Stdout, file) as log.SetOutput argument so each log line is written to both destinations in one Write call from the logger.

3. What does go import log mean?

Import "log" for Printf-style logging with the default logger, or import log/syslog when you want the system syslog daemon to receive messages on supported Unix hosts.

4. Is golang log syslog available on Windows?

The log/syslog package is Unix-oriented; builds target non-Unix systems by leaving the API unavailable. Use file or socket logging there, or a library like zap or logrus.
Tuan Nguyen

Data Scientist

Proficient in Golang, Python, Java, MongoDB, Selenium, Spring Boot, Kubernetes, Scrapy, API development, Docker, Data Scraping, PrimeFaces, Linux, Data Structures, and Data Mining. With expertise …