3 回答

TA貢獻1772條經驗 獲得超8個贊
在內部包下,我還有 6 個需要這個記錄器的包。他們每個人都依賴于它。(在日志包上),去處理它有什么好處嗎?
一個好的一般原則是尊重應用程序的選擇(是否記錄或不記錄)而不是設置策略。
讓目錄中的 Go pkgs
internal
成為支持包只會
error
在出現問題時返回不會記錄(控制臺或其他方式)
慣于
panic
讓您的應用程序(您
cmd
目錄中的包)決定在發生錯誤時的適當行為(日志/正常關閉/恢復到 100% 完整性)
這將通過僅在特定層進行日志記錄來簡化開發。注意:記得給應用程序提供足夠的上下文來確定操作
內部/流程/process.go
package process
import (
"errors"
)
var (
ErrNotFound = errors.New("Not Found")
ErrConnFail = errors.New("Connection Failed")
)
// function Process is a dummy function that returns error for certain arguments received
func Process(i int) error {
switch i {
case 6:
return ErrNotFound
case 7:
return ErrConnFail
default:
return nil
}
}
cmd/servi/main.go
package main
import (
"log"
p "../../internal/process"
)
func main() {
// sample: generic logging on any failure
err := p.Process(6)
if err != nil {
log.Println("FAIL", err)
}
// sample: this application decides how to handle error based on context
err = p.Process(7)
if err != nil {
switch err {
case p.ErrNotFound:
log.Println("DOESN'T EXIST. TRY ANOTHER")
case p.ErrConnFail:
log.Println("UNABLE TO CONNECT; WILL RETRY LATER")
}
}
}
如果我有更多需要傳遞的東西(比如記錄器),這里的方法/模式是什么
依賴注入始終是一個不錯的首選。僅當最簡單的實施方式不夠時才考慮其他方式。
下面的代碼使用依賴注入和一流的函數傳遞將template和打包在一起。logger
內部/日志/logger.go
package logger
import (
"github.com/sirupsen/logrus"
"github.com/x-cray/logrus-prefixed-formatter"
"os"
)
var Logger *logrus.Logger
func NewLogger() *logrus.Logger {
var level logrus.Level
level = LogLevel("info")
logger := &logrus.Logger{
Out: os.Stdout,
Level: level,
Formatter: &prefixed.TextFormatter{
DisableColors: true,
TimestampFormat: "2009-06-03 11:04:075",
},
}
Logger = logger
return Logger
}
func LogLevel(lvl string) logrus.Level {
switch lvl {
case "info":
return logrus.InfoLevel
case "error":
return logrus.ErrorLevel
default:
panic("Not supported")
}
}
內部/模板/template.go
package template
import (
"fmt"
"github.com/sirupsen/logrus"
)
type Template struct {
Name string
logger *logrus.Logger
}
// factory function New accepts a logging function and some data
func New(logger *logrus.Logger, data string) *Template {
return &Template{data, logger}
}
// dummy function DoSomething should do something and log using the given logger
func (t *Template) DoSomething() {
t.logger.Info(fmt.Sprintf("%s, %s", t.Name, "did something"))
}
命令/servi2/main.go
package main
import (
"../../internal/logs"
"../../internal/template"
)
func main() {
// wire our template and logger together
loggingFunc := logger.NewLogger()
t := template.New(loggingFunc, "Penguin Template")
// use the stuff we've set up
t.DoSomething()
}
希望這可以幫助。干杯,

TA貢獻1818條經驗 獲得超11個贊
有幾種可能性,每種可能性都有自己的權衡。
顯式傳遞依賴項
傳入具有所有依賴項的上下文
將結構用于方法的上下文
使用全局包并導入
它們在不同的情況下都有自己的位置,并且都有不同的權衡:
這非常清楚,但可能會變得非常混亂,并使您的函數因大量依賴而變得混亂。如果那是你的事,它使測試很容易模擬。
這是我最不喜歡的選項,因為它一開始很誘人,但很快就變成了一個混合了許多不相關問題的上帝對象。避免。
這在許多情況下非常有用,例如許多人以這種方式訪問數據庫。如果需要,也很容易模擬。這允許您設置/共享依賴項而無需在使用時更改代碼 - 基本上以比傳入顯式參數更簡潔的方式反轉控制。
這具有清晰性和正交性的優點。它將要求您為測試與生產添加單獨的設置,在使用之前將包初始化為正確的狀態。由于這個原因,有些人不喜歡它。
只要使用非常簡單的簽名,我更喜歡將包全局方法用于日志記錄之類的東西。我不傾向于測試日志輸出,或者經常更改記錄器??紤]你真正需要從日志中得到什么,以及是否最好只使用內置的日志包,也許可以嘗試其中一種方法,看看哪種方法適合你。
為簡潔起見,偽代碼示例:
// 1. Pass in dependencies explicitly
func MyFunc(log LoggerInterface, param, param)
// 2. Pass in a context with all dependencies
func MyFunc(c *ContextInterface, param, param)
// 3. Use a struct for context to methods
func (controller *MyController) MyFunc(param, param) {
controller.Logger.Printf("myfunc: doing foo:%s to bar:%s",foo, bar)
}
// 4. Use a package global and import
package log
var DefaultLogger PrintfLogger
func Printf(format, args) {DefaultLogger.Printf(format, args)}
// usage:
import "xxx/log"
log.Printf("myfunc: doing foo:%s to bar:%s",foo, bar)
我更喜歡這個選項 4 用于日志記錄和配置,甚至數據庫訪問,因為它是明確的、靈活的,并且允許輕松切換另一個記錄器——只需更改導入,無需更改接口。不過,最佳計算取決于具體情況——如果您正在編寫一個廣泛使用的庫,您可能更愿意允許在結構級別設置記錄器。
我通常需要在應用程序啟動時使用顯式設置進行設置,并且始終避免使用 init 函數,因為它們令人困惑且難以測試。

TA貢獻1799條經驗 獲得超6個贊
我總是明確地將 a 傳遞*logrus.Logger
給需要它的函數(或偶爾是對象)。這避免了奇怪的依賴循環,明確表明日志記錄是這個函數所做的事情的一部分,并且更容易在別處重用該函數或模塊。初始日志對象是在我的主函數中創建和配置的(可能在一些命令行參數處理之后)。
- 3 回答
- 0 關注
- 148 瀏覽
添加回答
舉報