我正在尝试使用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。 在其他系统中必须有其他方式。

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

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文档,os.Open()不适用于log.SetOutput,因为它打开了文件"供读取:"

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 mode O_RDONLY. If
there is an error, it will be of type *PathError.

编辑

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

  • 在检查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()))

    }

  • 在全局var中声明顶部,以便在需要时可以访问所有进程。

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