Golang官方log包详解

以下全是代码, 详解在注释中, 请从头到尾看

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // Package log implements a simple logging package. It defines a type, Logger,
// with methods for formatting output. It also has a predefined 'standard'
// Logger accessible through helper functions Print[f|ln], Fatal[f|ln], and
// Panic[f|ln], which are easier to use than creating a Logger manually.
// That logger writes to standard error and prints the date and time
// of each logged message.
// Every log message is output on a separate line: if the message being
// printed does not end in a newline, the logger will add one.
// The Fatal functions call os.Exit(1) after writing the log message.
// The Panic functions call panic after writing the log message.
// 官方包注释, 这个包让你自己不需要手动实现日志包, 但是还是我建议还是使用第三方包!
package golog import (
"fmt"
"io"
"os"
"runtime"
"sync"
"time"
) // 稍加注释!
// These flags define which text to prefix to each log entry generated by the Logger.
const (
// Bits or'ed together to control what's printed.
// There is no control over the order they appear (the order listed
// here) or the format they present (as described in the comments).
// The prefix is followed by a colon only when Llongfile or Lshortfile
// is specified.
// For example, flags Ldate | Ltime (or LstdFlags) produce,
// 2009/01/23 01:23:23 message
// while flags Ldate | Ltime | Lmicroseconds | Llongfile produce,
// 2009/01/23 01:23:23.123123 /a/b/c/d.go:23: message
// 二进制或标志!
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
// 默认不带文件名
LstdFlags = Ldate | Ltime // initial values for the standard 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 to write at beginning of each line
// 日志格式的标志
flag int // properties
// 写入的目标
out io.Writer // destination for output
// 日志缓存, 待写入
buf []byte // for accumulating text to write
} // New creates a new Logger. The out variable sets the
// destination to which log data will be written.
// The prefix appears at the beginning of each generated log line.
// The flag argument defines the logging properties.
// 新一个日志记录, 这个对象(结构对象)承接了日志输出的责任.
func New(out io.Writer, prefix string, flag int) *Logger {
return &Logger{out: out, prefix: prefix, flag: flag}
} // SetOutput sets the output destination for the logger.
// 设置日志输出的地方
func (l *Logger) SetOutput(w io.Writer) {
l.mu.Lock()
defer l.mu.Unlock()
l.out = w
} // 默认记录对象, 输出为标准输出, 日志没前缀, 格式是标准: 2009/01/23 01:23:23 message
var std = New(os.Stderr, "", LstdFlags) // Cheap integer to fixed-width decimal ASCII. Give a negative width to avoid zero-padding.
// 数字填充, 方便时间对齐
func itoa(buf *[]byte, i int, wid int) {
// Assemble decimal in reverse order.
// 填充后最长长度20!
var b [20]byte
bp := len(b) - 1 // 类似辗转相除法取最大公约数, 此处是每次取数字的后一位, 等到数字取不到位了就按照wid开始填充
// 比如 i=106 wid=5:
// wid q byte i
// 4 10 6 106
// 3 1 0 10
// 2 0 1 1
// 1 0 0 0 ===> 不符合i>=0 || wid>1, 跳出
// 00106
for i >= 10 || wid > 1 {
wid--
// 商, 如果第一次i是106, 那么就变成10
// 填到q为0时
q := i / 10
// 余数, 第一次i-q*10就是6 ==> '0' + 6 ==> '6' 原封不动
// 填到q为0时, i也为0了, 这时就按wid开始不断填'0'
b[bp] = byte('0' + i - q*10)
bp--
//fmt.Println(wid,q,string(byte('0' + i - q*10)),i)
i = q
}
// i < 10
b[bp] = byte('0' + i)
*buf = append(*buf, b[bp:]...)
} // formatHeader writes log header to buf in following order:
// * l.prefix (if it's not blank),
// * date and/or time (if corresponding flags are provided),
// * file and line number (if corresponding flags are provided).
// 日志输出时日志头部的格式化
func (l *Logger) formatHeader(buf *[]byte, t time.Time, file string, line int) {
// 前缀增加, 如果前缀为空, 啥都没发生!
*buf = append(*buf, l.prefix...)
// 二进制或的魅力来了
if l.flag&(Ldate|Ltime|Lmicroseconds) != 0 {
// 以上表示有使用了时间格式
// 如果有, 转换为UTC时间
if l.flag&LUTC != 0 {
t = t.UTC()
} // 日期转换
if l.flag&Ldate != 0 {
year, month, day := t.Date()
// 年份填充, 长度4位
itoa(buf, year, 4)
*buf = append(*buf, '/')
itoa(buf, int(month), 2)
*buf = append(*buf, '/')
itoa(buf, day, 2) // 空一点后面接文件名或者日志消息
*buf = append(*buf, ' ')
} // 时间或微妙格式
if l.flag&(Ltime|Lmicroseconds) != 0 {
hour, min, sec := t.Clock()
itoa(buf, hour, 2)
*buf = append(*buf, ':')
itoa(buf, min, 2)
*buf = append(*buf, ':')
itoa(buf, sec, 2) // 正常时间完, 是否有微秒?
if l.flag&Lmicroseconds != 0 {
*buf = append(*buf, '.')
itoa(buf, t.Nanosecond()/1e3, 6) // 纳秒转微秒, 长度6位
}
// 空一点后面接文件名或者日志消
*buf = append(*buf, ' ')
}
} // 长文件名, 短文件名输出
if l.flag&(Lshortfile|Llongfile) != 0 {
// 短文件名那么截取最后一个, 如a/b/c ==> c
if l.flag&Lshortfile != 0 {
short := file
for i := len(file) - 1; i > 0; i-- {
if file[i] == '/' {
short = file[i+1:]
break
}
}
file = short
} // 写入缓冲
*buf = append(*buf, file...)
*buf = append(*buf, ':') // 行数, 代码打日志所在的地方, wid为-1表示不填冲
itoa(buf, line, -1) // 后面开始接消息, :再空一点!
*buf = append(*buf, ": "...)
}
} // 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() // 使用文件标志格式, 可能会产生bug!
// 此篇文章: https://www.cnblogs.com/zhangym/p/6709282.html
// 重复加锁时协程会阻塞, 直到锁被解开, 只要不让主协程阻塞, 就不会死锁! 要及时解开锁!
if l.flag&(Lshortfile|Llongfile) != 0 {
// Release lock while getting caller info - it's expensive.
// 先将锁释放, 方便其他协程能更快获取到调用的文件路径. 文件调用获取路径是一个昂贵的过程
// 在这个解锁期间, 其他协程中的一个又会进来Output进行加锁, 加锁的这一个瞬间, 如果获取路径过程结束了, 之前的协程会再次加锁, 两次加锁会报错!
// may BE BUG
l.mu.Unlock()
var ok bool
// 0 表示获取自己的路径
// 1 表示获取上一层调用的路径
// 越往上那么逐次加一 // 在此, 直接调用Output, 那么calldepth为1时可以知道是谁调用了Output, 但是Output被Print等又封装了一层, 所以是2!
_, file, line, ok = runtime.Caller(calldepth) // 昂贵!
if !ok {
file = "???"
line = 0
} // 路径获取完后, 在加锁之后, 其他协程可能也进入开头加锁! Maybe bug
l.mu.Lock()
// 下面这个测试, 可以Lock两次? 只要保证主进程不阻塞即可?
//go func(){
// time.Sleep(time.Duration(2)*time.Second)
// l.mu.Unlock() // 最终还是会解开锁, Golang的智能检测会发现死锁? 如果主协程在一定时间内还没有响应!
//}()
//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
} // 下面的都是采用Output包装而来
// Printf calls l.Output to print to the logger.
// Arguments are handled in the manner of fmt.Printf.
func (l *Logger) Printf(format string, v ...interface{}) {
l.Output(2, fmt.Sprintf(format, v...))
} // Print calls l.Output to print to the logger.
// Arguments are handled in the manner of fmt.Print.
func (l *Logger) Print(v ...interface{}) { l.Output(2, fmt.Sprint(v...)) } // 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...)) } // Fatal is equivalent to l.Print() followed by a call to os.Exit(1).
func (l *Logger) Fatal(v ...interface{}) {
l.Output(2, fmt.Sprint(v...))
os.Exit(1)
} // Fatalf is equivalent to l.Printf() followed by a call to os.Exit(1).
func (l *Logger) Fatalf(format string, v ...interface{}) {
l.Output(2, fmt.Sprintf(format, v...))
os.Exit(1)
} // Fatalln is equivalent to l.Println() followed by a call to os.Exit(1).
func (l *Logger) Fatalln(v ...interface{}) {
l.Output(2, fmt.Sprintln(v...))
os.Exit(1)
} // Panic is equivalent to l.Print() followed by a call to panic().
func (l *Logger) Panic(v ...interface{}) {
s := fmt.Sprint(v...)
l.Output(2, s)
panic(s)
} // Panicf is equivalent to l.Printf() followed by a call to panic().
func (l *Logger) Panicf(format string, v ...interface{}) {
s := fmt.Sprintf(format, v...)
l.Output(2, s)
panic(s)
} // Panicln is equivalent to l.Println() followed by a call to panic().
func (l *Logger) Panicln(v ...interface{}) {
s := fmt.Sprintln(v...)
l.Output(2, s)
panic(s)
} // 以下都是原子更新
// Flags returns the output flags for the logger.
func (l *Logger) Flags() int {
l.mu.Lock()
defer l.mu.Unlock()
return l.flag
} // SetFlags sets the output flags for the logger.
func (l *Logger) SetFlags(flag int) {
l.mu.Lock()
defer l.mu.Unlock()
l.flag = flag
} // Prefix returns the output prefix for the logger.
func (l *Logger) Prefix() string {
l.mu.Lock()
defer l.mu.Unlock()
return l.prefix
} // SetPrefix sets the output prefix for the logger.
func (l *Logger) SetPrefix(prefix string) {
l.mu.Lock()
defer l.mu.Unlock()
l.prefix = prefix
} // SetOutput sets the output destination for the standard logger.
func SetOutput(w io.Writer) {
std.mu.Lock()
defer std.mu.Unlock()
std.out = w
} // Flags returns the output flags for the standard logger.
func Flags() int {
return std.Flags()
} // SetFlags sets the output flags for the standard logger.
func SetFlags(flag int) {
std.SetFlags(flag)
} // Prefix returns the output prefix for the standard logger.
func Prefix() string {
return std.Prefix()
} // SetPrefix sets the output prefix for the standard logger.
func SetPrefix(prefix string) {
std.SetPrefix(prefix)
} // These functions write to the standard logger. // Print calls Output to print to the standard logger.
// Arguments are handled in the manner of fmt.Print.
func Print(v ...interface{}) {
std.Output(2, fmt.Sprint(v...))
} // Printf calls Output to print to the standard logger.
// Arguments are handled in the manner of fmt.Printf.
func Printf(format string, v ...interface{}) {
std.Output(2, fmt.Sprintf(format, v...))
} // Println calls Output to print to the standard logger.
// Arguments are handled in the manner of fmt.Println.
func Println(v ...interface{}) {
std.Output(2, fmt.Sprintln(v...))
} // Fatal is equivalent to Print() followed by a call to os.Exit(1).
func Fatal(v ...interface{}) {
std.Output(2, fmt.Sprint(v...))
os.Exit(1)
} // Fatalf is equivalent to Printf() followed by a call to os.Exit(1).
func Fatalf(format string, v ...interface{}) {
std.Output(2, fmt.Sprintf(format, v...))
os.Exit(1)
} // Fatalln is equivalent to Println() followed by a call to os.Exit(1).
func Fatalln(v ...interface{}) {
std.Output(2, fmt.Sprintln(v...))
os.Exit(1)
} // Panic is equivalent to Print() followed by a call to panic().
func Panic(v ...interface{}) {
s := fmt.Sprint(v...)
std.Output(2, s)
panic(s)
} // Panicf is equivalent to Printf() followed by a call to panic().
func Panicf(format string, v ...interface{}) {
s := fmt.Sprintf(format, v...)
std.Output(2, s)
panic(s)
} // Panicln is equivalent to Println() followed by a call to panic().
func Panicln(v ...interface{}) {
s := fmt.Sprintln(v...)
std.Output(2, s)
panic(s)
} // 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 the count of the number of
// frames to skip when computing the file name and line number
// if Llongfile or Lshortfile is set; a value of 1 will print the details
// for the caller of Output. // 默认的让你再包装一层的函数...
func Output(calldepth int, s string) error {
return std.Output(calldepth+1, s) // +1 for this frame.
}

