How to change default log module logging format in Golang
Logging is a very important feature of an application in production, without which it's impossible to debug any problem that may occur in the application. Go log package is an incredible package that enables Go developers to log messages from their applications onto the console or inside a log file. It has a standard logger that can be accessed through helper functions such as Printf()
, Println()
, Fatalf()
, Fatalln()
, Panicf()
and Panicln()
.
The logger writes to standard error and prints the date and time for each logged message. The log package comes with flags which define which text to prefix on each log entry by the logger. In this article we will cover the following subtopics about the log package.
- How to change date and time format
- How to add different log levels
- Logging custom messages to the console
- Logging custom messages into log file
- Logging custom messages to both the console and into a log file
Getting started with the default log package
Let us have a first hand experience with the log package and advance to the other features that it has to offer to us. Create a new main.go
file in your working directory and enter the below code. To get access to the log package in Go, you have to import it from the go standard library.
Example
package main
import "log"
func main() {
log.Println("Hello, world!")
}
The above code prints out the results on the console with default logging format. Below is the result after executing the code. Output
2022/06/04 21:42:22 Hello, world!
Default supported flags with log module
The log package comes with different flags that can be used to format log messages.
- Ldate :to the local timezone e.g
2022/06/04
- Ltime :to the local time zone e.g
01:23:23
- Lmicroseconds : Microseconds resolution: 01:23:23.123123 assumes Ltime
- Llongfile: Full file name and line number :
/home/go/logger/main.go:25
- Lshortfile: Final file name element and line number, it overrides
Llongfile : main.go:25
- LUTC : Used if Ldate and Ltime is set, used UTC rather than the local the zone
- Lmsgprefix: Used to move the “prefix” from the beginning of the line to before the message
- LstdFlags: Initial values for the standard logger
Example
package main
import "log"
const (
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
Lmsgprefix // move the "prefix" from the beginning of the line to before the message
LstdFlags = Ldate | Ltime // initial values for the standard logger
)
func main() {
// Log to the console
log.Println("Default lof message")
// Set Date to the log : user log.SetFlag()
log.SetFlags(log.Ldate)
// Log to the console with date prepended
log.Println("Log to the console with date prepended")
// Set Time to the log
log.SetFlags(log.Ltime)
// Log to the console with date prepended
log.Println("Log to the console with time prepended")
// Set Date and Time to the log
log.SetFlags(log.Ldate | log.Ltime)
// Log to the console with date prepended
log.Println("Log to the console with date and time prepended")
// Set filename to the log
log.SetFlags(log.Lshortfile)
// Log to the console with filename prepended
log.Println("Log to the console with filename prepended")
// Set date, time and filename to the log
log.SetFlags(log.LstdFlags | log.Lshortfile)
// Log to the console with date, time and filename prepended
log.Println("Log to the console with date, time and filename prepended")
}
Output
2022/06/08 06:45:47 Default lof message
2022/06/08 Log to the console with date prepended
06:45:47 Log to the console with time prepended
2022/06/08 06:45:47 Log to the console with date and time prepended
main.go:34: Log to the console with filename prepended
2022/06/08 06:45:47 main.go:38: Log to the console with date, time and filename prepended
Modify date and time format of log module
It's possible to change date and time logging formats for the prefix for each log message that gets printed out. Go logger has a SetPrefix()
method which sets the output prefix for the standard logger.
To format the date and time, we need to define our date and time format that will be prepended in our log messages.
Example
package main
import (
"log"
"time"
)
const (
// YYYY-MM-DD: 2022-03-23
YYYYMMDD = "2006-01-02"
// 24h hh:mm:ss: 14:23:20
HHMMSS24h = "15:04:05"
// 12h hh:mm:ss: 2:23:20 PM
HHMMSS12h = "3:04:05 PM"
// text date: March 23, 2022
TextDate = "January 2, 2006"
// text date with weekday: Wednesday, March 23, 2022
TextDateWithWeekday = "Monday, January 2, 2006"
// abbreviated text date: Mar 23 Wed
AbbrTextDate = "Jan 2 Mon"
)
func main() {
// Add flags: prepend filename
log.SetFlags(log.Lshortfile)
// Add custom date
log.SetPrefix(time.Now().UTC().Format(YYYYMMDD) + ": ")
log.Println("Log message with date formatted")
// Add custom time
log.SetPrefix(time.Now().UTC().Format(HHMMSS12h) + ": ")
log.Println("Log message with time formatted")
// Add custom date and time
log.SetPrefix(time.Now().UTC().Format(YYYYMMDD+" "+HHMMSS12h) + ": ")
log.Println("Log message with time formatted")
}
Output
2022-06-08: main.go:28: Log message with date formatted
4:03:48 AM: main.go:31: Log message with time formatted
2022-06-08 4:03:48 AM: main.go:34: Log message with time formatted
Add different log levels to log module
The Log package can also be used to change logging format to define custom log levels that can be categorized into:
- Warning
- Error
- Info
- Debug
- Fatal
Method-1: Using log.SetPrefix() Method
Adding log level is achieved using the SetPrefix()
method.
Example
package main
import (
"log"
"time"
)
const (
// YYYY-MM-DD: 2022-03-23
YYYYMMDD = "2006-01-02"
// 24h hh:mm:ss: 14:23:20
HHMMSS24h = "15:04:05"
// 12h hh:mm:ss: 2:23:20 PM
HHMMSS12h = "3:04:05 PM"
// text date: March 23, 2022
TextDate = "January 2, 2006"
// text date with weekday: Wednesday, March 23, 2022
TextDateWithWeekday = "Monday, January 2, 2006"
// abbreviated text date: Mar 23 Wed
AbbrTextDate = "Jan 2 Mon"
)
func main() {
// Define flags to be used
flags := log.Lshortfile
// Log without log level
datetime := time.Now().UTC().Format(YYYYMMDD+" "+HHMMSS12h) + ": "
log.Println("Without log levels")
// Set log level SetPrefix
log.SetFlags(flags)
// INFO log level
log.SetPrefix("INFO: " + datetime)
log.Println("INFO level")
// WARNING log level
log.SetPrefix("WARN: " + datetime)
log.Println("WARN level")
// DEBUG log level
log.SetPrefix("DEBUG: " + datetime)
log.Println("DEBUG level")
// ERROR log level
log.SetPrefix("ERROR: " + datetime)
log.Println("ERROR level")
// FATA log level
log.SetPrefix("FATA: " + datetime)
log.Println("FATA level")
}
Output
2022/06/08 07:37:31 Without log levels
INFO: 2022-06-08 4:37:31 AM: main.go:33: INFO level
WARN: 2022-06-08 4:37:31 AM: main.go:36: WARN level
DEBUG: 2022-06-08 4:37:31 AM: main.go:39: DEBUG level
ERROR: 2022-06-08 4:37:31 AM: main.go:42: ERROR level
FATAL: 2022-06-08 4:37:31 AM: main.go:45: FATAL level
Method-2: Using log.New() Method
Log levels can also be defined using New()
function that takes three arguments, namely out, prefix and flag.
- Out: This is any type that implements the
io.Writer
interface e.gos.Stdout
- Prefix : This a simple text that is prepended at the beginning of each log message.
- Flag : These are flags that define which text to prefix logs generated by the logger
Example
package main
import (
"log"
"os"
"time"
)
const (
// YYYY-MM-DD: 2022-03-23
YYYYMMDD = "2006-01-02"
// 24h hh:mm:ss: 14:23:20
HHMMSS24h = "15:04:05"
// 12h hh:mm:ss: 2:23:20 PM
HHMMSS12h = "3:04:05 PM"
// text date: March 23, 2022
TextDate = "January 2, 2006"
// text date with weekday: Wednesday, March 23, 2022
TextDateWithWeekday = "Monday, January 2, 2006"
// abbreviated text date: Mar 23 Wed
AbbrTextDate = "Jan 2 Mon"
)
func main() {
// Defines flags
flags := log.Lshortfile
// Create a new logger
logger := log.New(os.Stdout, "", flags)
// Define date and time format
datetime := time.Now().UTC().Format(YYYYMMDD+" "+HHMMSS12h) + ": "
// Set INFO prefix
logger.SetPrefix("INFO: " + datetime)
logger.Println("INFO level")
// Set WARNING prefix
logger.SetPrefix("WARN: " + datetime)
logger.Println("WARN level")
// Set ERROR prefix
logger.SetPrefix("ERROR: " + datetime)
logger.Println("ERROR level")
// Set DEBUG prefix
logger.SetPrefix("DEBUG: " + datetime)
logger.Println("DEBUG level")
// Set FATAL prefix
logger.SetPrefix("FATAL: " + datetime)
logger.Println("FATAL level")
}
Output
INFO: 2022-06-08 4:36:42 AM: main.go:33: INFO level
WARN: 2022-06-08 4:36:42 AM: main.go:36: WARN level
ERROR: 2022-06-08 4:36:42 AM: main.go:39: ERROR level
DEBUG: 2022-06-08 4:36:42 AM: main.go:42: DEBUG level
FATAL: 2022-06-08 4:36:42 AM: main.go:45: FATAL level
Print messages to the console with custom logging format
By default, the log package prints messages on the console. It is possible to define the standard output explicitly if need be. Using the New()
function in the log package we can define where we want to print our messages.
Example
package main
import (
"log"
"time"
)
const (
// YYYY-MM-DD: 2022-03-23
YYYYMMDD = "2006-01-02"
// 24h hh:mm:ss: 14:23:20
HHMMSS24h = "15:04:05"
// 12h hh:mm:ss: 2:23:20 PM
HHMMSS12h = "3:04:05 PM"
// text date: March 23, 2022
TextDate = "January 2, 2006"
// text date with weekday: Wednesday, March 23, 2022
TextDateWithWeekday = "Monday, January 2, 2006"
// abbreviated text date: Mar 23 Wed
AbbrTextDate = "Jan 2 Mon"
)
func main() {
// Defines flags
flags := log.Lshortfile
// Define date and time format
datetime := time.Now().UTC().Format(YYYYMMDD+" "+HHMMSS12h) + ": "
// Set Flags
log.SetFlags(flags)
// Set INFO prefix
log.SetPrefix("INFO: " + datetime)
log.Println("Log to console")
}
Output
INFO: 2022-06-08 5:14:41 AM: main.go:32: Log to console
Log messages into log file with custom logging format
Logging messages into a log file involves creating a file to write logs into . The log file will store all the log messages instead of logging in the console.
Example
package main
import (
"log"
"os"
"time"
)
const (
// YYYY-MM-DD: 2022-03-23
YYYYMMDD = "2006-01-02"
// 24h hh:mm:ss: 14:23:20
HHMMSS24h = "15:04:05"
// 12h hh:mm:ss: 2:23:20 PM
HHMMSS12h = "3:04:05 PM"
// text date: March 23, 2022
TextDate = "January 2, 2006"
// text date with weekday: Wednesday, March 23, 2022
TextDateWithWeekday = "Monday, January 2, 2006"
// abbreviated text date: Mar 23 Wed
AbbrTextDate = "Jan 2 Mon"
)
func main() {
// Defines flags
flags := log.Lshortfile
// Define date and time format
datetime := time.Now().UTC().Format(YYYYMMDD+" "+HHMMSS12h) + ": "
// Set INFO prefix
// Open a file if it exist or create one if file does not exist
file, err := os.OpenFile("logs.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
// Set file as the destination to log messages with log level "FATAL"
logger := log.New(file, "", flags)
logger.SetPrefix("FATAL: " + datetime)
// Log a fatal message into the log file
logger.Println(err)
}
defer file.Close()
logger := log.New(file, "", flags)
logger.SetPrefix("INFO : " + datetime)
// Log a fatal message into the log file
logger.Println("Log custom date and time in a logs.log file")
}
Output
INFO : 2022-06-08 4:46:30 AM: main.go:45: Log custom date and time in a logs.log file
Log messages with custom logging format to both the console and into a log file
Logging on the console and file can be achieved with the call to io.MultiWriter()
that takes two arguments, os.Stdout()
and the log file. The write destination is defined by theSetOutput()
.
Example
package main
import (
"io"
"log"
"os"
"time"
)
const (
// YYYY-MM-DD: 2022-03-23
YYYYMMDD = "2006-01-02"
// 24h hh:mm:ss: 14:23:20
HHMMSS24h = "15:04:05"
// 12h hh:mm:ss: 2:23:20 PM
HHMMSS12h = "3:04:05 PM"
// text date: March 23, 2022
TextDate = "January 2, 2006"
// text date with weekday: Wednesday, March 23, 2022
TextDateWithWeekday = "Monday, January 2, 2006"
// abbreviated text date: Mar 23 Wed
AbbrTextDate = "Jan 2 Mon"
)
func main() {
// Defines flags
flags := log.Lshortfile
// Define date and time format
datetime := time.Now().UTC().Format(YYYYMMDD+" "+HHMMSS12h) + ": "
// Set INFO prefix
// Open a file if it exist or create one if file does not exist
file, err := os.OpenFile("logs.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
// Set file as the destination to log messages with log level "FATAL"
logger := log.New(file, "", flags)
logger.SetPrefix("FATAL: " + datetime)
// Log a fatal message into the log file
logger.Println(err)
}
defer file.Close()
logger := log.New(file, "", flags)
logger.SetPrefix("INFO : " + datetime)
// Log a fatal message into the log file
mw := io.MultiWriter(os.Stdout, file)
logger.SetOutput(mw)
logger.Println("Log to console and file")
}
Output
INFO : 2022-06-08 4:52:36 AM: main.go:48: Log to console and file
Summary
Applications running in production need to log messages so that it is easy to keep track of what is happening with the application. When applications in production run with logging messages , it will be almost impossible to debug issues or errors that come up. For these reasons and many others, go engineers added the log package to the standard library that can keep track of log messages and categorize them into different levels such as debug, error, warning, fatal and info. This can be achieved using multiple third party modules but for any one who wants to stick to default log module and still be able to customized logging format for date and time or adding log levels, this article can help them.
References
https://pkg.go.dev/log#New
https://www.ardanlabs.com/blog/2013/11/using-log-package-in-go.html