mirror of
https://github.com/datarhei/core.git
synced 2025-10-13 11:43:52 +08:00
WIP: raft
This commit is contained in:
1
vendor/github.com/hashicorp/go-hclog/.gitignore
generated
vendored
Normal file
1
vendor/github.com/hashicorp/go-hclog/.gitignore
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.idea*
|
19
vendor/github.com/hashicorp/go-hclog/LICENSE
generated
vendored
Normal file
19
vendor/github.com/hashicorp/go-hclog/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2017 HashiCorp, Inc.
|
||||
|
||||
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.
|
148
vendor/github.com/hashicorp/go-hclog/README.md
generated
vendored
Normal file
148
vendor/github.com/hashicorp/go-hclog/README.md
generated
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
# go-hclog
|
||||
|
||||
[][godocs]
|
||||
|
||||
[godocs]: https://godoc.org/github.com/hashicorp/go-hclog
|
||||
|
||||
`go-hclog` is a package for Go that provides a simple key/value logging
|
||||
interface for use in development and production environments.
|
||||
|
||||
It provides logging levels that provide decreased output based upon the
|
||||
desired amount of output, unlike the standard library `log` package.
|
||||
|
||||
It provides `Printf` style logging of values via `hclog.Fmt()`.
|
||||
|
||||
It provides a human readable output mode for use in development as well as
|
||||
JSON output mode for production.
|
||||
|
||||
## Stability Note
|
||||
|
||||
This library has reached 1.0 stability. Its API can be considered solidified
|
||||
and promised through future versions.
|
||||
|
||||
## Installation and Docs
|
||||
|
||||
Install using `go get github.com/hashicorp/go-hclog`.
|
||||
|
||||
Full documentation is available at
|
||||
http://godoc.org/github.com/hashicorp/go-hclog
|
||||
|
||||
## Usage
|
||||
|
||||
### Use the global logger
|
||||
|
||||
```go
|
||||
hclog.Default().Info("hello world")
|
||||
```
|
||||
|
||||
```text
|
||||
2017-07-05T16:15:55.167-0700 [INFO ] hello world
|
||||
```
|
||||
|
||||
(Note timestamps are removed in future examples for brevity.)
|
||||
|
||||
### Create a new logger
|
||||
|
||||
```go
|
||||
appLogger := hclog.New(&hclog.LoggerOptions{
|
||||
Name: "my-app",
|
||||
Level: hclog.LevelFromString("DEBUG"),
|
||||
})
|
||||
```
|
||||
|
||||
### Emit an Info level message with 2 key/value pairs
|
||||
|
||||
```go
|
||||
input := "5.5"
|
||||
_, err := strconv.ParseInt(input, 10, 32)
|
||||
if err != nil {
|
||||
appLogger.Info("Invalid input for ParseInt", "input", input, "error", err)
|
||||
}
|
||||
```
|
||||
|
||||
```text
|
||||
... [INFO ] my-app: Invalid input for ParseInt: input=5.5 error="strconv.ParseInt: parsing "5.5": invalid syntax"
|
||||
```
|
||||
|
||||
### Create a new Logger for a major subsystem
|
||||
|
||||
```go
|
||||
subsystemLogger := appLogger.Named("transport")
|
||||
subsystemLogger.Info("we are transporting something")
|
||||
```
|
||||
|
||||
```text
|
||||
... [INFO ] my-app.transport: we are transporting something
|
||||
```
|
||||
|
||||
Notice that logs emitted by `subsystemLogger` contain `my-app.transport`,
|
||||
reflecting both the application and subsystem names.
|
||||
|
||||
### Create a new Logger with fixed key/value pairs
|
||||
|
||||
Using `With()` will include a specific key-value pair in all messages emitted
|
||||
by that logger.
|
||||
|
||||
```go
|
||||
requestID := "5fb446b6-6eba-821d-df1b-cd7501b6a363"
|
||||
requestLogger := subsystemLogger.With("request", requestID)
|
||||
requestLogger.Info("we are transporting a request")
|
||||
```
|
||||
|
||||
```text
|
||||
... [INFO ] my-app.transport: we are transporting a request: request=5fb446b6-6eba-821d-df1b-cd7501b6a363
|
||||
```
|
||||
|
||||
This allows sub Loggers to be context specific without having to thread that
|
||||
into all the callers.
|
||||
|
||||
### Using `hclog.Fmt()`
|
||||
|
||||
```go
|
||||
totalBandwidth := 200
|
||||
appLogger.Info("total bandwidth exceeded", "bandwidth", hclog.Fmt("%d GB/s", totalBandwidth))
|
||||
```
|
||||
|
||||
```text
|
||||
... [INFO ] my-app: total bandwidth exceeded: bandwidth="200 GB/s"
|
||||
```
|
||||
|
||||
### Use this with code that uses the standard library logger
|
||||
|
||||
If you want to use the standard library's `log.Logger` interface you can wrap
|
||||
`hclog.Logger` by calling the `StandardLogger()` method. This allows you to use
|
||||
it with the familiar `Println()`, `Printf()`, etc. For example:
|
||||
|
||||
```go
|
||||
stdLogger := appLogger.StandardLogger(&hclog.StandardLoggerOptions{
|
||||
InferLevels: true,
|
||||
})
|
||||
// Printf() is provided by stdlib log.Logger interface, not hclog.Logger
|
||||
stdLogger.Printf("[DEBUG] %+v", stdLogger)
|
||||
```
|
||||
|
||||
```text
|
||||
... [DEBUG] my-app: &{mu:{state:0 sema:0} prefix: flag:0 out:0xc42000a0a0 buf:[]}
|
||||
```
|
||||
|
||||
Alternatively, you may configure the system-wide logger:
|
||||
|
||||
```go
|
||||
// log the standard logger from 'import "log"'
|
||||
log.SetOutput(appLogger.StandardWriter(&hclog.StandardLoggerOptions{InferLevels: true}))
|
||||
log.SetPrefix("")
|
||||
log.SetFlags(0)
|
||||
|
||||
log.Printf("[DEBUG] %d", 42)
|
||||
```
|
||||
|
||||
```text
|
||||
... [DEBUG] my-app: 42
|
||||
```
|
||||
|
||||
Notice that if `appLogger` is initialized with the `INFO` log level _and_ you
|
||||
specify `InferLevels: true`, you will not see any output here. You must change
|
||||
`appLogger` to `DEBUG` to see output. See the docs for more information.
|
||||
|
||||
If the log lines start with a timestamp you can use the
|
||||
`InferLevelsWithTimestamp` option to try and ignore them.
|
44
vendor/github.com/hashicorp/go-hclog/colorize_unix.go
generated
vendored
Normal file
44
vendor/github.com/hashicorp/go-hclog/colorize_unix.go
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package hclog
|
||||
|
||||
import (
|
||||
"github.com/mattn/go-isatty"
|
||||
)
|
||||
|
||||
// hasFD is used to check if the writer has an Fd value to check
|
||||
// if it's a terminal.
|
||||
type hasFD interface {
|
||||
Fd() uintptr
|
||||
}
|
||||
|
||||
// setColorization will mutate the values of this logger
|
||||
// to appropriately configure colorization options. It provides
|
||||
// a wrapper to the output stream on Windows systems.
|
||||
func (l *intLogger) setColorization(opts *LoggerOptions) {
|
||||
if opts.Color != AutoColor {
|
||||
return
|
||||
}
|
||||
|
||||
if sc, ok := l.writer.w.(SupportsColor); ok {
|
||||
if !sc.SupportsColor() {
|
||||
l.headerColor = ColorOff
|
||||
l.writer.color = ColorOff
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
fi, ok := l.writer.w.(hasFD)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if !isatty.IsTerminal(fi.Fd()) {
|
||||
l.headerColor = ColorOff
|
||||
l.writer.color = ColorOff
|
||||
}
|
||||
}
|
41
vendor/github.com/hashicorp/go-hclog/colorize_windows.go
generated
vendored
Normal file
41
vendor/github.com/hashicorp/go-hclog/colorize_windows.go
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package hclog
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
colorable "github.com/mattn/go-colorable"
|
||||
)
|
||||
|
||||
// setColorization will mutate the values of this logger
|
||||
// to appropriately configure colorization options. It provides
|
||||
// a wrapper to the output stream on Windows systems.
|
||||
func (l *intLogger) setColorization(opts *LoggerOptions) {
|
||||
if opts.Color == ColorOff {
|
||||
return
|
||||
}
|
||||
|
||||
fi, ok := l.writer.w.(*os.File)
|
||||
if !ok {
|
||||
l.writer.color = ColorOff
|
||||
l.headerColor = ColorOff
|
||||
return
|
||||
}
|
||||
|
||||
cfi := colorable.NewColorable(fi)
|
||||
|
||||
// NewColorable detects if color is possible and if it's not, then it
|
||||
// returns the original value. So we can test if we got the original
|
||||
// value back to know if color is possible.
|
||||
if cfi == fi {
|
||||
l.writer.color = ColorOff
|
||||
l.headerColor = ColorOff
|
||||
} else {
|
||||
l.writer.w = cfi
|
||||
}
|
||||
}
|
41
vendor/github.com/hashicorp/go-hclog/context.go
generated
vendored
Normal file
41
vendor/github.com/hashicorp/go-hclog/context.go
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package hclog
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// WithContext inserts a logger into the context and is retrievable
|
||||
// with FromContext. The optional args can be set with the same syntax as
|
||||
// Logger.With to set fields on the inserted logger. This will not modify
|
||||
// the logger argument in-place.
|
||||
func WithContext(ctx context.Context, logger Logger, args ...interface{}) context.Context {
|
||||
// While we could call logger.With even with zero args, we have this
|
||||
// check to avoid unnecessary allocations around creating a copy of a
|
||||
// logger.
|
||||
if len(args) > 0 {
|
||||
logger = logger.With(args...)
|
||||
}
|
||||
|
||||
return context.WithValue(ctx, contextKey, logger)
|
||||
}
|
||||
|
||||
// FromContext returns a logger from the context. This will return L()
|
||||
// (the default logger) if no logger is found in the context. Therefore,
|
||||
// this will never return a nil value.
|
||||
func FromContext(ctx context.Context) Logger {
|
||||
logger, _ := ctx.Value(contextKey).(Logger)
|
||||
if logger == nil {
|
||||
return L()
|
||||
}
|
||||
|
||||
return logger
|
||||
}
|
||||
|
||||
// Unexported new type so that our context key never collides with another.
|
||||
type contextKeyType struct{}
|
||||
|
||||
// contextKey is the key used for the context to store the logger.
|
||||
var contextKey = contextKeyType{}
|
74
vendor/github.com/hashicorp/go-hclog/exclude.go
generated
vendored
Normal file
74
vendor/github.com/hashicorp/go-hclog/exclude.go
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package hclog
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ExcludeByMessage provides a simple way to build a list of log messages that
|
||||
// can be queried and matched. This is meant to be used with the Exclude
|
||||
// option on Options to suppress log messages. This does not hold any mutexs
|
||||
// within itself, so normal usage would be to Add entries at setup and none after
|
||||
// Exclude is going to be called. Exclude is called with a mutex held within
|
||||
// the Logger, so that doesn't need to use a mutex. Example usage:
|
||||
//
|
||||
// f := new(ExcludeByMessage)
|
||||
// f.Add("Noisy log message text")
|
||||
// appLogger.Exclude = f.Exclude
|
||||
type ExcludeByMessage struct {
|
||||
messages map[string]struct{}
|
||||
}
|
||||
|
||||
// Add a message to be filtered. Do not call this after Exclude is to be called
|
||||
// due to concurrency issues.
|
||||
func (f *ExcludeByMessage) Add(msg string) {
|
||||
if f.messages == nil {
|
||||
f.messages = make(map[string]struct{})
|
||||
}
|
||||
|
||||
f.messages[msg] = struct{}{}
|
||||
}
|
||||
|
||||
// Return true if the given message should be included
|
||||
func (f *ExcludeByMessage) Exclude(level Level, msg string, args ...interface{}) bool {
|
||||
_, ok := f.messages[msg]
|
||||
return ok
|
||||
}
|
||||
|
||||
// ExcludeByPrefix is a simple type to match a message string that has a common prefix.
|
||||
type ExcludeByPrefix string
|
||||
|
||||
// Matches an message that starts with the prefix.
|
||||
func (p ExcludeByPrefix) Exclude(level Level, msg string, args ...interface{}) bool {
|
||||
return strings.HasPrefix(msg, string(p))
|
||||
}
|
||||
|
||||
// ExcludeByRegexp takes a regexp and uses it to match a log message string. If it matches
|
||||
// the log entry is excluded.
|
||||
type ExcludeByRegexp struct {
|
||||
Regexp *regexp.Regexp
|
||||
}
|
||||
|
||||
// Exclude the log message if the message string matches the regexp
|
||||
func (e ExcludeByRegexp) Exclude(level Level, msg string, args ...interface{}) bool {
|
||||
return e.Regexp.MatchString(msg)
|
||||
}
|
||||
|
||||
// ExcludeFuncs is a slice of functions that will called to see if a log entry
|
||||
// should be filtered or not. It stops calling functions once at least one returns
|
||||
// true.
|
||||
type ExcludeFuncs []func(level Level, msg string, args ...interface{}) bool
|
||||
|
||||
// Calls each function until one of them returns true
|
||||
func (ff ExcludeFuncs) Exclude(level Level, msg string, args ...interface{}) bool {
|
||||
for _, f := range ff {
|
||||
if f(level, msg, args...) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
67
vendor/github.com/hashicorp/go-hclog/global.go
generated
vendored
Normal file
67
vendor/github.com/hashicorp/go-hclog/global.go
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package hclog
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
protect sync.Once
|
||||
def Logger
|
||||
|
||||
// DefaultOptions is used to create the Default logger. These are read
|
||||
// only when the Default logger is created, so set them as soon as the
|
||||
// process starts.
|
||||
DefaultOptions = &LoggerOptions{
|
||||
Level: DefaultLevel,
|
||||
Output: DefaultOutput,
|
||||
TimeFn: time.Now,
|
||||
}
|
||||
)
|
||||
|
||||
// Default returns a globally held logger. This can be a good starting
|
||||
// place, and then you can use .With() and .Named() to create sub-loggers
|
||||
// to be used in more specific contexts.
|
||||
// The value of the Default logger can be set via SetDefault() or by
|
||||
// changing the options in DefaultOptions.
|
||||
//
|
||||
// This method is goroutine safe, returning a global from memory, but
|
||||
// care should be used if SetDefault() is called it random times
|
||||
// in the program as that may result in race conditions and an unexpected
|
||||
// Logger being returned.
|
||||
func Default() Logger {
|
||||
protect.Do(func() {
|
||||
// If SetDefault was used before Default() was called, we need to
|
||||
// detect that here.
|
||||
if def == nil {
|
||||
def = New(DefaultOptions)
|
||||
}
|
||||
})
|
||||
|
||||
return def
|
||||
}
|
||||
|
||||
// L is a short alias for Default().
|
||||
func L() Logger {
|
||||
return Default()
|
||||
}
|
||||
|
||||
// SetDefault changes the logger to be returned by Default()and L()
|
||||
// to the one given. This allows packages to use the default logger
|
||||
// and have higher level packages change it to match the execution
|
||||
// environment. It returns any old default if there is one.
|
||||
//
|
||||
// NOTE: This is expected to be called early in the program to setup
|
||||
// a default logger. As such, it does not attempt to make itself
|
||||
// not racy with regard to the value of the default logger. Ergo
|
||||
// if it is called in goroutines, you may experience race conditions
|
||||
// with other goroutines retrieving the default logger. Basically,
|
||||
// don't do that.
|
||||
func SetDefault(log Logger) Logger {
|
||||
old := def
|
||||
def = log
|
||||
return old
|
||||
}
|
207
vendor/github.com/hashicorp/go-hclog/interceptlogger.go
generated
vendored
Normal file
207
vendor/github.com/hashicorp/go-hclog/interceptlogger.go
generated
vendored
Normal file
@@ -0,0 +1,207 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package hclog
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
var _ Logger = &interceptLogger{}
|
||||
|
||||
type interceptLogger struct {
|
||||
Logger
|
||||
|
||||
mu *sync.Mutex
|
||||
sinkCount *int32
|
||||
Sinks map[SinkAdapter]struct{}
|
||||
}
|
||||
|
||||
func NewInterceptLogger(opts *LoggerOptions) InterceptLogger {
|
||||
l := newLogger(opts)
|
||||
if l.callerOffset > 0 {
|
||||
// extra frames for interceptLogger.{Warn,Info,Log,etc...}, and interceptLogger.log
|
||||
l.callerOffset += 2
|
||||
}
|
||||
intercept := &interceptLogger{
|
||||
Logger: l,
|
||||
mu: new(sync.Mutex),
|
||||
sinkCount: new(int32),
|
||||
Sinks: make(map[SinkAdapter]struct{}),
|
||||
}
|
||||
|
||||
atomic.StoreInt32(intercept.sinkCount, 0)
|
||||
|
||||
return intercept
|
||||
}
|
||||
|
||||
func (i *interceptLogger) Log(level Level, msg string, args ...interface{}) {
|
||||
i.log(level, msg, args...)
|
||||
}
|
||||
|
||||
// log is used to make the caller stack frame lookup consistent. If Warn,Info,etc
|
||||
// all called Log then direct calls to Log would have a different stack frame
|
||||
// depth. By having all the methods call the same helper we ensure the stack
|
||||
// frame depth is the same.
|
||||
func (i *interceptLogger) log(level Level, msg string, args ...interface{}) {
|
||||
i.Logger.Log(level, msg, args...)
|
||||
if atomic.LoadInt32(i.sinkCount) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
i.mu.Lock()
|
||||
defer i.mu.Unlock()
|
||||
for s := range i.Sinks {
|
||||
s.Accept(i.Name(), level, msg, i.retrieveImplied(args...)...)
|
||||
}
|
||||
}
|
||||
|
||||
// Emit the message and args at TRACE level to log and sinks
|
||||
func (i *interceptLogger) Trace(msg string, args ...interface{}) {
|
||||
i.log(Trace, msg, args...)
|
||||
}
|
||||
|
||||
// Emit the message and args at DEBUG level to log and sinks
|
||||
func (i *interceptLogger) Debug(msg string, args ...interface{}) {
|
||||
i.log(Debug, msg, args...)
|
||||
}
|
||||
|
||||
// Emit the message and args at INFO level to log and sinks
|
||||
func (i *interceptLogger) Info(msg string, args ...interface{}) {
|
||||
i.log(Info, msg, args...)
|
||||
}
|
||||
|
||||
// Emit the message and args at WARN level to log and sinks
|
||||
func (i *interceptLogger) Warn(msg string, args ...interface{}) {
|
||||
i.log(Warn, msg, args...)
|
||||
}
|
||||
|
||||
// Emit the message and args at ERROR level to log and sinks
|
||||
func (i *interceptLogger) Error(msg string, args ...interface{}) {
|
||||
i.log(Error, msg, args...)
|
||||
}
|
||||
|
||||
func (i *interceptLogger) retrieveImplied(args ...interface{}) []interface{} {
|
||||
top := i.Logger.ImpliedArgs()
|
||||
|
||||
cp := make([]interface{}, len(top)+len(args))
|
||||
copy(cp, top)
|
||||
copy(cp[len(top):], args)
|
||||
|
||||
return cp
|
||||
}
|
||||
|
||||
// Create a new sub-Logger that a name descending from the current name.
|
||||
// This is used to create a subsystem specific Logger.
|
||||
// Registered sinks will subscribe to these messages as well.
|
||||
func (i *interceptLogger) Named(name string) Logger {
|
||||
return i.NamedIntercept(name)
|
||||
}
|
||||
|
||||
// Create a new sub-Logger with an explicit name. This ignores the current
|
||||
// name. This is used to create a standalone logger that doesn't fall
|
||||
// within the normal hierarchy. Registered sinks will subscribe
|
||||
// to these messages as well.
|
||||
func (i *interceptLogger) ResetNamed(name string) Logger {
|
||||
return i.ResetNamedIntercept(name)
|
||||
}
|
||||
|
||||
// Create a new sub-Logger that a name decending from the current name.
|
||||
// This is used to create a subsystem specific Logger.
|
||||
// Registered sinks will subscribe to these messages as well.
|
||||
func (i *interceptLogger) NamedIntercept(name string) InterceptLogger {
|
||||
var sub interceptLogger
|
||||
|
||||
sub = *i
|
||||
sub.Logger = i.Logger.Named(name)
|
||||
return &sub
|
||||
}
|
||||
|
||||
// Create a new sub-Logger with an explicit name. This ignores the current
|
||||
// name. This is used to create a standalone logger that doesn't fall
|
||||
// within the normal hierarchy. Registered sinks will subscribe
|
||||
// to these messages as well.
|
||||
func (i *interceptLogger) ResetNamedIntercept(name string) InterceptLogger {
|
||||
var sub interceptLogger
|
||||
|
||||
sub = *i
|
||||
sub.Logger = i.Logger.ResetNamed(name)
|
||||
return &sub
|
||||
}
|
||||
|
||||
// Return a sub-Logger for which every emitted log message will contain
|
||||
// the given key/value pairs. This is used to create a context specific
|
||||
// Logger.
|
||||
func (i *interceptLogger) With(args ...interface{}) Logger {
|
||||
var sub interceptLogger
|
||||
|
||||
sub = *i
|
||||
|
||||
sub.Logger = i.Logger.With(args...)
|
||||
|
||||
return &sub
|
||||
}
|
||||
|
||||
// RegisterSink attaches a SinkAdapter to interceptLoggers sinks.
|
||||
func (i *interceptLogger) RegisterSink(sink SinkAdapter) {
|
||||
i.mu.Lock()
|
||||
defer i.mu.Unlock()
|
||||
|
||||
i.Sinks[sink] = struct{}{}
|
||||
|
||||
atomic.AddInt32(i.sinkCount, 1)
|
||||
}
|
||||
|
||||
// DeregisterSink removes a SinkAdapter from interceptLoggers sinks.
|
||||
func (i *interceptLogger) DeregisterSink(sink SinkAdapter) {
|
||||
i.mu.Lock()
|
||||
defer i.mu.Unlock()
|
||||
|
||||
delete(i.Sinks, sink)
|
||||
|
||||
atomic.AddInt32(i.sinkCount, -1)
|
||||
}
|
||||
|
||||
func (i *interceptLogger) StandardLoggerIntercept(opts *StandardLoggerOptions) *log.Logger {
|
||||
return i.StandardLogger(opts)
|
||||
}
|
||||
|
||||
func (i *interceptLogger) StandardLogger(opts *StandardLoggerOptions) *log.Logger {
|
||||
if opts == nil {
|
||||
opts = &StandardLoggerOptions{}
|
||||
}
|
||||
|
||||
return log.New(i.StandardWriter(opts), "", 0)
|
||||
}
|
||||
|
||||
func (i *interceptLogger) StandardWriterIntercept(opts *StandardLoggerOptions) io.Writer {
|
||||
return i.StandardWriter(opts)
|
||||
}
|
||||
|
||||
func (i *interceptLogger) StandardWriter(opts *StandardLoggerOptions) io.Writer {
|
||||
return &stdlogAdapter{
|
||||
log: i,
|
||||
inferLevels: opts.InferLevels,
|
||||
inferLevelsWithTimestamp: opts.InferLevelsWithTimestamp,
|
||||
forceLevel: opts.ForceLevel,
|
||||
}
|
||||
}
|
||||
|
||||
func (i *interceptLogger) ResetOutput(opts *LoggerOptions) error {
|
||||
if or, ok := i.Logger.(OutputResettable); ok {
|
||||
return or.ResetOutput(opts)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (i *interceptLogger) ResetOutputWithFlush(opts *LoggerOptions, flushable Flushable) error {
|
||||
if or, ok := i.Logger.(OutputResettable); ok {
|
||||
return or.ResetOutputWithFlush(opts, flushable)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
918
vendor/github.com/hashicorp/go-hclog/intlogger.go
generated
vendored
Normal file
918
vendor/github.com/hashicorp/go-hclog/intlogger.go
generated
vendored
Normal file
@@ -0,0 +1,918 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package hclog
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/fatih/color"
|
||||
)
|
||||
|
||||
// TimeFormat is the time format to use for plain (non-JSON) output.
|
||||
// This is a version of RFC3339 that contains millisecond precision.
|
||||
const TimeFormat = "2006-01-02T15:04:05.000Z0700"
|
||||
|
||||
// TimeFormatJSON is the time format to use for JSON output.
|
||||
// This is a version of RFC3339 that contains microsecond precision.
|
||||
const TimeFormatJSON = "2006-01-02T15:04:05.000000Z07:00"
|
||||
|
||||
// errJsonUnsupportedTypeMsg is included in log json entries, if an arg cannot be serialized to json
|
||||
const errJsonUnsupportedTypeMsg = "logging contained values that don't serialize to json"
|
||||
|
||||
var (
|
||||
_levelToBracket = map[Level]string{
|
||||
Debug: "[DEBUG]",
|
||||
Trace: "[TRACE]",
|
||||
Info: "[INFO] ",
|
||||
Warn: "[WARN] ",
|
||||
Error: "[ERROR]",
|
||||
}
|
||||
|
||||
_levelToColor = map[Level]*color.Color{
|
||||
Debug: color.New(color.FgHiWhite),
|
||||
Trace: color.New(color.FgHiGreen),
|
||||
Info: color.New(color.FgHiBlue),
|
||||
Warn: color.New(color.FgHiYellow),
|
||||
Error: color.New(color.FgHiRed),
|
||||
}
|
||||
|
||||
faintBoldColor = color.New(color.Faint, color.Bold)
|
||||
faintColor = color.New(color.Faint)
|
||||
faintMultiLinePrefix = faintColor.Sprint(" | ")
|
||||
faintFieldSeparator = faintColor.Sprint("=")
|
||||
faintFieldSeparatorWithNewLine = faintColor.Sprint("=\n")
|
||||
)
|
||||
|
||||
// Make sure that intLogger is a Logger
|
||||
var _ Logger = &intLogger{}
|
||||
|
||||
// intLogger is an internal logger implementation. Internal in that it is
|
||||
// defined entirely by this package.
|
||||
type intLogger struct {
|
||||
json bool
|
||||
callerOffset int
|
||||
name string
|
||||
timeFormat string
|
||||
timeFn TimeFunction
|
||||
disableTime bool
|
||||
|
||||
// This is an interface so that it's shared by any derived loggers, since
|
||||
// those derived loggers share the bufio.Writer as well.
|
||||
mutex Locker
|
||||
writer *writer
|
||||
level *int32
|
||||
|
||||
headerColor ColorOption
|
||||
fieldColor ColorOption
|
||||
|
||||
implied []interface{}
|
||||
|
||||
exclude func(level Level, msg string, args ...interface{}) bool
|
||||
|
||||
// create subloggers with their own level setting
|
||||
independentLevels bool
|
||||
|
||||
subloggerHook func(sub Logger) Logger
|
||||
}
|
||||
|
||||
// New returns a configured logger.
|
||||
func New(opts *LoggerOptions) Logger {
|
||||
return newLogger(opts)
|
||||
}
|
||||
|
||||
// NewSinkAdapter returns a SinkAdapter with configured settings
|
||||
// defined by LoggerOptions
|
||||
func NewSinkAdapter(opts *LoggerOptions) SinkAdapter {
|
||||
l := newLogger(opts)
|
||||
if l.callerOffset > 0 {
|
||||
// extra frames for interceptLogger.{Warn,Info,Log,etc...}, and SinkAdapter.Accept
|
||||
l.callerOffset += 2
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func newLogger(opts *LoggerOptions) *intLogger {
|
||||
if opts == nil {
|
||||
opts = &LoggerOptions{}
|
||||
}
|
||||
|
||||
output := opts.Output
|
||||
if output == nil {
|
||||
output = DefaultOutput
|
||||
}
|
||||
|
||||
level := opts.Level
|
||||
if level == NoLevel {
|
||||
level = DefaultLevel
|
||||
}
|
||||
|
||||
mutex := opts.Mutex
|
||||
if mutex == nil {
|
||||
mutex = new(sync.Mutex)
|
||||
}
|
||||
|
||||
var (
|
||||
primaryColor ColorOption = ColorOff
|
||||
headerColor ColorOption = ColorOff
|
||||
fieldColor ColorOption = ColorOff
|
||||
)
|
||||
switch {
|
||||
case opts.ColorHeaderOnly:
|
||||
headerColor = opts.Color
|
||||
case opts.ColorHeaderAndFields:
|
||||
fieldColor = opts.Color
|
||||
headerColor = opts.Color
|
||||
default:
|
||||
primaryColor = opts.Color
|
||||
}
|
||||
|
||||
l := &intLogger{
|
||||
json: opts.JSONFormat,
|
||||
name: opts.Name,
|
||||
timeFormat: TimeFormat,
|
||||
timeFn: time.Now,
|
||||
disableTime: opts.DisableTime,
|
||||
mutex: mutex,
|
||||
writer: newWriter(output, primaryColor),
|
||||
level: new(int32),
|
||||
exclude: opts.Exclude,
|
||||
independentLevels: opts.IndependentLevels,
|
||||
headerColor: headerColor,
|
||||
fieldColor: fieldColor,
|
||||
subloggerHook: opts.SubloggerHook,
|
||||
}
|
||||
if opts.IncludeLocation {
|
||||
l.callerOffset = offsetIntLogger + opts.AdditionalLocationOffset
|
||||
}
|
||||
|
||||
if l.json {
|
||||
l.timeFormat = TimeFormatJSON
|
||||
}
|
||||
if opts.TimeFn != nil {
|
||||
l.timeFn = opts.TimeFn
|
||||
}
|
||||
if opts.TimeFormat != "" {
|
||||
l.timeFormat = opts.TimeFormat
|
||||
}
|
||||
|
||||
if l.subloggerHook == nil {
|
||||
l.subloggerHook = identityHook
|
||||
}
|
||||
|
||||
l.setColorization(opts)
|
||||
|
||||
atomic.StoreInt32(l.level, int32(level))
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func identityHook(logger Logger) Logger {
|
||||
return logger
|
||||
}
|
||||
|
||||
// offsetIntLogger is the stack frame offset in the call stack for the caller to
|
||||
// one of the Warn, Info, Log, etc methods.
|
||||
const offsetIntLogger = 3
|
||||
|
||||
// Log a message and a set of key/value pairs if the given level is at
|
||||
// or more severe that the threshold configured in the Logger.
|
||||
func (l *intLogger) log(name string, level Level, msg string, args ...interface{}) {
|
||||
if level < Level(atomic.LoadInt32(l.level)) {
|
||||
return
|
||||
}
|
||||
|
||||
t := l.timeFn()
|
||||
|
||||
l.mutex.Lock()
|
||||
defer l.mutex.Unlock()
|
||||
|
||||
if l.exclude != nil && l.exclude(level, msg, args...) {
|
||||
return
|
||||
}
|
||||
|
||||
if l.json {
|
||||
l.logJSON(t, name, level, msg, args...)
|
||||
} else {
|
||||
l.logPlain(t, name, level, msg, args...)
|
||||
}
|
||||
|
||||
l.writer.Flush(level)
|
||||
}
|
||||
|
||||
// Cleanup a path by returning the last 2 segments of the path only.
|
||||
func trimCallerPath(path string) string {
|
||||
// lovely borrowed from zap
|
||||
// nb. To make sure we trim the path correctly on Windows too, we
|
||||
// counter-intuitively need to use '/' and *not* os.PathSeparator here,
|
||||
// because the path given originates from Go stdlib, specifically
|
||||
// runtime.Caller() which (as of Mar/17) returns forward slashes even on
|
||||
// Windows.
|
||||
//
|
||||
// See https://github.com/golang/go/issues/3335
|
||||
// and https://github.com/golang/go/issues/18151
|
||||
//
|
||||
// for discussion on the issue on Go side.
|
||||
|
||||
// Find the last separator.
|
||||
idx := strings.LastIndexByte(path, '/')
|
||||
if idx == -1 {
|
||||
return path
|
||||
}
|
||||
|
||||
// Find the penultimate separator.
|
||||
idx = strings.LastIndexByte(path[:idx], '/')
|
||||
if idx == -1 {
|
||||
return path
|
||||
}
|
||||
|
||||
return path[idx+1:]
|
||||
}
|
||||
|
||||
// isNormal indicates if the rune is one allowed to exist as an unquoted
|
||||
// string value. This is a subset of ASCII, `-` through `~`.
|
||||
func isNormal(r rune) bool {
|
||||
return 0x2D <= r && r <= 0x7E // - through ~
|
||||
}
|
||||
|
||||
// needsQuoting returns false if all the runes in string are normal, according
|
||||
// to isNormal
|
||||
func needsQuoting(str string) bool {
|
||||
for _, r := range str {
|
||||
if !isNormal(r) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// logPlain is the non-JSON logging format function which writes directly
|
||||
// to the underlying writer the logger was initialized with.
|
||||
//
|
||||
// If the logger was initialized with a color function, it also handles
|
||||
// applying the color to the log message.
|
||||
//
|
||||
// Color Options
|
||||
// 1. No color.
|
||||
// 2. Color the whole log line, based on the level.
|
||||
// 3. Color only the header (level) part of the log line.
|
||||
// 4. Color both the header and fields of the log line.
|
||||
func (l *intLogger) logPlain(t time.Time, name string, level Level, msg string, args ...interface{}) {
|
||||
|
||||
if !l.disableTime {
|
||||
l.writer.WriteString(t.Format(l.timeFormat))
|
||||
l.writer.WriteByte(' ')
|
||||
}
|
||||
|
||||
s, ok := _levelToBracket[level]
|
||||
if ok {
|
||||
if l.headerColor != ColorOff {
|
||||
color := _levelToColor[level]
|
||||
color.Fprint(l.writer, s)
|
||||
} else {
|
||||
l.writer.WriteString(s)
|
||||
}
|
||||
} else {
|
||||
l.writer.WriteString("[?????]")
|
||||
}
|
||||
|
||||
if l.callerOffset > 0 {
|
||||
if _, file, line, ok := runtime.Caller(l.callerOffset); ok {
|
||||
l.writer.WriteByte(' ')
|
||||
l.writer.WriteString(trimCallerPath(file))
|
||||
l.writer.WriteByte(':')
|
||||
l.writer.WriteString(strconv.Itoa(line))
|
||||
l.writer.WriteByte(':')
|
||||
}
|
||||
}
|
||||
|
||||
l.writer.WriteByte(' ')
|
||||
|
||||
if name != "" {
|
||||
l.writer.WriteString(name)
|
||||
if msg != "" {
|
||||
l.writer.WriteString(": ")
|
||||
l.writer.WriteString(msg)
|
||||
}
|
||||
} else if msg != "" {
|
||||
l.writer.WriteString(msg)
|
||||
}
|
||||
|
||||
args = append(l.implied, args...)
|
||||
|
||||
var stacktrace CapturedStacktrace
|
||||
|
||||
if len(args) > 0 {
|
||||
if len(args)%2 != 0 {
|
||||
cs, ok := args[len(args)-1].(CapturedStacktrace)
|
||||
if ok {
|
||||
args = args[:len(args)-1]
|
||||
stacktrace = cs
|
||||
} else {
|
||||
extra := args[len(args)-1]
|
||||
args = append(args[:len(args)-1], MissingKey, extra)
|
||||
}
|
||||
}
|
||||
|
||||
l.writer.WriteByte(':')
|
||||
|
||||
// Handle the field arguments, which come in pairs (key=val).
|
||||
FOR:
|
||||
for i := 0; i < len(args); i = i + 2 {
|
||||
var (
|
||||
key string
|
||||
val string
|
||||
raw bool
|
||||
)
|
||||
|
||||
// Convert the field value to a string.
|
||||
switch st := args[i+1].(type) {
|
||||
case string:
|
||||
val = st
|
||||
if st == "" {
|
||||
val = `""`
|
||||
raw = true
|
||||
}
|
||||
case int:
|
||||
val = strconv.FormatInt(int64(st), 10)
|
||||
case int64:
|
||||
val = strconv.FormatInt(int64(st), 10)
|
||||
case int32:
|
||||
val = strconv.FormatInt(int64(st), 10)
|
||||
case int16:
|
||||
val = strconv.FormatInt(int64(st), 10)
|
||||
case int8:
|
||||
val = strconv.FormatInt(int64(st), 10)
|
||||
case uint:
|
||||
val = strconv.FormatUint(uint64(st), 10)
|
||||
case uint64:
|
||||
val = strconv.FormatUint(uint64(st), 10)
|
||||
case uint32:
|
||||
val = strconv.FormatUint(uint64(st), 10)
|
||||
case uint16:
|
||||
val = strconv.FormatUint(uint64(st), 10)
|
||||
case uint8:
|
||||
val = strconv.FormatUint(uint64(st), 10)
|
||||
case Hex:
|
||||
val = "0x" + strconv.FormatUint(uint64(st), 16)
|
||||
case Octal:
|
||||
val = "0" + strconv.FormatUint(uint64(st), 8)
|
||||
case Binary:
|
||||
val = "0b" + strconv.FormatUint(uint64(st), 2)
|
||||
case CapturedStacktrace:
|
||||
stacktrace = st
|
||||
continue FOR
|
||||
case Format:
|
||||
val = fmt.Sprintf(st[0].(string), st[1:]...)
|
||||
case Quote:
|
||||
raw = true
|
||||
val = strconv.Quote(string(st))
|
||||
default:
|
||||
v := reflect.ValueOf(st)
|
||||
if v.Kind() == reflect.Slice {
|
||||
val = l.renderSlice(v)
|
||||
raw = true
|
||||
} else {
|
||||
val = fmt.Sprintf("%v", st)
|
||||
}
|
||||
}
|
||||
|
||||
// Convert the field key to a string.
|
||||
switch st := args[i].(type) {
|
||||
case string:
|
||||
key = st
|
||||
default:
|
||||
key = fmt.Sprintf("%s", st)
|
||||
}
|
||||
|
||||
// Optionally apply the ANSI "faint" and "bold"
|
||||
// SGR values to the key.
|
||||
if l.fieldColor != ColorOff {
|
||||
key = faintBoldColor.Sprint(key)
|
||||
}
|
||||
|
||||
// Values may contain multiple lines, and that format
|
||||
// is preserved, with each line prefixed with a " | "
|
||||
// to show it's part of a collection of lines.
|
||||
//
|
||||
// Values may also need quoting, if not all the runes
|
||||
// in the value string are "normal", like if they
|
||||
// contain ANSI escape sequences.
|
||||
if strings.Contains(val, "\n") {
|
||||
l.writer.WriteString("\n ")
|
||||
l.writer.WriteString(key)
|
||||
if l.fieldColor != ColorOff {
|
||||
l.writer.WriteString(faintFieldSeparatorWithNewLine)
|
||||
writeIndent(l.writer, val, faintMultiLinePrefix)
|
||||
} else {
|
||||
l.writer.WriteString("=\n")
|
||||
writeIndent(l.writer, val, " | ")
|
||||
}
|
||||
l.writer.WriteString(" ")
|
||||
} else if !raw && needsQuoting(val) {
|
||||
l.writer.WriteByte(' ')
|
||||
l.writer.WriteString(key)
|
||||
if l.fieldColor != ColorOff {
|
||||
l.writer.WriteString(faintFieldSeparator)
|
||||
} else {
|
||||
l.writer.WriteByte('=')
|
||||
}
|
||||
l.writer.WriteByte('"')
|
||||
writeEscapedForOutput(l.writer, val, true)
|
||||
l.writer.WriteByte('"')
|
||||
} else {
|
||||
l.writer.WriteByte(' ')
|
||||
l.writer.WriteString(key)
|
||||
if l.fieldColor != ColorOff {
|
||||
l.writer.WriteString(faintFieldSeparator)
|
||||
} else {
|
||||
l.writer.WriteByte('=')
|
||||
}
|
||||
l.writer.WriteString(val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
l.writer.WriteString("\n")
|
||||
|
||||
if stacktrace != "" {
|
||||
l.writer.WriteString(string(stacktrace))
|
||||
l.writer.WriteString("\n")
|
||||
}
|
||||
}
|
||||
|
||||
func writeIndent(w *writer, str string, indent string) {
|
||||
for {
|
||||
nl := strings.IndexByte(str, "\n"[0])
|
||||
if nl == -1 {
|
||||
if str != "" {
|
||||
w.WriteString(indent)
|
||||
writeEscapedForOutput(w, str, false)
|
||||
w.WriteString("\n")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteString(indent)
|
||||
writeEscapedForOutput(w, str[:nl], false)
|
||||
w.WriteString("\n")
|
||||
str = str[nl+1:]
|
||||
}
|
||||
}
|
||||
|
||||
func needsEscaping(str string) bool {
|
||||
for _, b := range str {
|
||||
if !unicode.IsPrint(b) || b == '"' {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
const (
|
||||
lowerhex = "0123456789abcdef"
|
||||
)
|
||||
|
||||
var bufPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(bytes.Buffer)
|
||||
},
|
||||
}
|
||||
|
||||
func writeEscapedForOutput(w io.Writer, str string, escapeQuotes bool) {
|
||||
if !needsEscaping(str) {
|
||||
w.Write([]byte(str))
|
||||
return
|
||||
}
|
||||
|
||||
bb := bufPool.Get().(*bytes.Buffer)
|
||||
bb.Reset()
|
||||
|
||||
defer bufPool.Put(bb)
|
||||
|
||||
for _, r := range str {
|
||||
if escapeQuotes && r == '"' {
|
||||
bb.WriteString(`\"`)
|
||||
} else if unicode.IsPrint(r) {
|
||||
bb.WriteRune(r)
|
||||
} else {
|
||||
switch r {
|
||||
case '\a':
|
||||
bb.WriteString(`\a`)
|
||||
case '\b':
|
||||
bb.WriteString(`\b`)
|
||||
case '\f':
|
||||
bb.WriteString(`\f`)
|
||||
case '\n':
|
||||
bb.WriteString(`\n`)
|
||||
case '\r':
|
||||
bb.WriteString(`\r`)
|
||||
case '\t':
|
||||
bb.WriteString(`\t`)
|
||||
case '\v':
|
||||
bb.WriteString(`\v`)
|
||||
default:
|
||||
switch {
|
||||
case r < ' ':
|
||||
bb.WriteString(`\x`)
|
||||
bb.WriteByte(lowerhex[byte(r)>>4])
|
||||
bb.WriteByte(lowerhex[byte(r)&0xF])
|
||||
case !utf8.ValidRune(r):
|
||||
r = 0xFFFD
|
||||
fallthrough
|
||||
case r < 0x10000:
|
||||
bb.WriteString(`\u`)
|
||||
for s := 12; s >= 0; s -= 4 {
|
||||
bb.WriteByte(lowerhex[r>>uint(s)&0xF])
|
||||
}
|
||||
default:
|
||||
bb.WriteString(`\U`)
|
||||
for s := 28; s >= 0; s -= 4 {
|
||||
bb.WriteByte(lowerhex[r>>uint(s)&0xF])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
w.Write(bb.Bytes())
|
||||
}
|
||||
|
||||
func (l *intLogger) renderSlice(v reflect.Value) string {
|
||||
var buf bytes.Buffer
|
||||
|
||||
buf.WriteRune('[')
|
||||
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
if i > 0 {
|
||||
buf.WriteString(", ")
|
||||
}
|
||||
|
||||
sv := v.Index(i)
|
||||
|
||||
var val string
|
||||
|
||||
switch sv.Kind() {
|
||||
case reflect.String:
|
||||
val = strconv.Quote(sv.String())
|
||||
case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
val = strconv.FormatInt(sv.Int(), 10)
|
||||
case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
val = strconv.FormatUint(sv.Uint(), 10)
|
||||
default:
|
||||
val = fmt.Sprintf("%v", sv.Interface())
|
||||
if strings.ContainsAny(val, " \t\n\r") {
|
||||
val = strconv.Quote(val)
|
||||
}
|
||||
}
|
||||
|
||||
buf.WriteString(val)
|
||||
}
|
||||
|
||||
buf.WriteRune(']')
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// JSON logging function
|
||||
func (l *intLogger) logJSON(t time.Time, name string, level Level, msg string, args ...interface{}) {
|
||||
vals := l.jsonMapEntry(t, name, level, msg)
|
||||
args = append(l.implied, args...)
|
||||
|
||||
if args != nil && len(args) > 0 {
|
||||
if len(args)%2 != 0 {
|
||||
cs, ok := args[len(args)-1].(CapturedStacktrace)
|
||||
if ok {
|
||||
args = args[:len(args)-1]
|
||||
vals["stacktrace"] = cs
|
||||
} else {
|
||||
extra := args[len(args)-1]
|
||||
args = append(args[:len(args)-1], MissingKey, extra)
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < len(args); i = i + 2 {
|
||||
val := args[i+1]
|
||||
switch sv := val.(type) {
|
||||
case error:
|
||||
// Check if val is of type error. If error type doesn't
|
||||
// implement json.Marshaler or encoding.TextMarshaler
|
||||
// then set val to err.Error() so that it gets marshaled
|
||||
switch sv.(type) {
|
||||
case json.Marshaler, encoding.TextMarshaler:
|
||||
default:
|
||||
val = sv.Error()
|
||||
}
|
||||
case Format:
|
||||
val = fmt.Sprintf(sv[0].(string), sv[1:]...)
|
||||
}
|
||||
|
||||
var key string
|
||||
|
||||
switch st := args[i].(type) {
|
||||
case string:
|
||||
key = st
|
||||
default:
|
||||
key = fmt.Sprintf("%s", st)
|
||||
}
|
||||
vals[key] = val
|
||||
}
|
||||
}
|
||||
|
||||
err := json.NewEncoder(l.writer).Encode(vals)
|
||||
if err != nil {
|
||||
if _, ok := err.(*json.UnsupportedTypeError); ok {
|
||||
plainVal := l.jsonMapEntry(t, name, level, msg)
|
||||
plainVal["@warn"] = errJsonUnsupportedTypeMsg
|
||||
|
||||
json.NewEncoder(l.writer).Encode(plainVal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l intLogger) jsonMapEntry(t time.Time, name string, level Level, msg string) map[string]interface{} {
|
||||
vals := map[string]interface{}{
|
||||
"@message": msg,
|
||||
}
|
||||
if !l.disableTime {
|
||||
vals["@timestamp"] = t.Format(l.timeFormat)
|
||||
}
|
||||
|
||||
var levelStr string
|
||||
switch level {
|
||||
case Error:
|
||||
levelStr = "error"
|
||||
case Warn:
|
||||
levelStr = "warn"
|
||||
case Info:
|
||||
levelStr = "info"
|
||||
case Debug:
|
||||
levelStr = "debug"
|
||||
case Trace:
|
||||
levelStr = "trace"
|
||||
default:
|
||||
levelStr = "all"
|
||||
}
|
||||
|
||||
vals["@level"] = levelStr
|
||||
|
||||
if name != "" {
|
||||
vals["@module"] = name
|
||||
}
|
||||
|
||||
if l.callerOffset > 0 {
|
||||
if _, file, line, ok := runtime.Caller(l.callerOffset + 1); ok {
|
||||
vals["@caller"] = fmt.Sprintf("%s:%d", file, line)
|
||||
}
|
||||
}
|
||||
return vals
|
||||
}
|
||||
|
||||
// Emit the message and args at the provided level
|
||||
func (l *intLogger) Log(level Level, msg string, args ...interface{}) {
|
||||
l.log(l.Name(), level, msg, args...)
|
||||
}
|
||||
|
||||
// Emit the message and args at DEBUG level
|
||||
func (l *intLogger) Debug(msg string, args ...interface{}) {
|
||||
l.log(l.Name(), Debug, msg, args...)
|
||||
}
|
||||
|
||||
// Emit the message and args at TRACE level
|
||||
func (l *intLogger) Trace(msg string, args ...interface{}) {
|
||||
l.log(l.Name(), Trace, msg, args...)
|
||||
}
|
||||
|
||||
// Emit the message and args at INFO level
|
||||
func (l *intLogger) Info(msg string, args ...interface{}) {
|
||||
l.log(l.Name(), Info, msg, args...)
|
||||
}
|
||||
|
||||
// Emit the message and args at WARN level
|
||||
func (l *intLogger) Warn(msg string, args ...interface{}) {
|
||||
l.log(l.Name(), Warn, msg, args...)
|
||||
}
|
||||
|
||||
// Emit the message and args at ERROR level
|
||||
func (l *intLogger) Error(msg string, args ...interface{}) {
|
||||
l.log(l.Name(), Error, msg, args...)
|
||||
}
|
||||
|
||||
// Indicate that the logger would emit TRACE level logs
|
||||
func (l *intLogger) IsTrace() bool {
|
||||
return Level(atomic.LoadInt32(l.level)) == Trace
|
||||
}
|
||||
|
||||
// Indicate that the logger would emit DEBUG level logs
|
||||
func (l *intLogger) IsDebug() bool {
|
||||
return Level(atomic.LoadInt32(l.level)) <= Debug
|
||||
}
|
||||
|
||||
// Indicate that the logger would emit INFO level logs
|
||||
func (l *intLogger) IsInfo() bool {
|
||||
return Level(atomic.LoadInt32(l.level)) <= Info
|
||||
}
|
||||
|
||||
// Indicate that the logger would emit WARN level logs
|
||||
func (l *intLogger) IsWarn() bool {
|
||||
return Level(atomic.LoadInt32(l.level)) <= Warn
|
||||
}
|
||||
|
||||
// Indicate that the logger would emit ERROR level logs
|
||||
func (l *intLogger) IsError() bool {
|
||||
return Level(atomic.LoadInt32(l.level)) <= Error
|
||||
}
|
||||
|
||||
const MissingKey = "EXTRA_VALUE_AT_END"
|
||||
|
||||
// Return a sub-Logger for which every emitted log message will contain
|
||||
// the given key/value pairs. This is used to create a context specific
|
||||
// Logger.
|
||||
func (l *intLogger) With(args ...interface{}) Logger {
|
||||
var extra interface{}
|
||||
|
||||
if len(args)%2 != 0 {
|
||||
extra = args[len(args)-1]
|
||||
args = args[:len(args)-1]
|
||||
}
|
||||
|
||||
sl := l.copy()
|
||||
|
||||
result := make(map[string]interface{}, len(l.implied)+len(args))
|
||||
keys := make([]string, 0, len(l.implied)+len(args))
|
||||
|
||||
// Read existing args, store map and key for consistent sorting
|
||||
for i := 0; i < len(l.implied); i += 2 {
|
||||
key := l.implied[i].(string)
|
||||
keys = append(keys, key)
|
||||
result[key] = l.implied[i+1]
|
||||
}
|
||||
// Read new args, store map and key for consistent sorting
|
||||
for i := 0; i < len(args); i += 2 {
|
||||
key := args[i].(string)
|
||||
_, exists := result[key]
|
||||
if !exists {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
result[key] = args[i+1]
|
||||
}
|
||||
|
||||
// Sort keys to be consistent
|
||||
sort.Strings(keys)
|
||||
|
||||
sl.implied = make([]interface{}, 0, len(l.implied)+len(args))
|
||||
for _, k := range keys {
|
||||
sl.implied = append(sl.implied, k)
|
||||
sl.implied = append(sl.implied, result[k])
|
||||
}
|
||||
|
||||
if extra != nil {
|
||||
sl.implied = append(sl.implied, MissingKey, extra)
|
||||
}
|
||||
|
||||
return l.subloggerHook(sl)
|
||||
}
|
||||
|
||||
// Create a new sub-Logger that a name decending from the current name.
|
||||
// This is used to create a subsystem specific Logger.
|
||||
func (l *intLogger) Named(name string) Logger {
|
||||
sl := l.copy()
|
||||
|
||||
if sl.name != "" {
|
||||
sl.name = sl.name + "." + name
|
||||
} else {
|
||||
sl.name = name
|
||||
}
|
||||
|
||||
return l.subloggerHook(sl)
|
||||
}
|
||||
|
||||
// Create a new sub-Logger with an explicit name. This ignores the current
|
||||
// name. This is used to create a standalone logger that doesn't fall
|
||||
// within the normal hierarchy.
|
||||
func (l *intLogger) ResetNamed(name string) Logger {
|
||||
sl := l.copy()
|
||||
|
||||
sl.name = name
|
||||
|
||||
return l.subloggerHook(sl)
|
||||
}
|
||||
|
||||
func (l *intLogger) ResetOutput(opts *LoggerOptions) error {
|
||||
if opts.Output == nil {
|
||||
return errors.New("given output is nil")
|
||||
}
|
||||
|
||||
l.mutex.Lock()
|
||||
defer l.mutex.Unlock()
|
||||
|
||||
return l.resetOutput(opts)
|
||||
}
|
||||
|
||||
func (l *intLogger) ResetOutputWithFlush(opts *LoggerOptions, flushable Flushable) error {
|
||||
if opts.Output == nil {
|
||||
return errors.New("given output is nil")
|
||||
}
|
||||
if flushable == nil {
|
||||
return errors.New("flushable is nil")
|
||||
}
|
||||
|
||||
l.mutex.Lock()
|
||||
defer l.mutex.Unlock()
|
||||
|
||||
if err := flushable.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return l.resetOutput(opts)
|
||||
}
|
||||
|
||||
func (l *intLogger) resetOutput(opts *LoggerOptions) error {
|
||||
l.writer = newWriter(opts.Output, opts.Color)
|
||||
l.setColorization(opts)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update the logging level on-the-fly. This will affect all subloggers as
|
||||
// well.
|
||||
func (l *intLogger) SetLevel(level Level) {
|
||||
atomic.StoreInt32(l.level, int32(level))
|
||||
}
|
||||
|
||||
// Returns the current level
|
||||
func (l *intLogger) GetLevel() Level {
|
||||
return Level(atomic.LoadInt32(l.level))
|
||||
}
|
||||
|
||||
// Create a *log.Logger that will send it's data through this Logger. This
|
||||
// allows packages that expect to be using the standard library log to actually
|
||||
// use this logger.
|
||||
func (l *intLogger) StandardLogger(opts *StandardLoggerOptions) *log.Logger {
|
||||
if opts == nil {
|
||||
opts = &StandardLoggerOptions{}
|
||||
}
|
||||
|
||||
return log.New(l.StandardWriter(opts), "", 0)
|
||||
}
|
||||
|
||||
func (l *intLogger) StandardWriter(opts *StandardLoggerOptions) io.Writer {
|
||||
newLog := *l
|
||||
if l.callerOffset > 0 {
|
||||
// the stack is
|
||||
// logger.printf() -> l.Output() ->l.out.writer(hclog:stdlogAdaptor.write) -> hclog:stdlogAdaptor.dispatch()
|
||||
// So plus 4.
|
||||
newLog.callerOffset = l.callerOffset + 4
|
||||
}
|
||||
return &stdlogAdapter{
|
||||
log: &newLog,
|
||||
inferLevels: opts.InferLevels,
|
||||
inferLevelsWithTimestamp: opts.InferLevelsWithTimestamp,
|
||||
forceLevel: opts.ForceLevel,
|
||||
}
|
||||
}
|
||||
|
||||
// Accept implements the SinkAdapter interface
|
||||
func (i *intLogger) Accept(name string, level Level, msg string, args ...interface{}) {
|
||||
i.log(name, level, msg, args...)
|
||||
}
|
||||
|
||||
// ImpliedArgs returns the loggers implied args
|
||||
func (i *intLogger) ImpliedArgs() []interface{} {
|
||||
return i.implied
|
||||
}
|
||||
|
||||
// Name returns the loggers name
|
||||
func (i *intLogger) Name() string {
|
||||
return i.name
|
||||
}
|
||||
|
||||
// copy returns a shallow copy of the intLogger, replacing the level pointer
|
||||
// when necessary
|
||||
func (l *intLogger) copy() *intLogger {
|
||||
sl := *l
|
||||
|
||||
if l.independentLevels {
|
||||
sl.level = new(int32)
|
||||
*sl.level = *l.level
|
||||
}
|
||||
|
||||
return &sl
|
||||
}
|
393
vendor/github.com/hashicorp/go-hclog/logger.go
generated
vendored
Normal file
393
vendor/github.com/hashicorp/go-hclog/logger.go
generated
vendored
Normal file
@@ -0,0 +1,393 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package hclog
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultOutput is used as the default log output.
|
||||
DefaultOutput io.Writer = os.Stderr
|
||||
|
||||
// DefaultLevel is used as the default log level.
|
||||
DefaultLevel = Info
|
||||
)
|
||||
|
||||
// Level represents a log level.
|
||||
type Level int32
|
||||
|
||||
const (
|
||||
// NoLevel is a special level used to indicate that no level has been
|
||||
// set and allow for a default to be used.
|
||||
NoLevel Level = 0
|
||||
|
||||
// Trace is the most verbose level. Intended to be used for the tracing
|
||||
// of actions in code, such as function enters/exits, etc.
|
||||
Trace Level = 1
|
||||
|
||||
// Debug information for programmer low-level analysis.
|
||||
Debug Level = 2
|
||||
|
||||
// Info information about steady state operations.
|
||||
Info Level = 3
|
||||
|
||||
// Warn information about rare but handled events.
|
||||
Warn Level = 4
|
||||
|
||||
// Error information about unrecoverable events.
|
||||
Error Level = 5
|
||||
|
||||
// Off disables all logging output.
|
||||
Off Level = 6
|
||||
)
|
||||
|
||||
// Format is a simple convenience type for when formatting is required. When
|
||||
// processing a value of this type, the logger automatically treats the first
|
||||
// argument as a Printf formatting string and passes the rest as the values
|
||||
// to be formatted. For example: L.Info(Fmt{"%d beans/day", beans}).
|
||||
type Format []interface{}
|
||||
|
||||
// Fmt returns a Format type. This is a convenience function for creating a Format
|
||||
// type.
|
||||
func Fmt(str string, args ...interface{}) Format {
|
||||
return append(Format{str}, args...)
|
||||
}
|
||||
|
||||
// A simple shortcut to format numbers in hex when displayed with the normal
|
||||
// text output. For example: L.Info("header value", Hex(17))
|
||||
type Hex int
|
||||
|
||||
// A simple shortcut to format numbers in octal when displayed with the normal
|
||||
// text output. For example: L.Info("perms", Octal(17))
|
||||
type Octal int
|
||||
|
||||
// A simple shortcut to format numbers in binary when displayed with the normal
|
||||
// text output. For example: L.Info("bits", Binary(17))
|
||||
type Binary int
|
||||
|
||||
// A simple shortcut to format strings with Go quoting. Control and
|
||||
// non-printable characters will be escaped with their backslash equivalents in
|
||||
// output. Intended for untrusted or multiline strings which should be logged
|
||||
// as concisely as possible.
|
||||
type Quote string
|
||||
|
||||
// ColorOption expresses how the output should be colored, if at all.
|
||||
type ColorOption uint8
|
||||
|
||||
const (
|
||||
// ColorOff is the default coloration, and does not
|
||||
// inject color codes into the io.Writer.
|
||||
ColorOff ColorOption = iota
|
||||
// AutoColor checks if the io.Writer is a tty,
|
||||
// and if so enables coloring.
|
||||
AutoColor
|
||||
// ForceColor will enable coloring, regardless of whether
|
||||
// the io.Writer is a tty or not.
|
||||
ForceColor
|
||||
)
|
||||
|
||||
// SupportsColor is an optional interface that can be implemented by the output
|
||||
// value. If implemented and SupportsColor() returns true, then AutoColor will
|
||||
// enable colorization.
|
||||
type SupportsColor interface {
|
||||
SupportsColor() bool
|
||||
}
|
||||
|
||||
// LevelFromString returns a Level type for the named log level, or "NoLevel" if
|
||||
// the level string is invalid. This facilitates setting the log level via
|
||||
// config or environment variable by name in a predictable way.
|
||||
func LevelFromString(levelStr string) Level {
|
||||
// We don't care about case. Accept both "INFO" and "info".
|
||||
levelStr = strings.ToLower(strings.TrimSpace(levelStr))
|
||||
switch levelStr {
|
||||
case "trace":
|
||||
return Trace
|
||||
case "debug":
|
||||
return Debug
|
||||
case "info":
|
||||
return Info
|
||||
case "warn":
|
||||
return Warn
|
||||
case "error":
|
||||
return Error
|
||||
case "off":
|
||||
return Off
|
||||
default:
|
||||
return NoLevel
|
||||
}
|
||||
}
|
||||
|
||||
func (l Level) String() string {
|
||||
switch l {
|
||||
case Trace:
|
||||
return "trace"
|
||||
case Debug:
|
||||
return "debug"
|
||||
case Info:
|
||||
return "info"
|
||||
case Warn:
|
||||
return "warn"
|
||||
case Error:
|
||||
return "error"
|
||||
case NoLevel:
|
||||
return "none"
|
||||
case Off:
|
||||
return "off"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// Logger describes the interface that must be implemented by all loggers.
|
||||
type Logger interface {
|
||||
// Args are alternating key, val pairs
|
||||
// keys must be strings
|
||||
// vals can be any type, but display is implementation specific
|
||||
// Emit a message and key/value pairs at a provided log level
|
||||
Log(level Level, msg string, args ...interface{})
|
||||
|
||||
// Emit a message and key/value pairs at the TRACE level
|
||||
Trace(msg string, args ...interface{})
|
||||
|
||||
// Emit a message and key/value pairs at the DEBUG level
|
||||
Debug(msg string, args ...interface{})
|
||||
|
||||
// Emit a message and key/value pairs at the INFO level
|
||||
Info(msg string, args ...interface{})
|
||||
|
||||
// Emit a message and key/value pairs at the WARN level
|
||||
Warn(msg string, args ...interface{})
|
||||
|
||||
// Emit a message and key/value pairs at the ERROR level
|
||||
Error(msg string, args ...interface{})
|
||||
|
||||
// Indicate if TRACE logs would be emitted. This and the other Is* guards
|
||||
// are used to elide expensive logging code based on the current level.
|
||||
IsTrace() bool
|
||||
|
||||
// Indicate if DEBUG logs would be emitted. This and the other Is* guards
|
||||
IsDebug() bool
|
||||
|
||||
// Indicate if INFO logs would be emitted. This and the other Is* guards
|
||||
IsInfo() bool
|
||||
|
||||
// Indicate if WARN logs would be emitted. This and the other Is* guards
|
||||
IsWarn() bool
|
||||
|
||||
// Indicate if ERROR logs would be emitted. This and the other Is* guards
|
||||
IsError() bool
|
||||
|
||||
// ImpliedArgs returns With key/value pairs
|
||||
ImpliedArgs() []interface{}
|
||||
|
||||
// Creates a sublogger that will always have the given key/value pairs
|
||||
With(args ...interface{}) Logger
|
||||
|
||||
// Returns the Name of the logger
|
||||
Name() string
|
||||
|
||||
// Create a logger that will prepend the name string on the front of all messages.
|
||||
// If the logger already has a name, the new value will be appended to the current
|
||||
// name. That way, a major subsystem can use this to decorate all it's own logs
|
||||
// without losing context.
|
||||
Named(name string) Logger
|
||||
|
||||
// Create a logger that will prepend the name string on the front of all messages.
|
||||
// This sets the name of the logger to the value directly, unlike Named which honor
|
||||
// the current name as well.
|
||||
ResetNamed(name string) Logger
|
||||
|
||||
// Updates the level. This should affect all related loggers as well,
|
||||
// unless they were created with IndependentLevels. If an
|
||||
// implementation cannot update the level on the fly, it should no-op.
|
||||
SetLevel(level Level)
|
||||
|
||||
// Returns the current level
|
||||
GetLevel() Level
|
||||
|
||||
// Return a value that conforms to the stdlib log.Logger interface
|
||||
StandardLogger(opts *StandardLoggerOptions) *log.Logger
|
||||
|
||||
// Return a value that conforms to io.Writer, which can be passed into log.SetOutput()
|
||||
StandardWriter(opts *StandardLoggerOptions) io.Writer
|
||||
}
|
||||
|
||||
// StandardLoggerOptions can be used to configure a new standard logger.
|
||||
type StandardLoggerOptions struct {
|
||||
// Indicate that some minimal parsing should be done on strings to try
|
||||
// and detect their level and re-emit them.
|
||||
// This supports the strings like [ERROR], [ERR] [TRACE], [WARN], [INFO],
|
||||
// [DEBUG] and strip it off before reapplying it.
|
||||
InferLevels bool
|
||||
|
||||
// Indicate that some minimal parsing should be done on strings to try
|
||||
// and detect their level and re-emit them while ignoring possible
|
||||
// timestamp values in the beginning of the string.
|
||||
// This supports the strings like [ERROR], [ERR] [TRACE], [WARN], [INFO],
|
||||
// [DEBUG] and strip it off before reapplying it.
|
||||
// The timestamp detection may result in false positives and incomplete
|
||||
// string outputs.
|
||||
InferLevelsWithTimestamp bool
|
||||
|
||||
// ForceLevel is used to force all output from the standard logger to be at
|
||||
// the specified level. Similar to InferLevels, this will strip any level
|
||||
// prefix contained in the logged string before applying the forced level.
|
||||
// If set, this override InferLevels.
|
||||
ForceLevel Level
|
||||
}
|
||||
|
||||
type TimeFunction = func() time.Time
|
||||
|
||||
// LoggerOptions can be used to configure a new logger.
|
||||
type LoggerOptions struct {
|
||||
// Name of the subsystem to prefix logs with
|
||||
Name string
|
||||
|
||||
// The threshold for the logger. Anything less severe is suppressed
|
||||
Level Level
|
||||
|
||||
// Where to write the logs to. Defaults to os.Stderr if nil
|
||||
Output io.Writer
|
||||
|
||||
// An optional Locker in case Output is shared. This can be a sync.Mutex or
|
||||
// a NoopLocker if the caller wants control over output, e.g. for batching
|
||||
// log lines.
|
||||
Mutex Locker
|
||||
|
||||
// Control if the output should be in JSON.
|
||||
JSONFormat bool
|
||||
|
||||
// Include file and line information in each log line
|
||||
IncludeLocation bool
|
||||
|
||||
// AdditionalLocationOffset is the number of additional stack levels to skip
|
||||
// when finding the file and line information for the log line
|
||||
AdditionalLocationOffset int
|
||||
|
||||
// The time format to use instead of the default
|
||||
TimeFormat string
|
||||
|
||||
// A function which is called to get the time object that is formatted using `TimeFormat`
|
||||
TimeFn TimeFunction
|
||||
|
||||
// Control whether or not to display the time at all. This is required
|
||||
// because setting TimeFormat to empty assumes the default format.
|
||||
DisableTime bool
|
||||
|
||||
// Color the output. On Windows, colored logs are only available for io.Writers that
|
||||
// are concretely instances of *os.File.
|
||||
Color ColorOption
|
||||
|
||||
// Only color the header, not the body. This can help with readability of long messages.
|
||||
ColorHeaderOnly bool
|
||||
|
||||
// Color the header and message body fields. This can help with readability
|
||||
// of long messages with multiple fields.
|
||||
ColorHeaderAndFields bool
|
||||
|
||||
// A function which is called with the log information and if it returns true the value
|
||||
// should not be logged.
|
||||
// This is useful when interacting with a system that you wish to suppress the log
|
||||
// message for (because it's too noisy, etc)
|
||||
Exclude func(level Level, msg string, args ...interface{}) bool
|
||||
|
||||
// IndependentLevels causes subloggers to be created with an independent
|
||||
// copy of this logger's level. This means that using SetLevel on this
|
||||
// logger will not affect any subloggers, and SetLevel on any subloggers
|
||||
// will not affect the parent or sibling loggers.
|
||||
IndependentLevels bool
|
||||
|
||||
// SubloggerHook registers a function that is called when a sublogger via
|
||||
// Named, With, or ResetNamed is created. If defined, the function is passed
|
||||
// the newly created Logger and the returned Logger is returned from the
|
||||
// original function. This option allows customization via interception and
|
||||
// wrapping of Logger instances.
|
||||
SubloggerHook func(sub Logger) Logger
|
||||
}
|
||||
|
||||
// InterceptLogger describes the interface for using a logger
|
||||
// that can register different output sinks.
|
||||
// This is useful for sending lower level log messages
|
||||
// to a different output while keeping the root logger
|
||||
// at a higher one.
|
||||
type InterceptLogger interface {
|
||||
// Logger is the root logger for an InterceptLogger
|
||||
Logger
|
||||
|
||||
// RegisterSink adds a SinkAdapter to the InterceptLogger
|
||||
RegisterSink(sink SinkAdapter)
|
||||
|
||||
// DeregisterSink removes a SinkAdapter from the InterceptLogger
|
||||
DeregisterSink(sink SinkAdapter)
|
||||
|
||||
// Create a interceptlogger that will prepend the name string on the front of all messages.
|
||||
// If the logger already has a name, the new value will be appended to the current
|
||||
// name. That way, a major subsystem can use this to decorate all it's own logs
|
||||
// without losing context.
|
||||
NamedIntercept(name string) InterceptLogger
|
||||
|
||||
// Create a interceptlogger that will prepend the name string on the front of all messages.
|
||||
// This sets the name of the logger to the value directly, unlike Named which honor
|
||||
// the current name as well.
|
||||
ResetNamedIntercept(name string) InterceptLogger
|
||||
|
||||
// Deprecated: use StandardLogger
|
||||
StandardLoggerIntercept(opts *StandardLoggerOptions) *log.Logger
|
||||
|
||||
// Deprecated: use StandardWriter
|
||||
StandardWriterIntercept(opts *StandardLoggerOptions) io.Writer
|
||||
}
|
||||
|
||||
// SinkAdapter describes the interface that must be implemented
|
||||
// in order to Register a new sink to an InterceptLogger
|
||||
type SinkAdapter interface {
|
||||
Accept(name string, level Level, msg string, args ...interface{})
|
||||
}
|
||||
|
||||
// Flushable represents a method for flushing an output buffer. It can be used
|
||||
// if Resetting the log to use a new output, in order to flush the writes to
|
||||
// the existing output beforehand.
|
||||
type Flushable interface {
|
||||
Flush() error
|
||||
}
|
||||
|
||||
// OutputResettable provides ways to swap the output in use at runtime
|
||||
type OutputResettable interface {
|
||||
// ResetOutput swaps the current output writer with the one given in the
|
||||
// opts. Color options given in opts will be used for the new output.
|
||||
ResetOutput(opts *LoggerOptions) error
|
||||
|
||||
// ResetOutputWithFlush swaps the current output writer with the one given
|
||||
// in the opts, first calling Flush on the given Flushable. Color options
|
||||
// given in opts will be used for the new output.
|
||||
ResetOutputWithFlush(opts *LoggerOptions, flushable Flushable) error
|
||||
}
|
||||
|
||||
// Locker is used for locking output. If not set when creating a logger, a
|
||||
// sync.Mutex will be used internally.
|
||||
type Locker interface {
|
||||
// Lock is called when the output is going to be changed or written to
|
||||
Lock()
|
||||
|
||||
// Unlock is called when the operation that called Lock() completes
|
||||
Unlock()
|
||||
}
|
||||
|
||||
// NoopLocker implements locker but does nothing. This is useful if the client
|
||||
// wants tight control over locking, in order to provide grouping of log
|
||||
// entries or other functionality.
|
||||
type NoopLocker struct{}
|
||||
|
||||
// Lock does nothing
|
||||
func (n NoopLocker) Lock() {}
|
||||
|
||||
// Unlock does nothing
|
||||
func (n NoopLocker) Unlock() {}
|
||||
|
||||
var _ Locker = (*NoopLocker)(nil)
|
63
vendor/github.com/hashicorp/go-hclog/nulllogger.go
generated
vendored
Normal file
63
vendor/github.com/hashicorp/go-hclog/nulllogger.go
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package hclog
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
)
|
||||
|
||||
// NewNullLogger instantiates a Logger for which all calls
|
||||
// will succeed without doing anything.
|
||||
// Useful for testing purposes.
|
||||
func NewNullLogger() Logger {
|
||||
return &nullLogger{}
|
||||
}
|
||||
|
||||
type nullLogger struct{}
|
||||
|
||||
func (l *nullLogger) Log(level Level, msg string, args ...interface{}) {}
|
||||
|
||||
func (l *nullLogger) Trace(msg string, args ...interface{}) {}
|
||||
|
||||
func (l *nullLogger) Debug(msg string, args ...interface{}) {}
|
||||
|
||||
func (l *nullLogger) Info(msg string, args ...interface{}) {}
|
||||
|
||||
func (l *nullLogger) Warn(msg string, args ...interface{}) {}
|
||||
|
||||
func (l *nullLogger) Error(msg string, args ...interface{}) {}
|
||||
|
||||
func (l *nullLogger) IsTrace() bool { return false }
|
||||
|
||||
func (l *nullLogger) IsDebug() bool { return false }
|
||||
|
||||
func (l *nullLogger) IsInfo() bool { return false }
|
||||
|
||||
func (l *nullLogger) IsWarn() bool { return false }
|
||||
|
||||
func (l *nullLogger) IsError() bool { return false }
|
||||
|
||||
func (l *nullLogger) ImpliedArgs() []interface{} { return []interface{}{} }
|
||||
|
||||
func (l *nullLogger) With(args ...interface{}) Logger { return l }
|
||||
|
||||
func (l *nullLogger) Name() string { return "" }
|
||||
|
||||
func (l *nullLogger) Named(name string) Logger { return l }
|
||||
|
||||
func (l *nullLogger) ResetNamed(name string) Logger { return l }
|
||||
|
||||
func (l *nullLogger) SetLevel(level Level) {}
|
||||
|
||||
func (l *nullLogger) GetLevel() Level { return NoLevel }
|
||||
|
||||
func (l *nullLogger) StandardLogger(opts *StandardLoggerOptions) *log.Logger {
|
||||
return log.New(l.StandardWriter(opts), "", log.LstdFlags)
|
||||
}
|
||||
|
||||
func (l *nullLogger) StandardWriter(opts *StandardLoggerOptions) io.Writer {
|
||||
return ioutil.Discard
|
||||
}
|
109
vendor/github.com/hashicorp/go-hclog/stacktrace.go
generated
vendored
Normal file
109
vendor/github.com/hashicorp/go-hclog/stacktrace.go
generated
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
// Copyright (c) 2016 Uber Technologies, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
package hclog
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
_stacktraceIgnorePrefixes = []string{
|
||||
"runtime.goexit",
|
||||
"runtime.main",
|
||||
}
|
||||
_stacktracePool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return newProgramCounters(64)
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// CapturedStacktrace represents a stacktrace captured by a previous call
|
||||
// to log.Stacktrace. If passed to a logging function, the stacktrace
|
||||
// will be appended.
|
||||
type CapturedStacktrace string
|
||||
|
||||
// Stacktrace captures a stacktrace of the current goroutine and returns
|
||||
// it to be passed to a logging function.
|
||||
func Stacktrace() CapturedStacktrace {
|
||||
return CapturedStacktrace(takeStacktrace())
|
||||
}
|
||||
|
||||
func takeStacktrace() string {
|
||||
programCounters := _stacktracePool.Get().(*programCounters)
|
||||
defer _stacktracePool.Put(programCounters)
|
||||
|
||||
var buffer bytes.Buffer
|
||||
|
||||
for {
|
||||
// Skip the call to runtime.Counters and takeStacktrace so that the
|
||||
// program counters start at the caller of takeStacktrace.
|
||||
n := runtime.Callers(2, programCounters.pcs)
|
||||
if n < cap(programCounters.pcs) {
|
||||
programCounters.pcs = programCounters.pcs[:n]
|
||||
break
|
||||
}
|
||||
// Don't put the too-short counter slice back into the pool; this lets
|
||||
// the pool adjust if we consistently take deep stacktraces.
|
||||
programCounters = newProgramCounters(len(programCounters.pcs) * 2)
|
||||
}
|
||||
|
||||
i := 0
|
||||
frames := runtime.CallersFrames(programCounters.pcs)
|
||||
for frame, more := frames.Next(); more; frame, more = frames.Next() {
|
||||
if shouldIgnoreStacktraceFunction(frame.Function) {
|
||||
continue
|
||||
}
|
||||
if i != 0 {
|
||||
buffer.WriteByte('\n')
|
||||
}
|
||||
i++
|
||||
buffer.WriteString(frame.Function)
|
||||
buffer.WriteByte('\n')
|
||||
buffer.WriteByte('\t')
|
||||
buffer.WriteString(frame.File)
|
||||
buffer.WriteByte(':')
|
||||
buffer.WriteString(strconv.Itoa(int(frame.Line)))
|
||||
}
|
||||
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
func shouldIgnoreStacktraceFunction(function string) bool {
|
||||
for _, prefix := range _stacktraceIgnorePrefixes {
|
||||
if strings.HasPrefix(function, prefix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type programCounters struct {
|
||||
pcs []uintptr
|
||||
}
|
||||
|
||||
func newProgramCounters(size int) *programCounters {
|
||||
return &programCounters{make([]uintptr, size)}
|
||||
}
|
113
vendor/github.com/hashicorp/go-hclog/stdlog.go
generated
vendored
Normal file
113
vendor/github.com/hashicorp/go-hclog/stdlog.go
generated
vendored
Normal file
@@ -0,0 +1,113 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package hclog
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Regex to ignore characters commonly found in timestamp formats from the
|
||||
// beginning of inputs.
|
||||
var logTimestampRegexp = regexp.MustCompile(`^[\d\s\:\/\.\+-TZ]*`)
|
||||
|
||||
// Provides a io.Writer to shim the data out of *log.Logger
|
||||
// and back into our Logger. This is basically the only way to
|
||||
// build upon *log.Logger.
|
||||
type stdlogAdapter struct {
|
||||
log Logger
|
||||
inferLevels bool
|
||||
inferLevelsWithTimestamp bool
|
||||
forceLevel Level
|
||||
}
|
||||
|
||||
// Take the data, infer the levels if configured, and send it through
|
||||
// a regular Logger.
|
||||
func (s *stdlogAdapter) Write(data []byte) (int, error) {
|
||||
str := string(bytes.TrimRight(data, " \t\n"))
|
||||
|
||||
if s.forceLevel != NoLevel {
|
||||
// Use pickLevel to strip log levels included in the line since we are
|
||||
// forcing the level
|
||||
_, str := s.pickLevel(str)
|
||||
|
||||
// Log at the forced level
|
||||
s.dispatch(str, s.forceLevel)
|
||||
} else if s.inferLevels {
|
||||
if s.inferLevelsWithTimestamp {
|
||||
str = s.trimTimestamp(str)
|
||||
}
|
||||
|
||||
level, str := s.pickLevel(str)
|
||||
s.dispatch(str, level)
|
||||
} else {
|
||||
s.log.Info(str)
|
||||
}
|
||||
|
||||
return len(data), nil
|
||||
}
|
||||
|
||||
func (s *stdlogAdapter) dispatch(str string, level Level) {
|
||||
switch level {
|
||||
case Trace:
|
||||
s.log.Trace(str)
|
||||
case Debug:
|
||||
s.log.Debug(str)
|
||||
case Info:
|
||||
s.log.Info(str)
|
||||
case Warn:
|
||||
s.log.Warn(str)
|
||||
case Error:
|
||||
s.log.Error(str)
|
||||
default:
|
||||
s.log.Info(str)
|
||||
}
|
||||
}
|
||||
|
||||
// Detect, based on conventions, what log level this is.
|
||||
func (s *stdlogAdapter) pickLevel(str string) (Level, string) {
|
||||
switch {
|
||||
case strings.HasPrefix(str, "[DEBUG]"):
|
||||
return Debug, strings.TrimSpace(str[7:])
|
||||
case strings.HasPrefix(str, "[TRACE]"):
|
||||
return Trace, strings.TrimSpace(str[7:])
|
||||
case strings.HasPrefix(str, "[INFO]"):
|
||||
return Info, strings.TrimSpace(str[6:])
|
||||
case strings.HasPrefix(str, "[WARN]"):
|
||||
return Warn, strings.TrimSpace(str[6:])
|
||||
case strings.HasPrefix(str, "[ERROR]"):
|
||||
return Error, strings.TrimSpace(str[7:])
|
||||
case strings.HasPrefix(str, "[ERR]"):
|
||||
return Error, strings.TrimSpace(str[5:])
|
||||
default:
|
||||
return Info, str
|
||||
}
|
||||
}
|
||||
|
||||
func (s *stdlogAdapter) trimTimestamp(str string) string {
|
||||
idx := logTimestampRegexp.FindStringIndex(str)
|
||||
return str[idx[1]:]
|
||||
}
|
||||
|
||||
type logWriter struct {
|
||||
l *log.Logger
|
||||
}
|
||||
|
||||
func (l *logWriter) Write(b []byte) (int, error) {
|
||||
l.l.Println(string(bytes.TrimRight(b, " \n\t")))
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
// Takes a standard library logger and returns a Logger that will write to it
|
||||
func FromStandardLogger(l *log.Logger, opts *LoggerOptions) Logger {
|
||||
var dl LoggerOptions = *opts
|
||||
|
||||
// Use the time format that log.Logger uses
|
||||
dl.DisableTime = true
|
||||
dl.Output = &logWriter{l}
|
||||
|
||||
return New(&dl)
|
||||
}
|
85
vendor/github.com/hashicorp/go-hclog/writer.go
generated
vendored
Normal file
85
vendor/github.com/hashicorp/go-hclog/writer.go
generated
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package hclog
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
)
|
||||
|
||||
type writer struct {
|
||||
b bytes.Buffer
|
||||
w io.Writer
|
||||
color ColorOption
|
||||
}
|
||||
|
||||
func newWriter(w io.Writer, color ColorOption) *writer {
|
||||
return &writer{w: w, color: color}
|
||||
}
|
||||
|
||||
func (w *writer) Flush(level Level) (err error) {
|
||||
var unwritten = w.b.Bytes()
|
||||
|
||||
if w.color != ColorOff {
|
||||
color := _levelToColor[level]
|
||||
unwritten = []byte(color.Sprintf("%s", unwritten))
|
||||
}
|
||||
|
||||
if lw, ok := w.w.(LevelWriter); ok {
|
||||
_, err = lw.LevelWrite(level, unwritten)
|
||||
} else {
|
||||
_, err = w.w.Write(unwritten)
|
||||
}
|
||||
w.b.Reset()
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *writer) Write(p []byte) (int, error) {
|
||||
return w.b.Write(p)
|
||||
}
|
||||
|
||||
func (w *writer) WriteByte(c byte) error {
|
||||
return w.b.WriteByte(c)
|
||||
}
|
||||
|
||||
func (w *writer) WriteString(s string) (int, error) {
|
||||
return w.b.WriteString(s)
|
||||
}
|
||||
|
||||
// LevelWriter is the interface that wraps the LevelWrite method.
|
||||
type LevelWriter interface {
|
||||
LevelWrite(level Level, p []byte) (n int, err error)
|
||||
}
|
||||
|
||||
// LeveledWriter writes all log messages to the standard writer,
|
||||
// except for log levels that are defined in the overrides map.
|
||||
type LeveledWriter struct {
|
||||
standard io.Writer
|
||||
overrides map[Level]io.Writer
|
||||
}
|
||||
|
||||
// NewLeveledWriter returns an initialized LeveledWriter.
|
||||
//
|
||||
// standard will be used as the default writer for all log levels,
|
||||
// except for log levels that are defined in the overrides map.
|
||||
func NewLeveledWriter(standard io.Writer, overrides map[Level]io.Writer) *LeveledWriter {
|
||||
return &LeveledWriter{
|
||||
standard: standard,
|
||||
overrides: overrides,
|
||||
}
|
||||
}
|
||||
|
||||
// Write implements io.Writer.
|
||||
func (lw *LeveledWriter) Write(p []byte) (int, error) {
|
||||
return lw.standard.Write(p)
|
||||
}
|
||||
|
||||
// LevelWrite implements LevelWriter.
|
||||
func (lw *LeveledWriter) LevelWrite(level Level, p []byte) (int, error) {
|
||||
w, ok := lw.overrides[level]
|
||||
if !ok {
|
||||
w = lw.standard
|
||||
}
|
||||
return w.Write(p)
|
||||
}
|
Reference in New Issue
Block a user