Golang官方log包详解的更多相关文章

  1. Golang Context 包详解

    Golang Context 包详解 0. 引言 在 Go 语言编写的服务器程序中,服务器通常要为每个 HTTP 请求创建一个 goroutine 以并发地处理业务.同时,这个 goroutine 也 ...

  2. 常见 jar包详解

        常见 jar包详解 jar包 用途 axis.jar SOAP引擎包 commons-discovery-0.2.jar 用来发现.查找和实现可插入式接口,提供一些一般类实例化.单件的生命周期 ...

  3. Spring 3.x jar 包详解 与 依赖关系

    以下的内容我会持续更新(当然是我有新发现的时候); 以下内容是我在网上搜索.整理.修改的而成的内容.由于很多内容都是转载了,无法追溯到源头,因此无法一一对原作者进行道谢. 这几天,我查阅大量的官方的文 ...

  4. Spring 3.x jar 包详解 与 依赖关系(转)

    以下的内容我会持续更新(当然是我有新发现的时候); 以下内容是我在网上搜索.整理.修改的而成的内容.由于很多内容都是转载了,无法追溯到源头,因此无法一一对原作者进行道谢. 这几天,我查阅大量的官方的文 ...

  5. 转:android Support 兼容包详解

    本文转自stormzhang的ANDROID SUPPORT兼容包详解 背景 来自于知乎上邀请回答的一个问题Android中AppCompat和Holo的一个问题?, 看来很多人还是对这些兼容包搞不清 ...

  6. Android SDK中的Support兼容包详解

    这篇文章主要介绍了Android SDK中的Support兼容包详解,本文详细区分了Support Library的版本区别.各种Theme的概念和使用注意事项等内容,需要的朋友可以参考下 背景 来自 ...

  7. Spring jar包详解

    Spring jar包详解 org.springframework.aop ——Spring的面向切面编程,提供AOP(面向切面编程)的实现 org.springframework.asm——spri ...

  8. Spring——jar包详解(转)

    Spring——jar包详解 org.springframework.aop ——Spring的面向切面编程,提供AOP(面向切面编程)的实现 org.springframework.asm——spr ...

  9. 2.TCP_IP互联线缆_TCP_UDP报文抓包详解

    TCP_IP互联线缆_TCP_UDP报文抓包详解 2.1网线标准 直通线 交叉线 异种设备互联使用直通线 同种设备互联使用交叉线 TCP和UDP 端口寻址 TCP数据格式 TCP三次握手 UDP数据格 ...

