Go:为将日志记录到MongoDB数据库创建io.Writer接口

huangapple go评论75阅读模式
英文:

Go: Create io.Writer inteface for logging to mongodb database

问题

使用Go(golang):

有没有一种方法可以创建一个将日志输出到数据库的记录器?

或者更准确地说,我可以实现某种io.Writer接口,将其作为log.New()的第一个参数传递吗?

例如:(dbLogger将接收日志的输出并将其写入数据库)

logger := log.New(dbLogger, "dbLog: ", log.Lshortfile) logger.Print("此消息将存储在数据库中")

我认为我应该只需创建自己的数据库日志函数,但我很好奇是否已经有一种使用语言中现有工具来实现这一点的方法。

为了提供一些背景,我正在使用mgo.v2来处理我的mongodb数据库,但我在那里没有看到除了GridFS之外的任何io.Writer接口,而我认为GridFS解决了不同的问题。

我还在努力理解这门语言,所以我可能在上面使用了一些术语不正确。非常欢迎任何更正。

英文:

Using go (golang):

Is there a way to create a logger that outputs to a database?

Or more precisely, can I implement some kind of io.Writer interface that I can pass as the first argument to log.New()?

EG: (dbLogger would receive the output of the log and write it to the database)

logger := log.New(dbLogger, "dbLog: ", log.Lshortfile)
logger.Print("This message will be stored in the database")

I would assume that I should just create my own database logging function, but I was curious to see if there is already a way of doing this using the existing tools in the language.

For some context, I'm using mgo.v2 to handle my mongodb database, but I don't see any io.Writer interfaces there other than in GridFS, which I think solves a different problem.

I'm also still getting my head around the language, so I may have used some terms above incorrecly. Any corrections are very welcome.

答案1

得分: 10

这是可以轻松实现的,因为log.Logger类型保证每条日志消息都通过单个Writer.Write()调用传递到目标io.Writer

> 每个日志操作都会调用Writer的Write方法一次。Logger可以同时从多个goroutine中使用;它保证对Writer的访问进行序列化。

因此,你只需要创建一个实现了io.Writer接口的类型,其Write()方法创建一个包含字节切片内容的新文档,并将其保存在MongoDB中。

下面是一个简单的实现示例:

type MongoWriter struct {
    sess *mgo.Session
}

func (mw *MongoWriter) Write(p []byte) (n int, err error) {
    c := mw.sess.DB("").C("log")
    err = c.Insert(bson.M{
        "created": time.Now(),
        "msg":     string(p),
    })
    if err != nil {
        return
    }
    return len(p), nil
}

使用示例:

sess := ... // 获取一个MongoDB会话

mw := &MongoWriter{sess}
log.SetOutput(mw)

// 现在log包的默认Logger将使用我们的MongoWriter。
// 生成一个将插入MongoDB的日志消息:
log.Println("我是第一条日志消息。")
log.Println("我是多行的,\n但仍然是一条日志消息。")

如果你使用另一个log.Logger实例,请将MongoWriter设置为该实例,例如:

mylogger := log.New(mw, "", 0)
mylogger.Println("自定义日志记录器")

请注意,日志消息以换行符结尾,因为log.Logger会在日志消息本身不以换行符结尾时追加它。如果你不想记录结尾的换行符,你可以简单地去掉它,例如:

func (mw *MongoWriter) Write(p []byte) (n int, err error) {
    origLen := len(p)
    if len(p) > 0 && p[len(p)-1] == '\n' {
        p = p[:len(p)-1] // 去掉结尾的换行符
    }

    c := mw.sess.DB("").C("log")

    // ... 其余部分相同

    return origLen, nil // 必须返回原始长度(我们重新切片了p)
}
英文:

This is easily doable, because the log.Logger type guarantees that each log message is delivered to the destination io.Writer with a single Writer.Write() call:

> Each logging operation makes a single call to the Writer's Write method. A Logger can be used simultaneously from multiple goroutines; it guarantees to serialize access to the Writer.

So basically you just need to create a type which implements io.Writer, and whose Write() method creates a new document with the contents of the byte slice, and saves it in the MongoDB.

Here's a simple implementation which does that:

type MongoWriter struct {
	sess *mgo.Session
}

func (mw *MongoWriter) Write(p []byte) (n int, err error) {
	c := mw.sess.DB("").C("log")
	err = c.Insert(bson.M{
		"created": time.Now(),
		"msg":     string(p),
	})
	if err != nil {
		return
	}
	return len(p), nil
}

Using it:

sess := ... // Get a MongoDB session

mw := &MongoWriter{sess}
log.SetOutput(mw)

// Now the default Logger of the log package uses our MongoWriter.
// Generate a log message that will be inserted into MongoDB:
log.Println("I'm the first log message.")
log.Println("I'm multi-line,\nbut will still be in a single log message.")

Obviously if you're using another log.Logger instance, set the MongoWriter to that, e.g.:

mylogger := log.New(mw, "", 0)
mylogger.Println("Custom logger")

Note that the log messages end with newline as log.Logger appends it even if the log message itself does not end with newline. If you don't want to log the ending newline, you may simply cut it, e.g.:

func (mw *MongoWriter) Write(p []byte) (n int, err error) {
    origLen := len(p)
	if len(p) > 0 && p[len(p)-1] == '\n' {
		p = p[:len(p)-1] // Cut terminating newline
	}

	c := mw.sess.DB("").C("log")

    // ... the rest is the same

    return origLen, nil // Must return original length (we resliced p)
}

huangapple
  • 本文由 发表于 2016年11月3日 16:05:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/40396499.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定