聊聊golang的log

本文主要研究一下golang的log

log

flags

const (
    Ldate         = 1 << iota     // the date in the local time zone: 2009/01/23
    Ltime                         // the time in the local time zone: 01:23:23
    Lmicroseconds                 // microsecond resolution: 01:23:23.123123.  assumes Ltime.
    Llongfile                     // full file name and line number: /a/b/c/d.go:23
    Lshortfile                    // final file name element and line number: d.go:23. overrides Llongfile
    LUTC                          // if Ldate or Ltime is set, use UTC rather than the local time zone
    Lmsgprefix                    // move the "prefix" from the beginning of the line to before the message
    LstdFlags     = Ldate | Ltime // initial values for the standard logger
)

这几个flags可以用来设置log的pattern

logger

type Logger struct {
    mu     sync.Mutex // ensures atomic writes; protects the following fields
    prefix string     // prefix on each line to identify the logger (but see Lmsgprefix)
    flag   int        // properties
    out    io.Writer  // destination for output
    buf    []byte     // for accumulating text to write
}

func New(out io.Writer, prefix string, flag int) *Logger {
    return &Logger{out: out, prefix: prefix, flag: flag}
}

func (l *Logger) SetOutput(w io.Writer) {
    l.mu.Lock()
    defer l.mu.Unlock()
    l.out = w
}

func (l *Logger) Output(calldepth int, s string) error {
    now := time.Now() // get this early.
    var file string
    var line int
    l.mu.Lock()
    defer l.mu.Unlock()
    if l.flag&(Lshortfile|Llongfile) != 0 {
        // Release lock while getting caller info - it's expensive.
        l.mu.Unlock()
        var ok bool
        _, file, line, ok = runtime.Caller(calldepth)
        if !ok {
            file = "???"
            line = 0
        }
        l.mu.Lock()
    }
    l.buf = l.buf[:0]
    l.formatHeader(&l.buf, now, file, line)
    l.buf = append(l.buf, s...)
    if len(s) == 0 || s[len(s)-1] != '\n' {
        l.buf = append(l.buf, '\n')
    }
    _, err := l.out.Write(l.buf)
    return err
}

func (l *Logger) Printf(format string, v ...interface{}) {
    l.Output(2, fmt.Sprintf(format, v...))
}

func (l *Logger) Print(v ...interface{}) { l.Output(2, fmt.Sprint(v...)) }

func (l *Logger) Println(v ...interface{}) { l.Output(2, fmt.Sprintln(v...)) }

func (l *Logger) Fatal(v ...interface{}) {
    l.Output(2, fmt.Sprint(v...))
    os.Exit(1)
}

func (l *Logger) Fatalf(format string, v ...interface{}) {
    l.Output(2, fmt.Sprintf(format, v...))
    os.Exit(1)
}

func (l *Logger) Fatalln(v ...interface{}) {
    l.Output(2, fmt.Sprintln(v...))
    os.Exit(1)
}

func (l *Logger) Panic(v ...interface{}) {
    s := fmt.Sprint(v...)
    l.Output(2, s)
    panic(s)
}

func (l *Logger) Panicf(format string, v ...interface{}) {
    s := fmt.Sprintf(format, v...)
    l.Output(2, s)
    panic(s)
}

func (l *Logger) Panicln(v ...interface{}) {
    s := fmt.Sprintln(v...)
    l.Output(2, s)
    panic(s)
}

func (l *Logger) SetFlags(flag int) {
    l.mu.Lock()
    defer l.mu.Unlock()
    l.flag = flag
}

func (l *Logger) SetPrefix(prefix string) {
    l.mu.Lock()
    defer l.mu.Unlock()
    l.prefix = prefix
}

func (l *Logger) Writer() io.Writer {
    l.mu.Lock()
    defer l.mu.Unlock()
    return l.out
}
  • log包默认提供的Print、Fatal、Panic方法使用的是std标准输出流,它也提供了New方法可以自定义输出
  • Fatal相关方法会执行os.Exit(1),Panic相关方法会执行panic,而panic执行的是os.Exit(1)
  • f结尾的方法执行的是fmt.Sprintf,可以使用format,ln结尾的方法执行的是fmt.Sprintln,会增加换行

