关于日志记录:Go / Golang将日志写入文件

Go/Golang write log to file

我正在尝试使用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
}

日志文件被创建,但是没有打印或附加任何内容。 为什么?


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()移至


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

1
./app 2>> logfile

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


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

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


    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脚本文件可以使其更加动态。


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