go - 在 Golang 高效的多包最佳实践中使用记录器/配置

标签 go dependency-injection interface

我有以下项目结构:

myGithubProject/
    |---- cmd
      |----- command
        |----- hello.go
    |---- internal
        |----- template
           |----- template.go
        |----- log
          |----- logger.go
    main.go

logtemplate在同一层(在internal包下) 在 logger.go 中,我使用 logrus 作为带有一些配置的记录器 我想在 template< 中使用 logger.go 对象包。 我应该如何以干净的方式做到这一点?

目前我在 template.go 文件中将它与 import logger 一起使用,

internal 包下,我还有 6packages 需要这个 logger。他们每个人都依赖于它。 (在 log 包中),go 中是否有好的处理方式?

更新:

如果我有更多需要传递的东西(比如记录器),这里的方法/模式是什么?也许使用 dependency injection接口(interface) ?其他清洁方法...

我需要一些最佳实践完整示例 我如何在hello.go 文件和模板中使用logger。去

这是我的项目

cliProject/main.go

package main

import (
    "cliProject/cmd"
    "cliProject/internal/logs"
)

func main() {
    cmd.Execute()
    logs.Logger.Error("starting")
}


**cliProject/cmd/root.go**

package cmd

import (
    "fmt"
    "github.com/spf13/cobra"
)

var rootCmd = &cobra.Command{
    Use:   "cliProject",
    Short: "A brief description of your application",
}

func Execute() {
    if err := rootCmd.Execute(); err != nil {
        fmt.Println(err)
    }
}

**cliProject/cmd/serve.go**

package cmd

import (
    "cliProject/internal/logs"
    "fmt"

    "github.com/spf13/cobra"
)

// serveCmd represents the serve command
var serveCmd = &cobra.Command{
    Use:   "serve",
    Short: "A brief description of your command",
    Run: func(cmd *cobra.Command, args []string) {
        fmt.Println("serve called")
        startServe()
        stoppingServe()
    },
}

func init() {
    rootCmd.AddCommand(serveCmd)
    serveCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

func startServe() {
    logs.Logger.Error("starting from function serve")
}

func stoppingServe() {
    logs.Logger.Error("stoping from function serve")
}


**cliProject/cmd/start.go**

package cmd

import (
    "cliProject/internal/logs"
    "github.com/spf13/cobra"
)

// startCmd represents the start command
var startCmd = &cobra.Command{
    Use:   "start",
    Short: "Start command",
    Run: func(cmd *cobra.Command, args []string) {
        // Print the logs via logger from internal
        logs.Logger.Println("start called inline")
        // example of many functions which should use the logs...
        start()
        stopping()

    },
}

func init() {
    logs.NewLogger()
    rootCmd.AddCommand(startCmd)
    startCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

func start() {
    logs.Logger.Error("starting from function start")
}

func stopping() {
    logs.Logger.Error("stoping from function start")
}


**cliProject/internal/logs/logger.go**

package logs

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")
    }
}

是这样的

enter image description here

最佳答案

And under internal package I’ve 6 more packages which needs this logger. and each of them is depend on it. (On the log package), is there a nice in go to handle it ?

一个好的通用原则是尊重应用程序的选择(是否记录或不记录)而不是设置策略。

  1. internal 目录中的 Go pkgs 成为支持包

    • 只会在出现问题时返回error
    • 不会记录(控制台或其他方式)
    • 不会 panic
  2. 让您的应用程序(cmd 目录中的程序包)决定发生错误时的适当行为(记录/正常关闭/恢复到 100% 完整性)

这将通过仅在特定层进行日志记录来简化开发。注意:记得给应用程序提供足够的上下文来确定操作

internal/process/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")
        }
    }
}

in case I've more things that I need to pass (like logger) what will be the approach/ pattern here

依赖注入(inject)始终是一个不错的首选。仅当最简单的实现方式不够时才考虑其他方式。

下面的代码使用依赖注入(inject)和一流函数传递将 templatelogger 包“连接”在一起。

internal/logs/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")
    }
}

internal/template/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"))
}

cmd/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()
}

希望这对您有所帮助。干杯,

关于go - 在 Golang 高效的多包最佳实践中使用记录器/配置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52899535/

相关文章:

c# - 无法从未打开的数据库创建命令

c# - 将类方法名称作为 lambda 表达式传递

mongodb - 如何在使用 go 的 mongodb 查询中使用 $or 和 $lookup?

Java MVVM 和 WPF 替代方案

javascript - ES6 中的导出类不起作用

php - Laravel 助手接口(interface)

c# - 如何从 WCF 服务返回 LINQ to SQL 表实体?

go - 如何在单个程序中使用多个 sync.WaitGroup

go - 如何在 Golang 回调函数中传递变量?

docker - 使用Dockerfile执行两个命令