GoGogoroutineGo
Go

日志包的选择

Goprintpanicfatallogruszapglog

log标准库

Gologlogger

logrus

logrusJSONJSONGoJSON
logrusWithFieldsInfo()Warn()Error()logrusJSON
package main
import (
  log "github.com/sirupsen/logrus"
)

func main() {
   log.SetFormatter(&log.JSONFormatter{})

   standardFields := log.Fields{
     "hostname": "staging-1",
     "appname":  "foo-app",
     "session":  "1ce3f6v",
   }
   requestLogger := log.withFields(standardFields)
   requestLogger.WithFields(log.Fields{"string": "foo", "int": 1, "float": 1.1}).Info("My first ssl event from Golang")

}
JSON
{"appname":"foo-app","float":1.1,"hostname":"staging-1","int":1,"level":"info","msg":"My first ssl event from Golang","session":"1ce3f6v","string":"foo","time":"2019-03-06T13:37:12-05:00"}

glog

glogifV()GoInfo()Warning()Error()Fatal()03
if err != nil && glog.V(2){
    glog.Error(err)
  }

日志库的选择

loglogrusgloglogrusDockerPrometheuslogrus

logrus的使用介绍

logrusGithubstarforklogrusDockerPrometheuslogrus
logrusGodebuginfowarnerrorfatalpanicGologrusHookhooklogstashelasticsearchmqlogrusJSONFormatterTextFormatterFieldlogrusFieldEntrylogrus.WithFields*EntryEntrytime

基本用法

logrusGologrus
package main

import (
    "os"
    log "github.com/sirupsen/logrus"
)

func init() {
    // 设置日志格式为json格式
    log.SetFormatter(&log.JSONFormatter{})

    // 设置将日志输出到指定文件(默认的输出为stderr,标准错误)
    // 日志消息输出可以是任意的io.writer类型
    logFile := ...
    file, _ := os.OpenFile(logFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
    log.SetOutput(file)

    // 设置只记录日志级别为warn及其以上的日志
    log.SetLevel(log.WarnLevel)
}

func main() {
    log.WithFields(log.Fields{
        "animal": "walrus",
        "size":   10,
    }).Info("A group of walrus emerges from the ocean")

    log.WithFields(log.Fields{
        "omg":    true,
        "number": 122,
    }).Warn("The group's number increased tremendously!")

    log.WithFields(log.Fields{
        "omg":    true,
        "number": 100,
    }).Fatal("The ice breaks!")
}

自定义Logger

logLogger
package main

import (
    "github.com/sirupsen/logrus"
    "os"
)

// logrus提供了New()函数来创建一个logrus的实例.
// 项目中,可以创建任意数量的logrus实例.
var log = logrus.New()

func main() {
    // 为当前logrus实例设置消息的输出,同样地,
    // 可以设置logrus实例的输出到任意io.writer
    log.Out = os.Stdout

    // 为当前logrus实例设置消息输出格式为json格式.
    // 同样地,也可以单独为某个logrus实例设置日志级别和hook,这里不详细叙述.
    log.Formatter = &logrus.JSONFormatter{}

    log.WithFields(logrus.Fields{
        "animal": "walrus",
        "size":   10,
    }).Info("A group of walrus emerges from the ocean")
}

Fields

logrusFields
log.Fatalf("Failed to send event %s to topic %s with key %d", event, topic, key)
logruslogrus
log.WithFields(log.Fields{
  "event": event,
  "topic": topic,
  "key": key,
}).Fatal("Failed to send event")
WithFieldsWithFields

Default Fields

HTTPrequest_iduser_ip。
log.WithFields(log.Fields{“request_id”: request_id, “user_ip”: user_ip})
logrus.EntryFieldslogrus.EntryLogger
requestLogger := log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})
requestLogger.Info("something happened on that request") # will log request_id and user_ip
requestLogger.Warn("something not great happened")

Hook接口

logrusHOOKlogrushooklogrus
logrushooklogrus.Entry
// logrus在记录Levels()返回的日志级别的消息时会触发HOOK,
// 按照Fire方法定义的内容修改logrus.Entry.
type Hook interface {
    Levels() []Level
    Fire(*Entry) error
}
hookDefaultFieldHookappName=”myAppName”
type DefaultFieldHook struct {
}

func (hook *DefaultFieldHook) Fire(entry *log.Entry) error {
    entry.Data["appName"] = "MyAppName"
    return nil
}

func (hook *DefaultFieldHook) Levels() []log.Level {
    return log.AllLevels
}
hooklog.AddHook(hook)hookHooksentry
logrussysloghookGithubhooksentryhook

sentry-hook

SentrySentryGitLabSentryDSNSentry
import (
  "github.com/sirupsen/logrus"
  "github.com/evalphobia/logrus_sentry"
)

func main() {
  log       := logrus.New()
  hook, err := logrus_sentry.NewSentryHook(YOUR_DSN, []logrus.Level{
    logrus.PanicLevel,
    logrus.FatalLevel,
    logrus.ErrorLevel,
  })

  if err == nil {
    log.Hooks.Add(hook)
  }
}

logrus 是线程安全的

Loggermutexmutexlogger.SetNoLock()

不需要锁的情况包括:

hookhooklogger.Outlogger.Outlogger.OutAppend

日志写入和存储的一些建议

Go
goroutineHTTP

避免在goroutine中使用日志记录器

goroutineio.Writergoroutinegoroutine

总是将日志写入文件

ElasticsearchLogstash

使用日志处理平台集中处理日志

ELK

使用唯一ID跨微服务跟踪Go日志

HTTP

参考链接: