Go 日志结构实例化实用方法的 Goroutine 线程安全

我正在使用一个新的 go 服务,我有一个SetupLogger实用程序函数,可以创建一个 go-kit 日志记录结构的新实例log.Logger。


从处理单独 go 例程中的请求的代码中调用此方法是否安全?


package utils


import (

    "fmt"

    "github.com/go-kit/kit/log"

    "io"

    "os"

    "path/filepath"

)


// If the environment-specified directory for writing log files exists, open the existing log file

// if it already exists or create a log file if no log file exists.

// If the environment-specified directory for writing log files does not exist, configure the logger

// to log to process stdout.

// Returns an instance of go-kit logger

func SetupLogger() log.Logger {

    var logWriter io.Writer

    var err error


    LOG_FILE_DIR := os.Getenv("CRAFT_API_LOG_FILE_DIR")

    LOG_FILE_NAME := os.Getenv("CRAFT_API_LOG_FILE_NAME")


    fullLogFilePath := filepath.Join(

        LOG_FILE_DIR,

        LOG_FILE_NAME,

    )


    if dirExists, _ := Exists(&ExistsOsCheckerStruct{}, LOG_FILE_DIR); dirExists {

        if logFileExists, _ := Exists(&ExistsOsCheckerStruct{}, fullLogFilePath); !logFileExists {

            os.Create(fullLogFilePath)

        }

        logWriter, err = os.OpenFile(fullLogFilePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)

        if err != nil {

            fmt.Println("Could not open log file. ", err)

        }

    } else {

        logWriter = os.Stdout

    }


    return log.NewContext(log.NewJSONLogger(logWriter)).With(

        "timestamp", log.DefaultTimestampUTC,

        "caller", log.DefaultCaller,

    )

}


哆啦的时光机
浏览 120回答 2
2回答

人到中年有点甜

第一个建议:使用-race标志 togo build和go test。它几乎总能告诉您您是否有竞争状况。尽管在这种情况下可能不会,因为您最终可能会同时调用您的os.Create()和您的os.OpenFile()。因此,第二个建议是尽可能避免“如果存在/匹配/具有权限,则打开/删除/随便什么”模式。这种模式会导致 TOCTTOU(检查时间到使用时间)错误,这通常是一个安全错误,至少会导致数据丢失。为了避免它将检查和使用包装到同一个互斥锁中,或者使用原子操作,例如创建文件的 OpenFile 调用,或者如果文件已经存在则返回错误(尽管是技术性的,它被锁定在操作系统内核中。只是就像原子 CPU 操作如何锁定在硬件总线中一样。)。在您的情况下,我不太确定为什么您有两个 Open 调用,因为看起来只有一个可以完成这项工作。

眼眸繁星

由于您对 Logger 的设置只涉及库实例化,如果它不存在则创建一个日志文件,打开日志文件并且不涉及写入,因此从不同的 go-routines 调用它不会有问题,因为共享数据没有得到篡改。旁注: 设计明智(假设 Logger 正在写入同一个文件)传递 Logger 的唯一实例化实例进行日志记录,这将防止两个 go-routines 同时调用您的 setup 函数。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go