内容简介:以下全是代码, 详解在注释中, 请从头到尾看
以下全是代码, 详解在注释中, 请从头到尾看
// 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. }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 以正确的方式下载和配置 ASP.NET Core 官方源码
- 亦来云被声讨“八宗罪” 官方群和官方已决裂
- Substrate 官方教程增强版
- Babel 插件开发手册(官方)
- [译]Kafka官方文档-快速入门
- PDMan 官方推出 Web 版啦
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Intel系列微处理器体系结构、编程与接口
布雷, / 机械工业出版社 / 2005-4 / 99.00元
本书是讲述Intel微处理器的国外经典教材,已经多次再版,经过长期教学使用,吐故纳新,不断完善,内容丰富,体系完整。第6版中包含了微处理器领域的最新技术发展,涵盖了Pentium 4的内容。本书结合实例讲解工作原理,并给出小结和习题,既适合教学使用,也适合自学。书中许多实例都可以作为开发类似应用的模板和原型,极具实用价值。附录还给出了备查资料,供设计和调试汇编语言时使用。本书可作为高等院校计算机、......一起来看看 《Intel系列微处理器体系结构、编程与接口》 这本书的介绍吧!
随机密码生成器
多种字符组合密码
HEX HSV 转换工具
HEX HSV 互换工具