mirror of
https://github.com/status-im/status-go.git
synced 2025-01-09 14:16:21 +00:00
eeca435064
Update vendor Integrate rendezvous into status node Add a test with failover using rendezvous Use multiple servers in client Use discovery V5 by default and test that node can be started with rendezvous discovet Fix linter Update rendezvous client to one with instrumented stream Address feedback Fix test with updated topic limits Apply several suggestions Change log to debug for request errors because we continue execution Remove web3js after rebase Update rendezvous package
369 lines
9.2 KiB
Go
369 lines
9.2 KiB
Go
// Copyright 2013, Örjan Persson. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package logging
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"regexp"
|
|
"runtime"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// TODO see Formatter interface in fmt/print.go
|
|
// TODO try text/template, maybe it have enough performance
|
|
// TODO other template systems?
|
|
// TODO make it possible to specify formats per backend?
|
|
type fmtVerb int
|
|
|
|
const (
|
|
fmtVerbTime fmtVerb = iota
|
|
fmtVerbLevel
|
|
fmtVerbId
|
|
fmtVerbPid
|
|
fmtVerbProgram
|
|
fmtVerbModule
|
|
fmtVerbMessage
|
|
fmtVerbLongfile
|
|
fmtVerbShortfile
|
|
fmtVerbLongpkg
|
|
fmtVerbShortpkg
|
|
fmtVerbLongfunc
|
|
fmtVerbShortfunc
|
|
fmtVerbLevelColor
|
|
|
|
// Keep last, there are no match for these below.
|
|
fmtVerbUnknown
|
|
fmtVerbStatic
|
|
)
|
|
|
|
var fmtVerbs = []string{
|
|
"time",
|
|
"level",
|
|
"id",
|
|
"pid",
|
|
"program",
|
|
"module",
|
|
"message",
|
|
"longfile",
|
|
"shortfile",
|
|
"longpkg",
|
|
"shortpkg",
|
|
"longfunc",
|
|
"shortfunc",
|
|
"color",
|
|
}
|
|
|
|
const rfc3339Milli = "2006-01-02T15:04:05.999Z07:00"
|
|
|
|
var defaultVerbsLayout = []string{
|
|
rfc3339Milli,
|
|
"s",
|
|
"d",
|
|
"d",
|
|
"s",
|
|
"s",
|
|
"s",
|
|
"s",
|
|
"s",
|
|
"s",
|
|
"s",
|
|
"s",
|
|
"s",
|
|
"",
|
|
}
|
|
|
|
var (
|
|
pid = os.Getpid()
|
|
program = filepath.Base(os.Args[0])
|
|
)
|
|
|
|
func getFmtVerbByName(name string) fmtVerb {
|
|
for i, verb := range fmtVerbs {
|
|
if name == verb {
|
|
return fmtVerb(i)
|
|
}
|
|
}
|
|
return fmtVerbUnknown
|
|
}
|
|
|
|
// Formatter is the required interface for a custom log record formatter.
|
|
type Formatter interface {
|
|
Format(calldepth int, r *Record, w io.Writer) error
|
|
}
|
|
|
|
// formatter is used by all backends unless otherwise overriden.
|
|
var formatter struct {
|
|
sync.RWMutex
|
|
def Formatter
|
|
}
|
|
|
|
func getFormatter() Formatter {
|
|
formatter.RLock()
|
|
defer formatter.RUnlock()
|
|
return formatter.def
|
|
}
|
|
|
|
var (
|
|
// DefaultFormatter is the default formatter used and is only the message.
|
|
DefaultFormatter Formatter = MustStringFormatter("%{message}")
|
|
|
|
// Glog format
|
|
GlogFormatter Formatter = MustStringFormatter("%{level:.1s}%{time:0102 15:04:05.999999} %{pid} %{shortfile}] %{message}")
|
|
)
|
|
|
|
// SetFormatter sets the default formatter for all new backends. A backend will
|
|
// fetch this value once it is needed to format a record. Note that backends
|
|
// will cache the formatter after the first point. For now, make sure to set
|
|
// the formatter before logging.
|
|
func SetFormatter(f Formatter) {
|
|
formatter.Lock()
|
|
defer formatter.Unlock()
|
|
formatter.def = f
|
|
}
|
|
|
|
var formatRe *regexp.Regexp = regexp.MustCompile(`%{([a-z]+)(?::(.*?[^\\]))?}`)
|
|
|
|
type part struct {
|
|
verb fmtVerb
|
|
layout string
|
|
}
|
|
|
|
// stringFormatter contains a list of parts which explains how to build the
|
|
// formatted string passed on to the logging backend.
|
|
type stringFormatter struct {
|
|
parts []part
|
|
}
|
|
|
|
// NewStringFormatter returns a new Formatter which outputs the log record as a
|
|
// string based on the 'verbs' specified in the format string.
|
|
//
|
|
// The verbs:
|
|
//
|
|
// General:
|
|
// %{id} Sequence number for log message (uint64).
|
|
// %{pid} Process id (int)
|
|
// %{time} Time when log occurred (time.Time)
|
|
// %{level} Log level (Level)
|
|
// %{module} Module (string)
|
|
// %{program} Basename of os.Args[0] (string)
|
|
// %{message} Message (string)
|
|
// %{longfile} Full file name and line number: /a/b/c/d.go:23
|
|
// %{shortfile} Final file name element and line number: d.go:23
|
|
// %{color} ANSI color based on log level
|
|
//
|
|
// For normal types, the output can be customized by using the 'verbs' defined
|
|
// in the fmt package, eg. '%{id:04d}' to make the id output be '%04d' as the
|
|
// format string.
|
|
//
|
|
// For time.Time, use the same layout as time.Format to change the time format
|
|
// when output, eg "2006-01-02T15:04:05.999Z-07:00".
|
|
//
|
|
// For the 'color' verb, the output can be adjusted to either use bold colors,
|
|
// i.e., '%{color:bold}' or to reset the ANSI attributes, i.e.,
|
|
// '%{color:reset}' Note that if you use the color verb explicitly, be sure to
|
|
// reset it or else the color state will persist past your log message. e.g.,
|
|
// "%{color:bold}%{time:15:04:05} %{level:-8s}%{color:reset} %{message}" will
|
|
// just colorize the time and level, leaving the message uncolored.
|
|
//
|
|
// There's also a couple of experimental 'verbs'. These are exposed to get
|
|
// feedback and needs a bit of tinkering. Hence, they might change in the
|
|
// future.
|
|
//
|
|
// Experimental:
|
|
// %{longpkg} Full package path, eg. github.com/go-logging
|
|
// %{shortpkg} Base package path, eg. go-logging
|
|
// %{longfunc} Full function name, eg. littleEndian.PutUint32
|
|
// %{shortfunc} Base function name, eg. PutUint32
|
|
func NewStringFormatter(format string) (*stringFormatter, error) {
|
|
var fmter = &stringFormatter{}
|
|
|
|
// Find the boundaries of all %{vars}
|
|
matches := formatRe.FindAllStringSubmatchIndex(format, -1)
|
|
if matches == nil {
|
|
return nil, errors.New("logger: invalid log format: " + format)
|
|
}
|
|
|
|
// Collect all variables and static text for the format
|
|
prev := 0
|
|
for _, m := range matches {
|
|
start, end := m[0], m[1]
|
|
if start > prev {
|
|
fmter.add(fmtVerbStatic, format[prev:start])
|
|
}
|
|
|
|
name := format[m[2]:m[3]]
|
|
verb := getFmtVerbByName(name)
|
|
if verb == fmtVerbUnknown {
|
|
return nil, errors.New("logger: unknown variable: " + name)
|
|
}
|
|
|
|
// Handle layout customizations or use the default. If this is not for the
|
|
// time or color formatting, we need to prefix with %.
|
|
layout := defaultVerbsLayout[verb]
|
|
if m[4] != -1 {
|
|
layout = format[m[4]:m[5]]
|
|
}
|
|
if verb != fmtVerbTime && verb != fmtVerbLevelColor {
|
|
layout = "%" + layout
|
|
}
|
|
|
|
fmter.add(verb, layout)
|
|
prev = end
|
|
}
|
|
end := format[prev:]
|
|
if end != "" {
|
|
fmter.add(fmtVerbStatic, end)
|
|
}
|
|
|
|
// Make a test run to make sure we can format it correctly.
|
|
t, err := time.Parse(time.RFC3339, "2010-02-04T21:00:57-08:00")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
r := &Record{
|
|
Id: 12345,
|
|
Time: t,
|
|
Module: "logger",
|
|
fmt: "hello %s",
|
|
args: []interface{}{"go"},
|
|
}
|
|
if err := fmter.Format(0, r, &bytes.Buffer{}); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return fmter, nil
|
|
}
|
|
|
|
// MustStringFormatter is equivalent to NewStringFormatter with a call to panic
|
|
// on error.
|
|
func MustStringFormatter(format string) *stringFormatter {
|
|
f, err := NewStringFormatter(format)
|
|
if err != nil {
|
|
panic("Failed to initialized string formatter: " + err.Error())
|
|
}
|
|
return f
|
|
}
|
|
|
|
func (f *stringFormatter) add(verb fmtVerb, layout string) {
|
|
f.parts = append(f.parts, part{verb, layout})
|
|
}
|
|
|
|
func (f *stringFormatter) Format(calldepth int, r *Record, output io.Writer) error {
|
|
for _, part := range f.parts {
|
|
if part.verb == fmtVerbStatic {
|
|
output.Write([]byte(part.layout))
|
|
} else if part.verb == fmtVerbTime {
|
|
output.Write([]byte(r.Time.Format(part.layout)))
|
|
} else if part.verb == fmtVerbLevelColor {
|
|
if part.layout == "bold" {
|
|
output.Write([]byte(boldcolors[r.Level]))
|
|
} else if part.layout == "reset" {
|
|
output.Write([]byte("\033[0m"))
|
|
} else {
|
|
output.Write([]byte(colors[r.Level]))
|
|
}
|
|
} else {
|
|
var v interface{}
|
|
switch part.verb {
|
|
case fmtVerbLevel:
|
|
v = r.Level
|
|
break
|
|
case fmtVerbId:
|
|
v = r.Id
|
|
break
|
|
case fmtVerbPid:
|
|
v = pid
|
|
break
|
|
case fmtVerbProgram:
|
|
v = program
|
|
break
|
|
case fmtVerbModule:
|
|
v = r.Module
|
|
break
|
|
case fmtVerbMessage:
|
|
v = r.Message()
|
|
break
|
|
case fmtVerbLongfile, fmtVerbShortfile:
|
|
_, file, line, ok := runtime.Caller(calldepth + 1)
|
|
if !ok {
|
|
file = "???"
|
|
line = 0
|
|
} else if part.verb == fmtVerbShortfile {
|
|
file = filepath.Base(file)
|
|
}
|
|
v = fmt.Sprintf("%s:%d", file, line)
|
|
case fmtVerbLongfunc, fmtVerbShortfunc,
|
|
fmtVerbLongpkg, fmtVerbShortpkg:
|
|
// TODO cache pc
|
|
v = "???"
|
|
if pc, _, _, ok := runtime.Caller(calldepth + 1); ok {
|
|
if f := runtime.FuncForPC(pc); f != nil {
|
|
v = formatFuncName(part.verb, f.Name())
|
|
}
|
|
}
|
|
default:
|
|
panic("unhandled format part")
|
|
}
|
|
fmt.Fprintf(output, part.layout, v)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// formatFuncName tries to extract certain part of the runtime formatted
|
|
// function name to some pre-defined variation.
|
|
//
|
|
// This function is known to not work properly if the package path or name
|
|
// contains a dot.
|
|
func formatFuncName(v fmtVerb, f string) string {
|
|
i := strings.LastIndex(f, "/")
|
|
j := strings.Index(f[i+1:], ".")
|
|
if j < 1 {
|
|
return "???"
|
|
}
|
|
pkg, fun := f[:i+j+1], f[i+j+2:]
|
|
switch v {
|
|
case fmtVerbLongpkg:
|
|
return pkg
|
|
case fmtVerbShortpkg:
|
|
return path.Base(pkg)
|
|
case fmtVerbLongfunc:
|
|
return fun
|
|
case fmtVerbShortfunc:
|
|
i = strings.LastIndex(fun, ".")
|
|
return fun[i+1:]
|
|
}
|
|
panic("unexpected func formatter")
|
|
}
|
|
|
|
// backendFormatter combines a backend with a specific formatter making it
|
|
// possible to have different log formats for different backends.
|
|
type backendFormatter struct {
|
|
b Backend
|
|
f Formatter
|
|
}
|
|
|
|
// NewBackendFormatter creates a new backend which makes all records that
|
|
// passes through it beeing formatted by the specific formatter.
|
|
func NewBackendFormatter(b Backend, f Formatter) *backendFormatter {
|
|
return &backendFormatter{b, f}
|
|
}
|
|
|
|
// Log implements the Log function required by the Backend interface.
|
|
func (bf *backendFormatter) Log(level Level, calldepth int, r *Record) error {
|
|
// Make a shallow copy of the record and replace any formatter
|
|
r2 := *r
|
|
r2.formatter = bf.f
|
|
return bf.b.Log(level, calldepth+1, &r2)
|
|
}
|