随机推荐

  1. JVM 数组创建的本质

    1.创建数组 创建一个MyParent4[] 数组 public class MyTest4 { public static void main(String[] args) { MyParent4[ ...

  2. 即时通信系统IM--XMPP

    即时通讯(Instant Messaging)是目前Internet上最为流行的通讯方式,各种各样的即时通讯软件也层出不穷:服务提供商也提供了越来越丰富的通讯服务功能. 不容置疑,Internet已经 ...

  3. 微信小程序的z-index在苹果ios无效

    1.在微信开发者工具可以正常显示 2.在安卓真机手机可以正常显示 3.在ios手机真机无法正常显示 原因:父级view的css属性有 position: fixed; ,把它注释掉即可

  4. Mybatis自定义控制台打印sql的日志工具

    调试mybatis源码时,想要更改日志的的实现工具,首先需要了解其原理. 源码包里有这部分的解释,翻译如下: Mybatis 的内置日志工厂提供日志功能,内置日志工厂将日志交给以下其中一种工具作代理: ...

  5. GIS地理工具案例教程——批量去除多边形的重叠部分

    GIS地理工具案例教程--批量去除多边形的重叠部分 商务合作,科技咨询,版权转让:向日葵,135-4855__4328,xiexiaokui#qq.com 问题:几乎所有的手工生产的数据,都存在多边形 ...

  6. java项目代码上线

    java项目代码上线   1:java项目代码上线架构图 ip地址及主机名规划 10.0.0.11 deploy 10.0.0.12 tomcat-web01 10.0.0.13 git.oldboy ...

  7. pythonUDP发送结构体,对齐到C++结构体

    给出程序先: import random import socket import struct import threading import pickle import json from str ...

  8. DataSource接口 Connection pooling(连接池

    一.DataSource接口是一个更好的连接数据源的方法:  JDBC1.0是原来是用DriverManager类来产生一个对数据源的连接.JDBC2.0用一种替代的方法,使用DataSource的实 ...

  9. typeScript模块<三>

    /*模块 1 模块的的概念 2 模块导出的几种方法 1.export 导出声明 2.export 导出语句 3.export default 4.import导入模块 3 模块化封装上一讲的DB库 * ...

  10. MFC加载大型osg模型

    MFC加载模型,发现打开 Navid 缓冲等选项后,加载大型模型的速度就快了很多. #include "stdafx.h" #include "OSGObject.h&q ...