Reputation: 5903
I'm trying to write to a log file with Go.
I have tried several approaches, all of which have failed. This is what I have tried:
func TestLogging(t *testing.T) {
if !FileExists("logfile") {
CreateFile("logfile")
}
f, err := os.Open("logfile")
if err != nil {
t.Fatalf("error: %v", err)
}
// attempt #1
log.SetOutput(io.MultiWriter(os.Stderr, f))
log.Println("hello, logfile")
// attempt #2
log.SetOutput(io.Writer(f))
log.Println("hello, logfile")
// attempt #3
log.SetOutput(f)
log.Println("hello, logfile")
}
func FileExists(name string) bool {
if _, err := os.Stat(name); err != nil {
if os.IsNotExist(err) {
return false
}
}
return true
}
func CreateFile(name string) error {
fo, err := os.Create(name)
if err != nil {
return err
}
defer func() {
fo.Close()
}()
return nil
}
The log file gets created, but nothing ever gets printed or appended to it. Why?
Upvotes: 146
Views: 216068
Reputation: 41
maybe this will help you (if the log file exists use it, if it does not exist create it):
package main
import (
"flag"
"log"
"os"
)
//Se declara la variable Log. Esta será usada para registrar los eventos.
var (
Log *log.Logger = Loggerx()
)
func Loggerx() *log.Logger {
LOG_FILE_LOCATION := os.Getenv("LOG_FILE_LOCATION")
//En el caso que la variable de entorno exista, el sistema usa la configuración del docker.
if LOG_FILE_LOCATION == "" {
LOG_FILE_LOCATION = "../logs/" + APP_NAME + ".log"
} else {
LOG_FILE_LOCATION = LOG_FILE_LOCATION + APP_NAME + ".log"
}
flag.Parse()
//Si el archivo existe se rehusa, es decir, no elimina el archivo log y crea uno nuevo.
if _, err := os.Stat(LOG_FILE_LOCATION); os.IsNotExist(err) {
file, err1 := os.Create(LOG_FILE_LOCATION)
if err1 != nil {
panic(err1)
}
//si no existe,se crea uno nuevo.
return log.New(file, "", log.Ldate|log.Ltime|log.Lshortfile)
} else {
//si existe se rehusa.
file, err := os.OpenFile(LOG_FILE_LOCATION, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666)
if err != nil {
panic(err)
}
return log.New(file, "", log.Ldate|log.Ltime|log.Lshortfile)
}
}
For more detail: https://su9.co/9BAE74B
Upvotes: 1
Reputation: 1078
To help others, I create a basic log function to handle the logging in both cases, if you want the output to stdout, then turn debug on, its straight forward to do a switch flag so you can choose your output.
func myLog(msg ...interface{}) {
defer func() { r := recover(); if r != nil { fmt.Print("Error detected logging:", r) } }()
if conf.DEBUG {
fmt.Println(msg)
} else {
logfile, err := os.OpenFile(conf.LOGDIR+"/"+conf.AppName+".log", os.O_RDWR | os.O_CREATE | os.O_APPEND,0666)
if !checkErr(err) {
log.SetOutput(logfile)
log.Println(msg)
}
defer logfile.Close()
}
}
Upvotes: 0
Reputation: 305
Building on Allison and Deepak's answer, I started using logrus and really like it:
var log = logrus.New()
func init() {
// log to console and file
f, err := os.OpenFile("crawler.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
log.Fatalf("error opening file: %v", err)
}
wrt := io.MultiWriter(os.Stdout, f)
log.SetOutput(wrt)
}
I have a defer f.Close() in the main function
Upvotes: 5
Reputation: 61
I'm writing logs to the files, which are generate on daily basis (per day one log file is getting generated). This approach is working fine for me :
var (
serverLogger *log.Logger
)
func init() {
// set location of log file
date := time.Now().Format("2006-01-02")
var logpath = os.Getenv(constant.XDirectoryPath) + constant.LogFilePath + date + constant.LogFileExtension
os.MkdirAll(os.Getenv(constant.XDirectoryPath)+constant.LogFilePath, os.ModePerm)
flag.Parse()
var file, err1 = os.OpenFile(logpath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err1 != nil {
panic(err1)
}
mw := io.MultiWriter(os.Stdout, file)
serverLogger = log.New(mw, constant.Empty, log.LstdFlags)
serverLogger.Println("LogFile : " + logpath)
}
// LogServer logs to server's log file
func LogServer(logLevel enum.LogLevel, message string) {
_, file, no, ok := runtime.Caller(1)
logLineData := "logger_server.go"
if ok {
file = shortenFilePath(file)
logLineData = fmt.Sprintf(file + constant.ColonWithSpace + strconv.Itoa(no) + constant.HyphenWithSpace)
}
serverLogger.Println(logLineData + logLevel.String() + constant.HyphenWithSpace + message)
}
// ShortenFilePath Shortens file path to a/b/c/d.go tp d.go
func shortenFilePath(file string) string {
short := file
for i := len(file) - 1; i > 0; i-- {
if file[i] == constant.ForwardSlash {
short = file[i+1:]
break
}
}
file = short
return file
}
"shortenFilePath()" method used to get the name of the file from full path of file. and "LogServer()" method is used to create a formatted log statement (contains : filename, line number, log level, error statement etc...)
Upvotes: 0
Reputation: 179
If you run binary on linux machine you could use shell script.
overwrite into a file
./binaryapp > binaryapp.log
append into a file
./binaryapp >> binaryapp.log
overwrite stderr into a file
./binaryapp &> binaryapp.error.log
append stderr into a file
./binaryapp &>> binalyapp.error.log
it can be more dynamic using shell script file.
Upvotes: 9
Reputation: 5363
I usually print the logs on screen and write into a file as well. Hope this helps someone.
f, err := os.OpenFile("/tmp/orders.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
log.Fatalf("error opening file: %v", err)
}
defer f.Close()
wrt := io.MultiWriter(os.Stdout, f)
log.SetOutput(wrt)
log.Println(" Orders API Called")
Upvotes: 51
Reputation: 15537
Declare up top in your global var
so all your processes can access if needed.
package main
import (
"log"
"os"
)
var (
outfile, _ = os.Create("path/to/my.log") // update path for your needs
l = log.New(outfile, "", 0)
)
func main() {
l.Println("hello, log!!!")
}
Upvotes: 5
Reputation: 5903
os.Open()
must have worked differently in the past, but this works for me:
f, err := os.OpenFile("testlogfile", os.O_RDWR | os.O_CREATE | os.O_APPEND, 0666)
if err != nil {
log.Fatalf("error opening file: %v", err)
}
defer f.Close()
log.SetOutput(f)
log.Println("This is a test log entry")
Based on the Go docs, os.Open()
can't work for log.SetOutput
, because it opens the file "for reading:"
func Open
func Open(name string) (file *File, err error)
Open
opens the named file for reading. If successful, methods on the returned file can be used for reading; the associated file descriptor has modeO_RDONLY
. If there is an error, it will be of type*PathError
.
EDIT
Moved defer f.Close()
to after if err != nil
check
Upvotes: 231
Reputation: 754
This works for me
created a package called logger.go
package logger
import (
"flag"
"os"
"log"
"go/build"
)
var (
Log *log.Logger
)
func init() {
// set location of log file
var logpath = build.Default.GOPATH + "/src/chat/logger/info.log"
flag.Parse()
var file, err1 = os.Create(logpath)
if err1 != nil {
panic(err1)
}
Log = log.New(file, "", log.LstdFlags|log.Lshortfile)
Log.Println("LogFile : " + logpath)
}
import the package wherever you want to log e.g main.go
package main
import (
"logger"
)
const (
VERSION = "0.13"
)
func main() {
// time to use our logger, print version, processID and number of running process
logger.Log.Printf("Server v%s pid=%d started with processes: %d", VERSION, os.Getpid(),runtime.GOMAXPROCS(runtime.NumCPU()))
}
Upvotes: 12
Reputation: 51
The default logger in Go writes to stderr (2). redirect to file
import (
"syscall"
"os"
)
func main(){
fErr, err = os.OpenFile("Errfile", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
syscall.Dup2(int(fErr.Fd()), 1) /* -- stdout */
syscall.Dup2(int(fErr.Fd()), 2) /* -- stderr */
}
Upvotes: 5
Reputation: 859
I prefer the simplicity and flexibility of the 12 factor app recommendation for logging. To append to a log file you can use shell redirection. The default logger in Go writes to stderr (2).
./app 2>> logfile
See also: http://12factor.net/logs
Upvotes: 55