实例

package main

import (
    "encoding/json"
    "errors"
    "io"
    "log"
    "os"
    "time"
)

var logFile *os.File

func init() {
    log.SetFlags(log.Ldate | log.Lmicroseconds | log.Lshortfile)
    log.SetPrefix("demo-app ")
    var err error
    logFile, err = os.OpenFile("/tmp/demo.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
    if err != nil {
        log.Fatalln("Failed to open log file")
    }
    mw := io.MultiWriter(os.Stdout, logFile)
    log.SetOutput(mw)
}

type Cart struct {
    UserId int `json:"userId"`
    SkuId  int `json:"skuId"`
}

func closeLogFile() {
    if logFile != nil {
        log.Println("closing log file")
        logFile.Close()
    }
}

func main() {
    defer closeLogFile()
    // log.Panicln("print and then exit with status 2")
    // log.Fatalln("print and then exit with status 1")
    log.Println("hello")
    log.Printf("hello %s", "abc")

    //print with var
    str1 := "abc"
    str2 := "cdf"
    log.Println("print with auto space:", str1, "append", str2)

    //print error
    err1 := errors.New("demo error")
    log.Println("error:", err1)

    //print time
    now1 := time.Now()
    log.Println("print time:", now1)
    log.Println("print time with format:", now1.Format(time.RFC3339))

    //print struct
    cart := Cart{
        UserId: 1,
        SkuId:  123,
    }
    log.Println("print struct:", cart)

    //print json
    jsonByte, err := json.Marshal(cart)
    if err != nil {
        log.Fatalln("error:", err)
    }
    log.Println("print cart json:", string(jsonByte))

    //log to file
}
  • 这里给log设置了MultiWriter,可以同时往std和文件输出

输出实例

demo-app 2020/12/03 23:54:27.245848 main.go:42: hello
demo-app 2020/12/03 23:54:27.245974 main.go:43: hello abc
demo-app 2020/12/03 23:54:27.245981 main.go:48: print with auto space: abc append cdf
demo-app 2020/12/03 23:54:27.245996 main.go:52: error: demo error
demo-app 2020/12/03 23:54:27.246015 main.go:56: print time: 2020-12-03 23:54:27.246001 +0800 CST m=+0.000260928
demo-app 2020/12/03 23:54:27.246022 main.go:57: print time with format: 2020-12-03T23:54:27+08:00
demo-app 2020/12/03 23:54:27.246047 main.go:64: print struct: {1 123}
demo-app 2020/12/03 23:54:27.246109 main.go:71: print cart json: {"userId":1,"skuId":123}
demo-app 2020/12/03 23:54:27.246116 main.go:33: closing log file

小结

  • log包默认提供的Print、Fatal、Panic方法使用的是std标准输出流,它也提供了New方法可以自定义输出
  • Fatal相关方法会执行os.Exit(1),Panic相关方法会执行panic,而panic执行的是os.Exit(1)
  • f结尾的方法执行的是fmt.Sprintf,可以使用format,ln结尾的方法执行的是fmt.Sprintln,会增加换行

doc

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

推荐阅读更多精彩内容

  • Lua 5.1 参考手册 by Roberto Ierusalimschy, Luiz Henrique de F...
    苏黎九歌阅读 13,517评论 0 38
  • 目录 1.go 各种代码运行 2.go 在线编辑代码运行 3.通过 Gob 包序列化二进制数据 4.使用 ...
    杨言锡阅读 1,068评论 0 1
  • 一、log日志包 log支持并发操作。其结构定义如下: log基本日志格式 (1)Golang's log模块主要...
    学生黄哲阅读 10,443评论 3 35
  • 一、错误异常 《快学 Go 语言》第 10 课 —— 错误与异常Go 语言的异常处理语法绝对是独树一帜,在我见过的...
    合肥黑阅读 1,069评论 0 3
  • 在我们开发程序后,如果有一些问题需要对程序进行调试的时候,日志是必不可少的,这是我们分析程序问题常用的手段。 日志...
    豆瓣奶茶阅读 18,316评论 0 22