我正在使用一个新的 go 服务,我有一个SetupLogger实用程序函数,可以创建一个 go-kit 日志记录结构的新实例log.Logger。从处理单独 go 例程中的请求的代码中调用此方法是否安全?package utilsimport ( "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 loggerfunc 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, )}
2 回答

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

眼眸繁星
TA贡献1873条经验 获得超9个赞
由于您对 Logger 的设置只涉及库实例化,如果它不存在则创建一个日志文件,打开日志文件并且不涉及写入,因此从不同的 go-routines 调用它不会有问题,因为共享数据没有得到篡改。
旁注: 设计明智(假设 Logger 正在写入同一个文件)传递 Logger 的唯一实例化实例进行日志记录,这将防止两个 go-routines 同时调用您的 setup 函数。
- 2 回答
- 0 关注
- 119 浏览
添加回答
举报
0/150
提交
取消