golang日志系统

什么不记录

通常,您不应记录任何形式的敏感业务数据或个人身份信息。这包括但不限于:

  • 名字
  • IP地址
  • 银行卡号码

从工程的角度来看,这些限制会使日志的用处不大,但是它们使您的应用程序更安全。在许多情况下,GDPR和HIPAA等法规可能会禁止记录个人数据。

日志包介绍

Go标准库具有一个内置log软件包,可提供大多数基本日志记录功能。尽管它没有日志级别(例如调试,警告或错误),但它仍然提供了设置基本日志策略所需的一切。

这是最基本的日志记录示例:

package main

import "log"

func main() {
    log.Println("Hello world!")
}

上面的代码输出文本“ Hello world!”。标准错误,但其中还包括日期和时间,这对于按日期过滤日志消息非常方便。

2019/12/09 17:21:53 Hello world!

默认情况下,log程序包将打印到标准错误(stderr)输出流,但是您可以使其写入本地文件或支持该io.Writer接口的任何目标位置。它还在没有任何其他配置的情况下向日志消息添加时间戳。

记录到文件

如果需要将日志消息存储在文件中,可以通过创建新文件或打开现有文件并将其设置为日志输出来进行。这是一个例子:

package main

import (
    "log"
    "os"
)

func main() {
    // If the file doesn't exist, create it or append to the file
    file, err := os.OpenFile("logs.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
    if err != nil {
        log.Fatal(err)
    }

    log.SetOutput(file)

    log.Println("Hello world!")
}

当我们运行代码时,将以下内容写入 logs.txt.

2019/12/09 17:22:47 Hello world!

如前所述,您基本上可以将日志输出到实现该io.Writer接口的任何目标,因此在决定在应用程序中的日志记录位置时,您具有很大的灵活性。

创建自定义记录器

尽管该log程序包实现了一个logger写入标准错误的预定义内容,但是我们可以使用该log.New()方法创建自定义记录器类型。

创建新的记录器时,您需要将三个参数传递给log.New()

  • out:实现io.Writer接口的任何类型,即将日志数据写入接口的类型
  • prefix:附加到每个日志行开头的字符串
  • flag:一组常量,使我们能够定义在记录器生成的每个日志条目中包括哪些记录属性(在下一节中将对此进行详细说明)

我们可以利用此功能来创建自定义记录器。下面是一个例子,它实现InfoWarningError记录器:

package main

import (
    "log"
    "os"
)

var (
    WarningLogger *log.Logger
    InfoLogger    *log.Logger
    ErrorLogger   *log.Logger
)

func init() {
    file, err := os.OpenFile("logs.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
    if err != nil {
        log.Fatal(err)
    }

    InfoLogger = log.New(file, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile)
    WarningLogger = log.New(file, "WARNING: ", log.Ldate|log.Ltime|log.Lshortfile)
    ErrorLogger = log.New(file, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile)
}

func main() {
    InfoLogger.Println("Starting the application...")
    InfoLogger.Println("Something noteworthy happened")
    WarningLogger.Println("There is something you should know about")
    ErrorLogger.Println("Something went wrong")
}

在函数logs.txt顶部创建或打开文件后init,我们然后通过提供输出目标,前缀字符串和日志标志来初始化三个定义的记录器。

在该main函数中,通过调用该Println函数来利用记录器,该函数将新的日志条目写入日志文件。当您运行该程序时,以下内容将被写入logs.txt

INFO: 2019/12/09 12:01:06 main.go:26: Starting the application...
INFO: 2019/12/09 12:01:06 main.go:27: Something noteworthy happened
WARNING: 2019/12/09 12:01:06 main.go:28: There is something you should know about
ERROR: 2019/12/09 12:01:06 main.go:29: Something went wrong

请注意,在此示例中,我们正在记录到单个文件,但是您可以通过在创建记录器时传递一个不同的文件来为每个记录器使用单独的文件。

日志标志

您可以使用日志标志常量来提供其他上下文信息,例如文件,行号,日期和时间,以丰富日志消息。例如,通过带有如下所示标志组合的记录器传递消息“发生了错误”:

log.Ldate|log.Ltime|log.Lshortfile

将打印

2019/12/09 12:01:06 main.go:29: Something went wrong

不幸的是,无法控制它们的显示顺序或显示格式。

日志记录框架介绍

log当获取快速反馈比生成丰富的结构化日志更为重要时,使用此软件包非常适合本地开发。除此之外,使用日志记录框架可能会更好。

使用日志记录框架的主要优点是,它有助于标准化日志数据。这意味着:

  • 阅读和理解日志数据更加容易。
  • 从多个来源收集日志并将它们提供给要分析的中央平台更加容易。

另外,日志记录几乎是一个已解决的问题。为什么要重新发明轮子?

选择日志记录框架

由于有多种选择,因此决定使用哪种框架可能是一个挑战。

Go的两个最受欢迎的日志记录框架似乎是 gloglogrus。glog的流行是令人惊讶的,因为它已经几年没有更新了。logrus可以更好地维护并在Docker等流行项目中使用,因此我们将重点关注它。

Logrus入门

安装logrus就像在终端中运行以下命令一样简单:

go get "github.com/Sirupsen/logrus"

logrus的一大优点是,它与log标准库的软件包完全兼容,因此您可以在任何地方替换您的日志导入,log "github.com/sirupsen/logrus"并且可以正常工作!

让我们修改我们先前使用日志包的“ hello world”示例,并改用logrus:

package main

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

func main() {
    log.Println("Hello world!")
}

运行以下代码将产生输出:

INFO[0000] Hello world!

再简单不过了!

登录JSON

logrus 非常适合JSON中的结构化日志记录,因为JSON是一个定义良好的标准,因此外部服务可以轻松解析您的日志,还可以通过使用字段使向日志消息中添加上下文相对简单,如图所示下面:

package main

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

func main() {
    log.SetFormatter(&log.JSONFormatter{})
    log.WithFields(
        log.Fields{
            "foo": "foo",
            "bar": "bar",
        },
    ).Info("Something happened")
}

生成的日志输出将是一个JSON对象,其中包括消息,日志级别,时间戳和包含的字段。

{"bar":"bar","foo":"foo","level":"info","msg":"Something happened","time":"2019-12-09T15:55:24+01:00"}

如果您不希望将日志输出为JSON,请注意,日志有多个第三方格式化程序,您可以在其Github页面上查看这些格式化程序。如果愿意,您甚至可以编写自己的格式化程序。

日志级别

与标准日志包不同,logrus支持日志级别。

logrus具有七个日志级别:跟踪,调试,信息,警告,错误,致命和紧急。每个级别的严重性随列表的增加而增加。

log.Trace("Something very low level.")
log.Debug("Useful debugging information.")
log.Info("Something noteworthy happened!")
log.Warn("You should probably take a look at this.")
log.Error("Something failed but I'm not quitting.")
// Calls os.Exit(1) after logging
log.Fatal("Bye.")
// Calls panic() after logging
log.Panic("I'm bailing.")

通过在记录器上设置日志记录级别,您可以根据环境仅记录所需的条目。默认情况下,logrus将记录任何信息或更高级别的信息(警告,错误,致命或紧急)。

package main

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

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

    log.Debug("Useful debugging information.")
    log.Info("Something noteworthy happened!")
    log.Warn("You should probably take a look at this.")
    log.Error("Something failed but I'm not quitting.")
}

运行上面的代码将产生以下输出:

{"level":"info","msg":"Something noteworthy happened!","time":"2019-12-09T16:18:21+01:00"}
{"level":"warning","msg":"You should probably take a look at this.","time":"2019-12-09T16:18:21+01:00"}
{"level":"error","msg":"Something failed but I'm not quitting.","time":"2019-12-09T16:18:21+01:00"}

请注意未打印调试级别消息。要将其包括在日志中,请设置log.Level为equal log.DebugLevel

log.SetLevel(log.DebugLevel)

包起来

在本文中,我们探讨了内置日志包的用法,并确定仅应将其用于琐碎的应用程序或构建快速原型时。对于其他所有方面,必须使用主流的日志记录框架。

我们还研究了确保日志中包含的信息一致且易于分析的方法,尤其是在集中式平台上进行汇总时。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354