Go语言 记录器实现:配置包和自定义日志包之间的循环dep

jvlzgdj9  于 2023-04-09  发布在  Go
关注(0)|答案(1)|浏览(127)

这是我第一次使用日志记录器,经过几次搜索,我最终选择了zerolog,我创建了一个简单的web服务器,它调用了一个虚拟的API端点来说明我的担忧。
GitHub Repo示例:https://github.com/BigBoulard/logger

var (
    router *gin.Engine
)

func main() {
    // load the env variables
    conf.LoadEnv()

    // GinMode is set to "debug" in the /.env file
    gin.SetMode(conf.Env.GinMode)
    router = gin.Default()

    // creates a controller that uses an httpclient to call https://jsonplaceholder.typicode.com/todos
    controller := controller.NewController(
        httpclient.NewClient(),
    )
    router.GET("/todos", controller.GetTodos)

    // run the router on host and port specified in the /.env file
    err := router.Run(fmt.Sprintf("%s:%s", conf.Env.Host, conf.Env.Port))
    if err != nil {
        log.Fatal("application/main", err)
    }
}

正如你已经注意到的,如果我在本地工作,或者如果服务器在生产环境中,这个服务器使用的环境变量需要以不同的方式加载,对吗?所以为了管理这个,我创建了一个conf包,看起来像这样。

package conf

import (
    "log"
    "os"

    "github.com/joho/godotenv"
)

var Env = &env{}

type env struct {
    AppMode string
    GinMode string
    Host    string
    Port    string
}

var IsLoaded = false

func LoadEnv() {
    // the httpclient and the Gin server both need the env vars
    // but we don't want to load them twice
    if IsLoaded {
        return
    }

    // if not "prod"
    if Env.AppMode != "prod" {
        curDir, err := os.Getwd()
        if err != nil {
            log.Fatal(err, "conf", "LoadEnv", "error loading os.Getwd()")
        }
        // load the /.env file
        loadErr := godotenv.Load(curDir + "/.env")
        if loadErr != nil {
            log.Fatal(loadErr, "conf", "LoadEnv", "can't load env file from current directory: "+curDir)
        }
        Env.GinMode = "debug"
    } else {
        Env.GinMode = "release"
    }

    // load the env vars
    Env.AppMode = os.Getenv("APP_MODE")
    Env.Host = os.Getenv("HOST")
    Env.Port = os.Getenv("PORT")

    IsLoaded = true
}

长话短说,我已经创建了一个日志包,我会包含在每个微服务中。这个日志包在启动时使用环境变量来确定日志级别并决定是否激活某些功能。日志包看起来像这样:

package log

var l *logger = NewLogger()
const API = "test api"

type logger struct {
    logger zerolog.Logger // see https://github.com/rs/zerolog#leveled-logging
}

func NewLogger() *logger {
    loadEnvFile()
    var zlog zerolog.Logger
    if os.Getenv("APP_ENV") == "dev" {
           // setup for dev ...
        } else {
          // setup for prod ...
       }
return &logger{
  logger: zlog,
}

func Error(path string, err error) {
    l.logger.
        Error().
        Stack().
        Str("path", path).
        Msg(err.Error())
}

func Fatal(path string, err error) {
    l.logger.
        Fatal().
        Stack().
        Str("path", path).
        Msg(err.Error())
}

// PROBLEM: I need to duplicate loadEnvFile() from conf.load_env.go
// because conf uses log ... but conversely, log need conf cause it needs the env var
func loadEnvFile() {
    curDir, err := os.Getwd()
    if err != nil {
        log.Fatal(err, "test api", "App", "gw - conf - LoadEnv - os.Getwd()")
    }
    loadErr := godotenv.Load(curDir + "/.env")
    if loadErr != nil {
        log.Fatal(err, "test api", "conf - LoadEnv", "godotenv.Load("+curDir+"/.env\")")
    }
}

正如你所看到的,我不能使用conf包中的loadEnvFile(),因为它会导致循环依赖,所以我想可能有一个更好的方法来实现它。
附带问题:我可以创建一个日志记录器Web服务,它将从所有其他服务接收日志消息,并将其打印到文件中,但我不确定这是否是一个好的做法。我希望能够将日志文件推到像Kibana这样的东西中,以更好地了解所有这些是如何执行的,但也许它最好每个服务都打印到标准输入中,将日志集中到一个我不知道的专用服务中。
非常感谢。

n6lpvg4x

n6lpvg4x1#

我只是在main函数中委托了日志记录器示例化,就像我们对控制器和http客户端所做的那样。
你可以用最后一次提交来检查repo:https://github.com/BigBoulard/logger

func main() {
    // load the env variables
    conf.LoadEnv()

    // instanciate the logger
    log.NewLogger()
    log.Info("/main", "starting application") // test

    // GinMode is set to "debug" in the /.env file
    gin.SetMode(conf.Env.GinMode)
    router = gin.Default()

    // creates a controller that uses an httpclient to call https://jsonplaceholder.typicode.com/todos
    controller := controller.NewController(
        httpclient.NewClient(),
    )
    router.GET("/todos", controller.GetTodos)

    // run the router on host and port specified in the /.env file
    err := router.Run(fmt.Sprintf("%s:%s", conf.Env.Host, conf.Env.Port))
    if err != nil {
        log.Fatal("/main", err)
    }
}
package log;
[...]
var l *logger
[...]
func NewLogger() {
   var zlog zerolog.Logger
   [...]
   l = &logger{
        logger: zlog,
   }
}

// for testing
func Info(path string, msg string) {
    l.logger.
        Info().
        Str("path", path).
        Msg(msg)
}

相关问题