GO 的日志怎么玩
上次咱们分享了 GO 的定时器 timer 和定时工作 cron
,咱们来回顾一下:
- Timer 是什么
- Timer 如何应用
- Ticker 是什么
- Ticker 如何应用
- cron 是什么
- cron 如何应用
要是想理解如上问题的答案,欢送查看文章 GO 的定时器 Timer 和定时工作 cron
明天咱们来看看 GO 的规范库外面的 日志包 log
具体源码门路:src/log/log.go
如何简略应用 log 包
咱们在编辑器中看看应用 log
包,会有什么提醒
一看,log
包外面就波及这些办法和数据结构,一点都不简单,办法如上图
咱们来用一用小案例,再来看数据结构
package main
import "log"
func main() {log.Println("小魔童打日志 ...")
test := "Hello wrold"
// Printf 有格局控制符
log.Printf("%s 小魔童打日志 ... \n", test)
log.Fatalln("小魔童 打日志,触发了 Fatal")
log.Panicln("小魔童 打日志,触发了 Panic")
}
运行上述代码,成果如下:
2021/06/xx xx:25:53 小魔童打日志 ...
2021/06/xx xx:25:53 Hello wrold 小魔童打日志 ...
2021/06/xx xx:25:53 小魔童 打日志,触发了 Fatal
exit status 1
默认能够打印出日期、工夫、以及打印的内容
如何配置 log 以及相应的原理
应用 GO
外面的 这个 log
包,咱们应用默认的 log
那必定是不够用的,例如上述小案例打印的日志,你就不晓得具体是代码的哪一行打印进去的,以及设置日志打印到哪个日志文件外面,等等
咱们一起来看看如何配置 log
,从创立 logger
开始看起
新建一个 logger
咱们在根本的日志上,加上一个前缀
func main() {
// 打印到规范输入上
myLog := log.New(os.Stdout, "<XMT>", log.Lshortfile|log.Ldate|log.Ltime)
myLog.Println("小魔童打印了带有前缀的日志 ...")
}
执行成果如下:
<XMT>2021/06/28 12:35:47 main.go:20: 小魔童打印了带有前缀的日志 ...
咱们看看 log.New
办法的具体实现
具体源码门路:src/log/log.go
func New(out io.Writer, prefix string, flag int) *Logger {return &Logger{out: out, prefix: prefix, flag: flag}
}
能够看出 func New(out io.Writer, prefix string, flag int) *Logger
办法实际上是调用了 Logger
数据结构,咱们瞅瞅
// A Logger represents an active logging object that generates lines of
// output to an io.Writer. 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.
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}
type Logger struct
有下面这几个成员,看上去每一个参数都比拟好了解,依据成员名字就可能根本晓得其含意
- mu sync.Mutex
锁,确保原子操作
- prefix string
每一行日志的前缀
- out io.Writer
输入地位,能够是文件,能够是规范输入
- buf []byte
缓冲区的 buffer
- flag int
具体属性,通过源码咱们能够看出,具体属性有如下几种抉择
这些参数,都是用于管制日志输入的细节,例如工夫,代码行数,前缀等等
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
)
源码写的正文还是很清晰的,具体每一个字段是做什么的,用了之后是什么样的成果,依据这个正文,高深莫测
咱们查看源码就晓得,为什么上述的小案例,日志外面默认就输入了 日期、工夫、具体内容,因为 log
包外面会默认 New
一个日志,供咱们默认应用
此处 var std = New(os.Stderr, "", LstdFlags)
New 外面的第三个参数须要填属性,此处默认填了 LstdFlags
LstdFlags = Ldate | Ltime // initial values for the standard logger
LstdFlags
属性,默认是打印日期,和工夫
// Println calls l.Output to print to the logger.
// Arguments are handled in the manner of fmt.Println.
func (l *Logger) Println(v ...interface{}) {l.Output(2, fmt.Sprintln(v...)) }
(l *Logger) Println
进行具体的输入,调用了(l *Logger) Output
// Output writes the output for a logging event. The string s contains
// the text to print after the prefix specified by the flags of the
// Logger. A newline is appended if the last character of s is not
// already a newline. Calldepth is used to recover the PC and is
// provided for generality, although at the moment on all pre-defined
// paths it will be 2.
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) Output(calldepth int, s string) error {
函数做了如下几个事件:
- 拼接日志字符串数据
- 输入到
out
中,此处的out
默认是规范输入,也能够本人设置输入到文件
配置一个 logger
咱们用一下 log
外面设置输入日志到文件中
func main() {logFile, err := os.OpenFile("./XMT.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {fmt.Println("os.OpenFile error :", err)
return
}
// 设置输入地位,外面有锁进行管制
log.SetOutput(logFile)
// 设置日志属性
log.SetFlags(log.Llongfile | log.Ltime | log.Ldate)
// 打印日志
log.Println("小魔童的 新 日志 ...")
// 手动设置前缀
log.SetPrefix("【重点】")
log.Println("小魔童的重要日志...")
}
运行上述代码,成果如下:
2021/06/28 12:57:14 D:/mycode/my_new_first/my_log/main.go:36: 小魔童的 新 日志 ...【重点】2021/06/28 12:57:14 D:/mycode/my_new_first/my_log/main.go:40: 小魔童的重要日志...
-
log.SetOutput
log.SetOutput
实际上是调用了Logger
对应的func (l *Logger) SetOutput(w io.Writer)
办法
func (l *Logger) SetOutput(w io.Writer) {l.mu.Lock()
defer l.mu.Unlock()
l.out = w
}
-
log.SetFlags
log.SetFlags
实际上是调用了Logger
对应的SetFlags
办法
SetPrefix
也是同样的情理
// SetFlags sets the output flags for the logger.
// The flag bits are Ldate, Ltime, and so on.
func (l *Logger) SetFlags(flag int) {l.mu.Lock()
defer l.mu.Unlock()
l.flag = flag
}
总结
- 如何应用
log
包 log
包原理和具体实现- 自定义日志
欢送点赞,关注,珍藏
敌人们,写作不易
你的反对和激励,是我保持分享,提高质量的能源
好了,本次就到这里,GO 的单元测试和性能测试分享
技术是凋谢的,咱们的心态,更应是凋谢的。拥抱变动,背阴而生,致力向前行。
我是 小魔童哪吒,欢送点赞关注珍藏,下次见~