Golang log日志

有一天我在goland里看着输出日志,很想知道它是哪个文件,第多少行打出来的,然后就找到了log功能。
TRACE: 2019/05/24 17:23:38 main.go:22: begin connect
Name: superWang
Phone: 13478808311
看代码吧:
func init() {
log.SetPrefix("TRACE: ")
log.SetFlags(log.LstdFlags | log.Lshortfile)
}
log.Println("begin connect")
fmt.Println("Name:", result.Name)
fmt.Println("Phone:", result.Phone)

一、官方自带log
1.如何使用最基本的 log 包
// This sample program demonstrates how to use the base log package.
package main

import (
    "log"
)

func init() {
    log.SetPrefix("TRACE: ")
    log.SetFlags(log.Ldate | log.Lmicroseconds | log.Llongfile)
}

func main() {
    // Println writes to the standard logger.
    log.Println("message")

    // Fatalln is Println() followed by a call to os.Exit(1).
    log.Fatalln("fatal message")

    // Panicln is Println() followed by a call to panic().
    log.Panicln("panic message")
}
-------------------------------
TRACE: 2019/04/09 14:24:32.868375 D:/go/TestFile/src/main/TestLog.go:15: message
TRACE: 2019/04/09 14:24:32.962329 D:/go/TestFile/src/main/TestLog.go:18: fatal message

Process finished with exit code 1

log.SetPrefix("TRACE: ")设置了一个字符串,作为每个日志项的前缀。这个字符串应该是能让用户从一般的程序输出中分辨出日志的字符串。传统上这个字符串的字符会全部大写

golang.org/src/log/log.go

const (
// 将下面的位使用或运算符连接在一起,可以控制要输出的信息。没有
// 办法控制这些信息出现的顺序(下面会给出顺序)或者打印的格式
// (格式在注释里描述)。这些项后面会有一个冒号:
// 2009/01/23 01:23:23.123123 /a/b/c/d.go:23: message
// 日期: 2009/01/23
Ldate = 1 << iota
// 时间: 01:23:23
Ltime
// 毫秒级时间: 01:23:23.123123。该设置会覆盖 Ltime 标志
Lmicroseconds
// 完整路径的文件名和行号: /a/b/c/d.go:23
Llongfile
// 最终的文件名元素和行号: d.go:23
// 覆盖 Llongfile
Lshortfile
// 标准日志记录器的初始值
LstdFlags = Ldate | Ltime
)

log 包有一个很方便的地方就是,这些日志记录器是多 goroutine 安全的。这意味着在多个goroutine 可以同时调用来自同一个日志记录器的这些函数,而不会有彼此间的写冲突。标准日志记录器具有这一性质,用户定制的日志记录器也应该满足这一性质。

2.定制的日志记录器
// 这个示例程序展示如何创建定制的日志记录器
package main

import (
    "io"
    "io/ioutil"
    "log"
    "os"
)

var (
    Trace   *log.Logger // 记录所有日志
    Info    *log.Logger // 重要的信息
    Warning *log.Logger // 需要注意的信息
    Error   *log.Logger // 非常严重的问题
)

