github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/log/format.go (about)

     1  
     2  //<developer>
     3  //    <name>linapex 曹一峰</name>
     4  //    <email>linapex@163.com</email>
     5  //    <wx>superexc</wx>
     6  //    <qqgroup>128148617</qqgroup>
     7  //    <url>https://jsq.ink</url>
     8  //    <role>pku engineer</role>
     9  //    <date>2019-03-16 12:09:41</date>
    10  //</624342646340456448>
    11  
    12  package log
    13  
    14  import (
    15  	"bytes"
    16  	"encoding/json"
    17  	"fmt"
    18  	"reflect"
    19  	"strconv"
    20  	"strings"
    21  	"sync"
    22  	"sync/atomic"
    23  	"time"
    24  	"unicode/utf8"
    25  )
    26  
    27  const (
    28  	timeFormat     = "2006-01-02T15:04:05-0700"
    29  	termTimeFormat = "01-02|15:04:05.000"
    30  	floatFormat    = 'f'
    31  	termMsgJust    = 40
    32  )
    33  
    34  //为了避免不灵活的日志行,对locationtrims进行了修剪以供显示。
    35  var locationTrims = []string{
    36  	"github.com/ethereum/go-ethereum/",
    37  }
    38  
    39  //printorigins设置或取消设置终端的日志位置(文件:行)打印
    40  //格式输出。
    41  func PrintOrigins(print bool) {
    42  	if print {
    43  		atomic.StoreUint32(&locationEnabled, 1)
    44  	} else {
    45  		atomic.StoreUint32(&locationEnabled, 0)
    46  	}
    47  }
    48  
    49  //locationEnabled是一个原子标记,用于控制终端格式化程序
    50  //打印条目时也应附加日志位置。
    51  var locationEnabled uint32
    52  
    53  //locationLength是遇到的最大路径长度,所有日志都是
    54  //填充以帮助对齐。
    55  var locationLength uint32
    56  
    57  //FieldPadding是一个全局映射,迄今为止字段值长度最大。
    58  //以更智能的方式填充日志上下文。
    59  var fieldPadding = make(map[string]int)
    60  
    61  //FieldPaddingLock是一个全局互斥体,用于保护字段填充映射。
    62  var fieldPaddingLock sync.RWMutex
    63  
    64  type Format interface {
    65  	Format(r *Record) []byte
    66  }
    67  
    68  //formatfunc返回一个新的格式对象,该对象使用
    69  //执行记录格式设置的给定函数。
    70  func FormatFunc(f func(*Record) []byte) Format {
    71  	return formatFunc(f)
    72  }
    73  
    74  type formatFunc func(*Record) []byte
    75  
    76  func (f formatFunc) Format(r *Record) []byte {
    77  	return f(r)
    78  }
    79  
    80  //TerminalStringer是与stdlib stringer类似的接口,允许
    81  //当打印到
    82  //屏幕。
    83  type TerminalStringer interface {
    84  	TerminalString() string
    85  }
    86  
    87  //TerminalFormat格式为人类可读性优化的日志记录
    88  //一种具有彩色编码电平输出和用户友好时间戳的终端。
    89  //此格式只能用于交互式程序或在开发时使用。
    90  //
    91  //[级别][时间]mesage key=value key=value…
    92  //
    93  //例子:
    94  //
    95  //[DBUG][5月16日20:58:45]删除路由ns=haproxy addr=127.0.0.1:50002
    96  //
    97  func TerminalFormat(usecolor bool) Format {
    98  	return FormatFunc(func(r *Record) []byte {
    99  		var color = 0
   100  		if usecolor {
   101  			switch r.Lvl {
   102  			case LvlCrit:
   103  				color = 35
   104  			case LvlError:
   105  				color = 31
   106  			case LvlWarn:
   107  				color = 33
   108  			case LvlInfo:
   109  				color = 32
   110  			case LvlDebug:
   111  				color = 36
   112  			case LvlTrace:
   113  				color = 34
   114  			}
   115  		}
   116  
   117  		b := &bytes.Buffer{}
   118  		lvl := r.Lvl.AlignedString()
   119  		if atomic.LoadUint32(&locationEnabled) != 0 {
   120  //已请求日志源打印,设置位置路径和行号的格式
   121  			location := fmt.Sprintf("%+v", r.Call)
   122  			for _, prefix := range locationTrims {
   123  				location = strings.TrimPrefix(location, prefix)
   124  			}
   125  //保持风扇对准的最大位置长度
   126  			align := int(atomic.LoadUint32(&locationLength))
   127  			if align < len(location) {
   128  				align = len(location)
   129  				atomic.StoreUint32(&locationLength, uint32(align))
   130  			}
   131  			padding := strings.Repeat(" ", align-len(location))
   132  
   133  //组装并打印日志标题
   134  			if color > 0 {
   135  				fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s|%s]%s %s ", color, lvl, r.Time.Format(termTimeFormat), location, padding, r.Msg)
   136  			} else {
   137  				fmt.Fprintf(b, "%s[%s|%s]%s %s ", lvl, r.Time.Format(termTimeFormat), location, padding, r.Msg)
   138  			}
   139  		} else {
   140  			if color > 0 {
   141  				fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %s ", color, lvl, r.Time.Format(termTimeFormat), r.Msg)
   142  			} else {
   143  				fmt.Fprintf(b, "%s[%s] %s ", lvl, r.Time.Format(termTimeFormat), r.Msg)
   144  			}
   145  		}
   146  //尝试调整短消息的日志输出
   147  		length := utf8.RuneCountInString(r.Msg)
   148  		if len(r.Ctx) > 0 && length < termMsgJust {
   149  			b.Write(bytes.Repeat([]byte{' '}, termMsgJust-length))
   150  		}
   151  //打印钥匙日志Fmt样式
   152  		logfmt(b, r.Ctx, color, true)
   153  		return b.Bytes()
   154  	})
   155  }
   156  
   157  //logfmt format以logfmt格式打印记录,这是一种易于机器分析但可供人阅读的格式。
   158  //键/值对的格式。
   159  //
   160  //有关更多详细信息,请参阅:http://godoc.org/github.com/kr/logfmt
   161  //
   162  func LogfmtFormat() Format {
   163  	return FormatFunc(func(r *Record) []byte {
   164  		common := []interface{}{r.KeyNames.Time, r.Time, r.KeyNames.Lvl, r.Lvl, r.KeyNames.Msg, r.Msg}
   165  		buf := &bytes.Buffer{}
   166  		logfmt(buf, append(common, r.Ctx...), 0, false)
   167  		return buf.Bytes()
   168  	})
   169  }
   170  
   171  func logfmt(buf *bytes.Buffer, ctx []interface{}, color int, term bool) {
   172  	for i := 0; i < len(ctx); i += 2 {
   173  		if i != 0 {
   174  			buf.WriteByte(' ')
   175  		}
   176  
   177  		k, ok := ctx[i].(string)
   178  		v := formatLogfmtValue(ctx[i+1], term)
   179  		if !ok {
   180  			k, v = errorKey, formatLogfmtValue(k, term)
   181  		}
   182  
   183  //我们应该检查一下你所有的密钥字节是否都是无效的。
   184  		fieldPaddingLock.RLock()
   185  		padding := fieldPadding[k]
   186  		fieldPaddingLock.RUnlock()
   187  
   188  		length := utf8.RuneCountInString(v)
   189  		if padding < length {
   190  			padding = length
   191  
   192  			fieldPaddingLock.Lock()
   193  			fieldPadding[k] = padding
   194  			fieldPaddingLock.Unlock()
   195  		}
   196  		if color > 0 {
   197  			fmt.Fprintf(buf, "\x1b[%dm%s\x1b[0m=", color, k)
   198  		} else {
   199  			buf.WriteString(k)
   200  			buf.WriteByte('=')
   201  		}
   202  		buf.WriteString(v)
   203  		if i < len(ctx)-2 {
   204  			buf.Write(bytes.Repeat([]byte{' '}, padding-length))
   205  		}
   206  	}
   207  	buf.WriteByte('\n')
   208  }
   209  
   210  //JSonFormat将日志记录格式化为用换行符分隔的JSON对象。
   211  //它相当于jsonFormatex(false,true)。
   212  func JSONFormat() Format {
   213  	return JSONFormatEx(false, true)
   214  }
   215  
   216  //jsonFormatOrderedEx将日志记录格式化为json数组。如果漂亮是真的,
   217  //记录将被打印得很漂亮。如果行分隔为真,则记录
   218  //将在每个记录之间用新行记录。
   219  func JSONFormatOrderedEx(pretty, lineSeparated bool) Format {
   220  	jsonMarshal := json.Marshal
   221  	if pretty {
   222  		jsonMarshal = func(v interface{}) ([]byte, error) {
   223  			return json.MarshalIndent(v, "", "    ")
   224  		}
   225  	}
   226  	return FormatFunc(func(r *Record) []byte {
   227  		props := make(map[string]interface{})
   228  
   229  		props[r.KeyNames.Time] = r.Time
   230  		props[r.KeyNames.Lvl] = r.Lvl.String()
   231  		props[r.KeyNames.Msg] = r.Msg
   232  
   233  		ctx := make([]string, len(r.Ctx))
   234  		for i := 0; i < len(r.Ctx); i += 2 {
   235  			k, ok := r.Ctx[i].(string)
   236  			if !ok {
   237  				props[errorKey] = fmt.Sprintf("%+v is not a string key,", r.Ctx[i])
   238  			}
   239  			ctx[i] = k
   240  			ctx[i+1] = formatLogfmtValue(r.Ctx[i+1], true)
   241  		}
   242  		props[r.KeyNames.Ctx] = ctx
   243  
   244  		b, err := jsonMarshal(props)
   245  		if err != nil {
   246  			b, _ = jsonMarshal(map[string]string{
   247  				errorKey: err.Error(),
   248  			})
   249  			return b
   250  		}
   251  		if lineSeparated {
   252  			b = append(b, '\n')
   253  		}
   254  		return b
   255  	})
   256  }
   257  
   258  //jsonFormatex将日志记录格式化为json对象。如果漂亮是真的,
   259  //记录将被打印得很漂亮。如果行分隔为真,则记录
   260  //将在每个记录之间用新行记录。
   261  func JSONFormatEx(pretty, lineSeparated bool) Format {
   262  	jsonMarshal := json.Marshal
   263  	if pretty {
   264  		jsonMarshal = func(v interface{}) ([]byte, error) {
   265  			return json.MarshalIndent(v, "", "    ")
   266  		}
   267  	}
   268  
   269  	return FormatFunc(func(r *Record) []byte {
   270  		props := make(map[string]interface{})
   271  
   272  		props[r.KeyNames.Time] = r.Time
   273  		props[r.KeyNames.Lvl] = r.Lvl.String()
   274  		props[r.KeyNames.Msg] = r.Msg
   275  
   276  		for i := 0; i < len(r.Ctx); i += 2 {
   277  			k, ok := r.Ctx[i].(string)
   278  			if !ok {
   279  				props[errorKey] = fmt.Sprintf("%+v is not a string key", r.Ctx[i])
   280  			}
   281  			props[k] = formatJSONValue(r.Ctx[i+1])
   282  		}
   283  
   284  		b, err := jsonMarshal(props)
   285  		if err != nil {
   286  			b, _ = jsonMarshal(map[string]string{
   287  				errorKey: err.Error(),
   288  			})
   289  			return b
   290  		}
   291  
   292  		if lineSeparated {
   293  			b = append(b, '\n')
   294  		}
   295  
   296  		return b
   297  	})
   298  }
   299  
   300  func formatShared(value interface{}) (result interface{}) {
   301  	defer func() {
   302  		if err := recover(); err != nil {
   303  			if v := reflect.ValueOf(value); v.Kind() == reflect.Ptr && v.IsNil() {
   304  				result = "nil"
   305  			} else {
   306  				panic(err)
   307  			}
   308  		}
   309  	}()
   310  
   311  	switch v := value.(type) {
   312  	case time.Time:
   313  		return v.Format(timeFormat)
   314  
   315  	case error:
   316  		return v.Error()
   317  
   318  	case fmt.Stringer:
   319  		return v.String()
   320  
   321  	default:
   322  		return v
   323  	}
   324  }
   325  
   326  func formatJSONValue(value interface{}) interface{} {
   327  	value = formatShared(value)
   328  	switch value.(type) {
   329  	case int, int8, int16, int32, int64, float32, float64, uint, uint8, uint16, uint32, uint64, string:
   330  		return value
   331  	default:
   332  		return fmt.Sprintf("%+v", value)
   333  	}
   334  }
   335  
   336  //FormatValue为序列化设置值的格式
   337  func formatLogfmtValue(value interface{}, term bool) string {
   338  	if value == nil {
   339  		return "nil"
   340  	}
   341  
   342  	if t, ok := value.(time.Time); ok {
   343  //性能优化:由于提供了
   344  //TimeFormat没有任何转义字符,转义是
   345  //昂贵。
   346  		return t.Format(timeFormat)
   347  	}
   348  	if term {
   349  		if s, ok := value.(TerminalStringer); ok {
   350  //提供自定义终端纵梁,使用
   351  			return escapeString(s.TerminalString())
   352  		}
   353  	}
   354  	value = formatShared(value)
   355  	switch v := value.(type) {
   356  	case bool:
   357  		return strconv.FormatBool(v)
   358  	case float32:
   359  		return strconv.FormatFloat(float64(v), floatFormat, 3, 64)
   360  	case float64:
   361  		return strconv.FormatFloat(v, floatFormat, 3, 64)
   362  	case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
   363  		return fmt.Sprintf("%d", value)
   364  	case string:
   365  		return escapeString(v)
   366  	default:
   367  		return escapeString(fmt.Sprintf("%+v", value))
   368  	}
   369  }
   370  
   371  var stringBufPool = sync.Pool{
   372  	New: func() interface{} { return new(bytes.Buffer) },
   373  }
   374  
   375  func escapeString(s string) string {
   376  	needsQuotes := false
   377  	needsEscape := false
   378  	for _, r := range s {
   379  		if r <= ' ' || r == '=' || r == '"' {
   380  			needsQuotes = true
   381  		}
   382  		if r == '\\' || r == '"' || r == '\n' || r == '\r' || r == '\t' {
   383  			needsEscape = true
   384  		}
   385  	}
   386  	if !needsEscape && !needsQuotes {
   387  		return s
   388  	}
   389  	e := stringBufPool.Get().(*bytes.Buffer)
   390  	e.WriteByte('"')
   391  	for _, r := range s {
   392  		switch r {
   393  		case '\\', '"':
   394  			e.WriteByte('\\')
   395  			e.WriteByte(byte(r))
   396  		case '\n':
   397  			e.WriteString("\\n")
   398  		case '\r':
   399  			e.WriteString("\\r")
   400  		case '\t':
   401  			e.WriteString("\\t")
   402  		default:
   403  			e.WriteRune(r)
   404  		}
   405  	}
   406  	e.WriteByte('"')
   407  	var ret string
   408  	if needsQuotes {
   409  		ret = e.String()
   410  	} else {
   411  		ret = string(e.Bytes()[1 : e.Len()-1])
   412  	}
   413  	e.Reset()
   414  	stringBufPool.Put(e)
   415  	return ret
   416  }
   417