我正在尝试使用Golang写入日志文件。
我尝试了几种方法,但都失败了。 这是我尝试过的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | 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 } |
日志文件被创建,但是没有打印或附加任何内容。 为什么?
- 如果您在Linux上部署程序,则只需将日志写入std输出,然后将输出通过管道传输至以下文件:./program 2>&1 | 发球logs.txt。 在其他系统中必须有其他方式。
1 2 3 4 5 6 7 8 | 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") |
根据Go文档,
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 .
编辑
在
- 在检查err为零之前,请勿延迟关闭!
- 它的无活性实际上有害于在所有情况下关闭。但是,并非所有类型都正确。
-
@Dustin
f 可能是nil ,这将导致恐慌。因此,建议在延迟呼叫之前检查err 。 -
@Allison小心地解释为什么
Open 不能与log.SetOutput 一起使用? - @nemo,因为os.Open()是O_RDONLY(只读游标),并且要写入日志,它必须是可写的。关于延迟,我假设如果err不是nil,则f可能仍然有值,这是不正确的?
- @AllisonA啊,对不起,错过了只读部分,现在很有意义:)。回覆。延迟,通常会在发生错误时返回nil值,因为通常不可能返回有效对象(由于该错误)。因此,当err为非nil时,您不能依赖于对返回值进行任何调用。
- 更安全的权限是0644甚至0664,以允许用户读/写,用户和组读/写,并且在两种情况下均不允许所有人写。
我更喜欢12要素应用推荐日志的简单性和灵活性。要附加到日志文件,可以使用外壳重定向。 Go中的默认记录器将写入stderr(2)。
1 | ./app 2>> logfile |
另请参阅:http://12factor.net/logs
- 当您想守护事物时,将不是一个好习惯,尤其是使用start-tsop-daemon
- @Shrey Systemd可以轻松处理日志记录以及启停功能。
- 尽管这不是一个好习惯,但这是我一直在Golang中寻找的日志类型。感谢您分享!
- Windows下有类似的东西吗?
通常,我会在屏幕上打印日志并写入文件。希望这对某人有帮助。
1 2 3 4 5 6 7 8 | 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") |
- 绝对有帮助!非常感谢
这对我有用
创建了一个名为logger.go的软件包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 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) } |
将包导入到您想登录的任何地方,例如main.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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())) } |
在全局
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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!!!") } |
- 不要以为这行得通...
- 嘿@CostaHuang,请留下详细的反馈。谢谢
- @CostaHuang,我只运行了我的代码段,它可以工作。
-
嗨@openwonk,我再次测试过,它在我的计算机上不起作用。我的版本是
go version go1.10.2 windowsamd64 ,您的版本是什么? -
@CostaHuang,我只是运行了与您相同的示例。该示例假定您已经设置了文件夹结构。有很简单的方法可以检查这一点,但是我的示例目标是显示写入日志文件相对简单。将您的代码更改为
outfile, _ = os.Create("my.log") ,它将按预期工作。 -
您的代码有效。我正在使用
outfile, _ = os.Create(".pathtomy.log") 。我以某种方式期望代码将创建pathto 文件夹和my.log 文件,但是显然它没有用。我建议您将答案修改为outfile, _ = os.Create(".my.log") 。这样,我们显然知道它在当前文件夹中创建了一个日志。
Go中的默认记录器将写入stderr(2)。
重定向到文件
1 2 3 4 5 6 7 8 9 10 | 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 */ } |
如果您在Linux机器上运行二进制文件,则可以使用Shell脚本。
覆盖到文件
1 | ./binaryapp > binaryapp.log |
附加到文件
1 | ./binaryapp >> binaryapp.log |
将stderr覆盖到文件中
1 | ./binaryapp &> binaryapp.error.log |
将stderr附加到文件中
1 | ./binaryapp &>> binalyapp.error.log |
使用shell脚本文件可以使其更加动态。
- 很高兴知道,我们如何覆盖stderr进行记录。
基于Allison和Deepak的答案,我开始使用logrus并非常喜欢它:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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) } |
我在主函数中有一个f.Close()延迟
我正在将日志写入每天生成的文件中(每天生成一个日志文件)。这种方法对我来说很好:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | 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()"方法,用于从文件的完整路径获取文件名。和" LogServer()"方法用于创建格式化的日志语句(包含:文件名,行号,日志级别,错误语句等。)