func init() {
    file, err := os.OpenFile("errors.txt",
        os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
    if err != nil {
        log.Fatalln("Failed to open error log file:", err)
    }

    Trace = log.New(ioutil.Discard,
        "TRACE: ",
        log.Ldate|log.Ltime|log.Lshortfile)

    Info = log.New(os.Stdout,
        "INFO: ",
        log.Ldate|log.Ltime|log.Lshortfile)

    Warning = log.New(os.Stdout,
        "WARNING: ",
        log.Ldate|log.Ltime|log.Lshortfile)

    Error = log.New(io.MultiWriter(file, os.Stderr),
        "ERROR: ",
        log.Ldate|log.Ltime|log.Lshortfile)
}

func main() {
    Trace.Println("I have something standard to say")
    Info.Println("Special Information")
    Warning.Println("There is something you need to know about")
    Error.Println("Something has failed")
}
------------------------------------
INFO: 2019/04/09 14:37:11 TestCustomLog.go:44: Special Information
ERROR: 2019/04/09 14:37:11 TestCustomLog.go:46: Something has failed
WARNING: 2019/04/09 14:37:11 TestCustomLog.go:45: 
There is something you need to know about

golang.org/src/log/log.go

// New 创建一个新的 Logger。out 参数设置日志数据将被写入的目的地
// 参数 prefix 会在生成的每行日志的最开始出现
// 参数 flag 定义日志记录包含哪些属性
func New(out io.Writer, prefix string, flag int) *Logger {
return &Logger{out: out, prefix: prefix, flag: flag}
}

Discard 是一个 io.Writer 接口,调用它的 Write 方法将不做任何事情并且始终成功返回。当某个等级的日志不重要时,使用 Discard 变量可以禁用这个等级的日志。

// devNull 是一个用 int 作为基础类型的类型
type devNull int
// Discard 是一个 io.Writer,所有的 Write 调用都不会有动作,但是会成功返回
var Discard io.Writer = devNull(0)
// io.Writer 接口的实现
func (devNull) Write(p []byte) (int, error) {
return len(p), nil
}

golang.org/src/os/file.go

// Stdin、Stdout 和 Stderr 是已经打开的文件,分别指向标准输入、标准输出和
// 标准错误的文件描述符
var (
Stdin = NewFile(uintptr(syscall.Stdin), "/dev/stdin")
Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout")
Stderr = NewFile(uintptr(syscall.Stderr), "/dev/stderr")
)
os/file_unix.go
// NewFile 用给出的文件描述符和名字返回一个新 File
func NewFile(fd uintptr, name string) *File {

可以看到 3 个变量的声明,分别表示所有操作系统里都有的 3 个标准输入/输出,即 Stdin、Stdout 和 Stderr。这 3 个变量都被声明为 File 类型的指针,这个类型实现了 io.Writer 接口。

MultiWriter 函数调用会返回一个 io.Writer接口类型值,这个值包含之前打开的文件file,以及 stderr。MultiWriter 函数是一个变参函数,可以接受任意个实现了io.Writer 接口的值。这个函数会返回一个io.Writer 值,这个值会把所有传入的 io.Writer 的值绑在一起。当对这个返回值进行写入时,会向所有绑在一起的io.Writer 值做写入。这让类似log.New 这样的函数可以同时向多个Writer 做输出。现在,当我们使用Error 记录器记录日志时,输出会同时写到文件和stderr。

每个记录器变量都包含一组方法,这组方法与 log 包里实现的那组函数完全一致

func (l *Logger) Fatal(v ...interface{})
func (l *Logger) Fatalf(format string, v ...interface{})
func (l *Logger) Fatalln(v ...interface{})
func (l *Logger) Flags() int
func (l *Logger) Output(calldepth int, s string) error
func (l *Logger) Panic(v ...interface{})
func (l *Logger) Panicf(format string, v ...interface{})
func (l *Logger) Panicln(v ...interface{})
func (l *Logger) Prefix() string
func (l *Logger) Print(v ...interface{})
func (l *Logger) Printf(format string, v ...interface{})
func (l *Logger) Println(v ...interface{})
func (l *Logger) SetFlags(flag int)
func (l *Logger) SetPrefix(prefix string)
二、第三方log库
1.简要汇总

参考
golang中的log库
Golang的log包哪个好用?
在Github中最受欢迎的Go日志库集合

截止2019.5.24star数统计

  • logrus 11018star 近期还在更新
  • zap 6901star 近期还在更新
  • oklog 2781star 近期还在更新
  • glog 2214star 已经很久不更新
  • seelog 1312star 已经很久不更新
  • zerolog 1981star 近期还在更新
2.选择JSON格式的日志

王健:最佳日志实践
最佳日志实践(v2.0)
达达 JSON日志文件

举个例子,在引入JSON格式的日志之后,我很快就用PHP做了一个日志查看页面,因为信息是结构化的,这个查看页面要做过滤筛选或者特殊显示就变得方便许多,不是先前做不到,而是做起来很繁琐。但是JSON日志比起二进制文件格式来说,有个明显的问题是JSON导致的文件体积膨胀。

logrus也是支持json格式日志的,可以参考Golang之使用Logrus

logrus 为什么不支持输出文件名和行号

3.性能

参考
如何开发高性能 go 组件?
Go零消耗debug log技巧
玩转高性能日志库ZAP (1)
玩转高性能日志库ZAP (2)
玩转高性能日志库ZAP(3)

看到题目,有人肯定会问,官方的log模块不好么? Debug Log一般很长,在生产环境还输出的话,也很难找。 再者,log的消耗是比较大的,特别是需要打印行号时。如果在代码中有大量的debug日志,这个损耗累积起来,那也是相当惊人的了。

我们的业务通常会记录大量的 Debug 日志,但在实际测试过程中,发现我们使用的日志库 seelog 性能存在严重的瓶颈,在我们的对比结果中发现:zap 表现非常突出,单线程 Qps 也是 logrus、seelog 的数倍。

其实我们最初的实践中并没有意识到日志框架的性能的重要性,直到开发后期进行系统的 benchmark 总是不尽人意,而且在不同的日志级别下性能差距明显。通过 go profiling 看到日志组件对于计算资源的消耗十分巨大,因此决心将其替换为一个高性能的日志框架,这也是选择用 zap 的一个重要的考量的点。

目前我们使用 zap 已有2年多的时间,zap 很好地解决了日志组件的低性能的问题。目前 zap 也从 beta 发布到了 1.8版本,对于 zap 我们不仅仅看到它的高性能,更重要的是理解它的设计与工程实践。日志属于 io 密集型的组件,这类组件如何做到高性能低成本,这也将直接影响到服务成本。

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

推荐阅读更多精彩内容

  • 在我们开发程序后,如果有一些问题需要对程序进行调试的时候,日志是必不可少的,这是我们分析程序问题常用的手段。 日志...
    豆瓣奶茶阅读 18,346评论 0 22
  • Lua 5.1 参考手册 by Roberto Ierusalimschy, Luiz Henrique de F...
    苏黎九歌阅读 13,709评论 0 38
  • ORA-00001: 违反唯一约束条件 (.) 错误说明:当在唯一索引所对应的列上键入重复值时,会触发此异常。 O...
    我想起个好名字阅读 5,102评论 0 9
  • 这几日总是睡不好,晚上睡不着,早上起不来。闹钟调到七点,总要撑到七点半起床。幸亏单位离得近,要不然工资要扣光。 早...
    飞飞鱼先生阅读 158评论 1 2
  • 南无阿弥陀佛 自私不得自由,利他才得自由; 自大不得自由,谦卑才得自由; 爱己不得自由,爱人才得自由; 虚假不得自...
    释宗音阅读 355评论 0 1