I am writing a Go program and using the log
package to write to a log file so that each new line will have a timestamp prepended to it. Sometimes I just want to append text to current line instead of starting a new line with the prepended timestamp.
I thought the log.Print()
method would do this, as the docs say it is supposed to work the same way as fmt.Print(). Instead, I have found that each call to Print()
generates a new line with a timestamp. I can't find another method in the package that does what I am looking for.
Is there a way to do this?
Example program:
logFile, err := os.Create("myLog.log")
defer logFile.Close()
logger := log.New(logFile, "", log.LstdFlags)
logger.Println("Starting first round")
for i := 1; i < 4; i++ {
request, err := http.NewRequest("GET", "http://www.someapi.com/pgNum=" + i, nil)
response, err := client.Do(request)
logger.Print(".")
}
logger.Println("Starting second round")
for j := 1; j < 4; j++ {
request, err := http.NewRequest("GET", "http://www.someotherapi.com/pgNum=" + j, nil)
response, err := client.Do(request)
logger.Print(".")
}
logger.Println("Done")
Current output:
2019/09/18 13:27:49 Starting first round
2019/09/18 13:27:49 .
2019/09/18 13:27:50 .
2019/09/18 13:27:51 .
2019/09/18 13:27:52 Starting second round
2019/09/18 13:27:52 .
2019/09/18 13:27:53 .
2019/09/18 13:27:54 .
2019/09/18 13:27:55 Done
Desired output:
2019/09/18 13:27:49 Starting first round...
2019/09/18 13:27:52 Starting second round...
2019/09/18 13:27:55 Done
This cannot be achived. Logging package always adds end of line character. See log.go line 169.
The logs can be written by many routines, therefore you no have guarantee that you attach to the previously logged line.
Grzegorz Żur is correct in his answer. Since the log
package always appends a new line to the end of every string written to the log, there is no way to append more text to the end of that line when using that Writer, even if you bring in a different type of Writer to do the appends. I ended up having to roll my own.
Package:
package mylogger
import (
"os"
"bufio"
"time"
)
var logWriter *bufio.Writer
func InitLogFile() (*os.File, error) {
logFile, err := os.Create(logPath)
if err != nil {
return nil, err
}
logWriter = bufio.NewWriter(logFile)
return logFile, nil
}
func Print(msg string) {
if logWriter != nil {
logWriter.WriteString(msg)
logWriter.Flush()
}
}
func NewLine(msg string) {
if logWriter != nil {
logWriter.WriteString("
" + time.Now().Format("2006/02/01 15:04:05") + " " + msg)
logWriter.Flush()
}
}
Main program:
logFile, err := mylogger.InitLogFile()
defer logFile.Close()
mylogger.NewLine("Starting first round")
for i := 1; i < 4; i++ {
request, err := http.NewRequest("GET", "http://www.someapi.com/pgNum=" + i, nil)
response, err := client.Do(request)
mylogger.Print(".")
}
mylogger.NewLine("Starting second round")
for j := 1; j < 4; j++ {
request, err := http.NewRequest("GET", "http://www.someotherapi.com/pgNum=" + j, nil)
response, err := client.Do(request)
mylogger.Print(".")
}
mylogger.NewLine("Done")
This gets my desired result.
Important note: This only works because my program operates in a single thread and has a linear flow. If there were any concurrency where different subroutines could call into the logging function at any time, this would fall apart very quickly.
This is not my actual program, but an abbreviated gist of what it does. Error handling omitted for brevity. I'm not a Go expert, so I am open to comment.