🎨 Introduce contextualized logging

This commit is multi-fold as it also refactors internally context and logger
as interfaces so it is easier to plug luet as a library externally.

Introduces a garbage collector (related to #227) but doesn't handle yet
parallelism.

Closes #265
This commit is contained in:
Ettore Di Giacinto
2021-12-17 15:21:03 +01:00
parent fd90e0d627
commit c98f427156
105 changed files with 2672 additions and 1271 deletions

21
vendor/github.com/ipfs/go-log/v2/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014 Juan Batiz-Benet
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
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, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

137
vendor/github.com/ipfs/go-log/v2/README.md generated vendored Normal file
View File

@@ -0,0 +1,137 @@
# go-log
[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](https://protocol.ai)
[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](https://ipfs.io/)
[![GoDoc](https://pkg.go.dev/badge/github.com/ipfs/go-log/v2.svg)](https://pkg.go.dev/github.com/ipfs/go-log/v2)
> The logging library used by go-ipfs
go-log wraps [zap](https://github.com/uber-go/zap) to provide a logging facade. go-log manages logging
instances and allows for their levels to be controlled individually.
## Install
```sh
go get github.com/ipfs/go-log
```
## Usage
Once the package is imported under the name `logging`, an instance of `EventLogger` can be created like so:
```go
var log = logging.Logger("subsystem name")
```
It can then be used to emit log messages in plain printf-style messages at seven standard levels:
Levels may be set for all loggers:
```go
lvl, err := logging.LevelFromString("error")
if err != nil {
panic(err)
}
logging.SetAllLoggers(lvl)
```
or individually:
```go
err := logging.SetLogLevel("net:pubsub", "info")
if err != nil {
panic(err)
}
```
or by regular expression:
```go
err := logging.SetLogLevelRegex("net:.*", "info")
if err != nil {
panic(err)
}
```
### Environment Variables
This package can be configured through various environment variables.
#### `GOLOG_LOG_LEVEL`
Specifies the log-level, both globally and on a per-subsystem basis.
For example, the following will set the global minimum log level to `error`, but reduce the minimum
log level for `subsystem1` to `info` and reduce the minimum log level for `subsystem2` to debug.
```bash
export GOLOG_LOG_LEVEL="error,subsystem1=info,subsystem2=debug"
```
`IPFS_LOGGING` is a deprecated alias for this environment variable.
#### `GOLOG_FILE`
Specifies that logs should be written to the specified file. If this option is _not_ specified, logs are written to standard error.
```bash
export GOLOG_FILE="/path/to/my/file.log"
```
#### `GOLOG_OUTPUT`
Specifies where logging output should be written. Can take one or more of the following values, combined with `+`:
- `stdout` -- write logs to standard out.
- `stderr` -- write logs to standard error.
- `file` -- write logs to the file specified by `GOLOG_FILE`
For example, if you want to log to both a file and standard error:
```bash
export GOLOG_FILE="/path/to/my/file.log"
export GOLOG_OUTPUT="stderr+file"
```
Setting _only_ `GOLOG_FILE` will prevent logs from being written to standard error.
#### `GOLOG_LOG_FMT`
Specifies the log message format. It supports the following values:
- `color` -- human readable, colorized (ANSI) output
- `nocolor` -- human readable, plain-text output.
- `json` -- structured JSON.
For example, to log structured JSON (for easier parsing):
```bash
export GOLOG_LOG_FMT="json"
```
The logging format defaults to `color` when the output is a terminal, and `nocolor` otherwise.
`IPFS_LOGGING_FMT` is a deprecated alias for this environment variable.
#### `GOLOG_LOG_LABELS`
Specifies a set of labels that should be added to all log messages as comma-separated key-value
pairs. For example, the following add `{"app": "example_app", "dc": "sjc-1"}` to every log entry.
```bash
export GOLOG_LOG_LABELS="app=example_app,dc=sjc-1"
```
## Contribute
Feel free to join in. All welcome. Open an [issue](https://github.com/ipfs/go-log/issues)!
This repository falls under the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md).
### Want to hack on IPFS?
[![](https://cdn.rawgit.com/jbenet/contribute-ipfs-gif/master/img/contribute.gif)](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md)
## License
MIT

121
vendor/github.com/ipfs/go-log/v2/core.go generated vendored Normal file
View File

@@ -0,0 +1,121 @@
package log
import (
"reflect"
"sync"
"go.uber.org/multierr"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
var _ zapcore.Core = (*lockedMultiCore)(nil)
type lockedMultiCore struct {
mu sync.RWMutex // guards mutations to cores slice
cores []zapcore.Core
}
func (l *lockedMultiCore) With(fields []zapcore.Field) zapcore.Core {
l.mu.RLock()
defer l.mu.RUnlock()
sub := &lockedMultiCore{
cores: make([]zapcore.Core, len(l.cores)),
}
for i := range l.cores {
sub.cores[i] = l.cores[i].With(fields)
}
return sub
}
func (l *lockedMultiCore) Enabled(lvl zapcore.Level) bool {
l.mu.RLock()
defer l.mu.RUnlock()
for i := range l.cores {
if l.cores[i].Enabled(lvl) {
return true
}
}
return false
}
func (l *lockedMultiCore) Check(ent zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry {
l.mu.RLock()
defer l.mu.RUnlock()
for i := range l.cores {
ce = l.cores[i].Check(ent, ce)
}
return ce
}
func (l *lockedMultiCore) Write(ent zapcore.Entry, fields []zapcore.Field) error {
l.mu.RLock()
defer l.mu.RUnlock()
var err error
for i := range l.cores {
err = multierr.Append(err, l.cores[i].Write(ent, fields))
}
return err
}
func (l *lockedMultiCore) Sync() error {
l.mu.RLock()
defer l.mu.RUnlock()
var err error
for i := range l.cores {
err = multierr.Append(err, l.cores[i].Sync())
}
return err
}
func (l *lockedMultiCore) AddCore(core zapcore.Core) {
l.mu.Lock()
defer l.mu.Unlock()
l.cores = append(l.cores, core)
}
func (l *lockedMultiCore) DeleteCore(core zapcore.Core) {
l.mu.Lock()
defer l.mu.Unlock()
w := 0
for i := 0; i < len(l.cores); i++ {
if reflect.DeepEqual(l.cores[i], core) {
continue
}
l.cores[w] = l.cores[i]
w++
}
l.cores = l.cores[:w]
}
func (l *lockedMultiCore) ReplaceCore(original, replacement zapcore.Core) {
l.mu.Lock()
defer l.mu.Unlock()
for i := 0; i < len(l.cores); i++ {
if reflect.DeepEqual(l.cores[i], original) {
l.cores[i] = replacement
}
}
}
func newCore(format LogFormat, ws zapcore.WriteSyncer, level LogLevel) zapcore.Core {
encCfg := zap.NewProductionEncoderConfig()
encCfg.EncodeTime = zapcore.ISO8601TimeEncoder
var encoder zapcore.Encoder
switch format {
case PlaintextOutput:
encCfg.EncodeLevel = zapcore.CapitalLevelEncoder
encoder = zapcore.NewConsoleEncoder(encCfg)
case JSONOutput:
encoder = zapcore.NewJSONEncoder(encCfg)
default:
encCfg.EncodeLevel = zapcore.CapitalColorLevelEncoder
encoder = zapcore.NewConsoleEncoder(encCfg)
}
return zapcore.NewCore(encoder, ws, zap.NewAtomicLevelAt(zapcore.Level(level)))
}

9
vendor/github.com/ipfs/go-log/v2/go.mod generated vendored Normal file
View File

@@ -0,0 +1,9 @@
module github.com/ipfs/go-log/v2
require (
github.com/mattn/go-isatty v0.0.14
go.uber.org/multierr v1.6.0
go.uber.org/zap v1.16.0
)
go 1.16

57
vendor/github.com/ipfs/go-log/v2/go.sum generated vendored Normal file
View File

@@ -0,0 +1,57 @@
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM=
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=

30
vendor/github.com/ipfs/go-log/v2/levels.go generated vendored Normal file
View File

@@ -0,0 +1,30 @@
package log
import "go.uber.org/zap/zapcore"
// LogLevel represents a log severity level. Use the package variables as an
// enum.
type LogLevel zapcore.Level
var (
LevelDebug = LogLevel(zapcore.DebugLevel)
LevelInfo = LogLevel(zapcore.InfoLevel)
LevelWarn = LogLevel(zapcore.WarnLevel)
LevelError = LogLevel(zapcore.ErrorLevel)
LevelDPanic = LogLevel(zapcore.DPanicLevel)
LevelPanic = LogLevel(zapcore.PanicLevel)
LevelFatal = LogLevel(zapcore.FatalLevel)
)
// LevelFromString parses a string-based level and returns the corresponding
// LogLevel.
//
// Supported strings are: DEBUG, INFO, WARN, ERROR, DPANIC, PANIC, FATAL, and
// their lower-case forms.
//
// The returned LogLevel must be discarded if error is not nil.
func LevelFromString(level string) (LogLevel, error) {
lvl := zapcore.InfoLevel // zero value
err := lvl.Set(level)
return LogLevel(lvl), err
}

84
vendor/github.com/ipfs/go-log/v2/log.go generated vendored Normal file
View File

@@ -0,0 +1,84 @@
// Package log is the logging library used by IPFS & libp2p
// (https://github.com/ipfs/go-ipfs).
package log
import (
"time"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
// StandardLogger provides API compatibility with standard printf loggers
// eg. go-logging
type StandardLogger interface {
Debug(args ...interface{})
Debugf(format string, args ...interface{})
Error(args ...interface{})
Errorf(format string, args ...interface{})
Fatal(args ...interface{})
Fatalf(format string, args ...interface{})
Info(args ...interface{})
Infof(format string, args ...interface{})
Panic(args ...interface{})
Panicf(format string, args ...interface{})
Warn(args ...interface{})
Warnf(format string, args ...interface{})
}
// EventLogger extends the StandardLogger interface to allow for log items
// containing structured metadata
type EventLogger interface {
StandardLogger
}
// Logger retrieves an event logger by name
func Logger(system string) *ZapEventLogger {
if len(system) == 0 {
setuplog := getLogger("setup-logger")
setuplog.Error("Missing name parameter")
system = "undefined"
}
logger := getLogger(system)
skipLogger := logger.Desugar().WithOptions(zap.AddCallerSkip(1)).Sugar()
return &ZapEventLogger{
system: system,
SugaredLogger: *logger,
skipLogger: *skipLogger,
}
}
// ZapEventLogger implements the EventLogger and wraps a go-logging Logger
type ZapEventLogger struct {
zap.SugaredLogger
// used to fix the caller location when calling Warning and Warningf.
skipLogger zap.SugaredLogger
system string
}
// Warning is for compatibility
// Deprecated: use Warn(args ...interface{}) instead
func (logger *ZapEventLogger) Warning(args ...interface{}) {
logger.skipLogger.Warn(args...)
}
// Warningf is for compatibility
// Deprecated: use Warnf(format string, args ...interface{}) instead
func (logger *ZapEventLogger) Warningf(format string, args ...interface{}) {
logger.skipLogger.Warnf(format, args...)
}
// FormatRFC3339 returns the given time in UTC with RFC3999Nano format.
func FormatRFC3339(t time.Time) string {
return t.UTC().Format(time.RFC3339Nano)
}
func WithStacktrace(l *ZapEventLogger, level LogLevel) *ZapEventLogger {
copyLogger := *l
copyLogger.SugaredLogger = *copyLogger.SugaredLogger.Desugar().
WithOptions(zap.AddStacktrace(zapcore.Level(level))).Sugar()
copyLogger.skipLogger = *copyLogger.SugaredLogger.Desugar().WithOptions(zap.AddCallerSkip(1)).Sugar()
return &copyLogger
}

12
vendor/github.com/ipfs/go-log/v2/path_other.go generated vendored Normal file
View File

@@ -0,0 +1,12 @@
//go:build !windows
// +build !windows
package log
import (
"path/filepath"
)
func normalizePath(p string) (string, error) {
return filepath.Abs(p)
}

36
vendor/github.com/ipfs/go-log/v2/path_windows.go generated vendored Normal file
View File

@@ -0,0 +1,36 @@
//go:build windows
// +build windows
package log
import (
"fmt"
"path/filepath"
"strings"
)
func normalizePath(p string) (string, error) {
if p == "" {
return "", fmt.Errorf("path empty")
}
p, err := filepath.Abs(p)
if err != nil {
return "", err
}
// Is this _really_ an absolute path?
if !strings.HasPrefix(p, "\\\\") {
// It's a drive: path!
// Return a UNC path.
p = "\\\\%3F\\" + p
}
// This will return file:////?/c:/foobar
//
// Why? Because:
// 1. Go will choke on file://c:/ because the "domain" includes a :.
// 2. Windows will choke on file:///c:/ because the path will be
// /c:/... which is _relative_ to the current drive.
//
// This path (a) has no "domain" and (b) starts with a slash. Yay!
return "file://" + filepath.ToSlash(p), nil
}

90
vendor/github.com/ipfs/go-log/v2/pipe.go generated vendored Normal file
View File

@@ -0,0 +1,90 @@
package log
import (
"io"
"go.uber.org/multierr"
"go.uber.org/zap/zapcore"
)
// A PipeReader is a reader that reads from the logger. It is synchronous
// so blocking on read will affect logging performance.
type PipeReader struct {
r *io.PipeReader
closer io.Closer
core zapcore.Core
}
// Read implements the standard Read interface
func (p *PipeReader) Read(data []byte) (int, error) {
return p.r.Read(data)
}
// Close unregisters the reader from the logger.
func (p *PipeReader) Close() error {
if p.core != nil {
loggerCore.DeleteCore(p.core)
}
return multierr.Append(p.core.Sync(), p.closer.Close())
}
// NewPipeReader creates a new in-memory reader that reads from all loggers
// The caller must call Close on the returned reader when done.
//
// By default, it:
//
// 1. Logs JSON. This can be changed by passing the PipeFormat option.
// 2. Logs everything that would otherwise be logged to the "primary" log
// output. That is, everything enabled by SetLogLevel. The minimum log level
// can be increased by passing the PipeLevel option.
func NewPipeReader(opts ...PipeReaderOption) *PipeReader {
opt := pipeReaderOptions{
format: JSONOutput,
level: LevelDebug,
}
for _, o := range opts {
o.setOption(&opt)
}
r, w := io.Pipe()
p := &PipeReader{
r: r,
closer: w,
core: newCore(opt.format, zapcore.AddSync(w), opt.level),
}
loggerCore.AddCore(p.core)
return p
}
type pipeReaderOptions struct {
format LogFormat
level LogLevel
}
type PipeReaderOption interface {
setOption(*pipeReaderOptions)
}
type pipeReaderOptionFunc func(*pipeReaderOptions)
func (p pipeReaderOptionFunc) setOption(o *pipeReaderOptions) {
p(o)
}
// PipeFormat sets the output format of the pipe reader
func PipeFormat(format LogFormat) PipeReaderOption {
return pipeReaderOptionFunc(func(o *pipeReaderOptions) {
o.format = format
})
}
// PipeLevel sets the log level of logs sent to the pipe reader.
func PipeLevel(level LogLevel) PipeReaderOption {
return pipeReaderOptionFunc(func(o *pipeReaderOptions) {
o.level = level
})
}

390
vendor/github.com/ipfs/go-log/v2/setup.go generated vendored Normal file
View File

@@ -0,0 +1,390 @@
package log
import (
"errors"
"fmt"
"os"
"regexp"
"strings"
"sync"
"github.com/mattn/go-isatty"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
func init() {
SetupLogging(configFromEnv())
}
// Logging environment variables
const (
// IPFS_* prefixed env vars kept for backwards compatibility
// for this release. They will not be available in the next
// release.
//
// GOLOG_* env vars take precedences over IPFS_* env vars.
envIPFSLogging = "IPFS_LOGGING"
envIPFSLoggingFmt = "IPFS_LOGGING_FMT"
envLogging = "GOLOG_LOG_LEVEL"
envLoggingFmt = "GOLOG_LOG_FMT"
envLoggingFile = "GOLOG_FILE" // /path/to/file
envLoggingURL = "GOLOG_URL" // url that will be processed by sink in the zap
envLoggingOutput = "GOLOG_OUTPUT" // possible values: stdout|stderr|file combine multiple values with '+'
envLoggingLabels = "GOLOG_LOG_LABELS" // comma-separated key-value pairs, i.e. "app=example_app,dc=sjc-1"
)
type LogFormat int
const (
ColorizedOutput LogFormat = iota
PlaintextOutput
JSONOutput
)
type Config struct {
// Format overrides the format of the log output. Defaults to ColorizedOutput
Format LogFormat
// Level is the default minimum enabled logging level.
Level LogLevel
// SubsystemLevels are the default levels per-subsystem. When unspecified, defaults to Level.
SubsystemLevels map[string]LogLevel
// Stderr indicates whether logs should be written to stderr.
Stderr bool
// Stdout indicates whether logs should be written to stdout.
Stdout bool
// File is a path to a file that logs will be written to.
File string
// URL with schema supported by zap. Use zap.RegisterSink
URL string
// Labels is a set of key-values to apply to all loggers
Labels map[string]string
}
// ErrNoSuchLogger is returned when the util pkg is asked for a non existant logger
var ErrNoSuchLogger = errors.New("error: No such logger")
var loggerMutex sync.RWMutex // guards access to global logger state
// loggers is the set of loggers in the system
var loggers = make(map[string]*zap.SugaredLogger)
var levels = make(map[string]zap.AtomicLevel)
// primaryFormat is the format of the primary core used for logging
var primaryFormat LogFormat = ColorizedOutput
// defaultLevel is the default log level
var defaultLevel LogLevel = LevelError
// primaryCore is the primary logging core
var primaryCore zapcore.Core
// loggerCore is the base for all loggers created by this package
var loggerCore = &lockedMultiCore{}
// SetupLogging will initialize the logger backend and set the flags.
// TODO calling this in `init` pushes all configuration to env variables
// - move it out of `init`? then we need to change all the code (js-ipfs, go-ipfs) to call this explicitly
// - have it look for a config file? need to define what that is
func SetupLogging(cfg Config) {
loggerMutex.Lock()
defer loggerMutex.Unlock()
primaryFormat = cfg.Format
defaultLevel = cfg.Level
outputPaths := []string{}
if cfg.Stderr {
outputPaths = append(outputPaths, "stderr")
}
if cfg.Stdout {
outputPaths = append(outputPaths, "stdout")
}
// check if we log to a file
if len(cfg.File) > 0 {
if path, err := normalizePath(cfg.File); err != nil {
fmt.Fprintf(os.Stderr, "failed to resolve log path '%q', logging to %s\n", cfg.File, outputPaths)
} else {
outputPaths = append(outputPaths, path)
}
}
if len(cfg.URL) > 0 {
outputPaths = append(outputPaths, cfg.URL)
}
ws, _, err := zap.Open(outputPaths...)
if err != nil {
panic(fmt.Sprintf("unable to open logging output: %v", err))
}
newPrimaryCore := newCore(primaryFormat, ws, LevelDebug) // the main core needs to log everything.
for k, v := range cfg.Labels {
newPrimaryCore = newPrimaryCore.With([]zap.Field{zap.String(k, v)})
}
setPrimaryCore(newPrimaryCore)
setAllLoggers(defaultLevel)
for name, level := range cfg.SubsystemLevels {
if leveler, ok := levels[name]; ok {
leveler.SetLevel(zapcore.Level(level))
} else {
levels[name] = zap.NewAtomicLevelAt(zapcore.Level(level))
}
}
}
// SetPrimaryCore changes the primary logging core. If the SetupLogging was
// called then the previously configured core will be replaced.
func SetPrimaryCore(core zapcore.Core) {
loggerMutex.Lock()
defer loggerMutex.Unlock()
setPrimaryCore(core)
}
func setPrimaryCore(core zapcore.Core) {
if primaryCore != nil {
loggerCore.ReplaceCore(primaryCore, core)
} else {
loggerCore.AddCore(core)
}
primaryCore = core
}
// SetDebugLogging calls SetAllLoggers with logging.DEBUG
func SetDebugLogging() {
SetAllLoggers(LevelDebug)
}
// SetAllLoggers changes the logging level of all loggers to lvl
func SetAllLoggers(lvl LogLevel) {
loggerMutex.RLock()
defer loggerMutex.RUnlock()
setAllLoggers(lvl)
}
func setAllLoggers(lvl LogLevel) {
for _, l := range levels {
l.SetLevel(zapcore.Level(lvl))
}
}
// SetLogLevel changes the log level of a specific subsystem
// name=="*" changes all subsystems
func SetLogLevel(name, level string) error {
lvl, err := LevelFromString(level)
if err != nil {
return err
}
// wildcard, change all
if name == "*" {
SetAllLoggers(lvl)
return nil
}
loggerMutex.RLock()
defer loggerMutex.RUnlock()
// Check if we have a logger by that name
if _, ok := levels[name]; !ok {
return ErrNoSuchLogger
}
levels[name].SetLevel(zapcore.Level(lvl))
return nil
}
// SetLogLevelRegex sets all loggers to level `l` that match expression `e`.
// An error is returned if `e` fails to compile.
func SetLogLevelRegex(e, l string) error {
lvl, err := LevelFromString(l)
if err != nil {
return err
}
rem, err := regexp.Compile(e)
if err != nil {
return err
}
loggerMutex.Lock()
defer loggerMutex.Unlock()
for name := range loggers {
if rem.MatchString(name) {
levels[name].SetLevel(zapcore.Level(lvl))
}
}
return nil
}
// GetSubsystems returns a slice containing the
// names of the current loggers
func GetSubsystems() []string {
loggerMutex.RLock()
defer loggerMutex.RUnlock()
subs := make([]string, 0, len(loggers))
for k := range loggers {
subs = append(subs, k)
}
return subs
}
func getLogger(name string) *zap.SugaredLogger {
loggerMutex.Lock()
defer loggerMutex.Unlock()
log, ok := loggers[name]
if !ok {
level, ok := levels[name]
if !ok {
level = zap.NewAtomicLevelAt(zapcore.Level(defaultLevel))
levels[name] = level
}
log = zap.New(loggerCore).
WithOptions(
zap.IncreaseLevel(level),
zap.AddCaller(),
).
Named(name).
Sugar()
loggers[name] = log
}
return log
}
// configFromEnv returns a Config with defaults populated using environment variables.
func configFromEnv() Config {
cfg := Config{
Format: ColorizedOutput,
Stderr: true,
Level: LevelError,
SubsystemLevels: map[string]LogLevel{},
Labels: map[string]string{},
}
format := os.Getenv(envLoggingFmt)
if format == "" {
format = os.Getenv(envIPFSLoggingFmt)
}
var noExplicitFormat bool
switch format {
case "color":
cfg.Format = ColorizedOutput
case "nocolor":
cfg.Format = PlaintextOutput
case "json":
cfg.Format = JSONOutput
default:
if format != "" {
fmt.Fprintf(os.Stderr, "ignoring unrecognized log format '%s'\n", format)
}
noExplicitFormat = true
}
lvl := os.Getenv(envLogging)
if lvl == "" {
lvl = os.Getenv(envIPFSLogging)
}
if lvl != "" {
for _, kvs := range strings.Split(lvl, ",") {
kv := strings.SplitN(kvs, "=", 2)
lvl, err := LevelFromString(kv[len(kv)-1])
if err != nil {
fmt.Fprintf(os.Stderr, "error setting log level %q: %s\n", kvs, err)
continue
}
switch len(kv) {
case 1:
cfg.Level = lvl
case 2:
cfg.SubsystemLevels[kv[0]] = lvl
}
}
}
cfg.File = os.Getenv(envLoggingFile)
// Disable stderr logging when a file is specified
// https://github.com/ipfs/go-log/issues/83
if cfg.File != "" {
cfg.Stderr = false
}
cfg.URL = os.Getenv(envLoggingURL)
output := os.Getenv(envLoggingOutput)
outputOptions := strings.Split(output, "+")
for _, opt := range outputOptions {
switch opt {
case "stdout":
cfg.Stdout = true
case "stderr":
cfg.Stderr = true
case "file":
if cfg.File == "" {
fmt.Fprint(os.Stderr, "please specify a GOLOG_FILE value to write to")
}
case "url":
if cfg.URL == "" {
fmt.Fprint(os.Stderr, "please specify a GOLOG_URL value to write to")
}
}
}
// Check that neither of the requested Std* nor the file are TTYs
// At this stage (configFromEnv) we do not have a uniform list to examine yet
if noExplicitFormat &&
!(cfg.Stdout && isTerm(os.Stdout)) &&
!(cfg.Stderr && isTerm(os.Stderr)) &&
// check this last: expensive
!(cfg.File != "" && pathIsTerm(cfg.File)) {
cfg.Format = PlaintextOutput
}
labels := os.Getenv(envLoggingLabels)
if labels != "" {
labelKVs := strings.Split(labels, ",")
for _, label := range labelKVs {
kv := strings.Split(label, "=")
if len(kv) != 2 {
fmt.Fprint(os.Stderr, "invalid label k=v: ", label)
continue
}
cfg.Labels[kv[0]] = kv[1]
}
}
return cfg
}
func isTerm(f *os.File) bool {
return isatty.IsTerminal(f.Fd()) || isatty.IsCygwinTerminal(f.Fd())
}
func pathIsTerm(p string) bool {
// !!!no!!! O_CREAT, if we fail - we fail
f, err := os.OpenFile(p, os.O_WRONLY, 0)
if f != nil {
defer f.Close() // nolint:errcheck
}
return err == nil && isTerm(f)
}

3
vendor/github.com/ipfs/go-log/v2/version.json generated vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"version": "v2.4.0"
}