I see a lot of Go
code that looks like this:
func main() {
response, _, err := http.Get("http://golang.org/")
if err != nil {
fmt.Printf("%s", err)
os.Exit(1)
}
defer response.Body.Close()
contents, err := ioutil.ReadAll(response.Body)
if err != nil {
fmt.Printf("%s", err)
os.Exit(1)
}
fmt.Printf("%s
", string(contents))
}
My questions are:
In production, should I keep these fmt.Printf
statements? Silly question I am sure but just checking
What logging options do you recommend for production code and also dev?
Thought it's acceptable to use printf
s in some situation. It's better, in production environment to use different logging strategies.
For instance if your application is a daemon running in background you are going to miss all of your fmt.Printf
unless you don't pipe them in a file.
A good way to do looging is to use log rotation (since you don't want to have incredibly huge files) instead of reinvent your own logrotate you can use standard packages or external packages based on your needs.
Golang provides a log package syslog subpackage that are optimal solution to do simple logging relying on the OS syslog.
fmt.Printf()
is good for starting to sketch out your program, but you should probably either delete them or replace with real logging statements.
I have had good luck with the standard log and logrus if I need more.
All logging depends on other ops related concerns, so who/what is processing/looking at the logs matters a lot.
I think the best way is to prepare your code for easy switch from develop environment to production environment maybe using an input parameter when you launch your go application.
You can create a variuos level of logs:
"Always" log level
This log level is always enabled, gives important information about the status of your application (if need). Also report crash messages.
"testing" log level
This log level contain useful informations when your code is finished but you have to check some information no longer useful when the code is on production
"Debug" log level
This log level contains useful information when you are in development environment developing new features or optimising already existent code
The best way to do this, in my opinion, is by using chans which allow you create an async daemon and log wherever you want whatever you want.
You can read some information about chan here.
An example of daemon could be:
func daemonLogging(importantMessage chan error, testingMessage chan error,debugMessage chan error){
for{
if globalDebugFlag == true{
// Log debug message
}
if globalTestingMessage == true{
// Log Testing message
}
// Log important message
}
}
Remember to call this function with the go
keyword
I think it is not important what package you use (if you have not any need) but it is important how you decide to log your messages
My take on this is that for relatively simple "service-like" solutions (a long-running program servicing user requests) this approach works quite OK provided you either switch to log.Fatal*()
which adds a timestamp to each message output or run your service under some sensible supervisor (systemd
, daemon
, runit
etc) which would forward what your program outputs into approptiate system log sink(s).
An approach to logging also highly depends on the nature of the program. Say, for command-line applications which do one-off actions (curl
or wget
is a good example of such a program—in the spirit of your code example) logging to their standard error stream and exiting is the only sane mode of operation. Daemons (services) are expected to run unattended, and hence you have to make sure what they log gets sent and stored somewhere, timestamped.
A minor note: a well-behaving program is supposed to output error messages to its standard error stream, so if you don't want to use log.Fatal*()
for some reaseon, at least do fmt.Fprint*(os.Stderr, ...
).