Log at the Proper Level Here I've given some advice: TRACE level: this is a code smell if used in production. This should be used during development to track bugs, but never committed to your VCS. DEBUG level: log at this level about anything that happens in the program.
- Create a single log.Logger and pass it around?
That is possible. A log.Logger can be used concurrently from multiple goroutines.
- Pass around a pointer to that log.Logger?
log.New returns a *Logger
which is usually an indication that you should pass the object around as a pointer. Passing it as value would create a copy of the struct (i.e. a copy of the Logger) and then multiple goroutines might write to the same io.Writer concurrently. That might be a serious problem, depending on the implementation of the writer.
- Should each goroutine or function create a logger?
I wouldn't create a separate logger for each function or goroutine. Goroutines (and functions) are used for very lightweight tasks that will not justify the maintenance of a separate logger. It's probably a good idea to create a logger for each bigger component of your project. For example, if your project uses a SMTP service for sending mails, creating a separate logger for the mail service sounds like a good idea so that you can filter and turn off the output separately.
- Should I create the logger as a global variable?
That depends on your package. In the previous mail service example, it would be probably a good idea to have one logger for each instance of your service, so that users can log failures while using the gmail mail service differently than failures that occured while using the local MTA (e.g. sendmail).
For simple cases, there is a global logger defined in the log package, log.Logger
. This global logger can be configured through log.SetFlags
.
Afterwards one can just call the top level functions of the log package like log.Printf
and log.Fatalf
, which use that global instance.
This is a simple logger
package customlogger
import (
"log"
"os"
"sync"
)
type logger struct {
filename string
*log.Logger
}
var logger *logger
var once sync.Once
// start loggeando
func GetInstance() *logger {
once.Do(func() {
logger = createLogger("mylogger.log")
})
return logger
}
func createLogger(fname string) *logger {
file, _ := os.OpenFile(fname, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
return &logger{
filename: fname,
Logger: log.New(file, "My app Name ", log.Lshortfile),
}
}
You can use it in this way
package main
import (
"customlogger"
"fmt"
"net/http"
)
func main() {
logger := customlogger.GetInstance()
logger.Println("Starting")
http.HandleFunc("/", sroot)
http.ListenAndServe(":8080", nil)
}
func sroot(w http.ResponseWriter, r *http.Request) {
logger := customlogger.GetInstance()
fmt.Fprintf(w, "welcome")
logger.Println("Starting")
}
I know this question is a bit old, but if, like me, your projects are made up of multiple smaller files I vote for your 4th option - I've created a logger.go
that is part of package main. This go file creates the logger, assigns it to a file, and provides it to the rest of main. Note I have not come up with a graceful way to close errorlog...
package main
import (
"fmt"
"log"
"os"
)
var errorlog *os.File
var logger *log.Logger
func init() {
errorlog, err := os.OpenFile(logfile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
fmt.Printf("error opening file: %v", err)
os.Exit(1)
}
logger = log.New(errorlog, "applog: ", log.Lshortfile|log.LstdFlags)
}
This is an older question, but I would like to suggest the use of http://github.com/romana/rlog (which we developed). It is configured through environment variables, the logger object is created and initialized when rlog is imported. Therefore, no need to pass around a logger.
rlog has quite a few features:
It is very small, has no external dependencies, except the standard Golang library and is actively being developed. Examples are provided in the repo.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With