log package is used for logging. The examples (unlike some other packages) are not very helpful. It's very bare bones and has only two logging levels.
For anything complicated use Google's glog package.
Basic logging is similar to other languages.
// 03.2-01-basic-logging.go
package main
import (
"log"
)
func main() {
a, b := 10, 20
log.Print("Use Print to log.")
log.Println("Ditto for Println.")
log.Printf("Use Printf and format strings. %d + %d = %d", a, b, a+b)
}
Each log is on a new line:
$ go run 03.2-01-basic-logging.go
2017/12/25 22:18:38 Use Print to log.
2017/12/25 22:18:38 Ditto for Println.
2017/12/25 22:18:38 Use Printf and format strings. 10 + 20 = 30
We can also forward the output to a file (or any number of io.Writer
s) with [log.SetOutput][setoutput1-log-pkg].
logFile, err := os.Create("log1.txt")
if err != nil {
panic("Could not open file")
}
log.SetOutput(logFile)
We can setup a custom logger with logger.New.
func New(out io.Writer, prefix string, flag int) *Logger
out
: Log destination. Anyio.Writer
like files.prefix
: Appears before each log entry. ThinkWarning/Info/Error
.flag
: Defines logging properties (e.g. the date time format).
Using out
we can log to files.
// 03.2-02-log-file.go
package main
import (
"log"
"os"
)
func main() {
// Create a file
logFile, err := os.Create("log1.txt")
if err != nil {
panic("Could not open file")
}
// Close the file after main returns
defer logFile.Close()
a, b := 10, 20
// We will not use the other options
myLog := log.New(logFile, "", 0)
myLog.Print("Use Print to log.")
myLog.Println("Ditto for Println.")
myLog.Printf("Use Printf and format strings. %d + %d = %d", a, b, a+b)
}
log1.txt
will contain:
Use Print to log.
Ditto for Println.
Use Printf and format strings. 10 + 20 = 30
After New
, mylog.SetOutput(w io.Writer)
can redirect the logger.
It's also possible to log to multiple files (or io.Writer
s) with io.MultiWriter. This is useful when we want to both output to stdout and to files.
// 03.2-03-log-multiple-files.go
package main
import (
"bytes"
"fmt"
"io"
"log"
"os"
)
func main() {
// Create a file
logFile, err := os.Create("log1.txt")
if err != nil {
panic("Could not open file")
}
// Close the file after main returns
defer logFile.Close()
// Create a second file
logFile2, err := os.Create("log2.txt")
if err != nil {
panic("Could not open file2")
}
defer logFile2.Close()
// Create a buffer
var buflog bytes.Buffer
multiW := io.MultiWriter(logFile, logFile2, &buflog, os.Stdout)
a, b := 10, 20
// Log to multiW
myLog := log.New(multiW, "", 0)
myLog.Print("Use Print to log.")
myLog.Println("Ditto for Println.")
myLog.Printf("Use Printf and format strings. %d + %d = %d", a, b, a+b)
// Print buffer
fmt.Println("Buffer:")
fmt.Println(buflog.String())
}
We can see what we logged in both stdout and buffer:
$ go run 03.2-03-log-multiple-files.go
Use Print to log.
Ditto for Println.
Use Printf and format strings. 10 + 20 = 30
Buffer:
Use Print to log.
Ditto for Println.
Use Printf and format strings. 10 + 20 = 30
Prefix should be next but by discussing flag we can see if it appears before flag format or not. flag
is an integer and is a collection of bits (like FLAGS
CPU register). The flags are defined as constants:
// https://godoc.org/log#pkg-constants
const (
// Bits or'ed together to control what's printed.
// There is no control over the order they appear (the order listed
// here) or the format they present (as described in the comments).
// The prefix is followed by a colon only when Llongfile or Lshortfile
// is specified.
// For example, flags Ldate | Ltime (or LstdFlags) produce,
// 2009/01/23 01:23:23 message
// while flags Ldate | Ltime | Lmicroseconds | Llongfile produce,
// 2009/01/23 01:23:23.123123 /a/b/c/d.go:23: message
Ldate = 1 << iota // the date in the local time zone: 2009/01/23
Ltime // the time in the local time zone: 01:23:23
Lmicroseconds // microsecond resolution: 01:23:23.123123. assumes Ltime.
Llongfile // full file name and line number: /a/b/c/d.go:23
Lshortfile // final file name element and line number: d.go:23. overrides Llongfile
LUTC // if Ldate or Ltime is set, use UTC rather than the local time zone
LstdFlags = Ldate | Ltime // initial values for the standard logger
)
There's only room for a few bits of customization (see what I did there?).
// 03.2-04-log-flags.go
package main
import (
"log"
"os"
)
func main() {
a, b := 10, 20
// New logger will output to stdout with flags
// Only log date and file
myLog := log.New(os.Stdout, "", log.Ldate|log.Lshortfile)
myLog.Print("Use Print to log.")
myLog.Println("Ditto for Println.")
myLog.Printf("Use Printf and format strings. %d + %d = %d", a, b, a+b)
}
We log date and filename:
$ go run 03.2-04-log-flags.go
2017/12/26 03.2-04-log-flags.go:25: Use Print to log.
2017/12/26 03.2-04-log-flags.go:26: Ditto for Println.
2017/12/26 03.2-04-log-flags.go:27: Use Printf and format strings. 10 + 20 = 30
prefix
adds a string to the beginning of each log line.
// 03.2-05-log-prefix.go
package main
import (
"log"
"os"
)
func main() {
a, b := 10, 20
// New logger will output to stdout with prefix "Log1: " and flags
// Note the space in prefix
myLog := log.New(os.Stdout, "Log1: ", log.Ldate|log.Lshortfile)
myLog.Print("Use Print to log.")
myLog.Println("Ditto for Println.")
myLog.Printf("Use Printf and format strings. %d + %d = %d", a, b, a+b)
}
Prefix is printed before flags:
$ go run 03.2-05-log-prefix.go
Log1: 2017/12/26 03.2-05-log-prefix.go:16: Use Print to log.
Log1: 2017/12/26 03.2-05-log-prefix.go:17: Ditto for Println.
Log1: 2017/12/26 03.2-05-log-prefix.go:18: Use Printf and format strings. 10 + 20 = 30
log
only supports two logging levels:
Both of these support ln
and f
variants (e.g. Fatalf
, Panicln
).