【问题标题】:How to write log to file如何将日志写入文件
【发布时间】:2013-11-26 18:19:22
【问题描述】:

我正在尝试使用 Go 写入日志文件。

我尝试了几种方法,但都失败了。这是我尝试过的:

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 中部署您的程序,您只需将日志写入标准输出,然后将输出通过管道传输到类似以下文件:./program 2>&1 | tee logs.txt。其他系统中肯定有其他方式。

标签: go logging


【解决方案1】:

os.Open() 过去的工作方式肯定有所不同,但这对我有用:

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 文档,os.Open() 不能用于 log.SetOutput,因为它会打开文件“用于读取:”

func Open

func Open(name string) (file *File, err error) Open 打开命名的 文件供阅读。如果成功,返回文件的方法可以是 用于阅读;关联文件描述符的模式为O_RDONLY。如果 有错误,类型为*PathError

编辑

defer f.Close() 移至if err != nil 之后检查

【讨论】:

  • 在检查 err 是否为零之前不要推迟关闭!
  • 在所有情况下关闭 iirc 并不是有害的活动。不过,并非所有类型都如此。
  • @Dustin f 可能是 nil,这会导致恐慌。因此,建议在推迟通话之前检查err
  • @Allison 想解释一下为什么Open 不能与log.SetOutput 一起使用?
  • 更安全的权限是 0644 甚至 0664 允许用户读/写,用户和组读/写,并且在这两种情况下都不允许所有人写。
【解决方案2】:

我更喜欢日志记录 12 因素应用推荐的简单性和灵活性。要附加到日志文件,您可以使用 shell 重定向。 Go 中的默认记录器写入标准错误 (2)。

./app 2>> logfile

另请参阅:http://12factor.net/logs

【讨论】:

  • 当你想守护事物时,这不是一个好习惯,尤其是使用 start-tsop-daemon
  • @Shrey Systemd 可以轻松处理日志记录以及启动-停止功能。
  • 尽管这不是一个好习惯,但这是我一直在 Golang 中寻找的日志记录类型。感谢分享!
  • windows下有类似的吗?
  • 就像$ cd /etc/systemd/system $ sudo vi app.service ExecStart=/bin/bash -c 'sudo go run main.go >> /home/ubuntu/go/src/html_menu_1/logfile' Me 不工作Ubuntu 18.04.3
【解决方案3】:

Go 中的默认记录器写入标准错误 (2)。 重定向到文件

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 */

}

【讨论】:

    【解决方案4】:

    这对我有用

    1. 创建了一个名为 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)
      }
      
      1. 将包导入到您要记录的任何位置,例如 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()))
        
        }
        

    【讨论】:

    • Afaik 这会为file 留下一个打开的文件描述符。 (错误检查后缺少defer file.Close())这是一个问题吗?
    【解决方案5】:

    在您的全局var 上声明,以便您的所有进程都可以在需要时访问。

    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!!!")
    }
    

    【讨论】:

    • 嘿@Co​​staHuang,请留下详细的反馈。谢谢
    • @CostaHuang,我刚刚运行了我的代码 sn-p 并且它可以工作。
    • 嗨@openwonk,我已经再次测试,但它在我的计算机上不起作用。我的版本是go version go1.10.2 windows/amd64,你的是什么?
    • @CostaHuang,我刚刚运行了与您设置相同的示例。该示例假定您已经设置了文件夹结构。有一些简单的方法可以检查这一点,但是我的示例目标是展示写入日志文件的相对简单程度。将您的代码更改为outfile, _ = os.Create("my.log"),它将按预期工作。
    • 您的代码有效。我正在使用outfile, _ = os.Create("./path/to/my.log")。不知何故,我期望代码将创建 path/to 文件夹和 my.log 文件,但显然它没有工作。我建议您将答案修改为outfile, _ = os.Create("./my.log")。这样我们就清楚地知道它正在当前文件夹中创建一个日志。
    【解决方案6】:

    我通常在屏幕上打印日志并写入文件。希望这对某人有所帮助。

    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")
    

    【讨论】:

      【解决方案7】:

      如果你在 linux 机器上运行二进制文件,你可以使用 shell 脚本。

      覆盖到文件中

      ./binaryapp > binaryapp.log
      

      添加到文件中

      ./binaryapp >> binaryapp.log
      

      将 stderr 覆盖到文件中

      ./binaryapp &> binaryapp.error.log
      

      将标准错误添加到文件中

      ./binaryapp &>> binalyapp.error.log
      

      使用 shell 脚本文件可以更加动态。

      【讨论】:

      • 很高兴知道,我们如何覆盖 stderr 以记录日志。
      【解决方案8】:

      我正在将日志写入文件,这些文件每天生成(每天生成一个日志文件)。这种方法对我来说很好用:

      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()”方法用于创建格式化的日志语句(包含:文件名、行号、日志级别、错误语句等...)

      【讨论】:

        【解决方案9】:

        基于 Allison 和 Deepak 的回答,我开始使用 logrus 并且非常喜欢它:

        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)
        }
        

        我在主函数中有一个 defer f.Close()

        【讨论】:

          【解决方案10】:

          为了帮助其他人,我创建了一个基本的日志函数来处理两种情况下的日志记录,如果你想输出到标准输出,然后打开调试,它直接做一个开关标志,这样你就可以选择你的输出。

          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()
              }
          }
          
          
          
          
          

          【讨论】:

            【解决方案11】:

            也许这会对你有所帮助(如果日志文件存在则使用它,如果它不存在则创建它):

            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)
                }
            }
            

            更多详情:https://su9.co/9BAE74B

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2012-04-06
              • 2023-03-31
              • 2020-01-02
              • 2014-12-13
              • 1970-01-01
              • 2012-01-16
              • 1970-01-01
              相关资源
              最近更新 更多