github.com/cranelv/ethereum_mpc@v0.0.0-20191031014521-23aeb1415092/log/handler_testGlog.go (about)

     1  // Copyright 2018 The MATRIX Authors as well as Copyright 2014-2017 The go-ethereum Authors
     2  // This file is consisted of the MATRIX library and part of the go-ethereum library.
     3  //
     4  // The MATRIX-ethereum library is free software: you can redistribute it and/or modify it under the terms of the MIT License.
     5  //
     6  // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
     7  // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 
     8  //and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject tothe following conditions:
     9  //
    10  //The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
    11  //
    12  //THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    13  //FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
    14  //WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISINGFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
    15  //OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    16  
    17  package log
    18  
    19  import (
    20  	"fmt"
    21  	"regexp"
    22  	"runtime"
    23  	"strconv"
    24  	"strings"
    25  	"sync"
    26  	"sync/atomic"
    27  )
    28  
    29  // GlogHandler is a log handler that mimics the filtering features of Google's
    30  // glog logger: setting global log levels; overriding with callsite pattern
    31  // matches; and requesting backtraces at certain positions.
    32  func InitLog(verbosity uint32) {
    33  	Root().SetHandler(NewTestHandler(verbosity))
    34  }
    35  
    36  type TestHandler struct {
    37  	fmtr Format
    38  
    39  	level     uint32 // Current log level, atomically accessible
    40  	override  uint32 // Flag whether overrides are used, atomically accessible
    41  	backtrace uint32 // Flag whether backtrace location is set
    42  
    43  	patterns  []pattern       // Current list of patterns to override with
    44  	siteCache map[uintptr]Lvl // Cache of callsite pattern evaluations
    45  	location  string          // file:line location where to do a stackdump at
    46  	lock      sync.RWMutex    // Lock protecting the override pattern list
    47  }
    48  
    49  // NewGlogHandler creates a new log handler with filtering functionality similar
    50  // to Google's glog logger. The returned handler implements Handler.
    51  func NewTestHandler(verbosity uint32) *TestHandler {
    52  	return &TestHandler{
    53  		fmtr:  TerminalFormat(false),
    54  		level: verbosity,
    55  		//		fmtr : JSONFormatEx(false,true),
    56  		//		fmtr : JSONFormatEx(false,true),
    57  	}
    58  }
    59  
    60  // pattern contains a filter for the Vmodule option, holding a verbosity level
    61  // and a file pattern to match.
    62  
    63  // Verbosity sets the glog verbosity ceiling. The verbosity of individual packages
    64  // and source files can be raised using Vmodule.
    65  func (h *TestHandler) Verbosity(level Lvl) {
    66  	atomic.StoreUint32(&h.level, uint32(level))
    67  }
    68  
    69  // Vmodule sets the glog verbosity pattern.
    70  //
    71  // The syntax of the argument is a comma-separated list of pattern=N, where the
    72  // pattern is a literal file name or "glob" pattern matching and N is a V level.
    73  //
    74  // For instance:
    75  //
    76  //  pattern="gopher.go=3"
    77  //   sets the V level to 3 in all Go files named "gopher.go"
    78  //
    79  //  pattern="foo=3"
    80  //   sets V to 3 in all files of any packages whose import path ends in "foo"
    81  //
    82  //  pattern="foo/*=3"
    83  //   sets V to 3 in all files of any packages whose import path contains "foo"
    84  func (h *TestHandler) Vmodule(ruleset string) error {
    85  	var filter []pattern
    86  	for _, rule := range strings.Split(ruleset, ",") {
    87  		// Empty strings such as from a trailing comma can be ignored
    88  		if len(rule) == 0 {
    89  			continue
    90  		}
    91  		// Ensure we have a pattern = level filter rule
    92  		parts := strings.Split(rule, "=")
    93  		if len(parts) != 2 {
    94  			return errVmoduleSyntax
    95  		}
    96  		parts[0] = strings.TrimSpace(parts[0])
    97  		parts[1] = strings.TrimSpace(parts[1])
    98  		if len(parts[0]) == 0 || len(parts[1]) == 0 {
    99  			return errVmoduleSyntax
   100  		}
   101  		// Parse the level and if correct, assemble the filter rule
   102  		level, err := strconv.Atoi(parts[1])
   103  		if err != nil {
   104  			return errVmoduleSyntax
   105  		}
   106  		if level <= 0 {
   107  			continue // Ignore. It's harmless but no point in paying the overhead.
   108  		}
   109  		// Compile the rule pattern into a regular expression
   110  		matcher := ".*"
   111  		for _, comp := range strings.Split(parts[0], "/") {
   112  			if comp == "*" {
   113  				matcher += "(/.*)?"
   114  			} else if comp != "" {
   115  				matcher += "/" + regexp.QuoteMeta(comp)
   116  			}
   117  		}
   118  		if !strings.HasSuffix(parts[0], ".go") {
   119  			matcher += "/[^/]+\\.go"
   120  		}
   121  		matcher = matcher + "$"
   122  
   123  		re, _ := regexp.Compile(matcher)
   124  		filter = append(filter, pattern{re, Lvl(level)})
   125  	}
   126  	// Swap out the vmodule pattern for the new filter system
   127  	h.lock.Lock()
   128  	defer h.lock.Unlock()
   129  
   130  	h.patterns = filter
   131  	h.siteCache = make(map[uintptr]Lvl)
   132  	atomic.StoreUint32(&h.override, uint32(len(filter)))
   133  
   134  	return nil
   135  }
   136  
   137  // BacktraceAt sets the glog backtrace location. When set to a file and line
   138  // number holding a logging statement, a stack trace will be written to the Info
   139  // log whenever execution hits that statement.
   140  //
   141  // Unlike with Vmodule, the ".go" must be present.
   142  func (h *TestHandler) BacktraceAt(location string) error {
   143  	// Ensure the backtrace location contains two non-empty elements
   144  	parts := strings.Split(location, ":")
   145  	if len(parts) != 2 {
   146  		return errTraceSyntax
   147  	}
   148  	parts[0] = strings.TrimSpace(parts[0])
   149  	parts[1] = strings.TrimSpace(parts[1])
   150  	if len(parts[0]) == 0 || len(parts[1]) == 0 {
   151  		return errTraceSyntax
   152  	}
   153  	// Ensure the .go prefix is present and the line is valid
   154  	if !strings.HasSuffix(parts[0], ".go") {
   155  		return errTraceSyntax
   156  	}
   157  	if _, err := strconv.Atoi(parts[1]); err != nil {
   158  		return errTraceSyntax
   159  	}
   160  	// All seems valid
   161  	h.lock.Lock()
   162  	defer h.lock.Unlock()
   163  
   164  	h.location = location
   165  	atomic.StoreUint32(&h.backtrace, uint32(len(location)))
   166  
   167  	return nil
   168  }
   169  
   170  // Log implements Handler.Log, filtering a log record through the global, local
   171  // and backtrace filters, finally emitting it if either allow it through.
   172  func (h *TestHandler) Log(r *Record) error {
   173  	// If backtracing is requested, check whether this is the callsite
   174  	if atomic.LoadUint32(&h.backtrace) > 0 {
   175  		// Everything below here is slow. Although we could cache the call sites the
   176  		// same way as for vmodule, backtracing is so rare it's not worth the extra
   177  		// complexity.
   178  		h.lock.RLock()
   179  		match := h.location == r.Call.String()
   180  		h.lock.RUnlock()
   181  
   182  		if match {
   183  			// Callsite matched, raise the log level to info and gather the stacks
   184  			r.Lvl = LvlInfo
   185  
   186  			buf := make([]byte, 1024*1024)
   187  			buf = buf[:runtime.Stack(buf, true)]
   188  			r.Msg += "\n\n" + string(buf)
   189  		}
   190  	}
   191  	// If the global log level allows, fast track logging
   192  	if atomic.LoadUint32(&h.level) >= uint32(r.Lvl) {
   193  		fmt.Print(r.Call.String(),"\t",r.Call1.String(),"\t",r.Call2.String(), ": \t", string(h.fmtr.Format(r)))
   194  		return nil
   195  	}
   196  	// If no local overrides are present, fast track skipping
   197  	if atomic.LoadUint32(&h.override) == 0 {
   198  		return nil
   199  	}
   200  	// Check callsite cache for previously calculated log levels
   201  	h.lock.RLock()
   202  	lvl, ok := h.siteCache[r.Call.PC()]
   203  	h.lock.RUnlock()
   204  
   205  	// If we didn't cache the callsite yet, calculate it
   206  	if !ok {
   207  		h.lock.Lock()
   208  		for _, rule := range h.patterns {
   209  			if rule.pattern.MatchString(fmt.Sprintf("%+s", r.Call)) {
   210  				h.siteCache[r.Call.PC()], lvl, ok = rule.level, rule.level, true
   211  				break
   212  			}
   213  		}
   214  		// If no rule matched, remember to drop log the next time
   215  		if !ok {
   216  			h.siteCache[r.Call.PC()] = 0
   217  		}
   218  		h.lock.Unlock()
   219  	}
   220  	if lvl >= r.Lvl {
   221  		fmt.Print(r.Call.String(),"\t",r.Call1.String(),"\t",r.Call2.String(), ": \t", string(h.fmtr.Format(r)))
   222  		return nil
   223  	}
   224  	return nil
   225  }