[英]Goroutine thread safety of Go logging struct instantiation utility method
我正在使用新的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,
)
}
由於您的Logger設置僅涉及庫實例化,如果不存在則創建日志文件,打開日志文件且不涉及寫入,因此不會從不同的例程調用它,因為共享數據未獲取篡改。
旁注:明智的設計(假設Logger正在寫入同一文件)可以傳遞Logger的唯一實例化實例以進行日志記錄,這樣可以防止兩個go-routines同時調用您的setup函數。
第一個建議:使用-race
標志進行go build
和go test
。 它幾乎總是能夠告訴您您是否有比賽狀況。 盡管在這種情況下可能不會,因為您最終可能同時調用os.Create()
和os.OpenFile()
。
因此,第二個建議是盡可能避免使用“如果存在/匹配/具有權限,然后打開/刪除/任何內容”模式。
這種模式會導致TOCTTOU(檢查時間到使用時間)錯誤,該錯誤通常是安全性錯誤,至少會導致數據丟失。
為了避免這種情況,可以將檢查並包裝到同一個互斥鎖中,也可以使用原子操作,例如創建文件的OpenFile調用,或者如果文件已經存在則返回錯誤(盡管從技術上講,它已鎖定在OS內核中。例如基本的CPU操作如何鎖定在硬件總線中。)。
在您的情況下,我不確定您為什么要進行兩次Open呼叫,因為看起來只有一個可以勝任。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.