From 1233054b483af56f6cda4a696059be13fea5c7f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Isabel=20Sch=C3=B6ps=20Thiel=20=40IsabelSchoepd?= <155141998+IST-Github@users.noreply.github.com> Date: Thu, 4 Jan 2024 04:30:59 +0100 Subject: [PATCH] Delete log directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Isabel Schöps Thiel @IsabelSchoepd <155141998+IST-Github@users.noreply.github.com> --- log/format.go | 369 -------------------------------------------- log/format_test.go | 24 --- log/handler.go | 192 ----------------------- log/handler_glog.go | 209 ------------------------- log/logger.go | 210 ------------------------- log/logger_test.go | 172 --------------------- log/root.go | 116 -------------- 7 files changed, 1292 deletions(-) delete mode 100644 log/format.go delete mode 100644 log/format_test.go delete mode 100644 log/handler.go delete mode 100644 log/handler_glog.go delete mode 100644 log/logger.go delete mode 100644 log/logger_test.go delete mode 100644 log/root.go diff --git a/log/format.go b/log/format.go deleted file mode 100644 index 6447f3c1f1..0000000000 --- a/log/format.go +++ /dev/null @@ -1,369 +0,0 @@ -package log - -import ( - "bytes" - "fmt" - "math/big" - "reflect" - "strconv" - "time" - "unicode/utf8" - - "github.com/holiman/uint256" - "golang.org/x/exp/slog" -) - -const ( - timeFormat = "2006-01-02T15:04:05-0700" - floatFormat = 'f' - termMsgJust = 40 - termCtxMaxPadding = 40 -) - -// 40 spaces -var spaces = []byte(" ") - -// TerminalStringer is an analogous interface to the stdlib stringer, allowing -// own types to have custom shortened serialization formats when printed to the -// screen. -type TerminalStringer interface { - TerminalString() string -} - -func (h *TerminalHandler) format(buf []byte, r slog.Record, usecolor bool) []byte { - msg := escapeMessage(r.Message) - var color = "" - if usecolor { - switch r.Level { - case LevelCrit: - color = "\x1b[35m" - case slog.LevelError: - color = "\x1b[31m" - case slog.LevelWarn: - color = "\x1b[33m" - case slog.LevelInfo: - color = "\x1b[32m" - case slog.LevelDebug: - color = "\x1b[36m" - case LevelTrace: - color = "\x1b[34m" - } - } - if buf == nil { - buf = make([]byte, 0, 30+termMsgJust) - } - b := bytes.NewBuffer(buf) - - if color != "" { // Start color - b.WriteString(color) - b.WriteString(LevelAlignedString(r.Level)) - b.WriteString("\x1b[0m") - } else { - b.WriteString(LevelAlignedString(r.Level)) - } - b.WriteString("[") - writeTimeTermFormat(b, r.Time) - b.WriteString("] ") - b.WriteString(msg) - - // try to justify the log output for short messages - //length := utf8.RuneCountInString(msg) - length := len(msg) - if (r.NumAttrs()+len(h.attrs)) > 0 && length < termMsgJust { - b.Write(spaces[:termMsgJust-length]) - } - // print the attributes - h.formatAttributes(b, r, color) - - return b.Bytes() -} - -func (h *TerminalHandler) formatAttributes(buf *bytes.Buffer, r slog.Record, color string) { - // tmp is a temporary buffer we use, until bytes.Buffer.AvailableBuffer() (1.21) - // can be used. - var tmp = make([]byte, 40) - writeAttr := func(attr slog.Attr, first, last bool) { - buf.WriteByte(' ') - - if color != "" { - buf.WriteString(color) - //buf.Write(appendEscapeString(buf.AvailableBuffer(), attr.Key)) - buf.Write(appendEscapeString(tmp[:0], attr.Key)) - buf.WriteString("\x1b[0m=") - } else { - //buf.Write(appendEscapeString(buf.AvailableBuffer(), attr.Key)) - buf.Write(appendEscapeString(tmp[:0], attr.Key)) - buf.WriteByte('=') - } - //val := FormatSlogValue(attr.Value, true, buf.AvailableBuffer()) - val := FormatSlogValue(attr.Value, tmp[:0]) - - padding := h.fieldPadding[attr.Key] - - length := utf8.RuneCount(val) - if padding < length && length <= termCtxMaxPadding { - padding = length - h.fieldPadding[attr.Key] = padding - } - buf.Write(val) - if !last && padding > length { - buf.Write(spaces[:padding-length]) - } - } - var n = 0 - var nAttrs = len(h.attrs) + r.NumAttrs() - for _, attr := range h.attrs { - writeAttr(attr, n == 0, n == nAttrs-1) - n++ - } - r.Attrs(func(attr slog.Attr) bool { - writeAttr(attr, n == 0, n == nAttrs-1) - n++ - return true - }) - buf.WriteByte('\n') -} - -// FormatSlogValue formats a slog.Value for serialization to terminal. -func FormatSlogValue(v slog.Value, tmp []byte) (result []byte) { - var value any - defer func() { - if err := recover(); err != nil { - if v := reflect.ValueOf(value); v.Kind() == reflect.Ptr && v.IsNil() { - result = []byte("") - } else { - panic(err) - } - } - }() - - switch v.Kind() { - case slog.KindString: - return appendEscapeString(tmp, v.String()) - case slog.KindInt64: // All int-types (int8, int16 etc) wind up here - return appendInt64(tmp, v.Int64()) - case slog.KindUint64: // All uint-types (uint8, uint16 etc) wind up here - return appendUint64(tmp, v.Uint64(), false) - case slog.KindFloat64: - return strconv.AppendFloat(tmp, v.Float64(), floatFormat, 3, 64) - case slog.KindBool: - return strconv.AppendBool(tmp, v.Bool()) - case slog.KindDuration: - value = v.Duration() - case slog.KindTime: - // Performance optimization: No need for escaping since the provided - // timeFormat doesn't have any escape characters, and escaping is - // expensive. - return v.Time().AppendFormat(tmp, timeFormat) - default: - value = v.Any() - } - if value == nil { - return []byte("") - } - switch v := value.(type) { - case *big.Int: // Need to be before fmt.Stringer-clause - return appendBigInt(tmp, v) - case *uint256.Int: // Need to be before fmt.Stringer-clause - return appendU256(tmp, v) - case error: - return appendEscapeString(tmp, v.Error()) - case TerminalStringer: - return appendEscapeString(tmp, v.TerminalString()) - case fmt.Stringer: - return appendEscapeString(tmp, v.String()) - } - - // We can use the 'tmp' as a scratch-buffer, to first format the - // value, and in a second step do escaping. - internal := fmt.Appendf(tmp, "%+v", value) - return appendEscapeString(tmp, string(internal)) -} - -// appendInt64 formats n with thousand separators and writes into buffer dst. -func appendInt64(dst []byte, n int64) []byte { - if n < 0 { - return appendUint64(dst, uint64(-n), true) - } - return appendUint64(dst, uint64(n), false) -} - -// appendUint64 formats n with thousand separators and writes into buffer dst. -func appendUint64(dst []byte, n uint64, neg bool) []byte { - // Small numbers are fine as is - if n < 100000 { - if neg { - return strconv.AppendInt(dst, -int64(n), 10) - } else { - return strconv.AppendInt(dst, int64(n), 10) - } - } - // Large numbers should be split - const maxLength = 26 - - var ( - out = make([]byte, maxLength) - i = maxLength - 1 - comma = 0 - ) - for ; n > 0; i-- { - if comma == 3 { - comma = 0 - out[i] = ',' - } else { - comma++ - out[i] = '0' + byte(n%10) - n /= 10 - } - } - if neg { - out[i] = '-' - i-- - } - return append(dst, out[i+1:]...) -} - -// FormatLogfmtUint64 formats n with thousand separators. -func FormatLogfmtUint64(n uint64) string { - return string(appendUint64(nil, n, false)) -} - -// appendBigInt formats n with thousand separators and writes to dst. -func appendBigInt(dst []byte, n *big.Int) []byte { - if n.IsUint64() { - return appendUint64(dst, n.Uint64(), false) - } - if n.IsInt64() { - return appendInt64(dst, n.Int64()) - } - - var ( - text = n.String() - buf = make([]byte, len(text)+len(text)/3) - comma = 0 - i = len(buf) - 1 - ) - for j := len(text) - 1; j >= 0; j, i = j-1, i-1 { - c := text[j] - - switch { - case c == '-': - buf[i] = c - case comma == 3: - buf[i] = ',' - i-- - comma = 0 - fallthrough - default: - buf[i] = c - comma++ - } - } - return append(dst, buf[i+1:]...) -} - -// appendU256 formats n with thousand separators. -func appendU256(dst []byte, n *uint256.Int) []byte { - if n.IsUint64() { - return appendUint64(dst, n.Uint64(), false) - } - res := []byte(n.PrettyDec(',')) - return append(dst, res...) -} - -// appendEscapeString writes the string s to the given writer, with -// escaping/quoting if needed. -func appendEscapeString(dst []byte, s string) []byte { - needsQuoting := false - needsEscaping := false - for _, r := range s { - // If it contains spaces or equal-sign, we need to quote it. - if r == ' ' || r == '=' { - needsQuoting = true - continue - } - // We need to escape it, if it contains - // - character " (0x22) and lower (except space) - // - characters above ~ (0x7E), plus equal-sign - if r <= '"' || r > '~' { - needsEscaping = true - break - } - } - if needsEscaping { - return strconv.AppendQuote(dst, s) - } - // No escaping needed, but we might have to place within quote-marks, in case - // it contained a space - if needsQuoting { - dst = append(dst, '"') - dst = append(dst, []byte(s)...) - return append(dst, '"') - } - return append(dst, []byte(s)...) -} - -// escapeMessage checks if the provided string needs escaping/quoting, similarly -// to escapeString. The difference is that this method is more lenient: it allows -// for spaces and linebreaks to occur without needing quoting. -func escapeMessage(s string) string { - needsQuoting := false - for _, r := range s { - // Allow CR/LF/TAB. This is to make multi-line messages work. - if r == '\r' || r == '\n' || r == '\t' { - continue - } - // We quote everything below (0x20) and above~ (0x7E), - // plus equal-sign - if r < ' ' || r > '~' || r == '=' { - needsQuoting = true - break - } - } - if !needsQuoting { - return s - } - return strconv.Quote(s) -} - -// writeTimeTermFormat writes on the format "01-02|15:04:05.000" -func writeTimeTermFormat(buf *bytes.Buffer, t time.Time) { - _, month, day := t.Date() - writePosIntWidth(buf, int(month), 2) - buf.WriteByte('-') - writePosIntWidth(buf, day, 2) - buf.WriteByte('|') - hour, min, sec := t.Clock() - writePosIntWidth(buf, hour, 2) - buf.WriteByte(':') - writePosIntWidth(buf, min, 2) - buf.WriteByte(':') - writePosIntWidth(buf, sec, 2) - ns := t.Nanosecond() - buf.WriteByte('.') - writePosIntWidth(buf, ns/1e6, 3) -} - -// writePosIntWidth writes non-negative integer i to the buffer, padded on the left -// by zeroes to the given width. Use a width of 0 to omit padding. -// Adapted from golang.org/x/exp/slog/internal/buffer/buffer.go -func writePosIntWidth(b *bytes.Buffer, i, width int) { - // Cheap integer to fixed-width decimal ASCII. - // Copied from log/log.go. - if i < 0 { - panic("negative int") - } - // Assemble decimal in reverse order. - var bb [20]byte - bp := len(bb) - 1 - for i >= 10 || width > 1 { - width-- - q := i / 10 - bb[bp] = byte('0' + i - q*10) - bp-- - i = q - } - // i < 10 - bb[bp] = byte('0' + i) - b.Write(bb[bp:]) -} diff --git a/log/format_test.go b/log/format_test.go deleted file mode 100644 index d4c1df4abc..0000000000 --- a/log/format_test.go +++ /dev/null @@ -1,24 +0,0 @@ -package log - -import ( - "math/rand" - "testing" -) - -var sink []byte - -func BenchmarkPrettyInt64Logfmt(b *testing.B) { - buf := make([]byte, 100) - b.ReportAllocs() - for i := 0; i < b.N; i++ { - sink = appendInt64(buf, rand.Int63()) - } -} - -func BenchmarkPrettyUint64Logfmt(b *testing.B) { - buf := make([]byte, 100) - b.ReportAllocs() - for i := 0; i < b.N; i++ { - sink = appendUint64(buf, rand.Uint64(), false) - } -} diff --git a/log/handler.go b/log/handler.go deleted file mode 100644 index 7459aad891..0000000000 --- a/log/handler.go +++ /dev/null @@ -1,192 +0,0 @@ -package log - -import ( - "context" - "fmt" - "io" - "math/big" - "reflect" - "sync" - "time" - - "github.com/holiman/uint256" - "golang.org/x/exp/slog" -) - -type discardHandler struct{} - -// DiscardHandler returns a no-op handler -func DiscardHandler() slog.Handler { - return &discardHandler{} -} - -func (h *discardHandler) Handle(_ context.Context, r slog.Record) error { - return nil -} - -func (h *discardHandler) Enabled(_ context.Context, level slog.Level) bool { - return false -} - -func (h *discardHandler) WithGroup(name string) slog.Handler { - panic("not implemented") -} - -func (h *discardHandler) WithAttrs(attrs []slog.Attr) slog.Handler { - return &discardHandler{} -} - -type TerminalHandler struct { - mu sync.Mutex - wr io.Writer - lvl slog.Level - useColor bool - attrs []slog.Attr - // fieldPadding is a map with maximum field value lengths seen until now - // to allow padding log contexts in a bit smarter way. - fieldPadding map[string]int - - buf []byte -} - -// NewTerminalHandler returns a handler which formats log records at all levels optimized for human readability on -// a terminal with color-coded level output and terser human friendly timestamp. -// This format should only be used for interactive programs or while developing. -// -// [LEVEL] [TIME] MESSAGE key=value key=value ... -// -// Example: -// -// [DBUG] [May 16 20:58:45] remove route ns=haproxy addr=127.0.0.1:50002 -func NewTerminalHandler(wr io.Writer, useColor bool) *TerminalHandler { - return NewTerminalHandlerWithLevel(wr, levelMaxVerbosity, useColor) -} - -// NewTerminalHandlerWithLevel returns the same handler as NewTerminalHandler but only outputs -// records which are less than or equal to the specified verbosity level. -func NewTerminalHandlerWithLevel(wr io.Writer, lvl slog.Level, useColor bool) *TerminalHandler { - return &TerminalHandler{ - wr: wr, - lvl: lvl, - useColor: useColor, - fieldPadding: make(map[string]int), - } -} - -func (h *TerminalHandler) Handle(_ context.Context, r slog.Record) error { - h.mu.Lock() - defer h.mu.Unlock() - buf := h.format(h.buf, r, h.useColor) - h.wr.Write(buf) - h.buf = buf[:0] - return nil -} - -func (h *TerminalHandler) Enabled(_ context.Context, level slog.Level) bool { - return level >= h.lvl -} - -func (h *TerminalHandler) WithGroup(name string) slog.Handler { - panic("not implemented") -} - -func (h *TerminalHandler) WithAttrs(attrs []slog.Attr) slog.Handler { - return &TerminalHandler{ - wr: h.wr, - lvl: h.lvl, - useColor: h.useColor, - attrs: append(h.attrs, attrs...), - fieldPadding: make(map[string]int), - } -} - -// ResetFieldPadding zeroes the field-padding for all attribute pairs. -func (t *TerminalHandler) ResetFieldPadding() { - t.mu.Lock() - t.fieldPadding = make(map[string]int) - t.mu.Unlock() -} - -type leveler struct{ minLevel slog.Level } - -func (l *leveler) Level() slog.Level { - return l.minLevel -} - -// JSONHandler returns a handler which prints records in JSON format. -func JSONHandler(wr io.Writer) slog.Handler { - return slog.NewJSONHandler(wr, &slog.HandlerOptions{ - ReplaceAttr: builtinReplaceJSON, - }) -} - -// LogfmtHandler returns a handler which prints records in logfmt format, an easy machine-parseable but human-readable -// format for key/value pairs. -// -// For more details see: http://godoc.org/github.com/kr/logfmt -func LogfmtHandler(wr io.Writer) slog.Handler { - return slog.NewTextHandler(wr, &slog.HandlerOptions{ - ReplaceAttr: builtinReplaceLogfmt, - }) -} - -// LogfmtHandlerWithLevel returns the same handler as LogfmtHandler but it only outputs -// records which are less than or equal to the specified verbosity level. -func LogfmtHandlerWithLevel(wr io.Writer, level slog.Level) slog.Handler { - return slog.NewTextHandler(wr, &slog.HandlerOptions{ - ReplaceAttr: builtinReplaceLogfmt, - Level: &leveler{level}, - }) -} - -func builtinReplaceLogfmt(_ []string, attr slog.Attr) slog.Attr { - return builtinReplace(nil, attr, true) -} - -func builtinReplaceJSON(_ []string, attr slog.Attr) slog.Attr { - return builtinReplace(nil, attr, false) -} - -func builtinReplace(_ []string, attr slog.Attr, logfmt bool) slog.Attr { - switch attr.Key { - case slog.TimeKey: - if attr.Value.Kind() == slog.KindTime { - if logfmt { - return slog.String("t", attr.Value.Time().Format(timeFormat)) - } else { - return slog.Attr{Key: "t", Value: attr.Value} - } - } - case slog.LevelKey: - if l, ok := attr.Value.Any().(slog.Level); ok { - attr = slog.Any("lvl", LevelString(l)) - return attr - } - } - - switch v := attr.Value.Any().(type) { - case time.Time: - if logfmt { - attr = slog.String(attr.Key, v.Format(timeFormat)) - } - case *big.Int: - if v == nil { - attr.Value = slog.StringValue("") - } else { - attr.Value = slog.StringValue(v.String()) - } - case *uint256.Int: - if v == nil { - attr.Value = slog.StringValue("") - } else { - attr.Value = slog.StringValue(v.Dec()) - } - case fmt.Stringer: - if v == nil || (reflect.ValueOf(v).Kind() == reflect.Pointer && reflect.ValueOf(v).IsNil()) { - attr.Value = slog.StringValue("") - } else { - attr.Value = slog.StringValue(v.String()) - } - } - return attr -} diff --git a/log/handler_glog.go b/log/handler_glog.go deleted file mode 100644 index fb1e03c5b5..0000000000 --- a/log/handler_glog.go +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package log - -import ( - "context" - "errors" - "fmt" - "regexp" - "runtime" - "strconv" - "strings" - "sync" - "sync/atomic" - - "golang.org/x/exp/slog" -) - -// errVmoduleSyntax is returned when a user vmodule pattern is invalid. -var errVmoduleSyntax = errors.New("expect comma-separated list of filename=N") - -// GlogHandler is a log handler that mimics the filtering features of Google's -// glog logger: setting global log levels; overriding with callsite pattern -// matches; and requesting backtraces at certain positions. -type GlogHandler struct { - origin slog.Handler // The origin handler this wraps - - level atomic.Int32 // Current log level, atomically accessible - override atomic.Bool // Flag whether overrides are used, atomically accessible - - patterns []pattern // Current list of patterns to override with - siteCache map[uintptr]slog.Level // Cache of callsite pattern evaluations - location string // file:line location where to do a stackdump at - lock sync.RWMutex // Lock protecting the override pattern list -} - -// NewGlogHandler creates a new log handler with filtering functionality similar -// to Google's glog logger. The returned handler implements Handler. -func NewGlogHandler(h slog.Handler) *GlogHandler { - return &GlogHandler{ - origin: h, - } -} - -// pattern contains a filter for the Vmodule option, holding a verbosity level -// and a file pattern to match. -type pattern struct { - pattern *regexp.Regexp - level slog.Level -} - -// Verbosity sets the glog verbosity ceiling. The verbosity of individual packages -// and source files can be raised using Vmodule. -func (h *GlogHandler) Verbosity(level slog.Level) { - h.level.Store(int32(level)) -} - -// Vmodule sets the glog verbosity pattern. -// -// The syntax of the argument is a comma-separated list of pattern=N, where the -// pattern is a literal file name or "glob" pattern matching and N is a V level. -// -// For instance: -// -// pattern="gopher.go=3" -// sets the V level to 3 in all Go files named "gopher.go" -// -// pattern="foo=3" -// sets V to 3 in all files of any packages whose import path ends in "foo" -// -// pattern="foo/*=3" -// sets V to 3 in all files of any packages whose import path contains "foo" -func (h *GlogHandler) Vmodule(ruleset string) error { - var filter []pattern - for _, rule := range strings.Split(ruleset, ",") { - // Empty strings such as from a trailing comma can be ignored - if len(rule) == 0 { - continue - } - // Ensure we have a pattern = level filter rule - parts := strings.Split(rule, "=") - if len(parts) != 2 { - return errVmoduleSyntax - } - parts[0] = strings.TrimSpace(parts[0]) - parts[1] = strings.TrimSpace(parts[1]) - if len(parts[0]) == 0 || len(parts[1]) == 0 { - return errVmoduleSyntax - } - // Parse the level and if correct, assemble the filter rule - l, err := strconv.Atoi(parts[1]) - if err != nil { - return errVmoduleSyntax - } - level := FromLegacyLevel(l) - - if level == LevelCrit { - continue // Ignore. It's harmless but no point in paying the overhead. - } - // Compile the rule pattern into a regular expression - matcher := ".*" - for _, comp := range strings.Split(parts[0], "/") { - if comp == "*" { - matcher += "(/.*)?" - } else if comp != "" { - matcher += "/" + regexp.QuoteMeta(comp) - } - } - if !strings.HasSuffix(parts[0], ".go") { - matcher += "/[^/]+\\.go" - } - matcher = matcher + "$" - - re, _ := regexp.Compile(matcher) - filter = append(filter, pattern{re, level}) - } - // Swap out the vmodule pattern for the new filter system - h.lock.Lock() - defer h.lock.Unlock() - - h.patterns = filter - h.siteCache = make(map[uintptr]slog.Level) - h.override.Store(len(filter) != 0) - - return nil -} - -func (h *GlogHandler) Enabled(ctx context.Context, lvl slog.Level) bool { - // fast-track skipping logging if override not enabled and the provided verbosity is above configured - return h.override.Load() || slog.Level(h.level.Load()) <= lvl -} - -func (h *GlogHandler) WithAttrs(attrs []slog.Attr) slog.Handler { - h.lock.RLock() - siteCache := make(map[uintptr]slog.Level) - for k, v := range h.siteCache { - siteCache[k] = v - } - h.lock.RUnlock() - - patterns := []pattern{} - patterns = append(patterns, h.patterns...) - - res := GlogHandler{ - origin: h.origin.WithAttrs(attrs), - patterns: patterns, - siteCache: siteCache, - location: h.location, - } - - res.level.Store(h.level.Load()) - res.override.Store(h.override.Load()) - return &res -} - -func (h *GlogHandler) WithGroup(name string) slog.Handler { - panic("not implemented") -} - -// Log implements Handler.Log, filtering a log record through the global, local -// and backtrace filters, finally emitting it if either allow it through. -func (h *GlogHandler) Handle(_ context.Context, r slog.Record) error { - // If the global log level allows, fast track logging - if slog.Level(h.level.Load()) <= r.Level { - return h.origin.Handle(context.Background(), r) - } - - // Check callsite cache for previously calculated log levels - h.lock.RLock() - lvl, ok := h.siteCache[r.PC] - h.lock.RUnlock() - - // If we didn't cache the callsite yet, calculate it - if !ok { - h.lock.Lock() - - fs := runtime.CallersFrames([]uintptr{r.PC}) - frame, _ := fs.Next() - - for _, rule := range h.patterns { - if rule.pattern.MatchString(fmt.Sprintf("%+s", frame.File)) { - h.siteCache[r.PC], lvl, ok = rule.level, rule.level, true - } - } - // If no rule matched, remember to drop log the next time - if !ok { - h.siteCache[r.PC] = 0 - } - h.lock.Unlock() - } - if lvl <= r.Level { - return h.origin.Handle(context.Background(), r) - } - return nil -} diff --git a/log/logger.go b/log/logger.go deleted file mode 100644 index 93d62f080b..0000000000 --- a/log/logger.go +++ /dev/null @@ -1,210 +0,0 @@ -package log - -import ( - "context" - "math" - "os" - "runtime" - "time" - - "golang.org/x/exp/slog" -) - -const errorKey = "LOG_ERROR" - -const ( - legacyLevelCrit = iota - legacyLevelError - legacyLevelWarn - legacyLevelInfo - legacyLevelDebug - legacyLevelTrace -) - -const ( - levelMaxVerbosity slog.Level = math.MinInt - LevelTrace slog.Level = -8 - LevelDebug = slog.LevelDebug - LevelInfo = slog.LevelInfo - LevelWarn = slog.LevelWarn - LevelError = slog.LevelError - LevelCrit slog.Level = 12 - - // for backward-compatibility - LvlTrace = LevelTrace - LvlInfo = LevelInfo - LvlDebug = LevelDebug -) - -// convert from old Geth verbosity level constants -// to levels defined by slog -func FromLegacyLevel(lvl int) slog.Level { - switch lvl { - case legacyLevelCrit: - return LevelCrit - case legacyLevelError: - return slog.LevelError - case legacyLevelWarn: - return slog.LevelWarn - case legacyLevelInfo: - return slog.LevelInfo - case legacyLevelDebug: - return slog.LevelDebug - case legacyLevelTrace: - return LevelTrace - default: - break - } - - // TODO: should we allow use of custom levels or force them to match existing max/min if they fall outside the range as I am doing here? - if lvl > legacyLevelTrace { - return LevelTrace - } - return LevelCrit -} - -// LevelAlignedString returns a 5-character string containing the name of a Lvl. -func LevelAlignedString(l slog.Level) string { - switch l { - case LevelTrace: - return "TRACE" - case slog.LevelDebug: - return "DEBUG" - case slog.LevelInfo: - return "INFO " - case slog.LevelWarn: - return "WARN " - case slog.LevelError: - return "ERROR" - case LevelCrit: - return "CRIT " - default: - return "unknown level" - } -} - -// LevelString returns a 5-character string containing the name of a Lvl. -func LevelString(l slog.Level) string { - switch l { - case LevelTrace: - return "trace" - case slog.LevelDebug: - return "debug" - case slog.LevelInfo: - return "info" - case slog.LevelWarn: - return "warn" - case slog.LevelError: - return "eror" - case LevelCrit: - return "crit" - default: - return "unknown" - } -} - -// A Logger writes key/value pairs to a Handler -type Logger interface { - // With returns a new Logger that has this logger's attributes plus the given attributes - With(ctx ...interface{}) Logger - - // With returns a new Logger that has this logger's attributes plus the given attributes. Identical to 'With'. - New(ctx ...interface{}) Logger - - // Log logs a message at the specified level with context key/value pairs - Log(level slog.Level, msg string, ctx ...interface{}) - - // Trace log a message at the trace level with context key/value pairs - Trace(msg string, ctx ...interface{}) - - // Debug logs a message at the debug level with context key/value pairs - Debug(msg string, ctx ...interface{}) - - // Info logs a message at the info level with context key/value pairs - Info(msg string, ctx ...interface{}) - - // Warn logs a message at the warn level with context key/value pairs - Warn(msg string, ctx ...interface{}) - - // Error logs a message at the error level with context key/value pairs - Error(msg string, ctx ...interface{}) - - // Crit logs a message at the crit level with context key/value pairs, and exits - Crit(msg string, ctx ...interface{}) - - // Write logs a message at the specified level - Write(level slog.Level, msg string, attrs ...any) - - // Enabled reports whether l emits log records at the given context and level. - Enabled(ctx context.Context, level slog.Level) bool -} - -type logger struct { - inner *slog.Logger -} - -// NewLogger returns a logger with the specified handler set -func NewLogger(h slog.Handler) Logger { - return &logger{ - slog.New(h), - } -} - -// write logs a message at the specified level: -func (l *logger) Write(level slog.Level, msg string, attrs ...any) { - if !l.inner.Enabled(context.Background(), level) { - return - } - - var pcs [1]uintptr - runtime.Callers(3, pcs[:]) - - if len(attrs)%2 != 0 { - attrs = append(attrs, nil, errorKey, "Normalized odd number of arguments by adding nil") - } - r := slog.NewRecord(time.Now(), level, msg, pcs[0]) - r.Add(attrs...) - l.inner.Handler().Handle(context.Background(), r) -} - -func (l *logger) Log(level slog.Level, msg string, attrs ...any) { - l.Write(level, msg, attrs...) -} - -func (l *logger) With(ctx ...interface{}) Logger { - return &logger{l.inner.With(ctx...)} -} - -func (l *logger) New(ctx ...interface{}) Logger { - return l.With(ctx...) -} - -// Enabled reports whether l emits log records at the given context and level. -func (l *logger) Enabled(ctx context.Context, level slog.Level) bool { - return l.inner.Enabled(ctx, level) -} - -func (l *logger) Trace(msg string, ctx ...interface{}) { - l.Write(LevelTrace, msg, ctx...) -} - -func (l *logger) Debug(msg string, ctx ...interface{}) { - l.Write(slog.LevelDebug, msg, ctx...) -} - -func (l *logger) Info(msg string, ctx ...interface{}) { - l.Write(slog.LevelInfo, msg, ctx...) -} - -func (l *logger) Warn(msg string, ctx ...any) { - l.Write(slog.LevelWarn, msg, ctx...) -} - -func (l *logger) Error(msg string, ctx ...interface{}) { - l.Write(slog.LevelError, msg, ctx...) -} - -func (l *logger) Crit(msg string, ctx ...interface{}) { - l.Write(LevelCrit, msg, ctx...) - os.Exit(1) -} diff --git a/log/logger_test.go b/log/logger_test.go deleted file mode 100644 index a633f5ad7a..0000000000 --- a/log/logger_test.go +++ /dev/null @@ -1,172 +0,0 @@ -package log - -import ( - "bytes" - "fmt" - "io" - "math/big" - "os" - "strings" - "testing" - "time" - - "github.com/holiman/uint256" - "golang.org/x/exp/slog" -) - -// TestLoggingWithVmodule checks that vmodule works. -func TestLoggingWithVmodule(t *testing.T) { - out := new(bytes.Buffer) - glog := NewGlogHandler(NewTerminalHandlerWithLevel(out, LevelTrace, false)) - glog.Verbosity(LevelCrit) - logger := NewLogger(glog) - logger.Warn("This should not be seen", "ignored", "true") - glog.Vmodule("logger_test.go=5") - logger.Trace("a message", "foo", "bar") - have := out.String() - // The timestamp is locale-dependent, so we want to trim that off - // "INFO [01-01|00:00:00.000] a messag ..." -> "a messag..." - have = strings.Split(have, "]")[1] - want := " a message foo=bar\n" - if have != want { - t.Errorf("\nhave: %q\nwant: %q\n", have, want) - } -} - -func TestTerminalHandlerWithAttrs(t *testing.T) { - out := new(bytes.Buffer) - glog := NewGlogHandler(NewTerminalHandlerWithLevel(out, LevelTrace, false).WithAttrs([]slog.Attr{slog.String("baz", "bat")})) - glog.Verbosity(LevelTrace) - logger := NewLogger(glog) - logger.Trace("a message", "foo", "bar") - have := out.String() - // The timestamp is locale-dependent, so we want to trim that off - // "INFO [01-01|00:00:00.000] a messag ..." -> "a messag..." - have = strings.Split(have, "]")[1] - want := " a message baz=bat foo=bar\n" - if have != want { - t.Errorf("\nhave: %q\nwant: %q\n", have, want) - } -} - -func BenchmarkTraceLogging(b *testing.B) { - SetDefault(NewLogger(NewTerminalHandler(os.Stderr, true))) - b.ResetTimer() - for i := 0; i < b.N; i++ { - Trace("a message", "v", i) - } -} - -func BenchmarkTerminalHandler(b *testing.B) { - l := NewLogger(NewTerminalHandler(io.Discard, false)) - benchmarkLogger(b, l) -} -func BenchmarkLogfmtHandler(b *testing.B) { - l := NewLogger(LogfmtHandler(io.Discard)) - benchmarkLogger(b, l) -} - -func BenchmarkJSONHandler(b *testing.B) { - l := NewLogger(JSONHandler(io.Discard)) - benchmarkLogger(b, l) -} - -func benchmarkLogger(b *testing.B, l Logger) { - var ( - bb = make([]byte, 10) - tt = time.Now() - bigint = big.NewInt(100) - nilbig *big.Int - err = fmt.Errorf("Oh nooes it's crap") - ) - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - l.Info("This is a message", - "foo", int16(i), - "bytes", bb, - "bonk", "a string with text", - "time", tt, - "bigint", bigint, - "nilbig", nilbig, - "err", err) - } - b.StopTimer() -} - -func TestLoggerOutput(t *testing.T) { - type custom struct { - A string - B int8 - } - var ( - customA = custom{"Foo", 12} - customB = custom{"Foo\nLinebreak", 122} - bb = make([]byte, 10) - tt = time.Time{} - bigint = big.NewInt(100) - nilbig *big.Int - err = fmt.Errorf("Oh nooes it's crap") - smallUint = uint256.NewInt(500_000) - bigUint = &uint256.Int{0xff, 0xff, 0xff, 0xff} - ) - - out := new(bytes.Buffer) - glogHandler := NewGlogHandler(NewTerminalHandler(out, false)) - glogHandler.Verbosity(LevelInfo) - NewLogger(glogHandler).Info("This is a message", - "foo", int16(123), - "bytes", bb, - "bonk", "a string with text", - "time", tt, - "bigint", bigint, - "nilbig", nilbig, - "err", err, - "struct", customA, - "struct", customB, - "ptrstruct", &customA, - "smalluint", smallUint, - "bigUint", bigUint) - - have := out.String() - t.Logf("output %v", out.String()) - want := `INFO [11-07|19:14:33.821] This is a message foo=123 bytes="[0 0 0 0 0 0 0 0 0 0]" bonk="a string with text" time=0001-01-01T00:00:00+0000 bigint=100 nilbig= err="Oh nooes it's crap" struct="{A:Foo B:12}" struct="{A:Foo\nLinebreak B:122}" ptrstruct="&{A:Foo B:12}" smalluint=500,000 bigUint=1,600,660,942,523,603,594,864,898,306,482,794,244,293,965,082,972,225,630,372,095 -` - if !bytes.Equal([]byte(have)[25:], []byte(want)[25:]) { - t.Errorf("Error\nhave: %q\nwant: %q", have, want) - } -} - -const termTimeFormat = "01-02|15:04:05.000" - -func BenchmarkAppendFormat(b *testing.B) { - var now = time.Now() - b.Run("fmt time.Format", func(b *testing.B) { - for i := 0; i < b.N; i++ { - fmt.Fprintf(io.Discard, "%s", now.Format(termTimeFormat)) - } - }) - b.Run("time.AppendFormat", func(b *testing.B) { - for i := 0; i < b.N; i++ { - now.AppendFormat(nil, termTimeFormat) - } - }) - var buf = new(bytes.Buffer) - b.Run("time.Custom", func(b *testing.B) { - for i := 0; i < b.N; i++ { - writeTimeTermFormat(buf, now) - buf.Reset() - } - }) -} - -func TestTermTimeFormat(t *testing.T) { - var now = time.Now() - want := now.AppendFormat(nil, termTimeFormat) - var b = new(bytes.Buffer) - writeTimeTermFormat(b, now) - have := b.Bytes() - if !bytes.Equal(have, want) { - t.Errorf("have != want\nhave: %q\nwant: %q\n", have, want) - } -} diff --git a/log/root.go b/log/root.go deleted file mode 100644 index 8662d87063..0000000000 --- a/log/root.go +++ /dev/null @@ -1,116 +0,0 @@ -package log - -import ( - "os" - "sync/atomic" - - "golang.org/x/exp/slog" -) - -var root atomic.Value - -func init() { - root.Store(&logger{slog.New(DiscardHandler())}) -} - -// SetDefault sets the default global logger -func SetDefault(l Logger) { - root.Store(l) - if lg, ok := l.(*logger); ok { - slog.SetDefault(lg.inner) - } -} - -// Root returns the root logger -func Root() Logger { - return root.Load().(Logger) -} - -// The following functions bypass the exported logger methods (logger.Debug, -// etc.) to keep the call depth the same for all paths to logger.Write so -// runtime.Caller(2) always refers to the call site in client code. - -// Trace is a convenient alias for Root().Trace -// -// Log a message at the trace level with context key/value pairs -// -// # Usage -// -// log.Trace("msg") -// log.Trace("msg", "key1", val1) -// log.Trace("msg", "key1", val1, "key2", val2) -func Trace(msg string, ctx ...interface{}) { - Root().Write(LevelTrace, msg, ctx...) -} - -// Debug is a convenient alias for Root().Debug -// -// Log a message at the debug level with context key/value pairs -// -// # Usage Examples -// -// log.Debug("msg") -// log.Debug("msg", "key1", val1) -// log.Debug("msg", "key1", val1, "key2", val2) -func Debug(msg string, ctx ...interface{}) { - Root().Write(slog.LevelDebug, msg, ctx...) -} - -// Info is a convenient alias for Root().Info -// -// Log a message at the info level with context key/value pairs -// -// # Usage Examples -// -// log.Info("msg") -// log.Info("msg", "key1", val1) -// log.Info("msg", "key1", val1, "key2", val2) -func Info(msg string, ctx ...interface{}) { - Root().Write(slog.LevelInfo, msg, ctx...) -} - -// Warn is a convenient alias for Root().Warn -// -// Log a message at the warn level with context key/value pairs -// -// # Usage Examples -// -// log.Warn("msg") -// log.Warn("msg", "key1", val1) -// log.Warn("msg", "key1", val1, "key2", val2) -func Warn(msg string, ctx ...interface{}) { - Root().Write(slog.LevelWarn, msg, ctx...) -} - -// Error is a convenient alias for Root().Error -// -// Log a message at the error level with context key/value pairs -// -// # Usage Examples -// -// log.Error("msg") -// log.Error("msg", "key1", val1) -// log.Error("msg", "key1", val1, "key2", val2) -func Error(msg string, ctx ...interface{}) { - Root().Write(slog.LevelError, msg, ctx...) -} - -// Crit is a convenient alias for Root().Crit -// -// Log a message at the crit level with context key/value pairs, and then exit. -// -// # Usage Examples -// -// log.Crit("msg") -// log.Crit("msg", "key1", val1) -// log.Crit("msg", "key1", val1, "key2", val2) -func Crit(msg string, ctx ...interface{}) { - Root().Write(LevelCrit, msg, ctx...) - os.Exit(1) -} - -// New returns a new logger with the given context. -// New is a convenient alias for Root().New -func New(ctx ...interface{}) Logger { - return Root().With(ctx...) -}