英文:
Logging globally (across packages)
问题
我现在已经花了很多时间搜索和阅读关于这个主题的帖子,但还没有完全得到我的问题的答案 - 或者也许我只是需要对现有答案有更多的澄清。
我读过这个帖子,它的标题和我的一样,但它是关于在go协程而不是包之间进行日志记录。
我试图解决的问题是在主应用程序和它使用的任何包之间进行日志记录。我需要一个可以将日志记录到多个位置的记录器(可以使用io.MultiWriter实现),并且可以执行log.Error()和log.Debug()等操作。
我知道有一些包可以做到这一点,也知道如何自己实现这些功能。
我无法理解的是如何在我的包中正确使用它。
一种方法当然是在主函数中创建记录器,然后将其传递给所有需要记录日志的函数。但这似乎很笨拙。
我理想的解决方案是拥有一个类似于log包中内置的全局记录器,但具有上述的附加功能。
我主要希望在包内进行可选的调试日志记录,这样我可以在生产版本中打开它。
这应该如何正确实现?
英文:
I have spent a lot of time now searching and reading posts on the subject but have note yet managed to fully get my question answered - or perhaps I just need more clarification on existing answers.
I have read this post which has the same title as mine, but it's about logging across go routines rather than packages.
What I'm trying to solve is logging across the main app and any packages it uses. I need a logger that can log to multiple locations (which can be done with io.MultiWriter) and can do things like log.Error() and log.Debug()
I know there are packages that do this and I know how to implement those things myself.
What I can't get my head around is how to properly use it with my packages.
One way is of course to create the logger in main and then pass it around to all functions that need logging. But it seems awkward.
My ideal solution would be to have a logger like the built in global logger from the log package, but with the added functionality as above.
I mostly want this for optional debug logging within packages, so I can turn this on in a production version if needed.
What is the proper way to do this?
答案1
得分: 8
正确的方式是你认为是理想方式的方式。只需创建一个包(最好遵循Go的约定https://golang.org/doc/code.html),并使您的日志全局可用:
package mylog
// 定义您的自定义日志记录器类型。
type logger struct { /* 任何你想要的 */ }
// 可选择将其作为接口。
type Logger interface { /* 你的函数 */ }
// 然后全局化。
var defaultLogger *Logger
func init(){
defaultLogger = new(logger)
}
func Debug(params ...string){
// 做些有趣的事情。
}
// ...
此外,我建议在文档中描述您的项目使用了该日志记录功能。
英文:
The proper way is what you think is the ideal way. Just create a package (preferably following Go's conventions https://golang.org/doc/code.html) and make your Log global:
package mylog
// Define your custom logger type.
type logger struct { /* Whatever you want */ }
// Optionally make it a interface.
type Logger interface { /* Your functions */ }
// And just go global.
var defaultLogger *Logger
func init(){
defaultLogger = new(logger)
}
func Debug(params ...string){
// Have some fun.
}
// ...
Also I would recommend to describe in documentation that your project uses that logging feature.
答案2
得分: 7
我正在为您翻译以下内容:
我正在发布对我有效的解决方案!我刚刚创建了自己的包,并使用了 init 函数。
package logging
import (
"io"
logging "log"
"os"
"github.com/Sirupsen/logrus"
)
var (
log *logrus.Logger
)
func init() {
f, err := os.OpenFile("logs/application.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
logging.Fatalf("error opening file: %v", err)
}
log = logrus.New()
//log.Formatter = &logrus.JSONFormatter{}
log.SetReportCaller(true)
mw := io.MultiWriter(os.Stdout, f)
log.SetOutput(mw)
}
// Info ...
func Info(format string, v ...interface{}) {
log.Infof(format, v...)
}
// Warn ...
func Warn(format string, v ...interface{}) {
log.Warnf(format, v...)
}
// Error ...
func Error(format string, v ...interface{}) {
log.Errorf(format, v...)
}
var (
// ConfigError ...
ConfigError = "%v type=config.error"
// HTTPError ...
HTTPError = "%v type=http.error"
// HTTPWarn ...
HTTPWarn = "%v type=http.warn"
// HTTPInfo ...
HTTPInfo = "%v type=http.info"
)
在任何包中,只需导入我的包并执行 (Info、Warn、Error) 函数
package main
import (
log "logging"
)
func main() {
log.Error(log.ConfigError, "Testing the error")
}
日志条目将在屏幕上显示并保存到文件中。
英文:
I'm posting the solution worked for me!. I just created my own package, and I used the init function.
package logging
import (
"io"
logging "log"
"os"
"github.com/Sirupsen/logrus"
)
var (
log *logrus.Logger
)
func init() {
f, err := os.OpenFile("logs/application.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
logging.Fatalf("error opening file: %v", err)
}
log = logrus.New()
//log.Formatter = &logrus.JSONFormatter{}
log.SetReportCaller(true)
mw := io.MultiWriter(os.Stdout, f)
log.SetOutput(mw)
}
// Info ...
func Info(format string, v ...interface{}) {
log.Infof(format, v...)
}
// Warn ...
func Warn(format string, v ...interface{}) {
log.Warnf(format, v...)
}
// Error ...
func Error(format string, v ...interface{}) {
log.Errorf(format, v...)
}
var (
// ConfigError ...
ConfigError = "%v type=config.error"
// HTTPError ...
HTTPError = "%v type=http.error"
// HTTPWarn ...
HTTPWarn = "%v type=http.warn"
// HTTPInfo ...
HTTPInfo = "%v type=http.info"
)
And on any package, just import my package and I execute the (Info, Warn, Error) function
package main
import (
log "logging"
)
func main() {
log.Error(log.ConfigError, "Testing the error")
}
The log entry will be saved rendered on the screen and it will be saved on a file.
答案3
得分: 6
@CedmundoMartinez的答案让我想到了一个(非常简单且相当明显,现在我可以使用后视镜)的答案。
我在这里发布我的答案,供对类似解决方案感兴趣的人参考。
我所做的是复制标准日志包(src/log/log.go)并进行扩展。获取一个全局记录器,它已经做了标准记录器所做的一切,并且还可以做其他任何你想要的事情,简直不能再容易了!在这种情况下,支持分级日志记录。
我只需要进行以下修改:
type Logger struct {
mu sync.Mutex // 确保原子写入;保护以下字段
prefix string // 在每行开头写入的前缀
flag int // 属性
out io.Writer // 输出目标
buf []byte // 用于累积要写入的文本
level int // DEBUG、ERROR、INFO之一
}
只添加了最后一行。日志包设置了一个全局变量std,可以从包中的任何函数中使用该结构字段。
接下来,我添加了不同日志级别的常量:
const (
DEBUG = 1 << iota
INFO
ERROR
)
然后,我添加了我的函数:
(注意:ct是一个包https://github.com/seago/go-colortext,允许在Windows上对控制台文本进行着色。因此,这里的所有错误都以红色打印)
func Error(v ...interface{}) {
if std.level <= ERROR {
ct.ChangeColor(ct.Red, true, ct.None, false)
s := fmt.Sprintf("ERROR: %v", v...)
std.Output(2, s)
ct.ResetColor()
}
}
func Info(format string, v ...interface{}) {
if std.level <= INFO {
s := fmt.Sprintf("INFO: "+format, v...)
std.Output(2, s)
}
}
func Debug(v ...interface{}) {
if std.level <= DEBUG {
s := fmt.Sprintf("DEBUG: %v", v...)
std.Output(2, s)
}
}
func SetLogLevel(lvl int) {
std.level = lvl
}
就是这样!现在我可以通过导入修改后的包而不是标准日志包来使用它,并进行日志记录:
import (
"errors"
"tryme/log"
)
func main() {
log.SetLogLevel(log.INFO)
log.Info("This is a test Info")
err := errors.New("This is a test error!!!")
log.Error(err)
log.Debug("Testing debugging") // 在log.INFO下不会被打印
}
当然,这只是一个演示,可以很容易地通过添加更多的日志级别、输出格式等来进一步扩展。
你可以使用标准日志包提供的所有函数,比如使用SetOutput将日志写入文件,或者使用MultiWriter将日志写入文件和控制台等。
英文:
@CedmundoMartinez answer rattled my head enough to come up with the (extremely simple and rather obvious, now that I can use my rear view mirrors) answer.
I'm posting my answer here for anyone who is interested in a similar solution.
What I did was to make a copy of the standard log package (src/log/log.go) and extend it. It couldn't be easier to get a global logger that already does everything the standard logger does plus anything else you want it to do! In this case support leveled logging.
The only modifications I had to make:
type Logger struct {
mu sync.Mutex // ensures atomic writes; protects the following fields
prefix string // prefix to write at beginning of each line
flag int // properties
out io.Writer // destination for output
buf []byte // for accumulating text to write
level int // One of DEBUG, ERROR, INFO
}
Only the last line was added. The log package sets a global variable std which can then be used to access the struct fields from any function in the package.
Next I added constants for the different log levels:
const (
DEBUG = 1 << iota
INFO
ERROR
)
Next I added my functions:
(Note: ct is package https://github.com/seago/go-colortext which allows to color console text on windows. So the errors here all print in red)
func Error(v ...interface{}) {
if std.level <= ERROR {
ct.ChangeColor(ct.Red, true, ct.None, false)
s := fmt.Sprintf("ERROR: %v", v...)
std.Output(2, s)
ct.ResetColor()
}
}
func Info(format string, v ...interface{}) {
if std.level <= INFO {
s := fmt.Sprintf("INFO: "+format, v...)
std.Output(2, s)
}
}
func Debug(v ...interface{}) {
if std.level <= DEBUG {
s := fmt.Sprintf("DEBUG: %v", v...)
std.Output(2, s)
}
}
func SetLogLevel(lvl int) {
std.level = lvl
}
And that's it! With that I can now use it by simply importing the modified package instead of the standard log package and log away:
import (
"errors"
"tryme/log"
)
func main() {
log.SetLogLevel(log.INFO)
log.Info("This is a test Info")
err := errors.New("This is a test error!!!")
log.Error(err)
log.Debug("Testing debugging") // won't be printed with log.INFO
}
This is of course just a demo and can easily be extended further with more log levels, output formatting etc.
You can use all the functions the standard log package provides like SetOutput to write to a file or a MultiWriter to write to file and console etc.
答案4
得分: 1
要添加的是,如果您想在多个Go应用程序上进行日志记录,可以使用RPC并创建一个日志记录服务。这将是一个独立的应用程序,为其他应用程序提供服务。Golang有自己的包可以实现这一功能。
英文:
To add, if you want to do logging over multiple Go applications, you can use RPC and create a logging service. This would be an application sits on its own, providing services to other applications. Golang has its own package for it
答案5
得分: 0
在Logger周围创建一个包装器,并将logger变量公开给所有其他包。
package logger
import (
"io"
logging "log"
"os"
"github.com/sirupsen/logrus"
)
var (
Log *logrus.Logger // 与所有包共享
)
func init() {
// 文件需要事先存在
f, err := os.OpenFile("logs/application.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
// 在配置Logrus之前使用go的logger
logging.Fatalf("打开文件时出错:%v", err)
}
// 配置Logrus
Log = logrus.New()
Log.Formatter = &logrus.JSONFormatter{}
Log.SetReportCaller(true)
mw := io.MultiWriter(os.Stdout, f)
Log.SetOutput(mw)
}
// 使用预配置的Logrus,使用公共变量
package main
import (
"logger" // 导入我们的Logrus包装器
)
func main() {
logger.Log.Info("Info") // 使用包装器和公共配置的Logrus
logger.Log.Error("Error")
}
英文:
Create a wrapper around the Logger and make the logger variable pubic to all other packages.
package logger
import (
"io"
logging "log"
"os"
"github.com/sirupsen/logrus"
)
var (
Log *logrus.Logger // share will all packages
)
func init() {
// the file needs to exist prior
f, err := os.OpenFile("logs/application.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
// use go's logger, while we configure Logrus
logging.Fatalf("error opening file: %v", err)
}
// configure Logrus
Log = logrus.New()
Log.Formatter = &logrus.JSONFormatter{}
Log.SetReportCaller(true)
mw := io.MultiWriter(os.Stdout, f)
Log.SetOutput(mw)
}
// use the pre-configured Logrus, using the public variable
package main
import (
"logger" // import our Logrus wrapper
)
func main() {
logger.Log.Info("Info") // use the wrapper and public configured Logrus
logger.Log.Error("Error")
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论