MM-36764 mlog refactor (#18118)
Refactor mlog - simplify mlog by removing redundant code - remove Zap dependency - update unit test helpers - update logging config - update auditing
Этот коммит содержится в:
11
vendor/github.com/mattermost/logr/config.go
сгенерированный
поставляемый
11
vendor/github.com/mattermost/logr/config.go
сгенерированный
поставляемый
@@ -1,11 +0,0 @@
|
||||
package logr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/wiggin77/cfg"
|
||||
)
|
||||
|
||||
func ConfigLogger(config *cfg.Config) error {
|
||||
return fmt.Errorf("Not implemented yet")
|
||||
}
|
||||
26
vendor/github.com/mattermost/logr/filter.go
сгенерированный
поставляемый
26
vendor/github.com/mattermost/logr/filter.go
сгенерированный
поставляемый
@@ -1,26 +0,0 @@
|
||||
package logr
|
||||
|
||||
// LevelID is the unique id of each level.
|
||||
type LevelID uint
|
||||
|
||||
// Level provides a mechanism to enable/disable specific log lines.
|
||||
type Level struct {
|
||||
ID LevelID
|
||||
Name string
|
||||
Stacktrace bool
|
||||
}
|
||||
|
||||
// String returns the name of this level.
|
||||
func (level Level) String() string {
|
||||
return level.Name
|
||||
}
|
||||
|
||||
// Filter allows targets to determine which Level(s) are active
|
||||
// for logging and which Level(s) require a stack trace to be output.
|
||||
// A default implementation using "panic, fatal..." is provided, and
|
||||
// a more flexible alternative implementation is also provided that
|
||||
// allows any number of custom levels.
|
||||
type Filter interface {
|
||||
IsEnabled(Level) bool
|
||||
IsStacktraceEnabled(Level) bool
|
||||
}
|
||||
273
vendor/github.com/mattermost/logr/format/json.go
сгенерированный
поставляемый
273
vendor/github.com/mattermost/logr/format/json.go
сгенерированный
поставляемый
@@ -1,273 +0,0 @@
|
||||
package format
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/francoispqt/gojay"
|
||||
"github.com/mattermost/logr"
|
||||
)
|
||||
|
||||
// ContextField is a name/value pair within the context fields.
|
||||
type ContextField struct {
|
||||
Key string
|
||||
Val interface{}
|
||||
}
|
||||
|
||||
// JSON formats log records as JSON.
|
||||
type JSON struct {
|
||||
// DisableTimestamp disables output of timestamp field.
|
||||
DisableTimestamp bool
|
||||
// DisableLevel disables output of level field.
|
||||
DisableLevel bool
|
||||
// DisableMsg disables output of msg field.
|
||||
DisableMsg bool
|
||||
// DisableContext disables output of all context fields.
|
||||
DisableContext bool
|
||||
// DisableStacktrace disables output of stack trace.
|
||||
DisableStacktrace bool
|
||||
|
||||
// TimestampFormat is an optional format for timestamps. If empty
|
||||
// then DefTimestampFormat is used.
|
||||
TimestampFormat string
|
||||
|
||||
// Deprecated: this has no effect.
|
||||
Indent string
|
||||
|
||||
// EscapeHTML determines if certain characters (e.g. `<`, `>`, `&`)
|
||||
// are escaped.
|
||||
EscapeHTML bool
|
||||
|
||||
// KeyTimestamp overrides the timestamp field key name.
|
||||
KeyTimestamp string
|
||||
|
||||
// KeyLevel overrides the level field key name.
|
||||
KeyLevel string
|
||||
|
||||
// KeyMsg overrides the msg field key name.
|
||||
KeyMsg string
|
||||
|
||||
// KeyContextFields when not empty will group all context fields
|
||||
// under this key.
|
||||
KeyContextFields string
|
||||
|
||||
// KeyStacktrace overrides the stacktrace field key name.
|
||||
KeyStacktrace string
|
||||
|
||||
// ContextSorter allows custom sorting for the context fields.
|
||||
ContextSorter func(fields logr.Fields) []ContextField
|
||||
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
// Format converts a log record to bytes in JSON format.
|
||||
func (j *JSON) Format(rec *logr.LogRec, stacktrace bool, buf *bytes.Buffer) (*bytes.Buffer, error) {
|
||||
j.once.Do(j.applyDefaultKeyNames)
|
||||
|
||||
if buf == nil {
|
||||
buf = &bytes.Buffer{}
|
||||
}
|
||||
enc := gojay.BorrowEncoder(buf)
|
||||
defer func() {
|
||||
enc.Release()
|
||||
}()
|
||||
|
||||
sorter := j.ContextSorter
|
||||
if sorter == nil {
|
||||
sorter = j.defaultContextSorter
|
||||
}
|
||||
|
||||
jlr := JSONLogRec{
|
||||
LogRec: rec,
|
||||
JSON: j,
|
||||
stacktrace: stacktrace,
|
||||
sorter: sorter,
|
||||
}
|
||||
|
||||
err := enc.EncodeObject(jlr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf.WriteByte('\n')
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (j *JSON) applyDefaultKeyNames() {
|
||||
if j.KeyTimestamp == "" {
|
||||
j.KeyTimestamp = "timestamp"
|
||||
}
|
||||
if j.KeyLevel == "" {
|
||||
j.KeyLevel = "level"
|
||||
}
|
||||
if j.KeyMsg == "" {
|
||||
j.KeyMsg = "msg"
|
||||
}
|
||||
if j.KeyStacktrace == "" {
|
||||
j.KeyStacktrace = "stacktrace"
|
||||
}
|
||||
}
|
||||
|
||||
// defaultContextSorter sorts the context fields alphabetically by key.
|
||||
func (j *JSON) defaultContextSorter(fields logr.Fields) []ContextField {
|
||||
keys := make([]string, 0, len(fields))
|
||||
for k := range fields {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
cf := make([]ContextField, 0, len(keys))
|
||||
for _, k := range keys {
|
||||
cf = append(cf, ContextField{Key: k, Val: fields[k]})
|
||||
}
|
||||
return cf
|
||||
}
|
||||
|
||||
// JSONLogRec decorates a LogRec adding JSON encoding.
|
||||
type JSONLogRec struct {
|
||||
*logr.LogRec
|
||||
*JSON
|
||||
stacktrace bool
|
||||
sorter func(fields logr.Fields) []ContextField
|
||||
}
|
||||
|
||||
// MarshalJSONObject encodes the LogRec as JSON.
|
||||
func (rec JSONLogRec) MarshalJSONObject(enc *gojay.Encoder) {
|
||||
if !rec.DisableTimestamp {
|
||||
timestampFmt := rec.TimestampFormat
|
||||
if timestampFmt == "" {
|
||||
timestampFmt = logr.DefTimestampFormat
|
||||
}
|
||||
time := rec.Time()
|
||||
enc.AddTimeKey(rec.KeyTimestamp, &time, timestampFmt)
|
||||
}
|
||||
if !rec.DisableLevel {
|
||||
enc.AddStringKey(rec.KeyLevel, rec.Level().Name)
|
||||
}
|
||||
if !rec.DisableMsg {
|
||||
enc.AddStringKey(rec.KeyMsg, rec.Msg())
|
||||
}
|
||||
if !rec.DisableContext {
|
||||
ctxFields := rec.sorter(rec.Fields())
|
||||
if rec.KeyContextFields != "" {
|
||||
enc.AddObjectKey(rec.KeyContextFields, jsonFields(ctxFields))
|
||||
} else {
|
||||
if len(ctxFields) > 0 {
|
||||
for _, cf := range ctxFields {
|
||||
key := rec.prefixCollision(cf.Key)
|
||||
encodeField(enc, key, cf.Val)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if rec.stacktrace && !rec.DisableStacktrace {
|
||||
frames := rec.StackFrames()
|
||||
if len(frames) > 0 {
|
||||
enc.AddArrayKey(rec.KeyStacktrace, stackFrames(frames))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// IsNil returns true if the LogRec pointer is nil.
|
||||
func (rec JSONLogRec) IsNil() bool {
|
||||
return rec.LogRec == nil
|
||||
}
|
||||
|
||||
func (rec JSONLogRec) prefixCollision(key string) string {
|
||||
switch key {
|
||||
case rec.KeyTimestamp, rec.KeyLevel, rec.KeyMsg, rec.KeyStacktrace:
|
||||
return rec.prefixCollision("_" + key)
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
type stackFrames []runtime.Frame
|
||||
|
||||
// MarshalJSONArray encodes stackFrames slice as JSON.
|
||||
func (s stackFrames) MarshalJSONArray(enc *gojay.Encoder) {
|
||||
for _, frame := range s {
|
||||
enc.AddObject(stackFrame(frame))
|
||||
}
|
||||
}
|
||||
|
||||
// IsNil returns true if stackFrames is empty slice.
|
||||
func (s stackFrames) IsNil() bool {
|
||||
return len(s) == 0
|
||||
}
|
||||
|
||||
type stackFrame runtime.Frame
|
||||
|
||||
// MarshalJSONArray encodes stackFrame as JSON.
|
||||
func (f stackFrame) MarshalJSONObject(enc *gojay.Encoder) {
|
||||
enc.AddStringKey("Function", f.Function)
|
||||
enc.AddStringKey("File", f.File)
|
||||
enc.AddIntKey("Line", f.Line)
|
||||
}
|
||||
|
||||
func (f stackFrame) IsNil() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type jsonFields []ContextField
|
||||
|
||||
// MarshalJSONObject encodes Fields map to JSON.
|
||||
func (f jsonFields) MarshalJSONObject(enc *gojay.Encoder) {
|
||||
for _, ctxField := range f {
|
||||
encodeField(enc, ctxField.Key, ctxField.Val)
|
||||
}
|
||||
}
|
||||
|
||||
// IsNil returns true if map is nil.
|
||||
func (f jsonFields) IsNil() bool {
|
||||
return f == nil
|
||||
}
|
||||
|
||||
func encodeField(enc *gojay.Encoder, key string, val interface{}) {
|
||||
switch vt := val.(type) {
|
||||
case gojay.MarshalerJSONObject:
|
||||
enc.AddObjectKey(key, vt)
|
||||
case gojay.MarshalerJSONArray:
|
||||
enc.AddArrayKey(key, vt)
|
||||
case string:
|
||||
enc.AddStringKey(key, vt)
|
||||
case error:
|
||||
enc.AddStringKey(key, vt.Error())
|
||||
case bool:
|
||||
enc.AddBoolKey(key, vt)
|
||||
case int:
|
||||
enc.AddIntKey(key, vt)
|
||||
case int64:
|
||||
enc.AddInt64Key(key, vt)
|
||||
case int32:
|
||||
enc.AddIntKey(key, int(vt))
|
||||
case int16:
|
||||
enc.AddIntKey(key, int(vt))
|
||||
case int8:
|
||||
enc.AddIntKey(key, int(vt))
|
||||
case uint64:
|
||||
enc.AddIntKey(key, int(vt))
|
||||
case uint32:
|
||||
enc.AddIntKey(key, int(vt))
|
||||
case uint16:
|
||||
enc.AddIntKey(key, int(vt))
|
||||
case uint8:
|
||||
enc.AddIntKey(key, int(vt))
|
||||
case float64:
|
||||
enc.AddFloatKey(key, vt)
|
||||
case float32:
|
||||
enc.AddFloat32Key(key, vt)
|
||||
case *gojay.EmbeddedJSON:
|
||||
enc.AddEmbeddedJSONKey(key, vt)
|
||||
case time.Time:
|
||||
enc.AddTimeKey(key, &vt, logr.DefTimestampFormat)
|
||||
case *time.Time:
|
||||
enc.AddTimeKey(key, vt, logr.DefTimestampFormat)
|
||||
default:
|
||||
s := fmt.Sprintf("%v", vt)
|
||||
enc.AddStringKey(key, s)
|
||||
}
|
||||
}
|
||||
75
vendor/github.com/mattermost/logr/format/plain.go
сгенерированный
поставляемый
75
vendor/github.com/mattermost/logr/format/plain.go
сгенерированный
поставляемый
@@ -1,75 +0,0 @@
|
||||
package format
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/mattermost/logr"
|
||||
)
|
||||
|
||||
// Plain is the simplest formatter, outputting only text with
|
||||
// no colors.
|
||||
type Plain struct {
|
||||
// DisableTimestamp disables output of timestamp field.
|
||||
DisableTimestamp bool
|
||||
// DisableLevel disables output of level field.
|
||||
DisableLevel bool
|
||||
// DisableMsg disables output of msg field.
|
||||
DisableMsg bool
|
||||
// DisableContext disables output of all context fields.
|
||||
DisableContext bool
|
||||
// DisableStacktrace disables output of stack trace.
|
||||
DisableStacktrace bool
|
||||
|
||||
// Delim is an optional delimiter output between each log field.
|
||||
// Defaults to a single space.
|
||||
Delim string
|
||||
|
||||
// TimestampFormat is an optional format for timestamps. If empty
|
||||
// then DefTimestampFormat is used.
|
||||
TimestampFormat string
|
||||
}
|
||||
|
||||
// Format converts a log record to bytes.
|
||||
func (p *Plain) Format(rec *logr.LogRec, stacktrace bool, buf *bytes.Buffer) (*bytes.Buffer, error) {
|
||||
delim := p.Delim
|
||||
if delim == "" {
|
||||
delim = " "
|
||||
}
|
||||
if buf == nil {
|
||||
buf = &bytes.Buffer{}
|
||||
}
|
||||
|
||||
timestampFmt := p.TimestampFormat
|
||||
if timestampFmt == "" {
|
||||
timestampFmt = logr.DefTimestampFormat
|
||||
}
|
||||
|
||||
if !p.DisableTimestamp {
|
||||
var arr [128]byte
|
||||
tbuf := rec.Time().AppendFormat(arr[:0], timestampFmt)
|
||||
buf.Write(tbuf)
|
||||
buf.WriteString(delim)
|
||||
}
|
||||
if !p.DisableLevel {
|
||||
fmt.Fprintf(buf, "%v%s", rec.Level().Name, delim)
|
||||
}
|
||||
if !p.DisableMsg {
|
||||
fmt.Fprint(buf, rec.Msg(), delim)
|
||||
}
|
||||
if !p.DisableContext {
|
||||
ctx := rec.Fields()
|
||||
if len(ctx) > 0 {
|
||||
logr.WriteFields(buf, ctx, " ")
|
||||
}
|
||||
}
|
||||
if stacktrace && !p.DisableStacktrace {
|
||||
frames := rec.StackFrames()
|
||||
if len(frames) > 0 {
|
||||
buf.WriteString("\n")
|
||||
logr.WriteStacktrace(buf, rec.StackFrames())
|
||||
}
|
||||
}
|
||||
buf.WriteString("\n")
|
||||
return buf, nil
|
||||
}
|
||||
119
vendor/github.com/mattermost/logr/formatter.go
сгенерированный
поставляемый
119
vendor/github.com/mattermost/logr/formatter.go
сгенерированный
поставляемый
@@ -1,119 +0,0 @@
|
||||
package logr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"runtime"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// Formatter turns a LogRec into a formatted string.
|
||||
type Formatter interface {
|
||||
// Format converts a log record to bytes. If buf is not nil then it will be
|
||||
// be filled with the formatted results, otherwise a new buffer will be allocated.
|
||||
Format(rec *LogRec, stacktrace bool, buf *bytes.Buffer) (*bytes.Buffer, error)
|
||||
}
|
||||
|
||||
const (
|
||||
// DefTimestampFormat is the default time stamp format used by
|
||||
// Plain formatter and others.
|
||||
DefTimestampFormat = "2006-01-02 15:04:05.000 Z07:00"
|
||||
)
|
||||
|
||||
// DefaultFormatter is the default formatter, outputting only text with
|
||||
// no colors and a space delimiter. Use `format.Plain` instead.
|
||||
type DefaultFormatter struct {
|
||||
}
|
||||
|
||||
// Format converts a log record to bytes.
|
||||
func (p *DefaultFormatter) Format(rec *LogRec, stacktrace bool, buf *bytes.Buffer) (*bytes.Buffer, error) {
|
||||
if buf == nil {
|
||||
buf = &bytes.Buffer{}
|
||||
}
|
||||
delim := " "
|
||||
timestampFmt := DefTimestampFormat
|
||||
|
||||
fmt.Fprintf(buf, "%s%s", rec.Time().Format(timestampFmt), delim)
|
||||
fmt.Fprintf(buf, "%v%s", rec.Level(), delim)
|
||||
fmt.Fprint(buf, rec.Msg(), delim)
|
||||
|
||||
ctx := rec.Fields()
|
||||
if len(ctx) > 0 {
|
||||
WriteFields(buf, ctx, " ")
|
||||
}
|
||||
|
||||
if stacktrace {
|
||||
frames := rec.StackFrames()
|
||||
if len(frames) > 0 {
|
||||
buf.WriteString("\n")
|
||||
WriteStacktrace(buf, rec.StackFrames())
|
||||
}
|
||||
}
|
||||
buf.WriteString("\n")
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// WriteFields writes zero or more name value pairs to the io.Writer.
|
||||
// The pairs are sorted by key name and output in key=value format
|
||||
// with optional separator between fields.
|
||||
func WriteFields(w io.Writer, flds Fields, separator string) {
|
||||
keys := make([]string, 0, len(flds))
|
||||
for k := range flds {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
sep := ""
|
||||
for _, key := range keys {
|
||||
writeField(w, key, flds[key], sep)
|
||||
sep = separator
|
||||
}
|
||||
}
|
||||
|
||||
func writeField(w io.Writer, key string, val interface{}, sep string) {
|
||||
var template string
|
||||
switch v := val.(type) {
|
||||
case error:
|
||||
val := v.Error()
|
||||
if shouldQuote(val) {
|
||||
template = "%s%s=%q"
|
||||
} else {
|
||||
template = "%s%s=%s"
|
||||
}
|
||||
case string:
|
||||
if shouldQuote(v) {
|
||||
template = "%s%s=%q"
|
||||
} else {
|
||||
template = "%s%s=%s"
|
||||
}
|
||||
default:
|
||||
template = "%s%s=%v"
|
||||
}
|
||||
fmt.Fprintf(w, template, sep, key, val)
|
||||
}
|
||||
|
||||
// shouldQuote returns true if val contains any characters that might be unsafe
|
||||
// when injecting log output into an aggregator, viewer or report.
|
||||
func shouldQuote(val string) bool {
|
||||
for _, c := range val {
|
||||
if !((c >= '0' && c <= '9') ||
|
||||
(c >= 'a' && c <= 'z') ||
|
||||
(c >= 'A' && c <= 'Z')) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// WriteStacktrace formats and outputs a stack trace to an io.Writer.
|
||||
func WriteStacktrace(w io.Writer, frames []runtime.Frame) {
|
||||
for _, frame := range frames {
|
||||
if frame.Function != "" {
|
||||
fmt.Fprintf(w, " %s\n", frame.Function)
|
||||
}
|
||||
if frame.File != "" {
|
||||
fmt.Fprintf(w, " %s:%d\n", frame.File, frame.Line)
|
||||
}
|
||||
}
|
||||
}
|
||||
45
vendor/github.com/mattermost/logr/levelcustom.go
сгенерированный
поставляемый
45
vendor/github.com/mattermost/logr/levelcustom.go
сгенерированный
поставляемый
@@ -1,45 +0,0 @@
|
||||
package logr
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// CustomFilter allows targets to enable logging via a list of levels.
|
||||
type CustomFilter struct {
|
||||
mux sync.RWMutex
|
||||
levels map[LevelID]Level
|
||||
}
|
||||
|
||||
// IsEnabled returns true if the specified Level exists in this list.
|
||||
func (st *CustomFilter) IsEnabled(level Level) bool {
|
||||
st.mux.RLock()
|
||||
defer st.mux.RUnlock()
|
||||
_, ok := st.levels[level.ID]
|
||||
return ok
|
||||
}
|
||||
|
||||
// IsStacktraceEnabled returns true if the specified Level requires a stack trace.
|
||||
func (st *CustomFilter) IsStacktraceEnabled(level Level) bool {
|
||||
st.mux.RLock()
|
||||
defer st.mux.RUnlock()
|
||||
lvl, ok := st.levels[level.ID]
|
||||
if ok {
|
||||
return lvl.Stacktrace
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Add adds one or more levels to the list. Adding a level enables logging for
|
||||
// that level on any targets using this CustomFilter.
|
||||
func (st *CustomFilter) Add(levels ...Level) {
|
||||
st.mux.Lock()
|
||||
defer st.mux.Unlock()
|
||||
|
||||
if st.levels == nil {
|
||||
st.levels = make(map[LevelID]Level)
|
||||
}
|
||||
|
||||
for _, s := range levels {
|
||||
st.levels[s.ID] = s
|
||||
}
|
||||
}
|
||||
37
vendor/github.com/mattermost/logr/levelstd.go
сгенерированный
поставляемый
37
vendor/github.com/mattermost/logr/levelstd.go
сгенерированный
поставляемый
@@ -1,37 +0,0 @@
|
||||
package logr
|
||||
|
||||
// StdFilter allows targets to filter via classic log levels where any level
|
||||
// beyond a certain verbosity/severity is enabled.
|
||||
type StdFilter struct {
|
||||
Lvl Level
|
||||
Stacktrace Level
|
||||
}
|
||||
|
||||
// IsEnabled returns true if the specified Level is at or above this verbosity. Also
|
||||
// determines if a stack trace is required.
|
||||
func (lt StdFilter) IsEnabled(level Level) bool {
|
||||
return level.ID <= lt.Lvl.ID
|
||||
}
|
||||
|
||||
// IsStacktraceEnabled returns true if the specified Level requires a stack trace.
|
||||
func (lt StdFilter) IsStacktraceEnabled(level Level) bool {
|
||||
return level.ID <= lt.Stacktrace.ID
|
||||
}
|
||||
|
||||
var (
|
||||
// Panic is the highest level of severity. Logs the message and then panics.
|
||||
Panic = Level{ID: 0, Name: "panic"}
|
||||
// Fatal designates a catastrophic error. Logs the message and then calls
|
||||
// `logr.Exit(1)`.
|
||||
Fatal = Level{ID: 1, Name: "fatal"}
|
||||
// Error designates a serious but possibly recoverable error.
|
||||
Error = Level{ID: 2, Name: "error"}
|
||||
// Warn designates non-critical error.
|
||||
Warn = Level{ID: 3, Name: "warn"}
|
||||
// Info designates information regarding application events.
|
||||
Info = Level{ID: 4, Name: "info"}
|
||||
// Debug designates verbose information typically used for debugging.
|
||||
Debug = Level{ID: 5, Name: "debug"}
|
||||
// Trace designates the highest verbosity of log output.
|
||||
Trace = Level{ID: 6, Name: "trace"}
|
||||
)
|
||||
218
vendor/github.com/mattermost/logr/logger.go
сгенерированный
поставляемый
218
vendor/github.com/mattermost/logr/logger.go
сгенерированный
поставляемый
@@ -1,218 +0,0 @@
|
||||
package logr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Fields type, used to pass to `WithFields`.
|
||||
type Fields map[string]interface{}
|
||||
|
||||
// Logger provides context for logging via fields.
|
||||
type Logger struct {
|
||||
logr *Logr
|
||||
fields Fields
|
||||
}
|
||||
|
||||
// Logr returns the `Logr` instance that created this `Logger`.
|
||||
func (logger Logger) Logr() *Logr {
|
||||
return logger.logr
|
||||
}
|
||||
|
||||
// WithField creates a new `Logger` with any existing fields
|
||||
// plus the new one.
|
||||
func (logger Logger) WithField(key string, value interface{}) Logger {
|
||||
return logger.WithFields(Fields{key: value})
|
||||
}
|
||||
|
||||
// WithFields creates a new `Logger` with any existing fields
|
||||
// plus the new ones.
|
||||
func (logger Logger) WithFields(fields Fields) Logger {
|
||||
l := Logger{logr: logger.logr}
|
||||
// if parent has no fields then avoid creating a new map.
|
||||
oldLen := len(logger.fields)
|
||||
if oldLen == 0 {
|
||||
l.fields = fields
|
||||
return l
|
||||
}
|
||||
|
||||
l.fields = make(Fields, len(fields)+oldLen)
|
||||
for k, v := range logger.fields {
|
||||
l.fields[k] = v
|
||||
}
|
||||
for k, v := range fields {
|
||||
l.fields[k] = v
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// Log checks that the level matches one or more targets, and
|
||||
// if so, generates a log record that is added to the Logr queue.
|
||||
// Arguments are handled in the manner of fmt.Print.
|
||||
func (logger Logger) Log(lvl Level, args ...interface{}) {
|
||||
status := logger.logr.IsLevelEnabled(lvl)
|
||||
if status.Enabled {
|
||||
rec := NewLogRec(lvl, logger, "", args, status.Stacktrace)
|
||||
logger.logr.enqueue(rec)
|
||||
}
|
||||
}
|
||||
|
||||
// Trace is a convenience method equivalent to `Log(TraceLevel, args...)`.
|
||||
func (logger Logger) Trace(args ...interface{}) {
|
||||
logger.Log(Trace, args...)
|
||||
}
|
||||
|
||||
// Debug is a convenience method equivalent to `Log(DebugLevel, args...)`.
|
||||
func (logger Logger) Debug(args ...interface{}) {
|
||||
logger.Log(Debug, args...)
|
||||
}
|
||||
|
||||
// Print ensures compatibility with std lib logger.
|
||||
func (logger Logger) Print(args ...interface{}) {
|
||||
logger.Info(args...)
|
||||
}
|
||||
|
||||
// Info is a convenience method equivalent to `Log(InfoLevel, args...)`.
|
||||
func (logger Logger) Info(args ...interface{}) {
|
||||
logger.Log(Info, args...)
|
||||
}
|
||||
|
||||
// Warn is a convenience method equivalent to `Log(WarnLevel, args...)`.
|
||||
func (logger Logger) Warn(args ...interface{}) {
|
||||
logger.Log(Warn, args...)
|
||||
}
|
||||
|
||||
// Error is a convenience method equivalent to `Log(ErrorLevel, args...)`.
|
||||
func (logger Logger) Error(args ...interface{}) {
|
||||
logger.Log(Error, args...)
|
||||
}
|
||||
|
||||
// Fatal is a convenience method equivalent to `Log(FatalLevel, args...)`
|
||||
// followed by a call to os.Exit(1).
|
||||
func (logger Logger) Fatal(args ...interface{}) {
|
||||
logger.Log(Fatal, args...)
|
||||
logger.logr.exit(1)
|
||||
}
|
||||
|
||||
// Panic is a convenience method equivalent to `Log(PanicLevel, args...)`
|
||||
// followed by a call to panic().
|
||||
func (logger Logger) Panic(args ...interface{}) {
|
||||
logger.Log(Panic, args...)
|
||||
panic(fmt.Sprint(args...))
|
||||
}
|
||||
|
||||
//
|
||||
// Printf style
|
||||
//
|
||||
|
||||
// Logf checks that the level matches one or more targets, and
|
||||
// if so, generates a log record that is added to the main
|
||||
// queue (channel). Arguments are handled in the manner of fmt.Printf.
|
||||
func (logger Logger) Logf(lvl Level, format string, args ...interface{}) {
|
||||
status := logger.logr.IsLevelEnabled(lvl)
|
||||
if status.Enabled {
|
||||
rec := NewLogRec(lvl, logger, format, args, status.Stacktrace)
|
||||
logger.logr.enqueue(rec)
|
||||
}
|
||||
}
|
||||
|
||||
// Tracef is a convenience method equivalent to `Logf(TraceLevel, args...)`.
|
||||
func (logger Logger) Tracef(format string, args ...interface{}) {
|
||||
logger.Logf(Trace, format, args...)
|
||||
}
|
||||
|
||||
// Debugf is a convenience method equivalent to `Logf(DebugLevel, args...)`.
|
||||
func (logger Logger) Debugf(format string, args ...interface{}) {
|
||||
logger.Logf(Debug, format, args...)
|
||||
}
|
||||
|
||||
// Infof is a convenience method equivalent to `Logf(InfoLevel, args...)`.
|
||||
func (logger Logger) Infof(format string, args ...interface{}) {
|
||||
logger.Logf(Info, format, args...)
|
||||
}
|
||||
|
||||
// Printf ensures compatibility with std lib logger.
|
||||
func (logger Logger) Printf(format string, args ...interface{}) {
|
||||
logger.Infof(format, args...)
|
||||
}
|
||||
|
||||
// Warnf is a convenience method equivalent to `Logf(WarnLevel, args...)`.
|
||||
func (logger Logger) Warnf(format string, args ...interface{}) {
|
||||
logger.Logf(Warn, format, args...)
|
||||
}
|
||||
|
||||
// Errorf is a convenience method equivalent to `Logf(ErrorLevel, args...)`.
|
||||
func (logger Logger) Errorf(format string, args ...interface{}) {
|
||||
logger.Logf(Error, format, args...)
|
||||
}
|
||||
|
||||
// Fatalf is a convenience method equivalent to `Logf(FatalLevel, args...)`
|
||||
// followed by a call to os.Exit(1).
|
||||
func (logger Logger) Fatalf(format string, args ...interface{}) {
|
||||
logger.Logf(Fatal, format, args...)
|
||||
logger.logr.exit(1)
|
||||
}
|
||||
|
||||
// Panicf is a convenience method equivalent to `Logf(PanicLevel, args...)`
|
||||
// followed by a call to panic().
|
||||
func (logger Logger) Panicf(format string, args ...interface{}) {
|
||||
logger.Logf(Panic, format, args...)
|
||||
}
|
||||
|
||||
//
|
||||
// Println style
|
||||
//
|
||||
|
||||
// Logln checks that the level matches one or more targets, and
|
||||
// if so, generates a log record that is added to the main
|
||||
// queue (channel). Arguments are handled in the manner of fmt.Println.
|
||||
func (logger Logger) Logln(lvl Level, args ...interface{}) {
|
||||
status := logger.logr.IsLevelEnabled(lvl)
|
||||
if status.Enabled {
|
||||
rec := NewLogRec(lvl, logger, "", args, status.Stacktrace)
|
||||
rec.newline = true
|
||||
logger.logr.enqueue(rec)
|
||||
}
|
||||
}
|
||||
|
||||
// Traceln is a convenience method equivalent to `Logln(TraceLevel, args...)`.
|
||||
func (logger Logger) Traceln(args ...interface{}) {
|
||||
logger.Logln(Trace, args...)
|
||||
}
|
||||
|
||||
// Debugln is a convenience method equivalent to `Logln(DebugLevel, args...)`.
|
||||
func (logger Logger) Debugln(args ...interface{}) {
|
||||
logger.Logln(Debug, args...)
|
||||
}
|
||||
|
||||
// Infoln is a convenience method equivalent to `Logln(InfoLevel, args...)`.
|
||||
func (logger Logger) Infoln(args ...interface{}) {
|
||||
logger.Logln(Info, args...)
|
||||
}
|
||||
|
||||
// Println ensures compatibility with std lib logger.
|
||||
func (logger Logger) Println(args ...interface{}) {
|
||||
logger.Infoln(args...)
|
||||
}
|
||||
|
||||
// Warnln is a convenience method equivalent to `Logln(WarnLevel, args...)`.
|
||||
func (logger Logger) Warnln(args ...interface{}) {
|
||||
logger.Logln(Warn, args...)
|
||||
}
|
||||
|
||||
// Errorln is a convenience method equivalent to `Logln(ErrorLevel, args...)`.
|
||||
func (logger Logger) Errorln(args ...interface{}) {
|
||||
logger.Logln(Error, args...)
|
||||
}
|
||||
|
||||
// Fatalln is a convenience method equivalent to `Logln(FatalLevel, args...)`
|
||||
// followed by a call to os.Exit(1).
|
||||
func (logger Logger) Fatalln(args ...interface{}) {
|
||||
logger.Logln(Fatal, args...)
|
||||
logger.logr.exit(1)
|
||||
}
|
||||
|
||||
// Panicln is a convenience method equivalent to `Logln(PanicLevel, args...)`
|
||||
// followed by a call to panic().
|
||||
func (logger Logger) Panicln(args ...interface{}) {
|
||||
logger.Logln(Panic, args...)
|
||||
}
|
||||
664
vendor/github.com/mattermost/logr/logr.go
сгенерированный
поставляемый
664
vendor/github.com/mattermost/logr/logr.go
сгенерированный
поставляемый
@@ -1,664 +0,0 @@
|
||||
package logr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/wiggin77/cfg"
|
||||
"github.com/wiggin77/merror"
|
||||
)
|
||||
|
||||
// Logr maintains a list of log targets and accepts incoming
|
||||
// log records.
|
||||
type Logr struct {
|
||||
tmux sync.RWMutex // target mutex
|
||||
targets []Target
|
||||
|
||||
mux sync.RWMutex
|
||||
maxQueueSizeActual int
|
||||
in chan *LogRec
|
||||
done chan struct{}
|
||||
once sync.Once
|
||||
shutdown bool
|
||||
lvlCache levelCache
|
||||
|
||||
metricsInitOnce sync.Once
|
||||
metricsCloseOnce sync.Once
|
||||
metricsDone chan struct{}
|
||||
metrics MetricsCollector
|
||||
queueSizeGauge Gauge
|
||||
loggedCounter Counter
|
||||
errorCounter Counter
|
||||
|
||||
bufferPool sync.Pool
|
||||
|
||||
// MaxQueueSize is the maximum number of log records that can be queued.
|
||||
// If exceeded, `OnQueueFull` is called which determines if the log
|
||||
// record will be dropped or block until add is successful.
|
||||
// If this is modified, it must be done before `Configure` or
|
||||
// `AddTarget`. Defaults to DefaultMaxQueueSize.
|
||||
MaxQueueSize int
|
||||
|
||||
// OnLoggerError, when not nil, is called any time an internal
|
||||
// logging error occurs. For example, this can happen when a
|
||||
// target cannot connect to its data sink.
|
||||
OnLoggerError func(error)
|
||||
|
||||
// OnQueueFull, when not nil, is called on an attempt to add
|
||||
// a log record to a full Logr queue.
|
||||
// `MaxQueueSize` can be used to modify the maximum queue size.
|
||||
// This function should return quickly, with a bool indicating whether
|
||||
// the log record should be dropped (true) or block until the log record
|
||||
// is successfully added (false). If nil then blocking (false) is assumed.
|
||||
OnQueueFull func(rec *LogRec, maxQueueSize int) bool
|
||||
|
||||
// OnTargetQueueFull, when not nil, is called on an attempt to add
|
||||
// a log record to a full target queue provided the target supports reporting
|
||||
// this condition.
|
||||
// This function should return quickly, with a bool indicating whether
|
||||
// the log record should be dropped (true) or block until the log record
|
||||
// is successfully added (false). If nil then blocking (false) is assumed.
|
||||
OnTargetQueueFull func(target Target, rec *LogRec, maxQueueSize int) bool
|
||||
|
||||
// OnExit, when not nil, is called when a FatalXXX style log API is called.
|
||||
// When nil, then the default behavior is to cleanly shut down this Logr and
|
||||
// call `os.Exit(code)`.
|
||||
OnExit func(code int)
|
||||
|
||||
// OnPanic, when not nil, is called when a PanicXXX style log API is called.
|
||||
// When nil, then the default behavior is to cleanly shut down this Logr and
|
||||
// call `panic(err)`.
|
||||
OnPanic func(err interface{})
|
||||
|
||||
// EnqueueTimeout is the amount of time a log record can take to be queued.
|
||||
// This only applies to blocking enqueue which happen after `logr.OnQueueFull`
|
||||
// is called and returns false.
|
||||
EnqueueTimeout time.Duration
|
||||
|
||||
// ShutdownTimeout is the amount of time `logr.Shutdown` can execute before
|
||||
// timing out.
|
||||
ShutdownTimeout time.Duration
|
||||
|
||||
// FlushTimeout is the amount of time `logr.Flush` can execute before
|
||||
// timing out.
|
||||
FlushTimeout time.Duration
|
||||
|
||||
// UseSyncMapLevelCache can be set to true before the first target is added
|
||||
// when high concurrency (e.g. >32 cores) is expected. This may improve
|
||||
// performance with large numbers of cores - benchmark for your use case.
|
||||
UseSyncMapLevelCache bool
|
||||
|
||||
// MaxPooledFormatBuffer determines the maximum size of a buffer that can be
|
||||
// pooled. To reduce allocations, the buffers needed during formatting (etc)
|
||||
// are pooled. A very large log item will grow a buffer that could stay in
|
||||
// memory indefinitely. This settings lets you control how big a pooled buffer
|
||||
// can be - anything larger will be garbage collected after use.
|
||||
// Defaults to 1MB.
|
||||
MaxPooledBuffer int
|
||||
|
||||
// DisableBufferPool when true disables the buffer pool. See MaxPooledBuffer.
|
||||
DisableBufferPool bool
|
||||
|
||||
// MetricsUpdateFreqMillis determines how often polled metrics are updated
|
||||
// when metrics are enabled.
|
||||
MetricsUpdateFreqMillis int64
|
||||
}
|
||||
|
||||
// Configure adds/removes targets via the supplied `Config`.
|
||||
func (logr *Logr) Configure(config *cfg.Config) error {
|
||||
// TODO
|
||||
return fmt.Errorf("not implemented yet")
|
||||
}
|
||||
|
||||
func (logr *Logr) ensureInit() {
|
||||
logr.once.Do(func() {
|
||||
defer func() {
|
||||
go logr.start()
|
||||
}()
|
||||
|
||||
logr.mux.Lock()
|
||||
defer logr.mux.Unlock()
|
||||
|
||||
logr.maxQueueSizeActual = logr.MaxQueueSize
|
||||
if logr.maxQueueSizeActual == 0 {
|
||||
logr.maxQueueSizeActual = DefaultMaxQueueSize
|
||||
}
|
||||
|
||||
if logr.maxQueueSizeActual < 0 {
|
||||
logr.maxQueueSizeActual = 0
|
||||
}
|
||||
|
||||
logr.in = make(chan *LogRec, logr.maxQueueSizeActual)
|
||||
logr.done = make(chan struct{})
|
||||
|
||||
if logr.UseSyncMapLevelCache {
|
||||
logr.lvlCache = &syncMapLevelCache{}
|
||||
} else {
|
||||
logr.lvlCache = &arrayLevelCache{}
|
||||
}
|
||||
|
||||
if logr.MaxPooledBuffer == 0 {
|
||||
logr.MaxPooledBuffer = DefaultMaxPooledBuffer
|
||||
}
|
||||
logr.bufferPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(bytes.Buffer)
|
||||
},
|
||||
}
|
||||
|
||||
logr.lvlCache.setup()
|
||||
})
|
||||
}
|
||||
|
||||
// AddTarget adds one or more targets to the logger which will receive
|
||||
// log records for outputting.
|
||||
func (logr *Logr) AddTarget(targets ...Target) error {
|
||||
if logr.IsShutdown() {
|
||||
return fmt.Errorf("AddTarget called after Logr shut down")
|
||||
}
|
||||
|
||||
logr.ensureInit()
|
||||
metrics := logr.getMetricsCollector()
|
||||
defer logr.ResetLevelCache() // call this after tmux is released
|
||||
|
||||
logr.tmux.Lock()
|
||||
defer logr.tmux.Unlock()
|
||||
|
||||
errs := merror.New()
|
||||
for _, t := range targets {
|
||||
if t == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
logr.targets = append(logr.targets, t)
|
||||
if metrics != nil {
|
||||
if tm, ok := t.(TargetWithMetrics); ok {
|
||||
if err := tm.EnableMetrics(metrics, logr.MetricsUpdateFreqMillis); err != nil {
|
||||
errs.Append(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return errs.ErrorOrNil()
|
||||
}
|
||||
|
||||
// NewLogger creates a Logger using defaults. A `Logger` is light-weight
|
||||
// enough to create on-demand, but typically one or more Loggers are
|
||||
// created and re-used.
|
||||
func (logr *Logr) NewLogger() Logger {
|
||||
logger := Logger{logr: logr}
|
||||
return logger
|
||||
}
|
||||
|
||||
var levelStatusDisabled = LevelStatus{}
|
||||
|
||||
// IsLevelEnabled returns true if at least one target has the specified
|
||||
// level enabled. The result is cached so that subsequent checks are fast.
|
||||
func (logr *Logr) IsLevelEnabled(lvl Level) LevelStatus {
|
||||
status, ok := logr.isLevelEnabledFromCache(lvl)
|
||||
if ok {
|
||||
return status
|
||||
}
|
||||
|
||||
// Check each target.
|
||||
logr.tmux.RLock()
|
||||
for _, t := range logr.targets {
|
||||
e, s := t.IsLevelEnabled(lvl)
|
||||
if e {
|
||||
status.Enabled = true
|
||||
if s {
|
||||
status.Stacktrace = true
|
||||
break // if both enabled then no sense checking more targets
|
||||
}
|
||||
}
|
||||
}
|
||||
logr.tmux.RUnlock()
|
||||
|
||||
// Cache and return the result.
|
||||
if err := logr.updateLevelCache(lvl.ID, status); err != nil {
|
||||
logr.ReportError(err)
|
||||
return LevelStatus{}
|
||||
}
|
||||
return status
|
||||
}
|
||||
|
||||
func (logr *Logr) isLevelEnabledFromCache(lvl Level) (LevelStatus, bool) {
|
||||
logr.mux.RLock()
|
||||
defer logr.mux.RUnlock()
|
||||
|
||||
// Don't accept new log records after shutdown.
|
||||
if logr.shutdown {
|
||||
return levelStatusDisabled, true
|
||||
}
|
||||
|
||||
// Check cache. lvlCache may still be nil if no targets added.
|
||||
if logr.lvlCache == nil {
|
||||
return levelStatusDisabled, true
|
||||
}
|
||||
status, ok := logr.lvlCache.get(lvl.ID)
|
||||
if ok {
|
||||
return status, true
|
||||
}
|
||||
return LevelStatus{}, false
|
||||
}
|
||||
|
||||
func (logr *Logr) updateLevelCache(id LevelID, status LevelStatus) error {
|
||||
logr.mux.RLock()
|
||||
defer logr.mux.RUnlock()
|
||||
if logr.lvlCache != nil {
|
||||
return logr.lvlCache.put(id, status)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// HasTargets returns true only if at least one target exists within the Logr.
|
||||
func (logr *Logr) HasTargets() bool {
|
||||
logr.tmux.RLock()
|
||||
defer logr.tmux.RUnlock()
|
||||
return len(logr.targets) > 0
|
||||
}
|
||||
|
||||
// TargetInfo provides name and type for a Target.
|
||||
type TargetInfo struct {
|
||||
Name string
|
||||
Type string
|
||||
}
|
||||
|
||||
// TargetInfos enumerates all the targets added to this Logr.
|
||||
// The resulting slice represents a snapshot at time of calling.
|
||||
func (logr *Logr) TargetInfos() []TargetInfo {
|
||||
logr.tmux.RLock()
|
||||
defer logr.tmux.RUnlock()
|
||||
|
||||
infos := make([]TargetInfo, 0)
|
||||
|
||||
for _, t := range logr.targets {
|
||||
inf := TargetInfo{
|
||||
Name: fmt.Sprintf("%v", t),
|
||||
Type: fmt.Sprintf("%T", t),
|
||||
}
|
||||
infos = append(infos, inf)
|
||||
}
|
||||
return infos
|
||||
}
|
||||
|
||||
// RemoveTargets safely removes one or more targets based on the filtering method.
|
||||
// f should return true to delete the target, false to keep it.
|
||||
// When removing a target, best effort is made to write any queued log records before
|
||||
// closing, with cxt determining how much time can be spent in total.
|
||||
// Note, keep the timeout short since this method blocks certain logging operations.
|
||||
func (logr *Logr) RemoveTargets(cxt context.Context, f func(ti TargetInfo) bool) error {
|
||||
var removed bool
|
||||
defer func() {
|
||||
if removed {
|
||||
// call this after tmux is released since
|
||||
// it will lock mux and we don't want to
|
||||
// introduce possible deadlock.
|
||||
logr.ResetLevelCache()
|
||||
}
|
||||
}()
|
||||
|
||||
errs := merror.New()
|
||||
|
||||
logr.tmux.Lock()
|
||||
defer logr.tmux.Unlock()
|
||||
|
||||
cp := make([]Target, 0)
|
||||
|
||||
for _, t := range logr.targets {
|
||||
inf := TargetInfo{
|
||||
Name: fmt.Sprintf("%v", t),
|
||||
Type: fmt.Sprintf("%T", t),
|
||||
}
|
||||
if f(inf) {
|
||||
if err := t.Shutdown(cxt); err != nil {
|
||||
errs.Append(err)
|
||||
}
|
||||
removed = true
|
||||
} else {
|
||||
cp = append(cp, t)
|
||||
}
|
||||
}
|
||||
logr.targets = cp
|
||||
return errs.ErrorOrNil()
|
||||
}
|
||||
|
||||
// ResetLevelCache resets the cached results of `IsLevelEnabled`. This is
|
||||
// called any time a Target is added or a target's level is changed.
|
||||
func (logr *Logr) ResetLevelCache() {
|
||||
// Write lock so that new cache entries cannot be stored while we
|
||||
// clear the cache.
|
||||
logr.mux.Lock()
|
||||
defer logr.mux.Unlock()
|
||||
logr.resetLevelCache()
|
||||
}
|
||||
|
||||
// resetLevelCache empties the level cache without locking.
|
||||
// mux.Lock must be held before calling this function.
|
||||
func (logr *Logr) resetLevelCache() {
|
||||
// lvlCache may still be nil if no targets added.
|
||||
if logr.lvlCache != nil {
|
||||
logr.lvlCache.clear()
|
||||
}
|
||||
}
|
||||
|
||||
// enqueue adds a log record to the logr queue. If the queue is full then
|
||||
// this function either blocks or the log record is dropped, depending on
|
||||
// the result of calling `OnQueueFull`.
|
||||
func (logr *Logr) enqueue(rec *LogRec) {
|
||||
if logr.in == nil {
|
||||
logr.ReportError(fmt.Errorf("AddTarget or Configure must be called before enqueue"))
|
||||
}
|
||||
|
||||
select {
|
||||
case logr.in <- rec:
|
||||
default:
|
||||
if logr.OnQueueFull != nil && logr.OnQueueFull(rec, logr.maxQueueSizeActual) {
|
||||
return // drop the record
|
||||
}
|
||||
select {
|
||||
case <-time.After(logr.enqueueTimeout()):
|
||||
logr.ReportError(fmt.Errorf("enqueue timed out for log rec [%v]", rec))
|
||||
case logr.in <- rec: // block until success or timeout
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// exit is called by one of the FatalXXX style APIS. If `logr.OnExit` is not nil
|
||||
// then that method is called, otherwise the default behavior is to shut down this
|
||||
// Logr cleanly then call `os.Exit(code)`.
|
||||
func (logr *Logr) exit(code int) {
|
||||
if logr.OnExit != nil {
|
||||
logr.OnExit(code)
|
||||
return
|
||||
}
|
||||
|
||||
if err := logr.Shutdown(); err != nil {
|
||||
logr.ReportError(err)
|
||||
}
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
// panic is called by one of the PanicXXX style APIS. If `logr.OnPanic` is not nil
|
||||
// then that method is called, otherwise the default behavior is to shut down this
|
||||
// Logr cleanly then call `panic(err)`.
|
||||
func (logr *Logr) panic(err interface{}) {
|
||||
if logr.OnPanic != nil {
|
||||
logr.OnPanic(err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := logr.Shutdown(); err != nil {
|
||||
logr.ReportError(err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Flush blocks while flushing the logr queue and all target queues, by
|
||||
// writing existing log records to valid targets.
|
||||
// Any attempts to add new log records will block until flush is complete.
|
||||
// `logr.FlushTimeout` determines how long flush can execute before
|
||||
// timing out. Use `IsTimeoutError` to determine if the returned error is
|
||||
// due to a timeout.
|
||||
func (logr *Logr) Flush() error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), logr.flushTimeout())
|
||||
defer cancel()
|
||||
return logr.FlushWithTimeout(ctx)
|
||||
}
|
||||
|
||||
// Flush blocks while flushing the logr queue and all target queues, by
|
||||
// writing existing log records to valid targets.
|
||||
// Any attempts to add new log records will block until flush is complete.
|
||||
// Use `IsTimeoutError` to determine if the returned error is
|
||||
// due to a timeout.
|
||||
func (logr *Logr) FlushWithTimeout(ctx context.Context) error {
|
||||
if !logr.HasTargets() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if logr.IsShutdown() {
|
||||
return errors.New("Flush called on shut down Logr")
|
||||
}
|
||||
|
||||
rec := newFlushLogRec(logr.NewLogger())
|
||||
logr.enqueue(rec)
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return newTimeoutError("logr queue shutdown timeout")
|
||||
case <-rec.flush:
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsShutdown returns true if this Logr instance has been shut down.
|
||||
// No further log records can be enqueued and no targets added after
|
||||
// shutdown.
|
||||
func (logr *Logr) IsShutdown() bool {
|
||||
logr.mux.Lock()
|
||||
defer logr.mux.Unlock()
|
||||
return logr.shutdown
|
||||
}
|
||||
|
||||
// Shutdown cleanly stops the logging engine after making best efforts
|
||||
// to flush all targets. Call this function right before application
|
||||
// exit - logr cannot be restarted once shut down.
|
||||
// `logr.ShutdownTimeout` determines how long shutdown can execute before
|
||||
// timing out. Use `IsTimeoutError` to determine if the returned error is
|
||||
// due to a timeout.
|
||||
func (logr *Logr) Shutdown() error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), logr.shutdownTimeout())
|
||||
defer cancel()
|
||||
return logr.ShutdownWithTimeout(ctx)
|
||||
}
|
||||
|
||||
// Shutdown cleanly stops the logging engine after making best efforts
|
||||
// to flush all targets. Call this function right before application
|
||||
// exit - logr cannot be restarted once shut down.
|
||||
// Use `IsTimeoutError` to determine if the returned error is due to a
|
||||
// timeout.
|
||||
func (logr *Logr) ShutdownWithTimeout(ctx context.Context) error {
|
||||
logr.mux.Lock()
|
||||
if logr.shutdown {
|
||||
logr.mux.Unlock()
|
||||
return errors.New("Shutdown called again after shut down")
|
||||
}
|
||||
logr.shutdown = true
|
||||
logr.resetLevelCache()
|
||||
logr.mux.Unlock()
|
||||
|
||||
logr.metricsCloseOnce.Do(func() {
|
||||
if logr.metricsDone != nil {
|
||||
close(logr.metricsDone)
|
||||
}
|
||||
})
|
||||
|
||||
errs := merror.New()
|
||||
|
||||
// close the incoming channel and wait for read loop to exit.
|
||||
if logr.in != nil {
|
||||
close(logr.in)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
errs.Append(newTimeoutError("logr queue shutdown timeout"))
|
||||
case <-logr.done:
|
||||
}
|
||||
}
|
||||
|
||||
// logr.in channel should now be drained to targets and no more log records
|
||||
// can be added.
|
||||
logr.tmux.RLock()
|
||||
defer logr.tmux.RUnlock()
|
||||
for _, t := range logr.targets {
|
||||
err := t.Shutdown(ctx)
|
||||
if err != nil {
|
||||
errs.Append(err)
|
||||
}
|
||||
}
|
||||
return errs.ErrorOrNil()
|
||||
}
|
||||
|
||||
// ReportError is used to notify the host application of any internal logging errors.
|
||||
// If `OnLoggerError` is not nil, it is called with the error, otherwise the error is
|
||||
// output to `os.Stderr`.
|
||||
func (logr *Logr) ReportError(err interface{}) {
|
||||
logr.incErrorCounter()
|
||||
|
||||
if logr.OnLoggerError == nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return
|
||||
}
|
||||
logr.OnLoggerError(fmt.Errorf("%v", err))
|
||||
}
|
||||
|
||||
// BorrowBuffer borrows a buffer from the pool. Release the buffer to reduce garbage collection.
|
||||
func (logr *Logr) BorrowBuffer() *bytes.Buffer {
|
||||
if logr.DisableBufferPool {
|
||||
return &bytes.Buffer{}
|
||||
}
|
||||
return logr.bufferPool.Get().(*bytes.Buffer)
|
||||
}
|
||||
|
||||
// ReleaseBuffer returns a buffer to the pool to reduce garbage collection. The buffer is only
|
||||
// retained if less than MaxPooledBuffer.
|
||||
func (logr *Logr) ReleaseBuffer(buf *bytes.Buffer) {
|
||||
if !logr.DisableBufferPool && buf.Cap() < logr.MaxPooledBuffer {
|
||||
buf.Reset()
|
||||
logr.bufferPool.Put(buf)
|
||||
}
|
||||
}
|
||||
|
||||
// enqueueTimeout returns amount of time a log record can take to be queued.
|
||||
// This only applies to blocking enqueue which happen after `logr.OnQueueFull` is called
|
||||
// and returns false.
|
||||
func (logr *Logr) enqueueTimeout() time.Duration {
|
||||
if logr.EnqueueTimeout == 0 {
|
||||
return DefaultEnqueueTimeout
|
||||
}
|
||||
return logr.EnqueueTimeout
|
||||
}
|
||||
|
||||
// shutdownTimeout returns the timeout duration for `logr.Shutdown`.
|
||||
func (logr *Logr) shutdownTimeout() time.Duration {
|
||||
if logr.ShutdownTimeout == 0 {
|
||||
return DefaultShutdownTimeout
|
||||
}
|
||||
return logr.ShutdownTimeout
|
||||
}
|
||||
|
||||
// flushTimeout returns the timeout duration for `logr.Flush`.
|
||||
func (logr *Logr) flushTimeout() time.Duration {
|
||||
if logr.FlushTimeout == 0 {
|
||||
return DefaultFlushTimeout
|
||||
}
|
||||
return logr.FlushTimeout
|
||||
}
|
||||
|
||||
// start selects on incoming log records until done channel signals.
|
||||
// Incoming log records are fanned out to all log targets.
|
||||
func (logr *Logr) start() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logr.ReportError(r)
|
||||
go logr.start()
|
||||
}
|
||||
}()
|
||||
|
||||
for rec := range logr.in {
|
||||
if rec.flush != nil {
|
||||
logr.flush(rec.flush)
|
||||
} else {
|
||||
rec.prep()
|
||||
logr.fanout(rec)
|
||||
}
|
||||
}
|
||||
close(logr.done)
|
||||
}
|
||||
|
||||
// startMetricsUpdater updates the metrics for any polled values every `MetricsUpdateFreqSecs` seconds until
|
||||
// logr is closed.
|
||||
func (logr *Logr) startMetricsUpdater() {
|
||||
for {
|
||||
updateFreq := logr.getMetricsUpdateFreqMillis()
|
||||
if updateFreq == 0 {
|
||||
updateFreq = DefMetricsUpdateFreqMillis
|
||||
}
|
||||
if updateFreq < 250 {
|
||||
updateFreq = 250 // don't peg the CPU
|
||||
}
|
||||
|
||||
select {
|
||||
case <-logr.metricsDone:
|
||||
return
|
||||
case <-time.After(time.Duration(updateFreq) * time.Millisecond):
|
||||
logr.setQueueSizeGauge(float64(len(logr.in)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (logr *Logr) getMetricsUpdateFreqMillis() int64 {
|
||||
logr.mux.RLock()
|
||||
defer logr.mux.RUnlock()
|
||||
return logr.MetricsUpdateFreqMillis
|
||||
}
|
||||
|
||||
// fanout pushes a LogRec to all targets.
|
||||
func (logr *Logr) fanout(rec *LogRec) {
|
||||
var target Target
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logr.ReportError(fmt.Errorf("fanout failed for target %s, %v", target, r))
|
||||
}
|
||||
}()
|
||||
|
||||
var logged bool
|
||||
defer func() {
|
||||
if logged {
|
||||
logr.incLoggedCounter() // call this after tmux is released
|
||||
}
|
||||
}()
|
||||
|
||||
logr.tmux.RLock()
|
||||
defer logr.tmux.RUnlock()
|
||||
for _, target = range logr.targets {
|
||||
if enabled, _ := target.IsLevelEnabled(rec.Level()); enabled {
|
||||
target.Log(rec)
|
||||
logged = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// flush drains the queue and notifies when done.
|
||||
func (logr *Logr) flush(done chan<- struct{}) {
|
||||
// first drain the logr queue.
|
||||
loop:
|
||||
for {
|
||||
var rec *LogRec
|
||||
select {
|
||||
case rec = <-logr.in:
|
||||
if rec.flush == nil {
|
||||
rec.prep()
|
||||
logr.fanout(rec)
|
||||
}
|
||||
default:
|
||||
break loop
|
||||
}
|
||||
}
|
||||
|
||||
logger := logr.NewLogger()
|
||||
|
||||
// drain all the targets; block until finished.
|
||||
logr.tmux.RLock()
|
||||
defer logr.tmux.RUnlock()
|
||||
for _, target := range logr.targets {
|
||||
rec := newFlushLogRec(logger)
|
||||
target.Log(rec)
|
||||
<-rec.flush
|
||||
}
|
||||
done <- struct{}{}
|
||||
}
|
||||
299
vendor/github.com/mattermost/logr/target.go
сгенерированный
поставляемый
299
vendor/github.com/mattermost/logr/target.go
сгенерированный
поставляемый
@@ -1,299 +0,0 @@
|
||||
package logr
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Target represents a destination for log records such as file,
|
||||
// database, TCP socket, etc.
|
||||
type Target interface {
|
||||
// SetName provides an optional name for the target.
|
||||
SetName(name string)
|
||||
|
||||
// IsLevelEnabled returns true if this target should emit
|
||||
// logs for the specified level. Also determines if
|
||||
// a stack trace is required.
|
||||
IsLevelEnabled(Level) (enabled bool, stacktrace bool)
|
||||
|
||||
// Formatter returns the Formatter associated with this Target.
|
||||
Formatter() Formatter
|
||||
|
||||
// Log outputs the log record to this target's destination.
|
||||
Log(rec *LogRec)
|
||||
|
||||
// Shutdown makes best effort to flush target queue and
|
||||
// frees/closes all resources.
|
||||
Shutdown(ctx context.Context) error
|
||||
}
|
||||
|
||||
// RecordWriter can convert a LogRecord to bytes and output to some data sink.
|
||||
type RecordWriter interface {
|
||||
Write(rec *LogRec) error
|
||||
}
|
||||
|
||||
// Basic provides the basic functionality of a Target that can be used
|
||||
// to more easily compose your own Targets. To use, just embed Basic
|
||||
// in your target type, implement `RecordWriter`, and call `(*Basic).Start`.
|
||||
type Basic struct {
|
||||
target Target
|
||||
|
||||
filter Filter
|
||||
formatter Formatter
|
||||
|
||||
in chan *LogRec
|
||||
done chan struct{}
|
||||
w RecordWriter
|
||||
|
||||
mux sync.RWMutex
|
||||
name string
|
||||
|
||||
metrics bool
|
||||
queueSizeGauge Gauge
|
||||
loggedCounter Counter
|
||||
errorCounter Counter
|
||||
droppedCounter Counter
|
||||
blockedCounter Counter
|
||||
|
||||
metricsUpdateFreqMillis int64
|
||||
}
|
||||
|
||||
// Start initializes this target helper and starts accepting log records for processing.
|
||||
func (b *Basic) Start(target Target, rw RecordWriter, filter Filter, formatter Formatter, maxQueued int) {
|
||||
if filter == nil {
|
||||
filter = &StdFilter{Lvl: Fatal}
|
||||
}
|
||||
if formatter == nil {
|
||||
formatter = &DefaultFormatter{}
|
||||
}
|
||||
|
||||
b.target = target
|
||||
b.filter = filter
|
||||
b.formatter = formatter
|
||||
b.in = make(chan *LogRec, maxQueued)
|
||||
b.done = make(chan struct{}, 1)
|
||||
b.w = rw
|
||||
go b.start()
|
||||
|
||||
if b.hasMetrics() {
|
||||
go b.startMetricsUpdater()
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Basic) SetName(name string) {
|
||||
b.mux.Lock()
|
||||
defer b.mux.Unlock()
|
||||
b.name = name
|
||||
}
|
||||
|
||||
// IsLevelEnabled returns true if this target should emit
|
||||
// logs for the specified level. Also determines if
|
||||
// a stack trace is required.
|
||||
func (b *Basic) IsLevelEnabled(lvl Level) (enabled bool, stacktrace bool) {
|
||||
return b.filter.IsEnabled(lvl), b.filter.IsStacktraceEnabled(lvl)
|
||||
}
|
||||
|
||||
// Formatter returns the Formatter associated with this Target.
|
||||
func (b *Basic) Formatter() Formatter {
|
||||
return b.formatter
|
||||
}
|
||||
|
||||
// Shutdown stops processing log records after making best
|
||||
// effort to flush queue.
|
||||
func (b *Basic) Shutdown(ctx context.Context) error {
|
||||
// close the incoming channel and wait for read loop to exit.
|
||||
close(b.in)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case <-b.done:
|
||||
}
|
||||
|
||||
// b.in channel should now be drained.
|
||||
return nil
|
||||
}
|
||||
|
||||
// Log outputs the log record to this targets destination.
|
||||
func (b *Basic) Log(rec *LogRec) {
|
||||
lgr := rec.Logger().Logr()
|
||||
select {
|
||||
case b.in <- rec:
|
||||
default:
|
||||
handler := lgr.OnTargetQueueFull
|
||||
if handler != nil && handler(b.target, rec, cap(b.in)) {
|
||||
b.incDroppedCounter()
|
||||
return // drop the record
|
||||
}
|
||||
b.incBlockedCounter()
|
||||
|
||||
select {
|
||||
case <-time.After(lgr.enqueueTimeout()):
|
||||
lgr.ReportError(fmt.Errorf("target enqueue timeout for log rec [%v]", rec))
|
||||
case b.in <- rec: // block until success or timeout
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Metrics enables metrics collection using the provided MetricsCollector.
|
||||
func (b *Basic) EnableMetrics(collector MetricsCollector, updateFreqMillis int64) error {
|
||||
name := fmt.Sprintf("%v", b)
|
||||
|
||||
b.mux.Lock()
|
||||
defer b.mux.Unlock()
|
||||
|
||||
b.metrics = true
|
||||
b.metricsUpdateFreqMillis = updateFreqMillis
|
||||
|
||||
var err error
|
||||
|
||||
if b.queueSizeGauge, err = collector.QueueSizeGauge(name); err != nil {
|
||||
return err
|
||||
}
|
||||
if b.loggedCounter, err = collector.LoggedCounter(name); err != nil {
|
||||
return err
|
||||
}
|
||||
if b.errorCounter, err = collector.ErrorCounter(name); err != nil {
|
||||
return err
|
||||
}
|
||||
if b.droppedCounter, err = collector.DroppedCounter(name); err != nil {
|
||||
return err
|
||||
}
|
||||
if b.blockedCounter, err = collector.BlockedCounter(name); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Basic) hasMetrics() bool {
|
||||
b.mux.RLock()
|
||||
defer b.mux.RUnlock()
|
||||
return b.metrics
|
||||
}
|
||||
|
||||
func (b *Basic) setQueueSizeGauge(val float64) {
|
||||
b.mux.RLock()
|
||||
defer b.mux.RUnlock()
|
||||
if b.queueSizeGauge != nil {
|
||||
b.queueSizeGauge.Set(val)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Basic) incLoggedCounter() {
|
||||
b.mux.RLock()
|
||||
defer b.mux.RUnlock()
|
||||
if b.loggedCounter != nil {
|
||||
b.loggedCounter.Inc()
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Basic) incErrorCounter() {
|
||||
b.mux.RLock()
|
||||
defer b.mux.RUnlock()
|
||||
if b.errorCounter != nil {
|
||||
b.errorCounter.Inc()
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Basic) incDroppedCounter() {
|
||||
b.mux.RLock()
|
||||
defer b.mux.RUnlock()
|
||||
if b.droppedCounter != nil {
|
||||
b.droppedCounter.Inc()
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Basic) incBlockedCounter() {
|
||||
b.mux.RLock()
|
||||
defer b.mux.RUnlock()
|
||||
if b.blockedCounter != nil {
|
||||
b.blockedCounter.Inc()
|
||||
}
|
||||
}
|
||||
|
||||
// String returns a name for this target. Use `SetName` to specify a name.
|
||||
func (b *Basic) String() string {
|
||||
b.mux.RLock()
|
||||
defer b.mux.RUnlock()
|
||||
|
||||
if b.name != "" {
|
||||
return b.name
|
||||
}
|
||||
return fmt.Sprintf("%T", b.target)
|
||||
}
|
||||
|
||||
// Start accepts log records via In channel and writes to the
|
||||
// supplied writer, until Done channel signaled.
|
||||
func (b *Basic) start() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
fmt.Fprintln(os.Stderr, "Basic.start -- ", r)
|
||||
go b.start()
|
||||
}
|
||||
}()
|
||||
|
||||
for rec := range b.in {
|
||||
if rec.flush != nil {
|
||||
b.flush(rec.flush)
|
||||
} else {
|
||||
err := b.w.Write(rec)
|
||||
if err != nil {
|
||||
b.incErrorCounter()
|
||||
rec.Logger().Logr().ReportError(err)
|
||||
} else {
|
||||
b.incLoggedCounter()
|
||||
}
|
||||
}
|
||||
}
|
||||
close(b.done)
|
||||
}
|
||||
|
||||
// startMetricsUpdater updates the metrics for any polled values every `MetricsUpdateFreqSecs` seconds until
|
||||
// target is closed.
|
||||
func (b *Basic) startMetricsUpdater() {
|
||||
for {
|
||||
updateFreq := b.getMetricsUpdateFreqMillis()
|
||||
if updateFreq == 0 {
|
||||
updateFreq = DefMetricsUpdateFreqMillis
|
||||
}
|
||||
if updateFreq < 250 {
|
||||
updateFreq = 250 // don't peg the CPU
|
||||
}
|
||||
|
||||
select {
|
||||
case <-b.done:
|
||||
return
|
||||
case <-time.After(time.Duration(updateFreq) * time.Millisecond):
|
||||
b.setQueueSizeGauge(float64(len(b.in)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Basic) getMetricsUpdateFreqMillis() int64 {
|
||||
b.mux.RLock()
|
||||
defer b.mux.RUnlock()
|
||||
return b.metricsUpdateFreqMillis
|
||||
}
|
||||
|
||||
// flush drains the queue and notifies when done.
|
||||
func (b *Basic) flush(done chan<- struct{}) {
|
||||
for {
|
||||
var rec *LogRec
|
||||
var err error
|
||||
select {
|
||||
case rec = <-b.in:
|
||||
// ignore any redundant flush records.
|
||||
if rec.flush == nil {
|
||||
err = b.w.Write(rec)
|
||||
if err != nil {
|
||||
b.incErrorCounter()
|
||||
rec.Logger().Logr().ReportError(err)
|
||||
}
|
||||
}
|
||||
default:
|
||||
done <- struct{}{}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
89
vendor/github.com/mattermost/logr/target/syslog.go
сгенерированный
поставляемый
89
vendor/github.com/mattermost/logr/target/syslog.go
сгенерированный
поставляемый
@@ -1,89 +0,0 @@
|
||||
// +build !windows,!nacl,!plan9
|
||||
|
||||
package target
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/syslog"
|
||||
|
||||
"github.com/mattermost/logr"
|
||||
"github.com/wiggin77/merror"
|
||||
)
|
||||
|
||||
// Syslog outputs log records to local or remote syslog.
|
||||
type Syslog struct {
|
||||
logr.Basic
|
||||
w *syslog.Writer
|
||||
}
|
||||
|
||||
// SyslogParams provides parameters for dialing a syslog daemon.
|
||||
type SyslogParams struct {
|
||||
Network string
|
||||
Raddr string
|
||||
Priority syslog.Priority
|
||||
Tag string
|
||||
}
|
||||
|
||||
// NewSyslogTarget creates a target capable of outputting log records to remote or local syslog.
|
||||
func NewSyslogTarget(filter logr.Filter, formatter logr.Formatter, params *SyslogParams, maxQueue int) (*Syslog, error) {
|
||||
writer, err := syslog.Dial(params.Network, params.Raddr, params.Priority, params.Tag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s := &Syslog{w: writer}
|
||||
s.Basic.Start(s, s, filter, formatter, maxQueue)
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Shutdown stops processing log records after making best
|
||||
// effort to flush queue.
|
||||
func (s *Syslog) Shutdown(ctx context.Context) error {
|
||||
errs := merror.New()
|
||||
|
||||
err := s.Basic.Shutdown(ctx)
|
||||
errs.Append(err)
|
||||
|
||||
err = s.w.Close()
|
||||
errs.Append(err)
|
||||
|
||||
return errs.ErrorOrNil()
|
||||
}
|
||||
|
||||
// Write converts the log record to bytes, via the Formatter,
|
||||
// and outputs to syslog.
|
||||
func (s *Syslog) Write(rec *logr.LogRec) error {
|
||||
_, stacktrace := s.IsLevelEnabled(rec.Level())
|
||||
|
||||
buf := rec.Logger().Logr().BorrowBuffer()
|
||||
defer rec.Logger().Logr().ReleaseBuffer(buf)
|
||||
|
||||
buf, err := s.Formatter().Format(rec, stacktrace, buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
txt := buf.String()
|
||||
|
||||
switch rec.Level() {
|
||||
case logr.Panic, logr.Fatal:
|
||||
err = s.w.Crit(txt)
|
||||
case logr.Error:
|
||||
err = s.w.Err(txt)
|
||||
case logr.Warn:
|
||||
err = s.w.Warning(txt)
|
||||
case logr.Debug, logr.Trace:
|
||||
err = s.w.Debug(txt)
|
||||
default:
|
||||
// logr.Info plus all custom levels.
|
||||
err = s.w.Info(txt)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
reporter := rec.Logger().Logr().ReportError
|
||||
reporter(fmt.Errorf("syslog write fail: %w", err))
|
||||
// syslog writer will try to reconnect.
|
||||
}
|
||||
return err
|
||||
}
|
||||
40
vendor/github.com/mattermost/logr/target/writer.go
сгенерированный
поставляемый
40
vendor/github.com/mattermost/logr/target/writer.go
сгенерированный
поставляемый
@@ -1,40 +0,0 @@
|
||||
package target
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/mattermost/logr"
|
||||
)
|
||||
|
||||
// Writer outputs log records to any `io.Writer`.
|
||||
type Writer struct {
|
||||
logr.Basic
|
||||
out io.Writer
|
||||
}
|
||||
|
||||
// NewWriterTarget creates a target capable of outputting log records to an io.Writer.
|
||||
func NewWriterTarget(filter logr.Filter, formatter logr.Formatter, out io.Writer, maxQueue int) *Writer {
|
||||
if out == nil {
|
||||
out = ioutil.Discard
|
||||
}
|
||||
w := &Writer{out: out}
|
||||
w.Basic.Start(w, w, filter, formatter, maxQueue)
|
||||
return w
|
||||
}
|
||||
|
||||
// Write converts the log record to bytes, via the Formatter,
|
||||
// and outputs to the io.Writer.
|
||||
func (w *Writer) Write(rec *logr.LogRec) error {
|
||||
_, stacktrace := w.IsLevelEnabled(rec.Level())
|
||||
|
||||
buf := rec.Logger().Logr().BorrowBuffer()
|
||||
defer rec.Logger().Logr().ReleaseBuffer(buf)
|
||||
|
||||
buf, err := w.Formatter().Format(rec, stacktrace, buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.out.Write(buf.Bytes())
|
||||
return err
|
||||
}
|
||||
1
vendor/github.com/mattermost/logr/.gitignore → vendor/github.com/mattermost/logr/v2/.gitignore
сгенерированный
поставляемый
1
vendor/github.com/mattermost/logr/.gitignore → vendor/github.com/mattermost/logr/v2/.gitignore
сгенерированный
поставляемый
@@ -34,3 +34,4 @@ logs
|
||||
# test apps
|
||||
test/cmd/testapp1/testapp1
|
||||
test/cmd/simple/simple
|
||||
test/cmd/gelf/gelf
|
||||
0
vendor/github.com/mattermost/logr/.travis.yml → vendor/github.com/mattermost/logr/v2/.travis.yml
сгенерированный
поставляемый
0
vendor/github.com/mattermost/logr/.travis.yml → vendor/github.com/mattermost/logr/v2/.travis.yml
сгенерированный
поставляемый
0
vendor/github.com/mattermost/logr/LICENSE → vendor/github.com/mattermost/logr/v2/LICENSE
сгенерированный
поставляемый
0
vendor/github.com/mattermost/logr/LICENSE → vendor/github.com/mattermost/logr/v2/LICENSE
сгенерированный
поставляемый
49
vendor/github.com/mattermost/logr/README.md → vendor/github.com/mattermost/logr/v2/README.md
сгенерированный
поставляемый
49
vendor/github.com/mattermost/logr/README.md → vendor/github.com/mattermost/logr/v2/README.md
сгенерированный
поставляемый
@@ -16,9 +16,9 @@ It is very much inspired by [Logrus](https://github.com/sirupsen/logrus) but add
|
||||
<!-- markdownlint-disable MD033 -->
|
||||
| entity | description |
|
||||
| ------ | ----------- |
|
||||
| Logr | Engine instance typically instantiated once; used to configure logging.<br>```lgr := &Logr{}```|
|
||||
| Logr | Engine instance typically instantiated once; used to configure logging.<br>```lgr,_ := logr.New()```|
|
||||
| Logger | Provides contextual logging via fields; lightweight, can be created once and accessed globally or create on demand.<br>```logger := lgr.NewLogger()```<br>```logger2 := logger.WithField("user", "Sam")```|
|
||||
| Target | A destination for log items such as console, file, database or just about anything that can be written to. Each target has its own filter/level and formatter, and any number of targets can be added to a Logr. Targets for syslog and any io.Writer are built-in and it is easy to create your own. You can also use any [Logrus hooks](https://github.com/sirupsen/logrus/wiki/Hooks) via a simple [adapter](https://github.com/wiggin77/logrus4logr).|
|
||||
| Target | A destination for log items such as console, file, database or just about anything that can be written to. Each target has its own filter/level and formatter, and any number of targets can be added to a Logr. Targets for file, syslog and any io.Writer are built-in and it is easy to create your own. You can also use any [Logrus hooks](https://github.com/sirupsen/logrus/wiki/Hooks) via a simple [adapter](https://github.com/wiggin77/logrus4logr).|
|
||||
| Filter | Determines which logging calls get written versus filtered out. Also determines which logging calls generate a stack trace.<br>```filter := &logr.StdFilter{Lvl: logr.Warn, Stacktrace: logr.Fatal}```|
|
||||
| Formatter | Formats the output. Logr includes built-in formatters for JSON and plain text with delimiters. It is easy to create your own formatters or you can also use any [Logrus formatters](https://github.com/sirupsen/logrus#formatters) via a simple [adapter](https://github.com/wiggin77/logrus4logr).<br>```formatter := &format.Plain{Delim: " \| "}```|
|
||||
|
||||
@@ -26,15 +26,15 @@ It is very much inspired by [Logrus](https://github.com/sirupsen/logrus) but add
|
||||
|
||||
```go
|
||||
// Create Logr instance.
|
||||
lgr := &logr.Logr{}
|
||||
lgr,_ := logr.New()
|
||||
|
||||
// Create a filter and formatter. Both can be shared by multiple
|
||||
// targets.
|
||||
filter := &logr.StdFilter{Lvl: logr.Warn, Stacktrace: logr.Error}
|
||||
formatter := &format.Plain{Delim: " | "}
|
||||
formatter := &formatters.Plain{Delim: " | "}
|
||||
|
||||
// WriterTarget outputs to any io.Writer
|
||||
t := target.NewWriterTarget(filter, formatter, os.StdOut, 1000)
|
||||
t := targets.NewWriterTarget(filter, formatter, os.StdOut, 1000)
|
||||
lgr.AddTarget(t)
|
||||
|
||||
// One or more Loggers can be created, shared, used concurrently,
|
||||
@@ -56,7 +56,7 @@ Fields allow for contextual logging, meaning information can be added to log sta
|
||||
Fields are added via Loggers:
|
||||
|
||||
```go
|
||||
lgr := &Logr{}
|
||||
lgr,_ := logr.New()
|
||||
// ... add targets ...
|
||||
logger := lgr.NewLogger().WithFields(logr.Fields{
|
||||
"user": user,
|
||||
@@ -88,14 +88,14 @@ Logr also supports custom filters (logr.CustomFilter) which allow fine grained i
|
||||
LoginLevel := logr.Level{ID: 100, Name: "login ", Stacktrace: false}
|
||||
LogoutLevel := logr.Level{ID: 101, Name: "logout", Stacktrace: false}
|
||||
|
||||
lgr := &logr.Logr{}
|
||||
lgr,_ := logr.New()
|
||||
|
||||
// create a custom filter with custom levels.
|
||||
filter := &logr.CustomFilter{}
|
||||
filter.Add(LoginLevel, LogoutLevel)
|
||||
|
||||
formatter := &format.Plain{Delim: " | "}
|
||||
tgr := target.NewWriterTarget(filter, formatter, os.StdOut, 1000)
|
||||
formatter := &formatters.Plain{Delim: " | "}
|
||||
tgr := targets.NewWriterTarget(filter, formatter, os.StdOut, 1000)
|
||||
lgr.AddTarget(tgr)
|
||||
logger := lgr.NewLogger().WithFields(logr.Fields{"user": "Bob", "role": "admin"})
|
||||
|
||||
@@ -113,36 +113,31 @@ You can use any [Logrus hooks](https://github.com/sirupsen/logrus/wiki/Hooks) vi
|
||||
|
||||
You can create your own target by implementing the [Target](./target.go) interface.
|
||||
|
||||
An easier method is to use the [logr.Basic](./target.go) type target and build your functionality on that. Basic handles all the queuing and other plumbing so you only need to implement two methods. Example target that outputs to `io.Writer`:
|
||||
Example target that outputs to `io.Writer`:
|
||||
|
||||
```go
|
||||
type Writer struct {
|
||||
logr.Basic
|
||||
out io.Writer
|
||||
}
|
||||
|
||||
func NewWriterTarget(filter logr.Filter, formatter logr.Formatter, out io.Writer, maxQueue int) *Writer {
|
||||
func NewWriterTarget(out io.Writer) *Writer {
|
||||
w := &Writer{out: out}
|
||||
w.Basic.Start(w, w, filter, formatter, maxQueue)
|
||||
return w
|
||||
}
|
||||
|
||||
// Called once to initialize target.
|
||||
func (w *Writer) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write will always be called by a single goroutine, so no locking needed.
|
||||
// Just convert a log record to a []byte using the formatter and output the
|
||||
// bytes to your sink.
|
||||
func (w *Writer) Write(rec *logr.LogRec) error {
|
||||
_, stacktrace := w.IsLevelEnabled(rec.Level())
|
||||
func (w *Writer) Write(p []byte, rec *logr.LogRec) (int, error) {
|
||||
return w.out.Write(buf.Bytes())
|
||||
}
|
||||
|
||||
// take a buffer from the pool to avoid allocations or just allocate a new one.
|
||||
buf := rec.Logger().Logr().BorrowBuffer()
|
||||
defer rec.Logger().Logr().ReleaseBuffer(buf)
|
||||
|
||||
buf, err := w.Formatter().Format(rec, stacktrace, buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.out.Write(buf.Bytes())
|
||||
return err
|
||||
// Called once to cleanup/free resources for target.
|
||||
func (w *Writer) Shutdown() error {
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
209
vendor/github.com/mattermost/logr/v2/config/config.go
сгенерированный
поставляемый
Обычный файл
209
vendor/github.com/mattermost/logr/v2/config/config.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,209 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/mattermost/logr/v2"
|
||||
"github.com/mattermost/logr/v2/formatters"
|
||||
"github.com/mattermost/logr/v2/targets"
|
||||
)
|
||||
|
||||
type TargetCfg struct {
|
||||
Type string `json:"type"` // one of "console", "file", "tcp", "syslog", "none".
|
||||
Options json.RawMessage `json:"options,omitempty"`
|
||||
Format string `json:"format"` // one of "json", "plain", "gelf"
|
||||
FormatOptions json.RawMessage `json:"format_options,omitempty"`
|
||||
Levels []logr.Level `json:"levels"`
|
||||
MaxQueueSize int `json:"maxqueuesize,omitempty"`
|
||||
}
|
||||
|
||||
type ConsoleOptions struct {
|
||||
Out string `json:"out"` // one of "stdout", "stderr"
|
||||
}
|
||||
|
||||
type TargetFactory func(targetType string, options json.RawMessage) (logr.Target, error)
|
||||
type FormatterFactory func(format string, options json.RawMessage) (logr.Formatter, error)
|
||||
|
||||
type Factories struct {
|
||||
targetFactory TargetFactory // can be nil
|
||||
formatterFactory FormatterFactory // can be nil
|
||||
}
|
||||
|
||||
var removeAll = func(ti logr.TargetInfo) bool { return true }
|
||||
|
||||
// ConfigureTargets replaces the current list of log targets with a new one based on a map
|
||||
// of name->TargetCfg. The map of TargetCfg's would typically be serialized from a JSON
|
||||
// source or can be programmatically created.
|
||||
//
|
||||
// An optional set of factories can be provided which will be called to create any target
|
||||
// types or formatters not built-in.
|
||||
//
|
||||
// To append log targets to an existing config, use `(*Logr).AddTarget` or
|
||||
// `(*Logr).AddTargetFromConfig` instead.
|
||||
func ConfigureTargets(lgr *logr.Logr, config map[string]TargetCfg, factories *Factories) error {
|
||||
if err := lgr.RemoveTargets(context.Background(), removeAll); err != nil {
|
||||
return fmt.Errorf("error removing existing log targets: %w", err)
|
||||
}
|
||||
|
||||
if factories == nil {
|
||||
factories = &Factories{nil, nil}
|
||||
}
|
||||
|
||||
for name, tcfg := range config {
|
||||
target, err := newTarget(tcfg.Type, tcfg.Options, factories.targetFactory)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating log target %s: %w", name, err)
|
||||
}
|
||||
|
||||
if target == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
formatter, err := newFormatter(tcfg.Format, tcfg.FormatOptions, factories.formatterFactory)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating formatter for log target %s: %w", name, err)
|
||||
}
|
||||
|
||||
filter := newFilter(tcfg.Levels)
|
||||
qSize := tcfg.MaxQueueSize
|
||||
if qSize == 0 {
|
||||
qSize = logr.DefaultMaxQueueSize
|
||||
}
|
||||
|
||||
if err = lgr.AddTarget(target, name, filter, formatter, qSize); err != nil {
|
||||
return fmt.Errorf("error adding log target %s: %w", name, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func newFilter(levels []logr.Level) logr.Filter {
|
||||
filter := &logr.CustomFilter{}
|
||||
for _, lvl := range levels {
|
||||
filter.Add(lvl)
|
||||
}
|
||||
return filter
|
||||
}
|
||||
|
||||
func newTarget(targetType string, options json.RawMessage, factory TargetFactory) (logr.Target, error) {
|
||||
switch strings.ToLower(targetType) {
|
||||
case "console":
|
||||
c := ConsoleOptions{}
|
||||
if len(options) != 0 {
|
||||
if err := json.Unmarshal(options, &c); err != nil {
|
||||
return nil, fmt.Errorf("error decoding console target options: %w", err)
|
||||
}
|
||||
}
|
||||
var w io.Writer
|
||||
switch c.Out {
|
||||
case "stderr":
|
||||
w = os.Stderr
|
||||
case "stdout", "":
|
||||
w = os.Stdout
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid console target option '%s'", c.Out)
|
||||
}
|
||||
return targets.NewWriterTarget(w), nil
|
||||
case "file":
|
||||
fo := targets.FileOptions{}
|
||||
if len(options) == 0 {
|
||||
return nil, errors.New("missing file target options")
|
||||
}
|
||||
if err := json.Unmarshal(options, &fo); err != nil {
|
||||
return nil, fmt.Errorf("error decoding file target options: %w", err)
|
||||
}
|
||||
if err := fo.CheckValid(); err != nil {
|
||||
return nil, fmt.Errorf("invalid file target options: %w", err)
|
||||
}
|
||||
return targets.NewFileTarget(fo), nil
|
||||
case "tcp":
|
||||
to := targets.TcpOptions{}
|
||||
if len(options) == 0 {
|
||||
return nil, errors.New("missing TCP target options")
|
||||
}
|
||||
if err := json.Unmarshal(options, &to); err != nil {
|
||||
return nil, fmt.Errorf("error decoding TCP target options: %w", err)
|
||||
}
|
||||
if err := to.CheckValid(); err != nil {
|
||||
return nil, fmt.Errorf("invalid TCP target options: %w", err)
|
||||
}
|
||||
return targets.NewTcpTarget(&to), nil
|
||||
case "syslog":
|
||||
so := targets.SyslogOptions{}
|
||||
if len(options) == 0 {
|
||||
return nil, errors.New("missing SysLog target options")
|
||||
}
|
||||
if err := json.Unmarshal(options, &so); err != nil {
|
||||
return nil, fmt.Errorf("error decoding Syslog target options: %w", err)
|
||||
}
|
||||
if err := so.CheckValid(); err != nil {
|
||||
return nil, fmt.Errorf("invalid SysLog target options: %w", err)
|
||||
}
|
||||
return targets.NewSyslogTarget(&so)
|
||||
case "none":
|
||||
return nil, nil
|
||||
default:
|
||||
if factory != nil {
|
||||
t, err := factory(targetType, options)
|
||||
if err != nil || t == nil {
|
||||
return nil, fmt.Errorf("error from target factory: %w", err)
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("target type '%s' is unrecogized", targetType)
|
||||
}
|
||||
|
||||
func newFormatter(format string, options json.RawMessage, factory FormatterFactory) (logr.Formatter, error) {
|
||||
switch strings.ToLower(format) {
|
||||
case "json":
|
||||
j := formatters.JSON{}
|
||||
if len(options) != 0 {
|
||||
if err := json.Unmarshal(options, &j); err != nil {
|
||||
return nil, fmt.Errorf("error decoding JSON formatter options: %w", err)
|
||||
}
|
||||
if err := j.CheckValid(); err != nil {
|
||||
return nil, fmt.Errorf("invalid JSON formatter options: %w", err)
|
||||
}
|
||||
}
|
||||
return &j, nil
|
||||
case "plain":
|
||||
p := formatters.Plain{}
|
||||
if len(options) != 0 {
|
||||
if err := json.Unmarshal(options, &p); err != nil {
|
||||
return nil, fmt.Errorf("error decoding Plain formatter options: %w", err)
|
||||
}
|
||||
if err := p.CheckValid(); err != nil {
|
||||
return nil, fmt.Errorf("invalid plain formatter options: %w", err)
|
||||
}
|
||||
}
|
||||
return &p, nil
|
||||
case "gelf":
|
||||
g := formatters.Gelf{}
|
||||
if len(options) != 0 {
|
||||
if err := json.Unmarshal(options, &g); err != nil {
|
||||
return nil, fmt.Errorf("error decoding Gelf formatter options: %w", err)
|
||||
}
|
||||
if err := g.CheckValid(); err != nil {
|
||||
return nil, fmt.Errorf("invalid GELF formatter options: %w", err)
|
||||
}
|
||||
}
|
||||
return &g, nil
|
||||
|
||||
default:
|
||||
if factory != nil {
|
||||
f, err := factory(format, options)
|
||||
if err != nil || f == nil {
|
||||
return nil, fmt.Errorf("error from formatter factory: %w", err)
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("format '%s' is unrecogized", format)
|
||||
}
|
||||
90
vendor/github.com/mattermost/logr/v2/config/sample-config.json
сгенерированный
поставляемый
Обычный файл
90
vendor/github.com/mattermost/logr/v2/config/sample-config.json
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,90 @@
|
||||
{
|
||||
"sample-console": {
|
||||
"type": "console",
|
||||
"options": {
|
||||
"out": "stdout"
|
||||
},
|
||||
"format": "plain",
|
||||
"format_options": {
|
||||
"delim": " | "
|
||||
},
|
||||
"levels": [
|
||||
{"id": 5, "name": "debug"},
|
||||
{"id": 4, "name": "info"},
|
||||
{"id": 3, "name": "warn"},
|
||||
{"id": 2, "name": "error", "stacktrace": true},
|
||||
{"id": 1, "name": "fatal", "stacktrace": true},
|
||||
{"id": 0, "name": "panic", "stacktrace": true}
|
||||
],
|
||||
"maxqueuesize": 1000
|
||||
},
|
||||
"sample-file": {
|
||||
"type": "file",
|
||||
"options": {
|
||||
"filename": "test.log",
|
||||
"max_size": 1000000,
|
||||
"max_age": 1,
|
||||
"max_backups": 10,
|
||||
"compress": true
|
||||
},
|
||||
"format": "json",
|
||||
"format_options": {
|
||||
},
|
||||
"levels": [
|
||||
{"id": 5, "name": "debug"},
|
||||
{"id": 4, "name": "info"},
|
||||
{"id": 3, "name": "warn"},
|
||||
{"id": 2, "name": "error", "stacktrace": true},
|
||||
{"id": 1, "name": "fatal", "stacktrace": true},
|
||||
{"id": 0, "name": "panic", "stacktrace": true}
|
||||
],
|
||||
"maxqueuesize": 1000
|
||||
},
|
||||
"sample-tcp": {
|
||||
"type": "tcp",
|
||||
"options": {
|
||||
"host": "localhost",
|
||||
"port": 18066,
|
||||
"tls": false,
|
||||
"cert": "",
|
||||
"insecure": false
|
||||
},
|
||||
"format": "gelf",
|
||||
"format_options": {
|
||||
"hostname": "server01"
|
||||
},
|
||||
"levels": [
|
||||
{"id": 5, "name": "debug"},
|
||||
{"id": 4, "name": "info"},
|
||||
{"id": 3, "name": "warn"},
|
||||
{"id": 2, "name": "error", "stacktrace": true},
|
||||
{"id": 1, "name": "fatal", "stacktrace": true},
|
||||
{"id": 0, "name": "panic", "stacktrace": true}
|
||||
],
|
||||
"maxqueuesize": 1000
|
||||
},
|
||||
"sample-syslog": {
|
||||
"type": "syslog",
|
||||
"options": {
|
||||
"host": "localhost",
|
||||
"port": 18066,
|
||||
"tls": false,
|
||||
"cert": "",
|
||||
"insecure": false,
|
||||
"tag": "testapp"
|
||||
},
|
||||
"format": "plain",
|
||||
"format_options": {
|
||||
"delim": " "
|
||||
},
|
||||
"levels": [
|
||||
{"id": 5, "name": "debug"},
|
||||
{"id": 4, "name": "info"},
|
||||
{"id": 3, "name": "warn"},
|
||||
{"id": 2, "name": "error", "stacktrace": true},
|
||||
{"id": 1, "name": "fatal", "stacktrace": true},
|
||||
{"id": 0, "name": "panic", "stacktrace": true}
|
||||
],
|
||||
"maxqueuesize": 1000
|
||||
}
|
||||
}
|
||||
2
vendor/github.com/mattermost/logr/const.go → vendor/github.com/mattermost/logr/v2/const.go
сгенерированный
поставляемый
2
vendor/github.com/mattermost/logr/const.go → vendor/github.com/mattermost/logr/v2/const.go
сгенерированный
поставляемый
@@ -13,7 +13,7 @@ const (
|
||||
|
||||
// MaxLevelID is the maximum value of a level ID. Some level cache implementations will
|
||||
// allocate a cache of this size. Cannot exceed uint.
|
||||
MaxLevelID = 256
|
||||
MaxLevelID = 65535
|
||||
|
||||
// DefaultEnqueueTimeout is the default amount of time a log record can take to be queued.
|
||||
// This only applies to blocking enqueue which happen after `logr.OnQueueFull` is called
|
||||
403
vendor/github.com/mattermost/logr/v2/field.go
сгенерированный
поставляемый
Обычный файл
403
vendor/github.com/mattermost/logr/v2/field.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,403 @@
|
||||
package logr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
Comma = []byte{','}
|
||||
Equals = []byte{'='}
|
||||
Space = []byte{' '}
|
||||
Newline = []byte{'\n'}
|
||||
Quote = []byte{'"'}
|
||||
Colon = []byte{'"'}
|
||||
)
|
||||
|
||||
// LogCloner is implemented by `Any` types that require a clone to be provided
|
||||
// to the logger because the original may mutate.
|
||||
type LogCloner interface {
|
||||
LogClone() interface{}
|
||||
}
|
||||
|
||||
// LogWriter is implemented by `Any` types that provide custom formatting for
|
||||
// log output. A string representation of the type should be written directly to
|
||||
// the `io.Writer`.
|
||||
type LogWriter interface {
|
||||
LogWrite(w io.Writer) error
|
||||
}
|
||||
|
||||
type FieldType uint8
|
||||
|
||||
const (
|
||||
UnknownType FieldType = iota
|
||||
StringType
|
||||
StringerType
|
||||
StructType
|
||||
ErrorType
|
||||
BoolType
|
||||
TimestampMillisType
|
||||
TimeType
|
||||
DurationType
|
||||
Int64Type
|
||||
Int32Type
|
||||
IntType
|
||||
Uint64Type
|
||||
Uint32Type
|
||||
UintType
|
||||
Float64Type
|
||||
Float32Type
|
||||
BinaryType
|
||||
ArrayType
|
||||
MapType
|
||||
)
|
||||
|
||||
type Field struct {
|
||||
Key string
|
||||
Type FieldType
|
||||
Integer int64
|
||||
Float float64
|
||||
String string
|
||||
Interface interface{}
|
||||
}
|
||||
|
||||
func quoteString(w io.Writer, s string, shouldQuote func(s string) bool) error {
|
||||
b := shouldQuote(s)
|
||||
if b {
|
||||
if _, err := w.Write(Quote); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := w.Write([]byte(s)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if b {
|
||||
if _, err := w.Write(Quote); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValueString converts a known type to a string using default formatting.
|
||||
// This is called lazily by a formatter.
|
||||
// Formatters can provide custom formatting or types passed via `Any` can implement
|
||||
// the `LogString` interface to generate output for logging.
|
||||
// If the optional shouldQuote callback is provided, then it will be called for any
|
||||
// string output that could potentially need to be quoted.
|
||||
func (f Field) ValueString(w io.Writer, shouldQuote func(s string) bool) error {
|
||||
if shouldQuote == nil {
|
||||
shouldQuote = func(s string) bool { return false }
|
||||
}
|
||||
var err error
|
||||
switch f.Type {
|
||||
case StringType:
|
||||
err = quoteString(w, f.String, shouldQuote)
|
||||
|
||||
case StringerType:
|
||||
s, ok := f.Interface.(fmt.Stringer)
|
||||
if ok {
|
||||
err = quoteString(w, s.String(), shouldQuote)
|
||||
} else if f.Interface == nil {
|
||||
err = quoteString(w, "", shouldQuote)
|
||||
} else {
|
||||
err = fmt.Errorf("invalid fmt.Stringer for key %s", f.Key)
|
||||
}
|
||||
|
||||
case StructType:
|
||||
s, ok := f.Interface.(LogWriter)
|
||||
if ok {
|
||||
err = s.LogWrite(w)
|
||||
break
|
||||
}
|
||||
// structs that do not implement LogWriter fall back to reflection via Printf.
|
||||
// TODO: create custom reflection-based encoder.
|
||||
_, err = fmt.Fprintf(w, "%v", f.Interface)
|
||||
|
||||
case ErrorType:
|
||||
// TODO: create custom error encoder.
|
||||
err = quoteString(w, fmt.Sprintf("%v", f.Interface), shouldQuote)
|
||||
|
||||
case BoolType:
|
||||
var b bool
|
||||
if f.Integer != 0 {
|
||||
b = true
|
||||
}
|
||||
_, err = io.WriteString(w, strconv.FormatBool(b))
|
||||
|
||||
case TimestampMillisType:
|
||||
ts := time.Unix(f.Integer/1000, (f.Integer%1000)*int64(time.Millisecond))
|
||||
err = quoteString(w, ts.UTC().Format(TimestampMillisFormat), shouldQuote)
|
||||
|
||||
case TimeType:
|
||||
t, ok := f.Interface.(time.Time)
|
||||
if !ok {
|
||||
err = errors.New("invalid time")
|
||||
break
|
||||
}
|
||||
err = quoteString(w, t.Format(DefTimestampFormat), shouldQuote)
|
||||
|
||||
case DurationType:
|
||||
_, err = fmt.Fprintf(w, "%s", time.Duration(f.Integer))
|
||||
|
||||
case Int64Type, Int32Type, IntType:
|
||||
_, err = io.WriteString(w, strconv.FormatInt(f.Integer, 10))
|
||||
|
||||
case Uint64Type, Uint32Type, UintType:
|
||||
_, err = io.WriteString(w, strconv.FormatUint(uint64(f.Integer), 10))
|
||||
|
||||
case Float64Type, Float32Type:
|
||||
size := 64
|
||||
if f.Type == Float32Type {
|
||||
size = 32
|
||||
}
|
||||
err = quoteString(w, strconv.FormatFloat(f.Float, 'f', -1, size), shouldQuote)
|
||||
|
||||
case BinaryType:
|
||||
b, ok := f.Interface.([]byte)
|
||||
if ok {
|
||||
_, err = fmt.Fprintf(w, "[%X]", b)
|
||||
break
|
||||
}
|
||||
_, err = fmt.Fprintf(w, "[%v]", f.Interface)
|
||||
|
||||
case ArrayType:
|
||||
a := reflect.ValueOf(f.Interface)
|
||||
arr:
|
||||
for i := 0; i < a.Len(); i++ {
|
||||
item := a.Index(i)
|
||||
switch v := item.Interface().(type) {
|
||||
case LogWriter:
|
||||
if err = v.LogWrite(w); err != nil {
|
||||
break arr
|
||||
}
|
||||
case fmt.Stringer:
|
||||
if err = quoteString(w, v.String(), shouldQuote); err != nil {
|
||||
break arr
|
||||
}
|
||||
default:
|
||||
s := fmt.Sprintf("%v", v)
|
||||
if err = quoteString(w, s, shouldQuote); err != nil {
|
||||
break arr
|
||||
}
|
||||
}
|
||||
if _, err = w.Write(Comma); err != nil {
|
||||
break arr
|
||||
}
|
||||
}
|
||||
|
||||
case MapType:
|
||||
a := reflect.ValueOf(f.Interface)
|
||||
iter := a.MapRange()
|
||||
it:
|
||||
for iter.Next() {
|
||||
if _, err = io.WriteString(w, iter.Key().String()); err != nil {
|
||||
break it
|
||||
}
|
||||
if _, err = w.Write(Equals); err != nil {
|
||||
break it
|
||||
}
|
||||
val := iter.Value().Interface()
|
||||
switch v := val.(type) {
|
||||
case LogWriter:
|
||||
if err = v.LogWrite(w); err != nil {
|
||||
break it
|
||||
}
|
||||
case fmt.Stringer:
|
||||
if err = quoteString(w, v.String(), shouldQuote); err != nil {
|
||||
break it
|
||||
}
|
||||
default:
|
||||
s := fmt.Sprintf("%v", v)
|
||||
if err = quoteString(w, s, shouldQuote); err != nil {
|
||||
break it
|
||||
}
|
||||
}
|
||||
if _, err = w.Write(Comma); err != nil {
|
||||
break it
|
||||
}
|
||||
}
|
||||
|
||||
case UnknownType:
|
||||
_, err = fmt.Fprintf(w, "%v", f.Interface)
|
||||
|
||||
default:
|
||||
err = fmt.Errorf("invalid type %d", f.Type)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func nilField(key string) Field {
|
||||
return String(key, "")
|
||||
}
|
||||
|
||||
func fieldForAny(key string, val interface{}) Field {
|
||||
switch v := val.(type) {
|
||||
case LogCloner:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
c := v.LogClone()
|
||||
return Field{Key: key, Type: StructType, Interface: c}
|
||||
case *LogCloner:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
c := (*v).LogClone()
|
||||
return Field{Key: key, Type: StructType, Interface: c}
|
||||
case LogWriter:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Field{Key: key, Type: StructType, Interface: v}
|
||||
case *LogWriter:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Field{Key: key, Type: StructType, Interface: *v}
|
||||
case bool:
|
||||
return Bool(key, v)
|
||||
case *bool:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Bool(key, *v)
|
||||
case float64:
|
||||
return Float64(key, v)
|
||||
case *float64:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Float64(key, *v)
|
||||
case float32:
|
||||
return Float32(key, v)
|
||||
case *float32:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Float32(key, *v)
|
||||
case int:
|
||||
return Int(key, v)
|
||||
case *int:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Int(key, *v)
|
||||
case int64:
|
||||
return Int64(key, v)
|
||||
case *int64:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Int64(key, *v)
|
||||
case int32:
|
||||
return Int32(key, v)
|
||||
case *int32:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Int32(key, *v)
|
||||
case int16:
|
||||
return Int32(key, int32(v))
|
||||
case *int16:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Int32(key, int32(*v))
|
||||
case int8:
|
||||
return Int32(key, int32(v))
|
||||
case *int8:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Int32(key, int32(*v))
|
||||
case string:
|
||||
return String(key, v)
|
||||
case *string:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return String(key, *v)
|
||||
case uint:
|
||||
return Uint(key, v)
|
||||
case *uint:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Uint(key, *v)
|
||||
case uint64:
|
||||
return Uint64(key, v)
|
||||
case *uint64:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Uint64(key, *v)
|
||||
case uint32:
|
||||
return Uint32(key, v)
|
||||
case *uint32:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Uint32(key, *v)
|
||||
case uint16:
|
||||
return Uint32(key, uint32(v))
|
||||
case *uint16:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Uint32(key, uint32(*v))
|
||||
case uint8:
|
||||
return Uint32(key, uint32(v))
|
||||
case *uint8:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Uint32(key, uint32(*v))
|
||||
case []byte:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Field{Key: key, Type: BinaryType, Interface: v}
|
||||
case time.Time:
|
||||
return Time(key, v)
|
||||
case *time.Time:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Time(key, *v)
|
||||
case time.Duration:
|
||||
return Duration(key, v)
|
||||
case *time.Duration:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Duration(key, *v)
|
||||
case error:
|
||||
return NamedErr(key, v)
|
||||
case fmt.Stringer:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Field{Key: key, Type: StringerType, Interface: v}
|
||||
case *fmt.Stringer:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Field{Key: key, Type: StringerType, Interface: *v}
|
||||
default:
|
||||
return Field{Key: key, Type: UnknownType, Interface: val}
|
||||
}
|
||||
}
|
||||
|
||||
// FieldSorter provides sorting of an array of fields by key.
|
||||
type FieldSorter []Field
|
||||
|
||||
func (fs FieldSorter) Len() int { return len(fs) }
|
||||
func (fs FieldSorter) Less(i, j int) bool { return fs[i].Key < fs[j].Key }
|
||||
func (fs FieldSorter) Swap(i, j int) { fs[i], fs[j] = fs[j], fs[i] }
|
||||
110
vendor/github.com/mattermost/logr/v2/fieldapi.go
сгенерированный
поставляемый
Обычный файл
110
vendor/github.com/mattermost/logr/v2/fieldapi.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,110 @@
|
||||
package logr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Any picks the best supported field type based on type of val.
|
||||
// For best performance when passing a struct (or struct pointer),
|
||||
// implement `logr.LogWriter` on the struct, otherwise reflection
|
||||
// will be used to generate a string representation.
|
||||
func Any(key string, val interface{}) Field {
|
||||
return fieldForAny(key, val)
|
||||
}
|
||||
|
||||
// Int64 constructs a field containing a key and Int64 value.
|
||||
func Int64(key string, val int64) Field {
|
||||
return Field{Key: key, Type: Int64Type, Integer: val}
|
||||
}
|
||||
|
||||
// Int32 constructs a field containing a key and Int32 value.
|
||||
func Int32(key string, val int32) Field {
|
||||
return Field{Key: key, Type: Int32Type, Integer: int64(val)}
|
||||
}
|
||||
|
||||
// Int constructs a field containing a key and Int value.
|
||||
func Int(key string, val int) Field {
|
||||
return Field{Key: key, Type: IntType, Integer: int64(val)}
|
||||
}
|
||||
|
||||
// Uint64 constructs a field containing a key and Uint64 value.
|
||||
func Uint64(key string, val uint64) Field {
|
||||
return Field{Key: key, Type: Uint64Type, Integer: int64(val)}
|
||||
}
|
||||
|
||||
// Uint32 constructs a field containing a key and Uint32 value.
|
||||
func Uint32(key string, val uint32) Field {
|
||||
return Field{Key: key, Type: Uint32Type, Integer: int64(val)}
|
||||
}
|
||||
|
||||
// Uint constructs a field containing a key and Uint value.
|
||||
func Uint(key string, val uint) Field {
|
||||
return Field{Key: key, Type: UintType, Integer: int64(val)}
|
||||
}
|
||||
|
||||
// Float64 constructs a field containing a key and Float64 value.
|
||||
func Float64(key string, val float64) Field {
|
||||
return Field{Key: key, Type: Float64Type, Float: val}
|
||||
}
|
||||
|
||||
// Float32 constructs a field containing a key and Float32 value.
|
||||
func Float32(key string, val float32) Field {
|
||||
return Field{Key: key, Type: Float32Type, Float: float64(val)}
|
||||
}
|
||||
|
||||
// String constructs a field containing a key and String value.
|
||||
func String(key string, val string) Field {
|
||||
return Field{Key: key, Type: StringType, String: val}
|
||||
}
|
||||
|
||||
// Stringer constructs a field containing a key and a `fmt.Stringer` value.
|
||||
// The `String` method will be called in lazy fashion.
|
||||
func Stringer(key string, val fmt.Stringer) Field {
|
||||
return Field{Key: key, Type: StringerType, Interface: val}
|
||||
}
|
||||
|
||||
// Err constructs a field containing a default key ("error") and error value.
|
||||
func Err(err error) Field {
|
||||
return NamedErr("error", err)
|
||||
}
|
||||
|
||||
// NamedErr constructs a field containing a key and error value.
|
||||
func NamedErr(key string, err error) Field {
|
||||
return Field{Key: key, Type: ErrorType, Interface: err}
|
||||
}
|
||||
|
||||
// Bool constructs a field containing a key and bool value.
|
||||
func Bool(key string, val bool) Field {
|
||||
var b int64
|
||||
if val {
|
||||
b = 1
|
||||
}
|
||||
return Field{Key: key, Type: BoolType, Integer: b}
|
||||
}
|
||||
|
||||
// Time constructs a field containing a key and time.Time value.
|
||||
func Time(key string, val time.Time) Field {
|
||||
return Field{Key: key, Type: TimeType, Interface: val}
|
||||
}
|
||||
|
||||
// Duration constructs a field containing a key and time.Duration value.
|
||||
func Duration(key string, val time.Duration) Field {
|
||||
return Field{Key: key, Type: DurationType, Integer: int64(val)}
|
||||
}
|
||||
|
||||
// Millis constructs a field containing a key and timestamp value.
|
||||
// The timestamp is expected to be milliseconds since Jan 1, 1970 UTC.
|
||||
func Millis(key string, val int64) Field {
|
||||
return Field{Key: key, Type: TimestampMillisType, Integer: val}
|
||||
}
|
||||
|
||||
// Array constructs a field containing a key and array value.
|
||||
func Array(key string, val interface{}) Field {
|
||||
return Field{Key: key, Type: ArrayType, Interface: val}
|
||||
}
|
||||
|
||||
// Map constructs a field containing a key and map value.
|
||||
func Map(key string, val interface{}) Field {
|
||||
return Field{Key: key, Type: MapType, Interface: val}
|
||||
}
|
||||
10
vendor/github.com/mattermost/logr/v2/filter.go
сгенерированный
поставляемый
Обычный файл
10
vendor/github.com/mattermost/logr/v2/filter.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,10 @@
|
||||
package logr
|
||||
|
||||
// Filter allows targets to determine which Level(s) are active
|
||||
// for logging and which Level(s) require a stack trace to be output.
|
||||
// A default implementation using "panic, fatal..." is provided, and
|
||||
// a more flexible alternative implementation is also provided that
|
||||
// allows any number of custom levels.
|
||||
type Filter interface {
|
||||
GetEnabledLevel(level Level) (Level, bool)
|
||||
}
|
||||
47
vendor/github.com/mattermost/logr/v2/filtercustom.go
сгенерированный
поставляемый
Обычный файл
47
vendor/github.com/mattermost/logr/v2/filtercustom.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,47 @@
|
||||
package logr
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// CustomFilter allows targets to enable logging via a list of discrete levels.
|
||||
type CustomFilter struct {
|
||||
mux sync.RWMutex
|
||||
levels map[LevelID]Level
|
||||
}
|
||||
|
||||
// NewCustomFilter creates a filter supporting discrete log levels.
|
||||
func NewCustomFilter(levels ...Level) *CustomFilter {
|
||||
filter := &CustomFilter{}
|
||||
filter.Add(levels...)
|
||||
return filter
|
||||
}
|
||||
|
||||
// GetEnabledLevel returns the Level with the specified Level.ID and whether the level
|
||||
// is enabled for this filter.
|
||||
func (cf *CustomFilter) GetEnabledLevel(level Level) (Level, bool) {
|
||||
cf.mux.RLock()
|
||||
defer cf.mux.RUnlock()
|
||||
levelEnabled, ok := cf.levels[level.ID]
|
||||
|
||||
if ok && levelEnabled.Name == "" {
|
||||
levelEnabled.Name = level.Name
|
||||
}
|
||||
|
||||
return levelEnabled, ok
|
||||
}
|
||||
|
||||
// Add adds one or more levels to the list. Adding a level enables logging for
|
||||
// that level on any targets using this CustomFilter.
|
||||
func (cf *CustomFilter) Add(levels ...Level) {
|
||||
cf.mux.Lock()
|
||||
defer cf.mux.Unlock()
|
||||
|
||||
if cf.levels == nil {
|
||||
cf.levels = make(map[LevelID]Level)
|
||||
}
|
||||
|
||||
for _, s := range levels {
|
||||
cf.levels[s.ID] = s
|
||||
}
|
||||
}
|
||||
65
vendor/github.com/mattermost/logr/v2/filterstd.go
сгенерированный
поставляемый
Обычный файл
65
vendor/github.com/mattermost/logr/v2/filterstd.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,65 @@
|
||||
package logr
|
||||
|
||||
// StdFilter allows targets to filter via classic log levels where any level
|
||||
// beyond a certain verbosity/severity is enabled.
|
||||
type StdFilter struct {
|
||||
Lvl Level
|
||||
Stacktrace Level
|
||||
}
|
||||
|
||||
// GetEnabledLevel returns the Level with the specified Level.ID and whether the level
|
||||
// is enabled for this filter.
|
||||
func (lt StdFilter) GetEnabledLevel(level Level) (Level, bool) {
|
||||
enabled := level.ID <= lt.Lvl.ID
|
||||
var levelEnabled Level
|
||||
|
||||
if enabled {
|
||||
switch level.ID {
|
||||
case Panic.ID:
|
||||
levelEnabled = Panic
|
||||
case Fatal.ID:
|
||||
levelEnabled = Fatal
|
||||
case Error.ID:
|
||||
levelEnabled = Error
|
||||
case Warn.ID:
|
||||
levelEnabled = Warn
|
||||
case Info.ID:
|
||||
levelEnabled = Info
|
||||
case Debug.ID:
|
||||
levelEnabled = Debug
|
||||
case Trace.ID:
|
||||
levelEnabled = Trace
|
||||
default:
|
||||
levelEnabled = level
|
||||
}
|
||||
}
|
||||
return levelEnabled, enabled
|
||||
}
|
||||
|
||||
// IsEnabled returns true if the specified Level is at or above this verbosity. Also
|
||||
// determines if a stack trace is required.
|
||||
func (lt StdFilter) IsEnabled(level Level) bool {
|
||||
return level.ID <= lt.Lvl.ID
|
||||
}
|
||||
|
||||
// IsStacktraceEnabled returns true if the specified Level requires a stack trace.
|
||||
func (lt StdFilter) IsStacktraceEnabled(level Level) bool {
|
||||
return level.ID <= lt.Stacktrace.ID
|
||||
}
|
||||
|
||||
var (
|
||||
// Panic is the highest level of severity.
|
||||
Panic = Level{ID: 0, Name: "panic", Color: Red}
|
||||
// Fatal designates a catastrophic error.
|
||||
Fatal = Level{ID: 1, Name: "fatal", Color: Red}
|
||||
// Error designates a serious but possibly recoverable error.
|
||||
Error = Level{ID: 2, Name: "error", Color: Red}
|
||||
// Warn designates non-critical error.
|
||||
Warn = Level{ID: 3, Name: "warn", Color: Yellow}
|
||||
// Info designates information regarding application events.
|
||||
Info = Level{ID: 4, Name: "info", Color: Cyan}
|
||||
// Debug designates verbose information typically used for debugging.
|
||||
Debug = Level{ID: 5, Name: "debug", Color: NoColor}
|
||||
// Trace designates the highest verbosity of log output.
|
||||
Trace = Level{ID: 6, Name: "trace", Color: NoColor}
|
||||
)
|
||||
184
vendor/github.com/mattermost/logr/v2/formatter.go
сгенерированный
поставляемый
Обычный файл
184
vendor/github.com/mattermost/logr/v2/formatter.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,184 @@
|
||||
package logr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"runtime"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Formatter turns a LogRec into a formatted string.
|
||||
type Formatter interface {
|
||||
// IsStacktraceNeeded returns true if this formatter requires a stacktrace to be
|
||||
// generated for each LogRecord. Enabling features such as `Caller` field require
|
||||
// a stacktrace.
|
||||
IsStacktraceNeeded() bool
|
||||
|
||||
// Format converts a log record to bytes. If buf is not nil then it will be
|
||||
// be filled with the formatted results, otherwise a new buffer will be allocated.
|
||||
Format(rec *LogRec, level Level, buf *bytes.Buffer) (*bytes.Buffer, error)
|
||||
}
|
||||
|
||||
const (
|
||||
// DefTimestampFormat is the default time stamp format used by Plain formatter and others.
|
||||
DefTimestampFormat = "2006-01-02 15:04:05.000 Z07:00"
|
||||
|
||||
// TimestampMillisFormat is the format for logging milliseconds UTC
|
||||
TimestampMillisFormat = "Jan _2 15:04:05.000"
|
||||
)
|
||||
|
||||
type Writer struct {
|
||||
io.Writer
|
||||
}
|
||||
|
||||
func (w Writer) Writes(elems ...[]byte) (int, error) {
|
||||
var count int
|
||||
for _, e := range elems {
|
||||
if c, err := w.Write(e); err != nil {
|
||||
return count + c, err
|
||||
} else {
|
||||
count += c
|
||||
}
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// DefaultFormatter is the default formatter, outputting only text with
|
||||
// no colors and a space delimiter. Use `format.Plain` instead.
|
||||
type DefaultFormatter struct {
|
||||
}
|
||||
|
||||
// IsStacktraceNeeded always returns false for default formatter since the
|
||||
// `Caller` field is not supported.
|
||||
func (p *DefaultFormatter) IsStacktraceNeeded() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Format converts a log record to bytes.
|
||||
func (p *DefaultFormatter) Format(rec *LogRec, level Level, buf *bytes.Buffer) (*bytes.Buffer, error) {
|
||||
if buf == nil {
|
||||
buf = &bytes.Buffer{}
|
||||
}
|
||||
timestampFmt := DefTimestampFormat
|
||||
|
||||
buf.WriteString(rec.Time().Format(timestampFmt))
|
||||
buf.Write(Space)
|
||||
|
||||
buf.WriteString(level.Name)
|
||||
buf.Write(Space)
|
||||
|
||||
buf.WriteString(rec.Msg())
|
||||
buf.Write(Space)
|
||||
|
||||
fields := rec.Fields()
|
||||
if len(fields) > 0 {
|
||||
if err := WriteFields(buf, fields, Space, NoColor); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if level.Stacktrace {
|
||||
frames := rec.StackFrames()
|
||||
if len(frames) > 0 {
|
||||
buf.Write(Newline)
|
||||
if err := WriteStacktrace(buf, rec.StackFrames()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
buf.Write(Newline)
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// WriteFields writes zero or more name value pairs to the io.Writer.
|
||||
// The pairs output in key=value format with optional separator between fields.
|
||||
func WriteFields(w io.Writer, fields []Field, separator []byte, color Color) error {
|
||||
ws := Writer{w}
|
||||
|
||||
sep := []byte{}
|
||||
for _, field := range fields {
|
||||
if err := writeField(ws, field, sep, color); err != nil {
|
||||
return err
|
||||
}
|
||||
sep = separator
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeField(ws Writer, field Field, sep []byte, color Color) error {
|
||||
if len(sep) != 0 {
|
||||
if _, err := ws.Write(sep); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := WriteWithColor(ws, field.Key, color); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := ws.Write(Equals); err != nil {
|
||||
return err
|
||||
}
|
||||
return field.ValueString(ws, shouldQuote)
|
||||
}
|
||||
|
||||
// shouldQuote returns true if val contains any characters that might be unsafe
|
||||
// when injecting log output into an aggregator, viewer or report.
|
||||
func shouldQuote(val string) bool {
|
||||
for _, c := range val {
|
||||
if !((c >= '0' && c <= '9') ||
|
||||
(c >= 'a' && c <= 'z') ||
|
||||
(c >= 'A' && c <= 'Z') ||
|
||||
c == '-' || c == '.' || c == '_' || c == '/' || c == '@' || c == '^' || c == '+') {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// WriteStacktrace formats and outputs a stack trace to an io.Writer.
|
||||
func WriteStacktrace(w io.Writer, frames []runtime.Frame) error {
|
||||
ws := Writer{w}
|
||||
for _, frame := range frames {
|
||||
if frame.Function != "" {
|
||||
if _, err := ws.Writes(Space, Space, []byte(frame.Function), Newline); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if frame.File != "" {
|
||||
s := strconv.FormatInt(int64(frame.Line), 10)
|
||||
if _, err := ws.Writes([]byte{' ', ' ', ' ', ' ', ' ', ' '}, []byte(frame.File), Colon, []byte(s), Newline); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteWithColor outputs a string with the specified ANSI color.
|
||||
func WriteWithColor(w io.Writer, s string, color Color) error {
|
||||
var err error
|
||||
|
||||
writer := func(buf []byte) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = w.Write(buf)
|
||||
}
|
||||
|
||||
if color != NoColor {
|
||||
writer(AnsiColorPrefix)
|
||||
writer([]byte(strconv.FormatInt(int64(color), 10)))
|
||||
writer(AnsiColorSuffix)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
_, err = io.WriteString(w, s)
|
||||
}
|
||||
|
||||
if color != NoColor {
|
||||
writer(AnsiColorPrefix)
|
||||
writer([]byte(strconv.FormatInt(int64(NoColor), 10)))
|
||||
writer(AnsiColorSuffix)
|
||||
}
|
||||
return err
|
||||
}
|
||||
152
vendor/github.com/mattermost/logr/v2/formatters/gelf.go
сгенерированный
поставляемый
Обычный файл
152
vendor/github.com/mattermost/logr/v2/formatters/gelf.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,152 @@
|
||||
package formatters
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/francoispqt/gojay"
|
||||
"github.com/mattermost/logr/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
GelfVersion = "1.1"
|
||||
GelfVersionKey = "version"
|
||||
GelfHostKey = "host"
|
||||
GelfShortKey = "short_message"
|
||||
GelfFullKey = "full_message"
|
||||
GelfTimestampKey = "timestamp"
|
||||
GelfLevelKey = "level"
|
||||
)
|
||||
|
||||
// Gelf formats log records as GELF rcords (https://docs.graylog.org/en/4.0/pages/gelf.html).
|
||||
type Gelf struct {
|
||||
// Hostname allows a custom hostname, otherwise os.Hostname is used
|
||||
Hostname string `json:"hostname"`
|
||||
|
||||
// EnableCaller enables output of the file and line number that emitted a log record.
|
||||
EnableCaller bool `json:"enable_caller"`
|
||||
|
||||
// FieldSorter allows custom sorting for the context fields.
|
||||
FieldSorter func(fields []logr.Field) []logr.Field `json:"-"`
|
||||
}
|
||||
|
||||
func (g *Gelf) CheckValid() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsStacktraceNeeded returns true if a stacktrace is needed so we can output the `Caller` field.
|
||||
func (g *Gelf) IsStacktraceNeeded() bool {
|
||||
return g.EnableCaller
|
||||
}
|
||||
|
||||
// Format converts a log record to bytes in GELF format.
|
||||
func (g *Gelf) Format(rec *logr.LogRec, level logr.Level, buf *bytes.Buffer) (*bytes.Buffer, error) {
|
||||
if buf == nil {
|
||||
buf = &bytes.Buffer{}
|
||||
}
|
||||
enc := gojay.BorrowEncoder(buf)
|
||||
defer func() {
|
||||
enc.Release()
|
||||
}()
|
||||
|
||||
gr := gelfRecord{
|
||||
LogRec: rec,
|
||||
Gelf: g,
|
||||
level: level,
|
||||
sorter: g.FieldSorter,
|
||||
}
|
||||
|
||||
err := enc.EncodeObject(gr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buf.WriteByte(0)
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
type gelfRecord struct {
|
||||
*logr.LogRec
|
||||
*Gelf
|
||||
level logr.Level
|
||||
sorter func(fields []logr.Field) []logr.Field
|
||||
}
|
||||
|
||||
// MarshalJSONObject encodes the LogRec as JSON.
|
||||
func (gr gelfRecord) MarshalJSONObject(enc *gojay.Encoder) {
|
||||
enc.AddStringKey(GelfVersionKey, GelfVersion)
|
||||
enc.AddStringKey(GelfHostKey, gr.getHostname())
|
||||
enc.AddStringKey(GelfShortKey, gr.Msg())
|
||||
|
||||
if gr.level.Stacktrace {
|
||||
frames := gr.StackFrames()
|
||||
if len(frames) != 0 {
|
||||
var sbuf strings.Builder
|
||||
for _, frame := range frames {
|
||||
fmt.Fprintf(&sbuf, "%s\n %s:%d\n", frame.Function, frame.File, frame.Line)
|
||||
}
|
||||
enc.AddStringKey(GelfFullKey, sbuf.String())
|
||||
}
|
||||
}
|
||||
|
||||
secs := float64(gr.Time().UTC().Unix())
|
||||
millis := float64(gr.Time().Nanosecond() / 1000000)
|
||||
ts := secs + (millis / 1000)
|
||||
enc.AddFloat64Key(GelfTimestampKey, ts)
|
||||
|
||||
enc.AddUint32Key(GelfLevelKey, uint32(gr.level.ID))
|
||||
|
||||
var fields []logr.Field
|
||||
if gr.EnableCaller {
|
||||
caller := logr.Field{
|
||||
Key: "_caller",
|
||||
Type: logr.StringType,
|
||||
String: gr.LogRec.Caller(),
|
||||
}
|
||||
fields = append(fields, caller)
|
||||
}
|
||||
|
||||
fields = append(fields, gr.Fields()...)
|
||||
if gr.sorter != nil {
|
||||
fields = gr.sorter(fields)
|
||||
}
|
||||
|
||||
if len(fields) > 0 {
|
||||
for _, field := range fields {
|
||||
if !strings.HasPrefix("_", field.Key) {
|
||||
field.Key = "_" + field.Key
|
||||
}
|
||||
if err := encodeField(enc, field); err != nil {
|
||||
enc.AddStringKey(field.Key, fmt.Sprintf("<error encoding field: %v>", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IsNil returns true if the gelf record pointer is nil.
|
||||
func (gr gelfRecord) IsNil() bool {
|
||||
return gr.LogRec == nil
|
||||
}
|
||||
|
||||
func (g *Gelf) getHostname() string {
|
||||
if g.Hostname != "" {
|
||||
return g.Hostname
|
||||
}
|
||||
h, err := os.Hostname()
|
||||
if err == nil {
|
||||
return h
|
||||
}
|
||||
|
||||
// get the egress IP by fake dialing any address. UDP ensures no dial.
|
||||
conn, err := net.Dial("udp", "8.8.8.8:80")
|
||||
if err != nil {
|
||||
return "unknown"
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
local := conn.LocalAddr().(*net.UDPAddr)
|
||||
return local.IP.String()
|
||||
}
|
||||
273
vendor/github.com/mattermost/logr/v2/formatters/json.go
сгенерированный
поставляемый
Обычный файл
273
vendor/github.com/mattermost/logr/v2/formatters/json.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,273 @@
|
||||
package formatters
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/francoispqt/gojay"
|
||||
"github.com/mattermost/logr/v2"
|
||||
)
|
||||
|
||||
// JSON formats log records as JSON.
|
||||
type JSON struct {
|
||||
// DisableTimestamp disables output of timestamp field.
|
||||
DisableTimestamp bool `json:"disable_timestamp"`
|
||||
// DisableLevel disables output of level field.
|
||||
DisableLevel bool `json:"disable_level"`
|
||||
// DisableMsg disables output of msg field.
|
||||
DisableMsg bool `json:"disable_msg"`
|
||||
// DisableFields disables output of all fields.
|
||||
DisableFields bool `json:"disable_fields"`
|
||||
// DisableStacktrace disables output of stack trace.
|
||||
DisableStacktrace bool `json:"disable_stacktrace"`
|
||||
// EnableCaller enables output of the file and line number that emitted a log record.
|
||||
EnableCaller bool `json:"enable_caller"`
|
||||
|
||||
// TimestampFormat is an optional format for timestamps. If empty
|
||||
// then DefTimestampFormat is used.
|
||||
TimestampFormat string `json:"timestamp_format"`
|
||||
|
||||
// KeyTimestamp overrides the timestamp field key name.
|
||||
KeyTimestamp string `json:"key_timestamp"`
|
||||
|
||||
// KeyLevel overrides the level field key name.
|
||||
KeyLevel string `json:"key_level"`
|
||||
|
||||
// KeyMsg overrides the msg field key name.
|
||||
KeyMsg string `json:"key_msg"`
|
||||
|
||||
// KeyGroupFields when not empty will group all context fields
|
||||
// under this key.
|
||||
KeyGroupFields string `json:"key_group_fields"`
|
||||
|
||||
// KeyStacktrace overrides the stacktrace field key name.
|
||||
KeyStacktrace string `json:"key_stacktrace"`
|
||||
|
||||
// KeyCaller overrides the caller field key name.
|
||||
KeyCaller string `json:"key_caller"`
|
||||
|
||||
// FieldSorter allows custom sorting of the fields. If nil then
|
||||
// no sorting is done.
|
||||
FieldSorter func(fields []logr.Field) []logr.Field `json:"-"`
|
||||
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
func (j *JSON) CheckValid() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsStacktraceNeeded returns true if a stacktrace is needed so we can output the `Caller` field.
|
||||
func (j *JSON) IsStacktraceNeeded() bool {
|
||||
return j.EnableCaller
|
||||
}
|
||||
|
||||
// Format converts a log record to bytes in JSON format.
|
||||
func (j *JSON) Format(rec *logr.LogRec, level logr.Level, buf *bytes.Buffer) (*bytes.Buffer, error) {
|
||||
j.once.Do(j.applyDefaultKeyNames)
|
||||
|
||||
if buf == nil {
|
||||
buf = &bytes.Buffer{}
|
||||
}
|
||||
enc := gojay.BorrowEncoder(buf)
|
||||
defer func() {
|
||||
enc.Release()
|
||||
}()
|
||||
|
||||
jlr := JSONLogRec{
|
||||
LogRec: rec,
|
||||
JSON: j,
|
||||
level: level,
|
||||
sorter: j.FieldSorter,
|
||||
}
|
||||
|
||||
err := enc.EncodeObject(jlr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf.WriteByte('\n')
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (j *JSON) applyDefaultKeyNames() {
|
||||
if j.KeyTimestamp == "" {
|
||||
j.KeyTimestamp = "timestamp"
|
||||
}
|
||||
if j.KeyLevel == "" {
|
||||
j.KeyLevel = "level"
|
||||
}
|
||||
if j.KeyMsg == "" {
|
||||
j.KeyMsg = "msg"
|
||||
}
|
||||
if j.KeyStacktrace == "" {
|
||||
j.KeyStacktrace = "stacktrace"
|
||||
}
|
||||
if j.KeyCaller == "" {
|
||||
j.KeyCaller = "caller"
|
||||
}
|
||||
}
|
||||
|
||||
// JSONLogRec decorates a LogRec adding JSON encoding.
|
||||
type JSONLogRec struct {
|
||||
*logr.LogRec
|
||||
*JSON
|
||||
level logr.Level
|
||||
sorter func(fields []logr.Field) []logr.Field
|
||||
}
|
||||
|
||||
// MarshalJSONObject encodes the LogRec as JSON.
|
||||
func (jlr JSONLogRec) MarshalJSONObject(enc *gojay.Encoder) {
|
||||
if !jlr.DisableTimestamp {
|
||||
timestampFmt := jlr.TimestampFormat
|
||||
if timestampFmt == "" {
|
||||
timestampFmt = logr.DefTimestampFormat
|
||||
}
|
||||
time := jlr.Time()
|
||||
enc.AddTimeKey(jlr.KeyTimestamp, &time, timestampFmt)
|
||||
}
|
||||
if !jlr.DisableLevel {
|
||||
enc.AddStringKey(jlr.KeyLevel, jlr.level.Name)
|
||||
}
|
||||
if !jlr.DisableMsg {
|
||||
enc.AddStringKey(jlr.KeyMsg, jlr.Msg())
|
||||
}
|
||||
if jlr.EnableCaller {
|
||||
enc.AddStringKey(jlr.KeyCaller, jlr.Caller())
|
||||
}
|
||||
if !jlr.DisableFields {
|
||||
fields := jlr.Fields()
|
||||
if jlr.sorter != nil {
|
||||
fields = jlr.sorter(fields)
|
||||
}
|
||||
if jlr.KeyGroupFields != "" {
|
||||
enc.AddObjectKey(jlr.KeyGroupFields, FieldArray(fields))
|
||||
} else {
|
||||
if len(fields) > 0 {
|
||||
for _, field := range fields {
|
||||
field = jlr.prefixCollision(field)
|
||||
if err := encodeField(enc, field); err != nil {
|
||||
enc.AddStringKey(field.Key, "<error encoding field: "+err.Error()+">")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if jlr.level.Stacktrace && !jlr.DisableStacktrace {
|
||||
frames := jlr.StackFrames()
|
||||
if len(frames) > 0 {
|
||||
enc.AddArrayKey(jlr.KeyStacktrace, stackFrames(frames))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IsNil returns true if the LogRec pointer is nil.
|
||||
func (rec JSONLogRec) IsNil() bool {
|
||||
return rec.LogRec == nil
|
||||
}
|
||||
|
||||
func (rec JSONLogRec) prefixCollision(field logr.Field) logr.Field {
|
||||
switch field.Key {
|
||||
case rec.KeyTimestamp, rec.KeyLevel, rec.KeyMsg, rec.KeyStacktrace:
|
||||
f := field
|
||||
f.Key = "_" + field.Key
|
||||
return rec.prefixCollision(f)
|
||||
}
|
||||
return field
|
||||
}
|
||||
|
||||
type stackFrames []runtime.Frame
|
||||
|
||||
// MarshalJSONArray encodes stackFrames slice as JSON.
|
||||
func (s stackFrames) MarshalJSONArray(enc *gojay.Encoder) {
|
||||
for _, frame := range s {
|
||||
enc.AddObject(stackFrame(frame))
|
||||
}
|
||||
}
|
||||
|
||||
// IsNil returns true if stackFrames is empty slice.
|
||||
func (s stackFrames) IsNil() bool {
|
||||
return len(s) == 0
|
||||
}
|
||||
|
||||
type stackFrame runtime.Frame
|
||||
|
||||
// MarshalJSONArray encodes stackFrame as JSON.
|
||||
func (f stackFrame) MarshalJSONObject(enc *gojay.Encoder) {
|
||||
enc.AddStringKey("Function", f.Function)
|
||||
enc.AddStringKey("File", f.File)
|
||||
enc.AddIntKey("Line", f.Line)
|
||||
}
|
||||
|
||||
func (f stackFrame) IsNil() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type FieldArray []logr.Field
|
||||
|
||||
// MarshalJSONObject encodes Fields map to JSON.
|
||||
func (fa FieldArray) MarshalJSONObject(enc *gojay.Encoder) {
|
||||
for _, fld := range fa {
|
||||
if err := encodeField(enc, fld); err != nil {
|
||||
enc.AddStringKey(fld.Key, "<error encoding field: "+err.Error()+">")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IsNil returns true if map is nil.
|
||||
func (fa FieldArray) IsNil() bool {
|
||||
return fa == nil
|
||||
}
|
||||
|
||||
func encodeField(enc *gojay.Encoder, field logr.Field) error {
|
||||
// first check if the value has a marshaller already.
|
||||
switch vt := field.Interface.(type) {
|
||||
case gojay.MarshalerJSONObject:
|
||||
enc.AddObjectKey(field.Key, vt)
|
||||
return nil
|
||||
case gojay.MarshalerJSONArray:
|
||||
enc.AddArrayKey(field.Key, vt)
|
||||
return nil
|
||||
}
|
||||
|
||||
switch field.Type {
|
||||
case logr.StringType:
|
||||
enc.AddStringKey(field.Key, field.String)
|
||||
|
||||
case logr.BoolType:
|
||||
var b bool
|
||||
if field.Integer != 0 {
|
||||
b = true
|
||||
}
|
||||
enc.AddBoolKey(field.Key, b)
|
||||
|
||||
case logr.StructType, logr.ArrayType, logr.MapType, logr.UnknownType:
|
||||
b, err := json.Marshal(field.Interface)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
embed := gojay.EmbeddedJSON(b)
|
||||
enc.AddEmbeddedJSONKey(field.Key, &embed)
|
||||
|
||||
case logr.StringerType, logr.ErrorType, logr.TimestampMillisType, logr.TimeType, logr.DurationType, logr.BinaryType:
|
||||
var buf strings.Builder
|
||||
_ = field.ValueString(&buf, nil)
|
||||
enc.AddStringKey(field.Key, buf.String())
|
||||
|
||||
case logr.Int64Type, logr.Int32Type, logr.IntType:
|
||||
enc.AddInt64Key(field.Key, field.Integer)
|
||||
|
||||
case logr.Uint64Type, logr.Uint32Type, logr.UintType:
|
||||
enc.AddUint64Key(field.Key, uint64(field.Integer))
|
||||
|
||||
case logr.Float64Type, logr.Float32Type:
|
||||
enc.AddFloat64Key(field.Key, field.Float)
|
||||
|
||||
default:
|
||||
return fmt.Errorf("invalid field type: %d", field.Type)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
146
vendor/github.com/mattermost/logr/v2/formatters/plain.go
сгенерированный
поставляемый
Обычный файл
146
vendor/github.com/mattermost/logr/v2/formatters/plain.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,146 @@
|
||||
package formatters
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/mattermost/logr/v2"
|
||||
)
|
||||
|
||||
// Plain is the simplest formatter, outputting only text with
|
||||
// no colors.
|
||||
type Plain struct {
|
||||
// DisableTimestamp disables output of timestamp field.
|
||||
DisableTimestamp bool `json:"disable_timestamp"`
|
||||
// DisableLevel disables output of level field.
|
||||
DisableLevel bool `json:"disable_level"`
|
||||
// DisableMsg disables output of msg field.
|
||||
DisableMsg bool `json:"disable_msg"`
|
||||
// DisableFields disables output of all fields.
|
||||
DisableFields bool `json:"disable_fields"`
|
||||
// DisableStacktrace disables output of stack trace.
|
||||
DisableStacktrace bool `json:"disable_stacktrace"`
|
||||
// EnableCaller enables output of the file and line number that emitted a log record.
|
||||
EnableCaller bool `json:"enable_caller"`
|
||||
|
||||
// Delim is an optional delimiter output between each log field.
|
||||
// Defaults to a single space.
|
||||
Delim string `json:"delim"`
|
||||
|
||||
// MinLevelLen sets the minimum level name length. If the level name is less
|
||||
// than the minimum it will be padded with spaces.
|
||||
MinLevelLen int `json:"min_level_len"`
|
||||
|
||||
// MinMessageLen sets the minimum msg length. If the msg text is less
|
||||
// than the minimum it will be padded with spaces.
|
||||
MinMessageLen int `json:"min_msg_len"`
|
||||
|
||||
// TimestampFormat is an optional format for timestamps. If empty
|
||||
// then DefTimestampFormat is used.
|
||||
TimestampFormat string `json:"timestamp_format"`
|
||||
|
||||
// LineEnd sets the end of line character(s). Defaults to '\n'.
|
||||
LineEnd string `json:"line_end"`
|
||||
|
||||
// EnableColor sets whether output should include color.
|
||||
EnableColor bool `json:"enable_color"`
|
||||
}
|
||||
|
||||
func (p *Plain) CheckValid() error {
|
||||
if p.MinMessageLen < 0 || p.MinMessageLen > 1024 {
|
||||
return fmt.Errorf("min_msg_len is invalid(%d)", p.MinMessageLen)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsStacktraceNeeded returns true if a stacktrace is needed so we can output the `Caller` field.
|
||||
func (p *Plain) IsStacktraceNeeded() bool {
|
||||
return p.EnableCaller
|
||||
}
|
||||
|
||||
// Format converts a log record to bytes.
|
||||
func (p *Plain) Format(rec *logr.LogRec, level logr.Level, buf *bytes.Buffer) (*bytes.Buffer, error) {
|
||||
delim := p.Delim
|
||||
if delim == "" {
|
||||
delim = " "
|
||||
}
|
||||
if buf == nil {
|
||||
buf = &bytes.Buffer{}
|
||||
}
|
||||
|
||||
timestampFmt := p.TimestampFormat
|
||||
if timestampFmt == "" {
|
||||
timestampFmt = logr.DefTimestampFormat
|
||||
}
|
||||
|
||||
color := logr.NoColor
|
||||
if p.EnableColor {
|
||||
color = level.Color
|
||||
}
|
||||
|
||||
if !p.DisableLevel {
|
||||
_ = logr.WriteWithColor(buf, level.Name, color)
|
||||
count := len(level.Name)
|
||||
if p.MinLevelLen > count {
|
||||
_, _ = buf.WriteString(strings.Repeat(" ", p.MinLevelLen-count))
|
||||
}
|
||||
buf.WriteString(delim)
|
||||
}
|
||||
|
||||
if !p.DisableTimestamp {
|
||||
var arr [128]byte
|
||||
tbuf := rec.Time().AppendFormat(arr[:0], timestampFmt)
|
||||
buf.WriteByte('[')
|
||||
buf.Write(tbuf)
|
||||
buf.WriteByte(']')
|
||||
buf.WriteString(delim)
|
||||
}
|
||||
|
||||
if !p.DisableMsg {
|
||||
count, _ := buf.WriteString(rec.Msg())
|
||||
if p.MinMessageLen > count {
|
||||
_, _ = buf.WriteString(strings.Repeat(" ", p.MinMessageLen-count))
|
||||
}
|
||||
_, _ = buf.WriteString(delim)
|
||||
}
|
||||
|
||||
var fields []logr.Field
|
||||
|
||||
if p.EnableCaller {
|
||||
fld := logr.Field{
|
||||
Key: "caller",
|
||||
Type: logr.StringType,
|
||||
String: rec.Caller(),
|
||||
}
|
||||
fields = append(fields, fld)
|
||||
}
|
||||
|
||||
if !p.DisableFields {
|
||||
fields = append(fields, rec.Fields()...)
|
||||
}
|
||||
|
||||
if len(fields) > 0 {
|
||||
if err := logr.WriteFields(buf, fields, logr.Space, color); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if level.Stacktrace && !p.DisableStacktrace {
|
||||
frames := rec.StackFrames()
|
||||
if len(frames) > 0 {
|
||||
buf.WriteString("\n")
|
||||
if err := logr.WriteStacktrace(buf, rec.StackFrames()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if p.LineEnd == "" {
|
||||
buf.WriteString("\n")
|
||||
} else {
|
||||
buf.WriteString(p.LineEnd)
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
6
vendor/github.com/mattermost/logr/go.mod → vendor/github.com/mattermost/logr/v2/go.mod
сгенерированный
поставляемый
6
vendor/github.com/mattermost/logr/go.mod → vendor/github.com/mattermost/logr/v2/go.mod
сгенерированный
поставляемый
@@ -1,11 +1,11 @@
|
||||
module github.com/mattermost/logr
|
||||
module github.com/mattermost/logr/v2
|
||||
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
github.com/francoispqt/gojay v1.2.13
|
||||
github.com/stretchr/testify v1.2.2
|
||||
github.com/wiggin77/cfg v1.0.2
|
||||
github.com/stretchr/testify v1.4.0
|
||||
github.com/wiggin77/merror v1.0.2
|
||||
github.com/wiggin77/srslog v1.0.1
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||
)
|
||||
8
vendor/github.com/mattermost/logr/go.sum → vendor/github.com/mattermost/logr/v2/go.sum
сгенерированный
поставляемый
8
vendor/github.com/mattermost/logr/go.sum → vendor/github.com/mattermost/logr/v2/go.sum
сгенерированный
поставляемый
@@ -15,6 +15,7 @@ github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBT
|
||||
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
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/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
@@ -93,15 +94,18 @@ github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYED
|
||||
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
|
||||
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
|
||||
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
||||
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
|
||||
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
|
||||
github.com/wiggin77/cfg v1.0.2 h1:NBUX+iJRr+RTncTqTNvajHwzduqbhCQjEqxLHr6Fk7A=
|
||||
github.com/wiggin77/cfg v1.0.2/go.mod h1:b3gotba2e5bXTqTW48DwIFoLc+4lWKP7WPi/CdvZ4aE=
|
||||
github.com/wiggin77/merror v1.0.2 h1:V0nH9eFp64ASyaXC+pB5WpvBoCg7NUwvaCSKdzlcHqw=
|
||||
github.com/wiggin77/merror v1.0.2/go.mod h1:uQTcIU0Z6jRK4OwqganPYerzQxSFJ4GSHM3aurxxQpg=
|
||||
github.com/wiggin77/srslog v1.0.1 h1:gA2XjSMy3DrRdX9UqLuDtuVAAshb8bE1NhX1YK0Qe+8=
|
||||
github.com/wiggin77/srslog v1.0.1/go.mod h1:fehkyYDq1QfuYn60TDPu9YdY2bB85VUW2mvN1WynEls=
|
||||
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
||||
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
|
||||
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
|
||||
34
vendor/github.com/mattermost/logr/v2/level.go
сгенерированный
поставляемый
Обычный файл
34
vendor/github.com/mattermost/logr/v2/level.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,34 @@
|
||||
package logr
|
||||
|
||||
var AnsiColorPrefix = []byte("\u001b[")
|
||||
var AnsiColorSuffix = []byte("m")
|
||||
|
||||
// Color for formatters that support color output.
|
||||
type Color uint8
|
||||
|
||||
const (
|
||||
NoColor Color = 0
|
||||
Red Color = 31
|
||||
Green Color = 32
|
||||
Yellow Color = 33
|
||||
Blue Color = 34
|
||||
Magenta Color = 35
|
||||
Cyan Color = 36
|
||||
White Color = 37
|
||||
)
|
||||
|
||||
// LevelID is the unique id of each level.
|
||||
type LevelID uint
|
||||
|
||||
// Level provides a mechanism to enable/disable specific log lines.
|
||||
type Level struct {
|
||||
ID LevelID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Stacktrace bool `json:"stacktrace,omitempty"`
|
||||
Color Color `json:"color,omitempty"`
|
||||
}
|
||||
|
||||
// String returns the name of this level.
|
||||
func (level Level) String() string {
|
||||
return level.Name
|
||||
}
|
||||
0
vendor/github.com/mattermost/logr/levelcache.go → vendor/github.com/mattermost/logr/v2/levelcache.go
сгенерированный
поставляемый
0
vendor/github.com/mattermost/logr/levelcache.go → vendor/github.com/mattermost/logr/v2/levelcache.go
сгенерированный
поставляемый
99
vendor/github.com/mattermost/logr/v2/logger.go
сгенерированный
поставляемый
Обычный файл
99
vendor/github.com/mattermost/logr/v2/logger.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,99 @@
|
||||
package logr
|
||||
|
||||
import "log"
|
||||
|
||||
// Logger provides context for logging via fields.
|
||||
type Logger struct {
|
||||
lgr *Logr
|
||||
fields []Field
|
||||
}
|
||||
|
||||
// Logr returns the `Logr` instance that created this `Logger`.
|
||||
func (logger Logger) Logr() *Logr {
|
||||
return logger.lgr
|
||||
}
|
||||
|
||||
// With creates a new `Logger` with any existing fields plus the new ones.
|
||||
func (logger Logger) With(fields ...Field) Logger {
|
||||
l := Logger{lgr: logger.lgr}
|
||||
size := len(logger.fields) + len(fields)
|
||||
if size > 0 {
|
||||
l.fields = make([]Field, 0, size)
|
||||
l.fields = append(l.fields, logger.fields...)
|
||||
l.fields = append(l.fields, fields...)
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// StdLogger creates a standard logger backed by this `Logr.Logger` instance.
|
||||
// All log records are emitted with the specified log level.
|
||||
func (logger Logger) StdLogger(level Level) *log.Logger {
|
||||
return NewStdLogger(level, logger)
|
||||
}
|
||||
|
||||
// IsLevelEnabled determines if the specified level is enabled for at least
|
||||
// one log target.
|
||||
func (logger Logger) IsLevelEnabled(level Level) bool {
|
||||
status := logger.Logr().IsLevelEnabled(level)
|
||||
return status.Enabled
|
||||
}
|
||||
|
||||
// Sugar creates a new `Logger` with a less structured API. Any fields are preserved.
|
||||
func (logger Logger) Sugar(fields ...Field) Sugar {
|
||||
return Sugar{
|
||||
logger: logger.With(fields...),
|
||||
}
|
||||
}
|
||||
|
||||
// Log checks that the level matches one or more targets, and
|
||||
// if so, generates a log record that is added to the Logr queue.
|
||||
// Arguments are handled in the manner of fmt.Print.
|
||||
func (logger Logger) Log(lvl Level, msg string, fields ...Field) {
|
||||
status := logger.lgr.IsLevelEnabled(lvl)
|
||||
if status.Enabled {
|
||||
rec := NewLogRec(lvl, logger, msg, fields, status.Stacktrace)
|
||||
logger.lgr.enqueue(rec)
|
||||
}
|
||||
}
|
||||
|
||||
// LogM calls `Log` multiple times, one for each level provided.
|
||||
func (logger Logger) LogM(levels []Level, msg string, fields ...Field) {
|
||||
for _, lvl := range levels {
|
||||
logger.Log(lvl, msg, fields...)
|
||||
}
|
||||
}
|
||||
|
||||
// Trace is a convenience method equivalent to `Log(TraceLevel, msg, fields...)`.
|
||||
func (logger Logger) Trace(msg string, fields ...Field) {
|
||||
logger.Log(Trace, msg, fields...)
|
||||
}
|
||||
|
||||
// Debug is a convenience method equivalent to `Log(DebugLevel, msg, fields...)`.
|
||||
func (logger Logger) Debug(msg string, fields ...Field) {
|
||||
logger.Log(Debug, msg, fields...)
|
||||
}
|
||||
|
||||
// Info is a convenience method equivalent to `Log(InfoLevel, msg, fields...)`.
|
||||
func (logger Logger) Info(msg string, fields ...Field) {
|
||||
logger.Log(Info, msg, fields...)
|
||||
}
|
||||
|
||||
// Warn is a convenience method equivalent to `Log(WarnLevel, msg, fields...)`.
|
||||
func (logger Logger) Warn(msg string, fields ...Field) {
|
||||
logger.Log(Warn, msg, fields...)
|
||||
}
|
||||
|
||||
// Error is a convenience method equivalent to `Log(ErrorLevel, msg, fields...)`.
|
||||
func (logger Logger) Error(msg string, fields ...Field) {
|
||||
logger.Log(Error, msg, fields...)
|
||||
}
|
||||
|
||||
// Fatal is a convenience method equivalent to `Log(FatalLevel, msg, fields...)`
|
||||
func (logger Logger) Fatal(msg string, fields ...Field) {
|
||||
logger.Log(Fatal, msg, fields...)
|
||||
}
|
||||
|
||||
// Panic is a convenience method equivalent to `Log(PanicLevel, msg, fields...)`
|
||||
func (logger Logger) Panic(msg string, fields ...Field) {
|
||||
logger.Log(Panic, msg, fields...)
|
||||
}
|
||||
471
vendor/github.com/mattermost/logr/v2/logr.go
сгенерированный
поставляемый
Обычный файл
471
vendor/github.com/mattermost/logr/v2/logr.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,471 @@
|
||||
package logr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/wiggin77/merror"
|
||||
)
|
||||
|
||||
// Logr maintains a list of log targets and accepts incoming
|
||||
// log records. Use `New` to create instances.
|
||||
type Logr struct {
|
||||
tmux sync.RWMutex // targetHosts mutex
|
||||
targetHosts []*TargetHost
|
||||
|
||||
in chan *LogRec
|
||||
quit chan struct{} // closed by Shutdown to exit read loop
|
||||
done chan struct{} // closed when read loop exited
|
||||
lvlCache levelCache
|
||||
bufferPool sync.Pool
|
||||
options *options
|
||||
|
||||
metricsMux sync.RWMutex
|
||||
metrics *metrics
|
||||
|
||||
shutdown int32
|
||||
}
|
||||
|
||||
// New creates a new Logr instance with one or more options specified.
|
||||
// Some options with invalid values can cause an error to be returned,
|
||||
// however `logr.New()` using just defaults never errors.
|
||||
func New(opts ...Option) (*Logr, error) {
|
||||
options := &options{
|
||||
maxQueueSize: DefaultMaxQueueSize,
|
||||
enqueueTimeout: DefaultEnqueueTimeout,
|
||||
shutdownTimeout: DefaultShutdownTimeout,
|
||||
flushTimeout: DefaultFlushTimeout,
|
||||
maxPooledBuffer: DefaultMaxPooledBuffer,
|
||||
}
|
||||
|
||||
lgr := &Logr{options: options}
|
||||
|
||||
// apply the options
|
||||
for _, opt := range opts {
|
||||
if err := opt(lgr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
pkgName := GetLogrPackageName()
|
||||
if pkgName != "" {
|
||||
opt := StackFilter(pkgName, pkgName+"/targets", pkgName+"/formatters")
|
||||
_ = opt(lgr)
|
||||
}
|
||||
|
||||
lgr.in = make(chan *LogRec, lgr.options.maxQueueSize)
|
||||
lgr.quit = make(chan struct{})
|
||||
lgr.done = make(chan struct{})
|
||||
|
||||
if lgr.options.useSyncMapLevelCache {
|
||||
lgr.lvlCache = &syncMapLevelCache{}
|
||||
} else {
|
||||
lgr.lvlCache = &arrayLevelCache{}
|
||||
}
|
||||
lgr.lvlCache.setup()
|
||||
|
||||
lgr.bufferPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(bytes.Buffer)
|
||||
},
|
||||
}
|
||||
|
||||
lgr.initMetrics(lgr.options.metricsCollector, lgr.options.metricsUpdateFreqMillis)
|
||||
|
||||
go lgr.start()
|
||||
|
||||
return lgr, nil
|
||||
}
|
||||
|
||||
// AddTarget adds a target to the logger which will receive
|
||||
// log records for outputting.
|
||||
func (lgr *Logr) AddTarget(target Target, name string, filter Filter, formatter Formatter, maxQueueSize int) error {
|
||||
if lgr.IsShutdown() {
|
||||
return fmt.Errorf("AddTarget called after Logr shut down")
|
||||
}
|
||||
|
||||
lgr.metricsMux.RLock()
|
||||
metrics := lgr.metrics
|
||||
lgr.metricsMux.RUnlock()
|
||||
|
||||
hostOpts := targetHostOptions{
|
||||
name: name,
|
||||
filter: filter,
|
||||
formatter: formatter,
|
||||
maxQueueSize: maxQueueSize,
|
||||
metrics: metrics,
|
||||
}
|
||||
|
||||
host, err := newTargetHost(target, hostOpts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lgr.tmux.Lock()
|
||||
defer lgr.tmux.Unlock()
|
||||
|
||||
lgr.targetHosts = append(lgr.targetHosts, host)
|
||||
|
||||
lgr.ResetLevelCache()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewLogger creates a Logger using defaults. A `Logger` is light-weight
|
||||
// enough to create on-demand, but typically one or more Loggers are
|
||||
// created and re-used.
|
||||
func (lgr *Logr) NewLogger() Logger {
|
||||
logger := Logger{lgr: lgr}
|
||||
return logger
|
||||
}
|
||||
|
||||
var levelStatusDisabled = LevelStatus{}
|
||||
|
||||
// IsLevelEnabled returns true if at least one target has the specified
|
||||
// level enabled. The result is cached so that subsequent checks are fast.
|
||||
func (lgr *Logr) IsLevelEnabled(lvl Level) LevelStatus {
|
||||
// No levels enabled after shutdown
|
||||
if atomic.LoadInt32(&lgr.shutdown) != 0 {
|
||||
return levelStatusDisabled
|
||||
}
|
||||
|
||||
// Check cache.
|
||||
status, ok := lgr.lvlCache.get(lvl.ID)
|
||||
if ok {
|
||||
return status
|
||||
}
|
||||
|
||||
status = LevelStatus{}
|
||||
|
||||
// Cache miss; check each target.
|
||||
lgr.tmux.RLock()
|
||||
defer lgr.tmux.RUnlock()
|
||||
for _, host := range lgr.targetHosts {
|
||||
enabled, level := host.IsLevelEnabled(lvl)
|
||||
if enabled {
|
||||
status.Enabled = true
|
||||
if level.Stacktrace || host.formatter.IsStacktraceNeeded() {
|
||||
status.Stacktrace = true
|
||||
break // if both level and stacktrace enabled then no sense checking more targets
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cache and return the result.
|
||||
if err := lgr.lvlCache.put(lvl.ID, status); err != nil {
|
||||
lgr.ReportError(err)
|
||||
return LevelStatus{}
|
||||
}
|
||||
return status
|
||||
}
|
||||
|
||||
// HasTargets returns true only if at least one target exists within the lgr.
|
||||
func (lgr *Logr) HasTargets() bool {
|
||||
lgr.tmux.RLock()
|
||||
defer lgr.tmux.RUnlock()
|
||||
return len(lgr.targetHosts) > 0
|
||||
}
|
||||
|
||||
// TargetInfo provides name and type for a Target.
|
||||
type TargetInfo struct {
|
||||
Name string
|
||||
Type string
|
||||
}
|
||||
|
||||
// TargetInfos enumerates all the targets added to this lgr.
|
||||
// The resulting slice represents a snapshot at time of calling.
|
||||
func (lgr *Logr) TargetInfos() []TargetInfo {
|
||||
infos := make([]TargetInfo, 0)
|
||||
|
||||
lgr.tmux.RLock()
|
||||
defer lgr.tmux.RUnlock()
|
||||
|
||||
for _, host := range lgr.targetHosts {
|
||||
inf := TargetInfo{
|
||||
Name: host.String(),
|
||||
Type: fmt.Sprintf("%T", host.target),
|
||||
}
|
||||
infos = append(infos, inf)
|
||||
}
|
||||
return infos
|
||||
}
|
||||
|
||||
// RemoveTargets safely removes one or more targets based on the filtering method.
|
||||
// f should return true to delete the target, false to keep it.
|
||||
// When removing a target, best effort is made to write any queued log records before
|
||||
// closing, with cxt determining how much time can be spent in total.
|
||||
// Note, keep the timeout short since this method blocks certain logging operations.
|
||||
func (lgr *Logr) RemoveTargets(cxt context.Context, f func(ti TargetInfo) bool) error {
|
||||
errs := merror.New()
|
||||
hosts := make([]*TargetHost, 0)
|
||||
|
||||
lgr.tmux.Lock()
|
||||
defer lgr.tmux.Unlock()
|
||||
|
||||
for _, host := range lgr.targetHosts {
|
||||
inf := TargetInfo{
|
||||
Name: host.String(),
|
||||
Type: fmt.Sprintf("%T", host.target),
|
||||
}
|
||||
if f(inf) {
|
||||
if err := host.Shutdown(cxt); err != nil {
|
||||
errs.Append(err)
|
||||
}
|
||||
} else {
|
||||
hosts = append(hosts, host)
|
||||
}
|
||||
}
|
||||
|
||||
lgr.targetHosts = hosts
|
||||
lgr.ResetLevelCache()
|
||||
|
||||
return errs.ErrorOrNil()
|
||||
}
|
||||
|
||||
// ResetLevelCache resets the cached results of `IsLevelEnabled`. This is
|
||||
// called any time a Target is added or a target's level is changed.
|
||||
func (lgr *Logr) ResetLevelCache() {
|
||||
lgr.lvlCache.clear()
|
||||
}
|
||||
|
||||
// SetMetricsCollector sets (or resets) the metrics collector to be used for gathering
|
||||
// metrics for all targets. Only targets added after this call will use the collector.
|
||||
//
|
||||
// To ensure all targets use a collector, use the `SetMetricsCollector` option when
|
||||
// creating the Logr instead, or configure/reconfigure the Logr after calling this method.
|
||||
func (lgr *Logr) SetMetricsCollector(collector MetricsCollector, updateFreqMillis int64) {
|
||||
lgr.initMetrics(collector, updateFreqMillis)
|
||||
}
|
||||
|
||||
// enqueue adds a log record to the logr queue. If the queue is full then
|
||||
// this function either blocks or the log record is dropped, depending on
|
||||
// the result of calling `OnQueueFull`.
|
||||
func (lgr *Logr) enqueue(rec *LogRec) {
|
||||
select {
|
||||
case lgr.in <- rec:
|
||||
default:
|
||||
if lgr.options.onQueueFull != nil && lgr.options.onQueueFull(rec, cap(lgr.in)) {
|
||||
return // drop the record
|
||||
}
|
||||
select {
|
||||
case <-time.After(lgr.options.enqueueTimeout):
|
||||
lgr.ReportError(fmt.Errorf("enqueue timed out for log rec [%v]", rec))
|
||||
case lgr.in <- rec: // block until success or timeout
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Flush blocks while flushing the logr queue and all target queues, by
|
||||
// writing existing log records to valid targets.
|
||||
// Any attempts to add new log records will block until flush is complete.
|
||||
// `logr.FlushTimeout` determines how long flush can execute before
|
||||
// timing out. Use `IsTimeoutError` to determine if the returned error is
|
||||
// due to a timeout.
|
||||
func (lgr *Logr) Flush() error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), lgr.options.flushTimeout)
|
||||
defer cancel()
|
||||
return lgr.FlushWithTimeout(ctx)
|
||||
}
|
||||
|
||||
// Flush blocks while flushing the logr queue and all target queues, by
|
||||
// writing existing log records to valid targets.
|
||||
// Any attempts to add new log records will block until flush is complete.
|
||||
// Use `IsTimeoutError` to determine if the returned error is
|
||||
// due to a timeout.
|
||||
func (lgr *Logr) FlushWithTimeout(ctx context.Context) error {
|
||||
if !lgr.HasTargets() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if lgr.IsShutdown() {
|
||||
return errors.New("Flush called on shut down Logr")
|
||||
}
|
||||
|
||||
rec := newFlushLogRec(lgr.NewLogger())
|
||||
lgr.enqueue(rec)
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return newTimeoutError("logr queue flush timeout")
|
||||
case <-rec.flush:
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsShutdown returns true if this Logr instance has been shut down.
|
||||
// No further log records can be enqueued and no targets added after
|
||||
// shutdown.
|
||||
func (lgr *Logr) IsShutdown() bool {
|
||||
return atomic.LoadInt32(&lgr.shutdown) != 0
|
||||
}
|
||||
|
||||
// Shutdown cleanly stops the logging engine after making best efforts
|
||||
// to flush all targets. Call this function right before application
|
||||
// exit - logr cannot be restarted once shut down.
|
||||
// `logr.ShutdownTimeout` determines how long shutdown can execute before
|
||||
// timing out. Use `IsTimeoutError` to determine if the returned error is
|
||||
// due to a timeout.
|
||||
func (lgr *Logr) Shutdown() error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), lgr.options.shutdownTimeout)
|
||||
defer cancel()
|
||||
return lgr.ShutdownWithTimeout(ctx)
|
||||
}
|
||||
|
||||
// Shutdown cleanly stops the logging engine after making best efforts
|
||||
// to flush all targets. Call this function right before application
|
||||
// exit - logr cannot be restarted once shut down.
|
||||
// Use `IsTimeoutError` to determine if the returned error is due to a
|
||||
// timeout.
|
||||
func (lgr *Logr) ShutdownWithTimeout(ctx context.Context) error {
|
||||
if err := lgr.FlushWithTimeout(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if atomic.SwapInt32(&lgr.shutdown, 1) != 0 {
|
||||
return errors.New("Shutdown called again after shut down")
|
||||
}
|
||||
|
||||
lgr.ResetLevelCache()
|
||||
lgr.stopMetricsUpdater()
|
||||
|
||||
close(lgr.quit)
|
||||
|
||||
errs := merror.New()
|
||||
|
||||
// Wait for read loop to exit
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
errs.Append(newTimeoutError("logr queue shutdown timeout"))
|
||||
case <-lgr.done:
|
||||
}
|
||||
|
||||
// logr.in channel should now be drained to targets and no more log records
|
||||
// can be added.
|
||||
lgr.tmux.RLock()
|
||||
defer lgr.tmux.RUnlock()
|
||||
for _, host := range lgr.targetHosts {
|
||||
err := host.Shutdown(ctx)
|
||||
if err != nil {
|
||||
errs.Append(err)
|
||||
}
|
||||
}
|
||||
return errs.ErrorOrNil()
|
||||
}
|
||||
|
||||
// ReportError is used to notify the host application of any internal logging errors.
|
||||
// If `OnLoggerError` is not nil, it is called with the error, otherwise the error is
|
||||
// output to `os.Stderr`.
|
||||
func (lgr *Logr) ReportError(err interface{}) {
|
||||
lgr.incErrorCounter()
|
||||
|
||||
if lgr.options.onLoggerError == nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return
|
||||
}
|
||||
lgr.options.onLoggerError(fmt.Errorf("%v", err))
|
||||
}
|
||||
|
||||
// BorrowBuffer borrows a buffer from the pool. Release the buffer to reduce garbage collection.
|
||||
func (lgr *Logr) BorrowBuffer() *bytes.Buffer {
|
||||
if lgr.options.disableBufferPool {
|
||||
return &bytes.Buffer{}
|
||||
}
|
||||
return lgr.bufferPool.Get().(*bytes.Buffer)
|
||||
}
|
||||
|
||||
// ReleaseBuffer returns a buffer to the pool to reduce garbage collection. The buffer is only
|
||||
// retained if less than MaxPooledBuffer.
|
||||
func (lgr *Logr) ReleaseBuffer(buf *bytes.Buffer) {
|
||||
if !lgr.options.disableBufferPool && buf.Cap() < lgr.options.maxPooledBuffer {
|
||||
buf.Reset()
|
||||
lgr.bufferPool.Put(buf)
|
||||
}
|
||||
}
|
||||
|
||||
// start selects on incoming log records until shutdown record is received.
|
||||
// Incoming log records are fanned out to all log targets.
|
||||
func (lgr *Logr) start() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
lgr.ReportError(r)
|
||||
go lgr.start()
|
||||
} else {
|
||||
close(lgr.done)
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
var rec *LogRec
|
||||
select {
|
||||
case rec = <-lgr.in:
|
||||
if rec.flush != nil {
|
||||
lgr.flush(rec.flush)
|
||||
} else {
|
||||
rec.prep()
|
||||
lgr.fanout(rec)
|
||||
}
|
||||
case <-lgr.quit:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fanout pushes a LogRec to all targets.
|
||||
func (lgr *Logr) fanout(rec *LogRec) {
|
||||
var host *TargetHost
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
lgr.ReportError(fmt.Errorf("fanout failed for target %s, %v", host.String(), r))
|
||||
}
|
||||
}()
|
||||
|
||||
var logged bool
|
||||
|
||||
lgr.tmux.RLock()
|
||||
defer lgr.tmux.RUnlock()
|
||||
for _, host = range lgr.targetHosts {
|
||||
if enabled, _ := host.IsLevelEnabled(rec.Level()); enabled {
|
||||
host.Log(rec)
|
||||
logged = true
|
||||
}
|
||||
}
|
||||
|
||||
if logged {
|
||||
lgr.incLoggedCounter()
|
||||
}
|
||||
}
|
||||
|
||||
// flush drains the queue and notifies when done.
|
||||
func (lgr *Logr) flush(done chan<- struct{}) {
|
||||
// first drain the logr queue.
|
||||
loop:
|
||||
for {
|
||||
var rec *LogRec
|
||||
select {
|
||||
case rec = <-lgr.in:
|
||||
if rec.flush == nil {
|
||||
rec.prep()
|
||||
lgr.fanout(rec)
|
||||
}
|
||||
default:
|
||||
break loop
|
||||
}
|
||||
}
|
||||
|
||||
logger := lgr.NewLogger()
|
||||
|
||||
// drain all the targets; block until finished.
|
||||
lgr.tmux.RLock()
|
||||
defer lgr.tmux.RUnlock()
|
||||
for _, host := range lgr.targetHosts {
|
||||
rec := newFlushLogRec(logger)
|
||||
host.Log(rec)
|
||||
<-rec.flush
|
||||
}
|
||||
done <- struct{}{}
|
||||
}
|
||||
117
vendor/github.com/mattermost/logr/logrec.go → vendor/github.com/mattermost/logr/v2/logrec.go
сгенерированный
поставляемый
117
vendor/github.com/mattermost/logr/logrec.go → vendor/github.com/mattermost/logr/v2/logrec.go
сгенерированный
поставляемый
@@ -2,24 +2,13 @@ package logr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
logrPkg string
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Calc current package name
|
||||
pcs := make([]uintptr, 2)
|
||||
_ = runtime.Callers(0, pcs)
|
||||
tmp := runtime.FuncForPC(pcs[1]).Name()
|
||||
logrPkg = getPackageName(tmp)
|
||||
}
|
||||
|
||||
// LogRec collects raw, unformatted data to be logged.
|
||||
// TODO: pool these? how to reliably know when targets are done with them? Copy for each target?
|
||||
type LogRec struct {
|
||||
@@ -29,9 +18,9 @@ type LogRec struct {
|
||||
level Level
|
||||
logger Logger
|
||||
|
||||
template string
|
||||
newline bool
|
||||
args []interface{}
|
||||
msg string
|
||||
newline bool
|
||||
fields []Field
|
||||
|
||||
stackPC []uintptr
|
||||
stackCount int
|
||||
@@ -40,13 +29,14 @@ type LogRec struct {
|
||||
flush chan struct{}
|
||||
|
||||
// remaining fields calculated by `prep`
|
||||
msg string
|
||||
frames []runtime.Frame
|
||||
frames []runtime.Frame
|
||||
fieldsAll []Field
|
||||
caller string
|
||||
}
|
||||
|
||||
// NewLogRec creates a new LogRec with the current time and optional stack trace.
|
||||
func NewLogRec(lvl Level, logger Logger, template string, args []interface{}, incStacktrace bool) *LogRec {
|
||||
rec := &LogRec{time: time.Now(), logger: logger, level: lvl, template: template, args: args}
|
||||
func NewLogRec(lvl Level, logger Logger, msg string, fields []Field, incStacktrace bool) *LogRec {
|
||||
rec := &LogRec{time: time.Now(), logger: logger, level: lvl, msg: msg, fields: fields}
|
||||
if incStacktrace {
|
||||
rec.stackPC = make([]uintptr, DefaultMaxStackFrames)
|
||||
rec.stackCount = runtime.Callers(2, rec.stackPC)
|
||||
@@ -60,44 +50,40 @@ func newFlushLogRec(logger Logger) *LogRec {
|
||||
return &LogRec{logger: logger, flush: make(chan struct{})}
|
||||
}
|
||||
|
||||
// prep resolves all args and field values to strings, and
|
||||
// resolves stack trace to frames.
|
||||
// prep resolves stack trace to frames.
|
||||
func (rec *LogRec) prep() {
|
||||
rec.mux.Lock()
|
||||
defer rec.mux.Unlock()
|
||||
|
||||
// resolve args
|
||||
if rec.template == "" {
|
||||
if rec.newline {
|
||||
rec.msg = fmt.Sprintln(rec.args...)
|
||||
} else {
|
||||
rec.msg = fmt.Sprint(rec.args...)
|
||||
}
|
||||
} else {
|
||||
rec.msg = fmt.Sprintf(rec.template, rec.args...)
|
||||
}
|
||||
// include log rec fields and logger fields added via "With"
|
||||
rec.fieldsAll = make([]Field, 0, len(rec.fields)+len(rec.logger.fields))
|
||||
rec.fieldsAll = append(rec.fieldsAll, rec.logger.fields...)
|
||||
rec.fieldsAll = append(rec.fieldsAll, rec.fields...)
|
||||
|
||||
filter := rec.logger.lgr.options.stackFilter
|
||||
|
||||
// resolve stack trace
|
||||
if rec.stackCount > 0 {
|
||||
rec.frames = make([]runtime.Frame, 0, rec.stackCount)
|
||||
frames := runtime.CallersFrames(rec.stackPC[:rec.stackCount])
|
||||
for {
|
||||
f, more := frames.Next()
|
||||
rec.frames = append(rec.frames, f)
|
||||
frame, more := frames.Next()
|
||||
|
||||
// remove all package entries that are in filter.
|
||||
pkg := ResolvePackageName(frame.Function)
|
||||
if _, ok := filter[pkg]; !ok && pkg != "" {
|
||||
rec.frames = append(rec.frames, frame)
|
||||
}
|
||||
|
||||
if !more {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove leading logr package entries.
|
||||
var start int
|
||||
for i, frame := range rec.frames {
|
||||
pkg := getPackageName(frame.Function)
|
||||
if pkg != "" && pkg != logrPkg {
|
||||
start = i
|
||||
break
|
||||
}
|
||||
}
|
||||
rec.frames = rec.frames[start:]
|
||||
// calc caller if stack trace provided
|
||||
if len(rec.frames) > 0 {
|
||||
rec.caller = calcCaller(rec.frames)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,10 +98,9 @@ func (rec *LogRec) WithTime(time time.Time) *LogRec {
|
||||
time: time,
|
||||
level: rec.level,
|
||||
logger: rec.logger,
|
||||
template: rec.template,
|
||||
newline: rec.newline,
|
||||
args: rec.args,
|
||||
msg: rec.msg,
|
||||
newline: rec.newline,
|
||||
fields: rec.fields,
|
||||
stackPC: rec.stackPC,
|
||||
stackCount: rec.stackCount,
|
||||
frames: rec.frames,
|
||||
@@ -140,9 +125,9 @@ func (rec *LogRec) Level() Level {
|
||||
}
|
||||
|
||||
// Fields returns this log record's Fields.
|
||||
func (rec *LogRec) Fields() Fields {
|
||||
func (rec *LogRec) Fields() []Field {
|
||||
// no locking needed as this field is not mutated.
|
||||
return rec.logger.fields
|
||||
return rec.fieldsAll
|
||||
}
|
||||
|
||||
// Msg returns this log record's message text.
|
||||
@@ -160,6 +145,15 @@ func (rec *LogRec) StackFrames() []runtime.Frame {
|
||||
return rec.frames
|
||||
}
|
||||
|
||||
// Caller returns this log record's caller info, meaning the file and line
|
||||
// number where this log record was emitted. Returns empty string if no
|
||||
// stack trace was provided.
|
||||
func (rec *LogRec) Caller() string {
|
||||
rec.mux.RLock()
|
||||
defer rec.mux.RUnlock()
|
||||
return rec.caller
|
||||
}
|
||||
|
||||
// String returns a string representation of this log record.
|
||||
func (rec *LogRec) String() string {
|
||||
if rec.flush != nil {
|
||||
@@ -167,23 +161,22 @@ func (rec *LogRec) String() string {
|
||||
}
|
||||
|
||||
f := &DefaultFormatter{}
|
||||
buf := rec.logger.logr.BorrowBuffer()
|
||||
defer rec.logger.logr.ReleaseBuffer(buf)
|
||||
buf, _ = f.Format(rec, true, buf)
|
||||
buf := rec.logger.lgr.BorrowBuffer()
|
||||
defer rec.logger.lgr.ReleaseBuffer(buf)
|
||||
buf, _ = f.Format(rec, rec.Level(), buf)
|
||||
return strings.TrimSpace(buf.String())
|
||||
}
|
||||
|
||||
// getPackageName reduces a fully qualified function name to the package name
|
||||
// By sirupsen: https://github.com/sirupsen/logrus/blob/master/entry.go
|
||||
func getPackageName(f string) string {
|
||||
for {
|
||||
lastPeriod := strings.LastIndex(f, ".")
|
||||
lastSlash := strings.LastIndex(f, "/")
|
||||
if lastPeriod > lastSlash {
|
||||
f = f[:lastPeriod]
|
||||
} else {
|
||||
break
|
||||
func calcCaller(frames []runtime.Frame) string {
|
||||
for _, frame := range frames {
|
||||
if frame.File == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
dir, file := filepath.Split(frame.File)
|
||||
base := filepath.Base(dir)
|
||||
|
||||
return fmt.Sprintf("%s/%s:%d", base, file, frame.Line)
|
||||
}
|
||||
return f
|
||||
return ""
|
||||
}
|
||||
129
vendor/github.com/mattermost/logr/metrics.go → vendor/github.com/mattermost/logr/v2/metrics.go
сгенерированный
поставляемый
129
vendor/github.com/mattermost/logr/metrics.go → vendor/github.com/mattermost/logr/v2/metrics.go
сгенерированный
поставляемый
@@ -1,10 +1,6 @@
|
||||
package logr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/wiggin77/merror"
|
||||
)
|
||||
import "time"
|
||||
|
||||
const (
|
||||
DefMetricsUpdateFreqMillis = 15000 // 15 seconds
|
||||
@@ -52,66 +48,93 @@ type TargetWithMetrics interface {
|
||||
EnableMetrics(collector MetricsCollector, updateFreqMillis int64) error
|
||||
}
|
||||
|
||||
func (logr *Logr) getMetricsCollector() MetricsCollector {
|
||||
logr.mux.RLock()
|
||||
defer logr.mux.RUnlock()
|
||||
return logr.metrics
|
||||
type metrics struct {
|
||||
collector MetricsCollector
|
||||
updateFreqMillis int64
|
||||
queueSizeGauge Gauge
|
||||
loggedCounter Counter
|
||||
errorCounter Counter
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
// SetMetricsCollector enables metrics collection by supplying a MetricsCollector.
|
||||
// The MetricsCollector provides counters and gauges that are updated by log targets.
|
||||
func (logr *Logr) SetMetricsCollector(collector MetricsCollector) error {
|
||||
// initMetrics initializes metrics collection.
|
||||
func (lgr *Logr) initMetrics(collector MetricsCollector, updatefreq int64) {
|
||||
lgr.stopMetricsUpdater()
|
||||
|
||||
if collector == nil {
|
||||
return errors.New("collector cannot be nil")
|
||||
lgr.metricsMux.Lock()
|
||||
lgr.metrics = nil
|
||||
lgr.metricsMux.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
logr.mux.Lock()
|
||||
logr.metrics = collector
|
||||
logr.queueSizeGauge, _ = collector.QueueSizeGauge("_logr")
|
||||
logr.loggedCounter, _ = collector.LoggedCounter("_logr")
|
||||
logr.errorCounter, _ = collector.ErrorCounter("_logr")
|
||||
logr.mux.Unlock()
|
||||
metrics := &metrics{
|
||||
collector: collector,
|
||||
updateFreqMillis: updatefreq,
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
metrics.queueSizeGauge, _ = collector.QueueSizeGauge("_logr")
|
||||
metrics.loggedCounter, _ = collector.LoggedCounter("_logr")
|
||||
metrics.errorCounter, _ = collector.ErrorCounter("_logr")
|
||||
|
||||
logr.metricsInitOnce.Do(func() {
|
||||
logr.metricsDone = make(chan struct{})
|
||||
go logr.startMetricsUpdater()
|
||||
})
|
||||
lgr.metricsMux.Lock()
|
||||
lgr.metrics = metrics
|
||||
lgr.metricsMux.Unlock()
|
||||
|
||||
merr := merror.New()
|
||||
go lgr.startMetricsUpdater()
|
||||
}
|
||||
|
||||
logr.tmux.RLock()
|
||||
defer logr.tmux.RUnlock()
|
||||
for _, target := range logr.targets {
|
||||
if tm, ok := target.(TargetWithMetrics); ok {
|
||||
if err := tm.EnableMetrics(collector, logr.MetricsUpdateFreqMillis); err != nil {
|
||||
merr.Append(err)
|
||||
}
|
||||
func (lgr *Logr) setQueueSizeGauge(val float64) {
|
||||
lgr.metricsMux.RLock()
|
||||
defer lgr.metricsMux.RUnlock()
|
||||
|
||||
if lgr.metrics != nil {
|
||||
lgr.metrics.queueSizeGauge.Set(val)
|
||||
}
|
||||
}
|
||||
|
||||
func (lgr *Logr) incLoggedCounter() {
|
||||
lgr.metricsMux.RLock()
|
||||
defer lgr.metricsMux.RUnlock()
|
||||
|
||||
if lgr.metrics != nil {
|
||||
lgr.metrics.loggedCounter.Inc()
|
||||
}
|
||||
}
|
||||
|
||||
func (lgr *Logr) incErrorCounter() {
|
||||
lgr.metricsMux.RLock()
|
||||
defer lgr.metricsMux.RUnlock()
|
||||
|
||||
if lgr.metrics != nil {
|
||||
lgr.metrics.errorCounter.Inc()
|
||||
}
|
||||
}
|
||||
|
||||
// startMetricsUpdater updates the metrics for any polled values every `metricsUpdateFreqSecs` seconds until
|
||||
// logr is closed.
|
||||
func (lgr *Logr) startMetricsUpdater() {
|
||||
for {
|
||||
lgr.metricsMux.RLock()
|
||||
metrics := lgr.metrics
|
||||
c := metrics.done
|
||||
lgr.metricsMux.RUnlock()
|
||||
|
||||
select {
|
||||
case <-c:
|
||||
return
|
||||
case <-time.After(time.Duration(metrics.updateFreqMillis) * time.Millisecond):
|
||||
lgr.setQueueSizeGauge(float64(len(lgr.in)))
|
||||
}
|
||||
|
||||
}
|
||||
return merr.ErrorOrNil()
|
||||
}
|
||||
|
||||
func (logr *Logr) setQueueSizeGauge(val float64) {
|
||||
logr.mux.RLock()
|
||||
defer logr.mux.RUnlock()
|
||||
if logr.queueSizeGauge != nil {
|
||||
logr.queueSizeGauge.Set(val)
|
||||
}
|
||||
}
|
||||
|
||||
func (logr *Logr) incLoggedCounter() {
|
||||
logr.mux.RLock()
|
||||
defer logr.mux.RUnlock()
|
||||
if logr.loggedCounter != nil {
|
||||
logr.loggedCounter.Inc()
|
||||
}
|
||||
}
|
||||
func (lgr *Logr) stopMetricsUpdater() {
|
||||
lgr.metricsMux.Lock()
|
||||
defer lgr.metricsMux.Unlock()
|
||||
|
||||
func (logr *Logr) incErrorCounter() {
|
||||
logr.mux.RLock()
|
||||
defer logr.mux.RUnlock()
|
||||
if logr.errorCounter != nil {
|
||||
logr.errorCounter.Inc()
|
||||
if lgr.metrics != nil && lgr.metrics.done != nil {
|
||||
close(lgr.metrics.done)
|
||||
lgr.metrics.done = nil
|
||||
}
|
||||
}
|
||||
192
vendor/github.com/mattermost/logr/v2/options.go
сгенерированный
поставляемый
Обычный файл
192
vendor/github.com/mattermost/logr/v2/options.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,192 @@
|
||||
package logr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Option func(*Logr) error
|
||||
|
||||
type options struct {
|
||||
maxQueueSize int
|
||||
onLoggerError func(error)
|
||||
onQueueFull func(rec *LogRec, maxQueueSize int) bool
|
||||
onTargetQueueFull func(target Target, rec *LogRec, maxQueueSize int) bool
|
||||
onExit func(code int)
|
||||
onPanic func(err interface{})
|
||||
enqueueTimeout time.Duration
|
||||
shutdownTimeout time.Duration
|
||||
flushTimeout time.Duration
|
||||
useSyncMapLevelCache bool
|
||||
maxPooledBuffer int
|
||||
disableBufferPool bool
|
||||
metricsCollector MetricsCollector
|
||||
metricsUpdateFreqMillis int64
|
||||
stackFilter map[string]struct{}
|
||||
}
|
||||
|
||||
// MaxQueueSize is the maximum number of log records that can be queued.
|
||||
// If exceeded, `OnQueueFull` is called which determines if the log
|
||||
// record will be dropped or block until add is successful.
|
||||
// Defaults to DefaultMaxQueueSize.
|
||||
func MaxQueueSize(size int) Option {
|
||||
return func(l *Logr) error {
|
||||
if size < 0 {
|
||||
return errors.New("size cannot be less than zero")
|
||||
}
|
||||
l.options.maxQueueSize = size
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// OnLoggerError, when not nil, is called any time an internal
|
||||
// logging error occurs. For example, this can happen when a
|
||||
// target cannot connect to its data sink.
|
||||
func OnLoggerError(f func(error)) Option {
|
||||
return func(l *Logr) error {
|
||||
l.options.onLoggerError = f
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// OnQueueFull, when not nil, is called on an attempt to add
|
||||
// a log record to a full Logr queue.
|
||||
// `MaxQueueSize` can be used to modify the maximum queue size.
|
||||
// This function should return quickly, with a bool indicating whether
|
||||
// the log record should be dropped (true) or block until the log record
|
||||
// is successfully added (false). If nil then blocking (false) is assumed.
|
||||
func OnQueueFull(f func(rec *LogRec, maxQueueSize int) bool) Option {
|
||||
return func(l *Logr) error {
|
||||
l.options.onQueueFull = f
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// OnTargetQueueFull, when not nil, is called on an attempt to add
|
||||
// a log record to a full target queue provided the target supports reporting
|
||||
// this condition.
|
||||
// This function should return quickly, with a bool indicating whether
|
||||
// the log record should be dropped (true) or block until the log record
|
||||
// is successfully added (false). If nil then blocking (false) is assumed.
|
||||
func OnTargetQueueFull(f func(target Target, rec *LogRec, maxQueueSize int) bool) Option {
|
||||
return func(l *Logr) error {
|
||||
l.options.onTargetQueueFull = f
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// OnExit, when not nil, is called when a FatalXXX style log API is called.
|
||||
// When nil, then the default behavior is to cleanly shut down this Logr and
|
||||
// call `os.Exit(code)`.
|
||||
func OnExit(f func(code int)) Option {
|
||||
return func(l *Logr) error {
|
||||
l.options.onExit = f
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// OnPanic, when not nil, is called when a PanicXXX style log API is called.
|
||||
// When nil, then the default behavior is to cleanly shut down this Logr and
|
||||
// call `panic(err)`.
|
||||
func OnPanic(f func(err interface{})) Option {
|
||||
return func(l *Logr) error {
|
||||
l.options.onPanic = f
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// EnqueueTimeout is the amount of time a log record can take to be queued.
|
||||
// This only applies to blocking enqueue which happen after `logr.OnQueueFull`
|
||||
// is called and returns false.
|
||||
func EnqueueTimeout(dur time.Duration) Option {
|
||||
return func(l *Logr) error {
|
||||
l.options.enqueueTimeout = dur
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ShutdownTimeout is the amount of time `logr.Shutdown` can execute before
|
||||
// timing out. An alternative is to use `logr.ShutdownWithContext` and supply
|
||||
// a timeout.
|
||||
func ShutdownTimeout(dur time.Duration) Option {
|
||||
return func(l *Logr) error {
|
||||
l.options.shutdownTimeout = dur
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// FlushTimeout is the amount of time `logr.Flush` can execute before
|
||||
// timing out. An alternative is to use `logr.FlushWithContext` and supply
|
||||
// a timeout.
|
||||
func FlushTimeout(dur time.Duration) Option {
|
||||
return func(l *Logr) error {
|
||||
l.options.flushTimeout = dur
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// UseSyncMapLevelCache can be set to true when high concurrency (e.g. >32 cores)
|
||||
// is expected. This may improve performance with large numbers of cores - benchmark
|
||||
// for your use case.
|
||||
func UseSyncMapLevelCache(use bool) Option {
|
||||
return func(l *Logr) error {
|
||||
l.options.useSyncMapLevelCache = use
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MaxPooledBufferSize determines the maximum size of a buffer that can be
|
||||
// pooled. To reduce allocations, the buffers needed during formatting (etc)
|
||||
// are pooled. A very large log item will grow a buffer that could stay in
|
||||
// memory indefinitely. This setting lets you control how big a pooled buffer
|
||||
// can be - anything larger will be garbage collected after use.
|
||||
// Defaults to 1MB.
|
||||
func MaxPooledBufferSize(size int) Option {
|
||||
return func(l *Logr) error {
|
||||
l.options.maxPooledBuffer = size
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// DisableBufferPool when true disables the buffer pool. See MaxPooledBuffer.
|
||||
func DisableBufferPool(disable bool) Option {
|
||||
return func(l *Logr) error {
|
||||
l.options.disableBufferPool = disable
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// SetMetricsCollector enables metrics collection by supplying a MetricsCollector.
|
||||
// The MetricsCollector provides counters and gauges that are updated by log targets.
|
||||
// `updateFreqMillis` determines how often polled metrics are updated. Defaults to 15000 (15 seconds)
|
||||
// and must be at least 250 so we don't peg the CPU.
|
||||
func SetMetricsCollector(collector MetricsCollector, updateFreqMillis int64) Option {
|
||||
return func(l *Logr) error {
|
||||
if collector == nil {
|
||||
return errors.New("collector cannot be nil")
|
||||
}
|
||||
if updateFreqMillis < 250 {
|
||||
return errors.New("updateFreqMillis cannot be less than 250")
|
||||
}
|
||||
l.options.metricsCollector = collector
|
||||
l.options.metricsUpdateFreqMillis = updateFreqMillis
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// StackFilter provides a list of package names to exclude from the top of
|
||||
// stack traces. The Logr packages are automatically filtered.
|
||||
func StackFilter(pkg ...string) Option {
|
||||
return func(l *Logr) error {
|
||||
if l.options.stackFilter == nil {
|
||||
l.options.stackFilter = make(map[string]struct{})
|
||||
}
|
||||
|
||||
for _, p := range pkg {
|
||||
if p != "" {
|
||||
l.options.stackFilter[p] = struct{}{}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
57
vendor/github.com/mattermost/logr/v2/pkg.go
сгенерированный
поставляемый
Обычный файл
57
vendor/github.com/mattermost/logr/v2/pkg.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,57 @@
|
||||
package logr
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
maximumStackDepth int = 30
|
||||
)
|
||||
|
||||
var (
|
||||
logrPkg string
|
||||
pkgCalcOnce sync.Once
|
||||
)
|
||||
|
||||
// GetPackageName returns the root package name of Logr.
|
||||
func GetLogrPackageName() string {
|
||||
pkgCalcOnce.Do(func() {
|
||||
logrPkg = GetPackageName("GetLogrPackageName")
|
||||
})
|
||||
return logrPkg
|
||||
}
|
||||
|
||||
// GetPackageName returns the package name of the caller.
|
||||
// `callingFuncName` should be the name of the calling function and
|
||||
// should be unique enough not to collide with any runtime methods.
|
||||
func GetPackageName(callingFuncName string) string {
|
||||
var pkgName string
|
||||
|
||||
pcs := make([]uintptr, maximumStackDepth)
|
||||
_ = runtime.Callers(0, pcs)
|
||||
|
||||
for _, pc := range pcs {
|
||||
funcName := runtime.FuncForPC(pc).Name()
|
||||
if strings.Contains(funcName, callingFuncName) {
|
||||
pkgName = ResolvePackageName(funcName)
|
||||
break
|
||||
}
|
||||
}
|
||||
return pkgName
|
||||
}
|
||||
|
||||
// ResolvePackageName reduces a fully qualified function name to the package name
|
||||
func ResolvePackageName(f string) string {
|
||||
for {
|
||||
lastPeriod := strings.LastIndex(f, ".")
|
||||
lastSlash := strings.LastIndex(f, "/")
|
||||
if lastPeriod > lastSlash {
|
||||
f = f[:lastPeriod]
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return f
|
||||
}
|
||||
56
vendor/github.com/mattermost/logr/v2/stdlogger.go
сгенерированный
поставляемый
Обычный файл
56
vendor/github.com/mattermost/logr/v2/stdlogger.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,56 @@
|
||||
package logr
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// NewStdLogger creates a standard logger backed by a Logr instance.
|
||||
// All log records are emitted with the specified log level.
|
||||
func NewStdLogger(level Level, logger Logger) *log.Logger {
|
||||
adapter := newStdLogAdapter(logger, level)
|
||||
return log.New(adapter, "", 0)
|
||||
}
|
||||
|
||||
// RedirectStdLog redirects output from the standard library's package-global logger
|
||||
// to this logger at the specified level and with zero or more Field's. Since Logr already
|
||||
// handles caller annotations, timestamps, etc., it automatically disables the standard
|
||||
// library's annotations and prefixing.
|
||||
// A function is returned that restores the original prefix and flags and resets the standard
|
||||
// library's output to os.Stderr.
|
||||
func (lgr *Logr) RedirectStdLog(level Level, fields ...Field) func() {
|
||||
flags := log.Flags()
|
||||
prefix := log.Prefix()
|
||||
log.SetFlags(0)
|
||||
log.SetPrefix("")
|
||||
|
||||
logger := lgr.NewLogger().With(fields...)
|
||||
adapter := newStdLogAdapter(logger, level)
|
||||
log.SetOutput(adapter)
|
||||
|
||||
return func() {
|
||||
log.SetFlags(flags)
|
||||
log.SetPrefix(prefix)
|
||||
log.SetOutput(os.Stderr)
|
||||
}
|
||||
}
|
||||
|
||||
type stdLogAdapter struct {
|
||||
logger Logger
|
||||
level Level
|
||||
}
|
||||
|
||||
func newStdLogAdapter(logger Logger, level Level) *stdLogAdapter {
|
||||
return &stdLogAdapter{
|
||||
logger: logger,
|
||||
level: level,
|
||||
}
|
||||
}
|
||||
|
||||
// Write implements io.Writer
|
||||
func (a *stdLogAdapter) Write(p []byte) (int, error) {
|
||||
s := strings.TrimSpace(string(p))
|
||||
a.logger.Log(a.level, s)
|
||||
return len(p), nil
|
||||
}
|
||||
119
vendor/github.com/mattermost/logr/v2/sugar.go
сгенерированный
поставляемый
Обычный файл
119
vendor/github.com/mattermost/logr/v2/sugar.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,119 @@
|
||||
package logr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Sugar provides a less structured API for logging.
|
||||
type Sugar struct {
|
||||
logger Logger
|
||||
}
|
||||
|
||||
func (s Sugar) sugarLog(lvl Level, msg string, args ...interface{}) {
|
||||
if s.logger.IsLevelEnabled(lvl) {
|
||||
fields := make([]Field, 0, len(args))
|
||||
for _, arg := range args {
|
||||
fields = append(fields, Any("", arg))
|
||||
}
|
||||
s.logger.Log(lvl, msg, fields...)
|
||||
}
|
||||
}
|
||||
|
||||
// Trace is a convenience method equivalent to `Log(TraceLevel, msg, args...)`.
|
||||
func (s Sugar) Trace(msg string, args ...interface{}) {
|
||||
s.sugarLog(Trace, msg, args...)
|
||||
}
|
||||
|
||||
// Debug is a convenience method equivalent to `Log(DebugLevel, msg, args...)`.
|
||||
func (s Sugar) Debug(msg string, args ...interface{}) {
|
||||
s.sugarLog(Debug, msg, args...)
|
||||
}
|
||||
|
||||
// Print ensures compatibility with std lib logger.
|
||||
func (s Sugar) Print(msg string, args ...interface{}) {
|
||||
s.Info(msg, args...)
|
||||
}
|
||||
|
||||
// Info is a convenience method equivalent to `Log(InfoLevel, msg, args...)`.
|
||||
func (s Sugar) Info(msg string, args ...interface{}) {
|
||||
s.sugarLog(Info, msg, args...)
|
||||
}
|
||||
|
||||
// Warn is a convenience method equivalent to `Log(WarnLevel, msg, args...)`.
|
||||
func (s Sugar) Warn(msg string, args ...interface{}) {
|
||||
s.sugarLog(Warn, msg, args...)
|
||||
}
|
||||
|
||||
// Error is a convenience method equivalent to `Log(ErrorLevel, msg, args...)`.
|
||||
func (s Sugar) Error(msg string, args ...interface{}) {
|
||||
s.sugarLog(Error, msg, args...)
|
||||
}
|
||||
|
||||
// Fatal is a convenience method equivalent to `Log(FatalLevel, msg, args...)`
|
||||
func (s Sugar) Fatal(msg string, args ...interface{}) {
|
||||
s.sugarLog(Fatal, msg, args...)
|
||||
}
|
||||
|
||||
// Panic is a convenience method equivalent to `Log(PanicLevel, msg, args...)`
|
||||
func (s Sugar) Panic(msg string, args ...interface{}) {
|
||||
s.sugarLog(Panic, msg, args...)
|
||||
}
|
||||
|
||||
//
|
||||
// Printf style
|
||||
//
|
||||
|
||||
// Logf checks that the level matches one or more targets, and
|
||||
// if so, generates a log record that is added to the main
|
||||
// queue (channel). Arguments are handled in the manner of fmt.Printf.
|
||||
func (s Sugar) Logf(lvl Level, format string, args ...interface{}) {
|
||||
if s.logger.IsLevelEnabled(lvl) {
|
||||
var msg string
|
||||
if format == "" {
|
||||
msg = fmt.Sprint(args...)
|
||||
} else {
|
||||
msg = fmt.Sprintf(format, args...)
|
||||
}
|
||||
s.logger.Log(lvl, msg)
|
||||
}
|
||||
}
|
||||
|
||||
// Tracef is a convenience method equivalent to `Logf(TraceLevel, args...)`.
|
||||
func (s Sugar) Tracef(format string, args ...interface{}) {
|
||||
s.Logf(Trace, format, args...)
|
||||
}
|
||||
|
||||
// Debugf is a convenience method equivalent to `Logf(DebugLevel, args...)`.
|
||||
func (s Sugar) Debugf(format string, args ...interface{}) {
|
||||
s.Logf(Debug, format, args...)
|
||||
}
|
||||
|
||||
// Infof is a convenience method equivalent to `Logf(InfoLevel, args...)`.
|
||||
func (s Sugar) Infof(format string, args ...interface{}) {
|
||||
s.Logf(Info, format, args...)
|
||||
}
|
||||
|
||||
// Printf ensures compatibility with std lib logger.
|
||||
func (s Sugar) Printf(format string, args ...interface{}) {
|
||||
s.Infof(format, args...)
|
||||
}
|
||||
|
||||
// Warnf is a convenience method equivalent to `Logf(WarnLevel, args...)`.
|
||||
func (s Sugar) Warnf(format string, args ...interface{}) {
|
||||
s.Logf(Warn, format, args...)
|
||||
}
|
||||
|
||||
// Errorf is a convenience method equivalent to `Logf(ErrorLevel, args...)`.
|
||||
func (s Sugar) Errorf(format string, args ...interface{}) {
|
||||
s.Logf(Error, format, args...)
|
||||
}
|
||||
|
||||
// Fatalf is a convenience method equivalent to `Logf(FatalLevel, args...)`
|
||||
func (s Sugar) Fatalf(format string, args ...interface{}) {
|
||||
s.Logf(Fatal, format, args...)
|
||||
}
|
||||
|
||||
// Panicf is a convenience method equivalent to `Logf(PanicLevel, args...)`
|
||||
func (s Sugar) Panicf(format string, args ...interface{}) {
|
||||
s.Logf(Panic, format, args...)
|
||||
}
|
||||
304
vendor/github.com/mattermost/logr/v2/target.go
сгенерированный
поставляемый
Обычный файл
304
vendor/github.com/mattermost/logr/v2/target.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,304 @@
|
||||
package logr
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Target represents a destination for log records such as file,
|
||||
// database, TCP socket, etc.
|
||||
type Target interface {
|
||||
// Init is called once to initialize the target.
|
||||
Init() error
|
||||
|
||||
// Write outputs to this target's destination.
|
||||
Write(p []byte, rec *LogRec) (int, error)
|
||||
|
||||
// Shutdown is called once to free/close any resources.
|
||||
// Target queue is already drained when this is called.
|
||||
Shutdown() error
|
||||
}
|
||||
|
||||
type targetMetrics struct {
|
||||
queueSizeGauge Gauge
|
||||
loggedCounter Counter
|
||||
errorCounter Counter
|
||||
droppedCounter Counter
|
||||
blockedCounter Counter
|
||||
}
|
||||
|
||||
type targetHostOptions struct {
|
||||
name string
|
||||
filter Filter
|
||||
formatter Formatter
|
||||
maxQueueSize int
|
||||
metrics *metrics
|
||||
}
|
||||
|
||||
// TargetHost hosts and manages the lifecycle of a target.
|
||||
// Incoming log records are queued and formatted before
|
||||
// being passed to the target.
|
||||
type TargetHost struct {
|
||||
target Target
|
||||
name string
|
||||
|
||||
filter Filter
|
||||
formatter Formatter
|
||||
|
||||
in chan *LogRec
|
||||
quit chan struct{} // closed by Shutdown to exit read loop
|
||||
done chan struct{} // closed when read loop exited
|
||||
targetMetrics *targetMetrics
|
||||
|
||||
shutdown int32
|
||||
}
|
||||
|
||||
func newTargetHost(target Target, options targetHostOptions) (*TargetHost, error) {
|
||||
host := &TargetHost{
|
||||
target: target,
|
||||
name: options.name,
|
||||
filter: options.filter,
|
||||
formatter: options.formatter,
|
||||
in: make(chan *LogRec, options.maxQueueSize),
|
||||
quit: make(chan struct{}),
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
|
||||
if host.name == "" {
|
||||
host.name = fmt.Sprintf("%T", target)
|
||||
}
|
||||
|
||||
if host.filter == nil {
|
||||
host.filter = &StdFilter{Lvl: Fatal}
|
||||
}
|
||||
if host.formatter == nil {
|
||||
host.formatter = &DefaultFormatter{}
|
||||
}
|
||||
|
||||
err := host.initMetrics(options.metrics)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = target.Init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go host.start()
|
||||
|
||||
return host, nil
|
||||
}
|
||||
|
||||
func (h *TargetHost) initMetrics(metrics *metrics) error {
|
||||
if metrics == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
tmetrics := &targetMetrics{}
|
||||
|
||||
if tmetrics.queueSizeGauge, err = metrics.collector.QueueSizeGauge(h.name); err != nil {
|
||||
return err
|
||||
}
|
||||
if tmetrics.loggedCounter, err = metrics.collector.LoggedCounter(h.name); err != nil {
|
||||
return err
|
||||
}
|
||||
if tmetrics.errorCounter, err = metrics.collector.ErrorCounter(h.name); err != nil {
|
||||
return err
|
||||
}
|
||||
if tmetrics.droppedCounter, err = metrics.collector.DroppedCounter(h.name); err != nil {
|
||||
return err
|
||||
}
|
||||
if tmetrics.blockedCounter, err = metrics.collector.BlockedCounter(h.name); err != nil {
|
||||
return err
|
||||
}
|
||||
h.targetMetrics = tmetrics
|
||||
|
||||
updateFreqMillis := metrics.updateFreqMillis
|
||||
if updateFreqMillis == 0 {
|
||||
updateFreqMillis = DefMetricsUpdateFreqMillis
|
||||
}
|
||||
if updateFreqMillis < 250 {
|
||||
updateFreqMillis = 250 // don't peg the CPU
|
||||
}
|
||||
|
||||
go h.startMetricsUpdater(updateFreqMillis)
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsLevelEnabled returns true if this target should emit logs for the specified level.
|
||||
func (h *TargetHost) IsLevelEnabled(lvl Level) (enabled bool, level Level) {
|
||||
level, enabled = h.filter.GetEnabledLevel(lvl)
|
||||
return enabled, level
|
||||
}
|
||||
|
||||
// Shutdown stops processing log records after making best
|
||||
// effort to flush queue.
|
||||
func (h *TargetHost) Shutdown(ctx context.Context) error {
|
||||
if atomic.SwapInt32(&h.shutdown, 1) != 0 {
|
||||
return errors.New("targetHost shutdown called more than once")
|
||||
}
|
||||
|
||||
close(h.quit)
|
||||
|
||||
// No more records can be accepted; now wait for read loop to exit.
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case <-h.done:
|
||||
}
|
||||
|
||||
// b.in channel should now be drained.
|
||||
return h.target.Shutdown()
|
||||
}
|
||||
|
||||
// Log queues a log record to be output to this target's destination.
|
||||
func (h *TargetHost) Log(rec *LogRec) {
|
||||
if atomic.LoadInt32(&h.shutdown) != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
lgr := rec.Logger().Logr()
|
||||
select {
|
||||
case h.in <- rec:
|
||||
default:
|
||||
handler := lgr.options.onTargetQueueFull
|
||||
if handler != nil && handler(h.target, rec, cap(h.in)) {
|
||||
h.incDroppedCounter()
|
||||
return // drop the record
|
||||
}
|
||||
h.incBlockedCounter()
|
||||
|
||||
select {
|
||||
case <-time.After(lgr.options.enqueueTimeout):
|
||||
lgr.ReportError(fmt.Errorf("target enqueue timeout for log rec [%v]", rec))
|
||||
case h.in <- rec: // block until success or timeout
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *TargetHost) setQueueSizeGauge(val float64) {
|
||||
if h.targetMetrics != nil {
|
||||
h.targetMetrics.queueSizeGauge.Set(val)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *TargetHost) incLoggedCounter() {
|
||||
if h.targetMetrics != nil {
|
||||
h.targetMetrics.loggedCounter.Inc()
|
||||
}
|
||||
}
|
||||
|
||||
func (h *TargetHost) incErrorCounter() {
|
||||
if h.targetMetrics != nil {
|
||||
h.targetMetrics.errorCounter.Inc()
|
||||
}
|
||||
}
|
||||
|
||||
func (h *TargetHost) incDroppedCounter() {
|
||||
if h.targetMetrics != nil {
|
||||
h.targetMetrics.droppedCounter.Inc()
|
||||
}
|
||||
}
|
||||
|
||||
func (h *TargetHost) incBlockedCounter() {
|
||||
if h.targetMetrics != nil {
|
||||
h.targetMetrics.blockedCounter.Inc()
|
||||
}
|
||||
}
|
||||
|
||||
// String returns a name for this target.
|
||||
func (h *TargetHost) String() string {
|
||||
return h.name
|
||||
}
|
||||
|
||||
// start accepts log records via In channel and writes to the
|
||||
// supplied target, until Done channel signaled.
|
||||
func (h *TargetHost) start() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
fmt.Fprintln(os.Stderr, "TargetHost.start -- ", r)
|
||||
go h.start()
|
||||
} else {
|
||||
close(h.done)
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
var rec *LogRec
|
||||
select {
|
||||
case rec = <-h.in:
|
||||
if rec.flush != nil {
|
||||
h.flush(rec.flush)
|
||||
} else {
|
||||
err := h.writeRec(rec)
|
||||
if err != nil {
|
||||
h.incErrorCounter()
|
||||
rec.Logger().Logr().ReportError(err)
|
||||
} else {
|
||||
h.incLoggedCounter()
|
||||
}
|
||||
}
|
||||
case <-h.quit:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *TargetHost) writeRec(rec *LogRec) error {
|
||||
level, enabled := h.filter.GetEnabledLevel(rec.Level())
|
||||
if !enabled {
|
||||
// how did we get here?
|
||||
return fmt.Errorf("level %s not enabled for target %s", rec.Level().Name, h.name)
|
||||
}
|
||||
|
||||
buf := rec.logger.lgr.BorrowBuffer()
|
||||
defer rec.logger.lgr.ReleaseBuffer(buf)
|
||||
|
||||
buf, err := h.formatter.Format(rec, level, buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = h.target.Write(buf.Bytes(), rec)
|
||||
return err
|
||||
}
|
||||
|
||||
// startMetricsUpdater updates the metrics for any polled values every `updateFreqMillis` seconds until
|
||||
// target is shut down.
|
||||
func (h *TargetHost) startMetricsUpdater(updateFreqMillis int64) {
|
||||
for {
|
||||
select {
|
||||
case <-h.done:
|
||||
return
|
||||
case <-time.After(time.Duration(updateFreqMillis) * time.Millisecond):
|
||||
h.setQueueSizeGauge(float64(len(h.in)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// flush drains the queue and notifies when done.
|
||||
func (h *TargetHost) flush(done chan<- struct{}) {
|
||||
for {
|
||||
var rec *LogRec
|
||||
var err error
|
||||
select {
|
||||
case rec = <-h.in:
|
||||
// ignore any redundant flush records.
|
||||
if rec.flush == nil {
|
||||
err = h.writeRec(rec)
|
||||
if err != nil {
|
||||
h.incErrorCounter()
|
||||
rec.Logger().Logr().ReportError(err)
|
||||
}
|
||||
}
|
||||
default:
|
||||
done <- struct{}{}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
65
vendor/github.com/mattermost/logr/target/file.go → vendor/github.com/mattermost/logr/v2/targets/file.go
сгенерированный
поставляемый
65
vendor/github.com/mattermost/logr/target/file.go → vendor/github.com/mattermost/logr/v2/targets/file.go
сгенерированный
поставляемый
@@ -1,11 +1,10 @@
|
||||
package target
|
||||
package targets
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"github.com/mattermost/logr"
|
||||
"github.com/wiggin77/merror"
|
||||
"github.com/mattermost/logr/v2"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
)
|
||||
|
||||
@@ -13,38 +12,44 @@ type FileOptions struct {
|
||||
// Filename is the file to write logs to. Backup log files will be retained
|
||||
// in the same directory. It uses <processname>-lumberjack.log in
|
||||
// os.TempDir() if empty.
|
||||
Filename string
|
||||
Filename string `json:"filename"`
|
||||
|
||||
// MaxSize is the maximum size in megabytes of the log file before it gets
|
||||
// rotated. It defaults to 100 megabytes.
|
||||
MaxSize int
|
||||
MaxSize int `json:"max_size"`
|
||||
|
||||
// MaxAge is the maximum number of days to retain old log files based on the
|
||||
// timestamp encoded in their filename. Note that a day is defined as 24
|
||||
// hours and may not exactly correspond to calendar days due to daylight
|
||||
// savings, leap seconds, etc. The default is not to remove old log files
|
||||
// based on age.
|
||||
MaxAge int
|
||||
MaxAge int `json:"max_age"`
|
||||
|
||||
// MaxBackups is the maximum number of old log files to retain. The default
|
||||
// is to retain all old log files (though MaxAge may still cause them to get
|
||||
// deleted.)
|
||||
MaxBackups int
|
||||
MaxBackups int `json:"max_backups"`
|
||||
|
||||
// Compress determines if the rotated log files should be compressed
|
||||
// using gzip. The default is not to perform compression.
|
||||
Compress bool
|
||||
Compress bool `json:"compress"`
|
||||
}
|
||||
|
||||
func (fo FileOptions) CheckValid() error {
|
||||
if fo.Filename == "" {
|
||||
return errors.New("filename cannot be empty")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// File outputs log records to a file which can be log rotated based on size or age.
|
||||
// Uses `https://github.com/natefinch/lumberjack` for rotation.
|
||||
type File struct {
|
||||
logr.Basic
|
||||
out io.WriteCloser
|
||||
}
|
||||
|
||||
// NewFileTarget creates a target capable of outputting log records to a rotated file.
|
||||
func NewFileTarget(filter logr.Filter, formatter logr.Formatter, opts FileOptions, maxQueue int) *File {
|
||||
func NewFileTarget(opts FileOptions) *File {
|
||||
lumber := &lumberjack.Logger{
|
||||
Filename: opts.Filename,
|
||||
MaxSize: opts.MaxSize,
|
||||
@@ -53,35 +58,21 @@ func NewFileTarget(filter logr.Filter, formatter logr.Formatter, opts FileOption
|
||||
Compress: opts.Compress,
|
||||
}
|
||||
f := &File{out: lumber}
|
||||
f.Basic.Start(f, f, filter, formatter, maxQueue)
|
||||
return f
|
||||
}
|
||||
|
||||
// Write converts the log record to bytes, via the Formatter,
|
||||
// and outputs to a file.
|
||||
func (f *File) Write(rec *logr.LogRec) error {
|
||||
_, stacktrace := f.IsLevelEnabled(rec.Level())
|
||||
|
||||
buf := rec.Logger().Logr().BorrowBuffer()
|
||||
defer rec.Logger().Logr().ReleaseBuffer(buf)
|
||||
|
||||
buf, err := f.Formatter().Format(rec, stacktrace, buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = f.out.Write(buf.Bytes())
|
||||
return err
|
||||
// Init is called once to initialize the target.
|
||||
func (f *File) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Shutdown flushes any remaining log records and closes the file.
|
||||
func (f *File) Shutdown(ctx context.Context) error {
|
||||
errs := merror.New()
|
||||
|
||||
err := f.Basic.Shutdown(ctx)
|
||||
errs.Append(err)
|
||||
|
||||
err = f.out.Close()
|
||||
errs.Append(err)
|
||||
|
||||
return errs.ErrorOrNil()
|
||||
// Write outputs bytes to this file target.
|
||||
func (f *File) Write(p []byte, rec *logr.LogRec) (int, error) {
|
||||
return f.out.Write(p)
|
||||
}
|
||||
|
||||
// Shutdown is called once to free/close any resources.
|
||||
// Target queue is already drained when this is called.
|
||||
func (f *File) Shutdown() error {
|
||||
return f.out.Close()
|
||||
}
|
||||
112
vendor/github.com/mattermost/logr/v2/targets/syslog.go
сгенерированный
поставляемый
Обычный файл
112
vendor/github.com/mattermost/logr/v2/targets/syslog.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,112 @@
|
||||
// +build !windows,!nacl,!plan9
|
||||
|
||||
package targets
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/mattermost/logr/v2"
|
||||
syslog "github.com/wiggin77/srslog"
|
||||
)
|
||||
|
||||
// Syslog outputs log records to local or remote syslog.
|
||||
type Syslog struct {
|
||||
params *SyslogOptions
|
||||
writer *syslog.Writer
|
||||
}
|
||||
|
||||
// SyslogOptions provides parameters for dialing a syslog daemon.
|
||||
type SyslogOptions struct {
|
||||
IP string `json:"ip,omitempty"` // deprecated
|
||||
Host string `json:"host"`
|
||||
Port int `json:"port"`
|
||||
TLS bool `json:"tls"`
|
||||
Cert string `json:"cert"`
|
||||
Insecure bool `json:"insecure"`
|
||||
Tag string `json:"tag"`
|
||||
}
|
||||
|
||||
func (so SyslogOptions) CheckValid() error {
|
||||
if so.Host == "" && so.IP == "" {
|
||||
return errors.New("missing host")
|
||||
}
|
||||
if so.Port == 0 {
|
||||
return errors.New("missing port")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewSyslogTarget creates a target capable of outputting log records to remote or local syslog, with or without TLS.
|
||||
func NewSyslogTarget(params *SyslogOptions) (*Syslog, error) {
|
||||
if params == nil {
|
||||
return nil, errors.New("params cannot be nil")
|
||||
}
|
||||
|
||||
s := &Syslog{
|
||||
params: params,
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Init is called once to initialize the target.
|
||||
func (s *Syslog) Init() error {
|
||||
network := "tcp"
|
||||
var config *tls.Config
|
||||
|
||||
if s.params.TLS {
|
||||
network = "tcp+tls"
|
||||
config = &tls.Config{InsecureSkipVerify: s.params.Insecure}
|
||||
if s.params.Cert != "" {
|
||||
pool, err := GetCertPool(s.params.Cert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config.RootCAs = pool
|
||||
}
|
||||
}
|
||||
raddr := fmt.Sprintf("%s:%d", s.params.IP, s.params.Port)
|
||||
if raddr == ":0" {
|
||||
// If no IP:port provided then connect to local syslog.
|
||||
raddr = ""
|
||||
network = ""
|
||||
}
|
||||
|
||||
var err error
|
||||
s.writer, err = syslog.DialWithTLSConfig(network, raddr, syslog.LOG_INFO, s.params.Tag, config)
|
||||
return err
|
||||
}
|
||||
|
||||
// Write outputs bytes to this file target.
|
||||
func (s *Syslog) Write(p []byte, rec *logr.LogRec) (int, error) {
|
||||
txt := string(p)
|
||||
n := len(txt)
|
||||
var err error
|
||||
|
||||
switch rec.Level() {
|
||||
case logr.Panic, logr.Fatal:
|
||||
err = s.writer.Crit(txt)
|
||||
case logr.Error:
|
||||
err = s.writer.Err(txt)
|
||||
case logr.Warn:
|
||||
err = s.writer.Warning(txt)
|
||||
case logr.Debug, logr.Trace:
|
||||
err = s.writer.Debug(txt)
|
||||
default:
|
||||
// logr.Info plus all custom levels.
|
||||
err = s.writer.Info(txt)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
n = 0
|
||||
// syslog writer will try to reconnect.
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Shutdown is called once to free/close any resources.
|
||||
// Target queue is already drained when this is called.
|
||||
func (s *Syslog) Shutdown() error {
|
||||
return s.writer.Close()
|
||||
}
|
||||
56
vendor/github.com/mattermost/logr/v2/targets/syslog_unsupported.go
сгенерированный
поставляемый
Обычный файл
56
vendor/github.com/mattermost/logr/v2/targets/syslog_unsupported.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,56 @@
|
||||
// +build windows nacl plan9
|
||||
|
||||
package targets
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/mattermost/logr/v2"
|
||||
syslog "github.com/wiggin77/srslog"
|
||||
)
|
||||
|
||||
const (
|
||||
unsupported = "Syslog target is not supported on this platform."
|
||||
)
|
||||
|
||||
// Syslog outputs log records to local or remote syslog.
|
||||
type Syslog struct {
|
||||
params *SyslogOptions
|
||||
writer *syslog.Writer
|
||||
}
|
||||
|
||||
// SyslogOptions provides parameters for dialing a syslog daemon.
|
||||
type SyslogOptions struct {
|
||||
IP string `json:"ip,omitempty"` // deprecated
|
||||
Host string `json:"host"`
|
||||
Port int `json:"port"`
|
||||
TLS bool `json:"tls"`
|
||||
Cert string `json:"cert"`
|
||||
Insecure bool `json:"insecure"`
|
||||
Tag string `json:"tag"`
|
||||
}
|
||||
|
||||
func (so SyslogOptions) CheckValid() error {
|
||||
return errors.New(unsupported)
|
||||
}
|
||||
|
||||
// NewSyslogTarget creates a target capable of outputting log records to remote or local syslog, with or without TLS.
|
||||
func NewSyslogTarget(params *SyslogOptions) (*Syslog, error) {
|
||||
return nil, errors.New(unsupported)
|
||||
}
|
||||
|
||||
// Init is called once to initialize the target.
|
||||
func (s *Syslog) Init() error {
|
||||
return errors.New(unsupported)
|
||||
}
|
||||
|
||||
// Write outputs bytes to this file target.
|
||||
func (s *Syslog) Write(p []byte, rec *logr.LogRec) (int, error) {
|
||||
return 0, errors.New(unsupported)
|
||||
}
|
||||
|
||||
// Shutdown is called once to free/close any resources.
|
||||
// Target queue is already drained when this is called.
|
||||
func (s *Syslog) Shutdown() error {
|
||||
return errors.New(unsupported)
|
||||
}
|
||||
251
vendor/github.com/mattermost/logr/v2/targets/tcp.go
сгенерированный
поставляемый
Обычный файл
251
vendor/github.com/mattermost/logr/v2/targets/tcp.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,251 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package targets
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/logr/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
DialTimeoutSecs = 30
|
||||
WriteTimeoutSecs = 30
|
||||
RetryBackoffMillis int64 = 100
|
||||
MaxRetryBackoffMillis int64 = 30 * 1000 // 30 seconds
|
||||
)
|
||||
|
||||
// Tcp outputs log records to raw socket server.
|
||||
type Tcp struct {
|
||||
options *TcpOptions
|
||||
addy string
|
||||
|
||||
mutex sync.Mutex
|
||||
conn net.Conn
|
||||
monitor chan struct{}
|
||||
shutdown chan struct{}
|
||||
}
|
||||
|
||||
// TcpOptions provides parameters for dialing a socket server.
|
||||
type TcpOptions struct {
|
||||
IP string `json:"ip,omitempty"` // deprecated
|
||||
Host string `json:"host"`
|
||||
Port int `json:"port"`
|
||||
TLS bool `json:"tls"`
|
||||
Cert string `json:"cert"`
|
||||
Insecure bool `json:"insecure"`
|
||||
}
|
||||
|
||||
func (to TcpOptions) CheckValid() error {
|
||||
if to.Host == "" && to.IP == "" {
|
||||
return errors.New("missing host")
|
||||
}
|
||||
if to.Port == 0 {
|
||||
return errors.New("missing port")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewTcpTarget creates a target capable of outputting log records to a raw socket, with or without TLS.
|
||||
func NewTcpTarget(options *TcpOptions) *Tcp {
|
||||
tcp := &Tcp{
|
||||
options: options,
|
||||
addy: fmt.Sprintf("%s:%d", options.IP, options.Port),
|
||||
monitor: make(chan struct{}),
|
||||
shutdown: make(chan struct{}),
|
||||
}
|
||||
return tcp
|
||||
}
|
||||
|
||||
// Init is called once to initialize the target.
|
||||
func (tcp *Tcp) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// getConn provides a net.Conn. If a connection already exists, it is returned immediately,
|
||||
// otherwise this method blocks until a new connection is created, timeout or shutdown.
|
||||
func (tcp *Tcp) getConn(reporter func(err interface{})) (net.Conn, error) {
|
||||
tcp.mutex.Lock()
|
||||
defer tcp.mutex.Unlock()
|
||||
|
||||
if tcp.conn != nil {
|
||||
return tcp.conn, nil
|
||||
}
|
||||
|
||||
type result struct {
|
||||
conn net.Conn
|
||||
err error
|
||||
}
|
||||
|
||||
connChan := make(chan result)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*DialTimeoutSecs)
|
||||
defer cancel()
|
||||
|
||||
go func(ctx context.Context, ch chan result) {
|
||||
conn, err := tcp.dial(ctx)
|
||||
if err != nil {
|
||||
reporter(fmt.Errorf("log target %s connection error: %w", tcp.String(), err))
|
||||
return
|
||||
}
|
||||
tcp.conn = conn
|
||||
tcp.monitor = make(chan struct{})
|
||||
go monitor(tcp.conn, tcp.monitor)
|
||||
ch <- result{conn: conn, err: err}
|
||||
}(ctx, connChan)
|
||||
|
||||
select {
|
||||
case <-tcp.shutdown:
|
||||
return nil, errors.New("shutdown")
|
||||
case res := <-connChan:
|
||||
return res.conn, res.err
|
||||
}
|
||||
}
|
||||
|
||||
// dial connects to a TCP socket, and optionally performs a TLS handshake.
|
||||
// A non-nil context must be provided which can cancel the dial.
|
||||
func (tcp *Tcp) dial(ctx context.Context) (net.Conn, error) {
|
||||
var dialer net.Dialer
|
||||
dialer.Timeout = time.Second * DialTimeoutSecs
|
||||
conn, err := dialer.DialContext(ctx, "tcp", fmt.Sprintf("%s:%d", tcp.options.IP, tcp.options.Port))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !tcp.options.TLS {
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
tlsconfig := &tls.Config{
|
||||
ServerName: tcp.options.IP,
|
||||
InsecureSkipVerify: tcp.options.Insecure,
|
||||
}
|
||||
if tcp.options.Cert != "" {
|
||||
pool, err := GetCertPool(tcp.options.Cert)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsconfig.RootCAs = pool
|
||||
}
|
||||
|
||||
tlsConn := tls.Client(conn, tlsconfig)
|
||||
if err := tlsConn.Handshake(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tlsConn, nil
|
||||
}
|
||||
|
||||
func (tcp *Tcp) close() error {
|
||||
tcp.mutex.Lock()
|
||||
defer tcp.mutex.Unlock()
|
||||
|
||||
var err error
|
||||
if tcp.conn != nil {
|
||||
close(tcp.monitor)
|
||||
err = tcp.conn.Close()
|
||||
tcp.conn = nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Shutdown stops processing log records after making best effort to flush queue.
|
||||
func (tcp *Tcp) Shutdown() error {
|
||||
err := tcp.close()
|
||||
close(tcp.shutdown)
|
||||
return err
|
||||
}
|
||||
|
||||
// Write converts the log record to bytes, via the Formatter, and outputs to the socket.
|
||||
// Called by dedicated target goroutine and will block until success or shutdown.
|
||||
func (tcp *Tcp) Write(p []byte, rec *logr.LogRec) (int, error) {
|
||||
try := 1
|
||||
backoff := RetryBackoffMillis
|
||||
for {
|
||||
select {
|
||||
case <-tcp.shutdown:
|
||||
return 0, nil
|
||||
default:
|
||||
}
|
||||
|
||||
reporter := rec.Logger().Logr().ReportError
|
||||
|
||||
conn, err := tcp.getConn(reporter)
|
||||
if err != nil {
|
||||
reporter(fmt.Errorf("log target %s connection error: %w", tcp.String(), err))
|
||||
backoff = tcp.sleep(backoff)
|
||||
continue
|
||||
}
|
||||
|
||||
err = conn.SetWriteDeadline(time.Now().Add(time.Second * WriteTimeoutSecs))
|
||||
if err != nil {
|
||||
reporter(fmt.Errorf("log target %s set write deadline error: %w", tcp.String(), err))
|
||||
}
|
||||
|
||||
count, err := conn.Write(p)
|
||||
if err == nil {
|
||||
return count, nil
|
||||
}
|
||||
|
||||
reporter(fmt.Errorf("log target %s write error: %w", tcp.String(), err))
|
||||
|
||||
_ = tcp.close()
|
||||
|
||||
backoff = tcp.sleep(backoff)
|
||||
try++
|
||||
}
|
||||
}
|
||||
|
||||
// monitor continuously tries to read from the connection to detect socket close.
|
||||
// This is needed because TCP target uses a write only socket and Linux systems
|
||||
// take a long time to detect a loss of connectivity on a socket when only writing;
|
||||
// the writes simply fail without an error returned.
|
||||
func monitor(conn net.Conn, done <-chan struct{}) {
|
||||
buf := make([]byte, 1)
|
||||
for {
|
||||
select {
|
||||
case <-done:
|
||||
return
|
||||
case <-time.After(1 * time.Second):
|
||||
}
|
||||
|
||||
err := conn.SetReadDeadline(time.Now().Add(time.Second * 30))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
_, err = conn.Read(buf)
|
||||
|
||||
if errt, ok := err.(net.Error); ok && errt.Timeout() {
|
||||
// read timeout is expected, keep looping.
|
||||
continue
|
||||
}
|
||||
|
||||
// Any other error closes the connection, forcing a reconnect.
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// String returns a string representation of this target.
|
||||
func (tcp *Tcp) String() string {
|
||||
return fmt.Sprintf("TcpTarget[%s:%d]", tcp.options.IP, tcp.options.Port)
|
||||
}
|
||||
|
||||
func (tcp *Tcp) sleep(backoff int64) int64 {
|
||||
select {
|
||||
case <-tcp.shutdown:
|
||||
case <-time.After(time.Millisecond * time.Duration(backoff)):
|
||||
}
|
||||
|
||||
nextBackoff := backoff + (backoff >> 1)
|
||||
if nextBackoff > MaxRetryBackoffMillis {
|
||||
nextBackoff = MaxRetryBackoffMillis
|
||||
}
|
||||
return nextBackoff
|
||||
}
|
||||
43
vendor/github.com/mattermost/logr/v2/targets/test-tls-client-cert.pem
сгенерированный
поставляемый
Обычный файл
43
vendor/github.com/mattermost/logr/v2/targets/test-tls-client-cert.pem
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,43 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDjzCCAnegAwIBAgIRAPYfRSwdzKopBKxYxKqslJUwDQYJKoZIhvcNAQELBQAw
|
||||
JzElMCMGA1UEAwwcTWF0dGVybW9zdCwgSW5jLiBJbnRlcm5hbCBDQTAeFw0xOTAz
|
||||
MjIwMDE0MTVaFw0yMjAzMDYwMDE0MTVaMDsxOTA3BgNVBAMTME1hdHRlcm1vc3Qs
|
||||
IEluYy4gSW50ZXJuYWwgSW50ZXJtZWRpYXRlIEF1dGhvcml0eTCCASIwDQYJKoZI
|
||||
hvcNAQEBBQADggEPADCCAQoCggEBAMjliRdmvnNL4u/Jr/M2dPwQmTJXEBY/Vq9Q
|
||||
vAU52X3tRMCPxcaFz+x6ftuvdO2NdohXGAmtx9QU5LZcvFeTDpoVEBo9A+4jtLvD
|
||||
DZYaTNLpJmoSoJHaDbdWX+OAOqyDiWS741LuiMKWHhew9QOisat2ZINPxjmAd9wE
|
||||
xthTMgzsv7MUqnMer8U5OGQ0Qy7wAmNRc+2K3qPwkxe2RUvcte50DUFNgxEginsh
|
||||
vrkOXR383vUCZfu72qu8oggjiQpyTllu5je2Ap6JLjYLkEMiMqrYADuWor/ZHwa6
|
||||
WrFqVETxWfAV5u9Eh0wZM/KKYwRQuw9y+Nans77FmUl1tVWWNN8CAwEAAaOBoTCB
|
||||
njAMBgNVHRMEBTADAQH/MB0GA1UdDgQWBBQY4Uqswyr2hO/HetZt2RDxJdTIPjBi
|
||||
BgNVHSMEWzBZgBRFZXVg2Z5tNIsWeWjBLEy2yzKbMKErpCkwJzElMCMGA1UEAwwc
|
||||
TWF0dGVybW9zdCwgSW5jLiBJbnRlcm5hbCBDQYIUEifGUOM+bIFZo1tkjZB5YGBr
|
||||
0xEwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBCwUAA4IBAQAEdexL30Q0zBHmPAH8
|
||||
LhdK7dbzW1CmILbxRZlKAwRN+hKRXiMW3MHIkhNuoV9Aev602Q+ja4lWsRi/ktOL
|
||||
ni1FWx5gSScgdG8JGj47dOmoT3vXKX7+umiv4rQLPDl9/DKMuv204OYJq6VT+uNU
|
||||
6C6kL157jGJEO76H4fMZ8oYsD7Sq0zjiNKtuCYii0ngH3j3gB1jACLqRgveU7MdT
|
||||
pqOV2KfY31+h8VBtkUvljNztQ9xNY8Fjmt0SMf7E3FaUcaar3ZCr70G5aU3dKbe7
|
||||
47vGOBa5tCqw4YK0jgDKid3IJQul9a3J1mSsH8Wy3to9cAV4KGZBQLnzCX15a/+v
|
||||
3yVh
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDfjCCAmagAwIBAgIUEifGUOM+bIFZo1tkjZB5YGBr0xEwDQYJKoZIhvcNAQEL
|
||||
BQAwJzElMCMGA1UEAwwcTWF0dGVybW9zdCwgSW5jLiBJbnRlcm5hbCBDQTAeFw0x
|
||||
OTAzMjEyMTI4NDNaFw0yOTAzMTgyMTI4NDNaMCcxJTAjBgNVBAMMHE1hdHRlcm1v
|
||||
c3QsIEluYy4gSW50ZXJuYWwgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
|
||||
AoIBAQDH0Xq5rMBGpKOVWTpb5MnaJIWFP/vOtvEk+7hVrfOfe1/5x0Kk3UgAHj85
|
||||
otaEZD1Lhn/JLkEqCiE/UXMJFwJDlNcO4CkdKBSpYX4bKAqy5q/X3QwioMSNpJG1
|
||||
+YYrNGBH0sgKcKjyCaLhmqYLD0xZDVOmWIYBU9jUPyXw5U0tnsVrTqGMxVkm1xCY
|
||||
krCWN1ZoUrLvL0MCZc5qpxoPTopr9UO9cqSBSuy6BVWVuEWBZhpqHt+ul8VxhzzY
|
||||
q1k4l7r2qw+/wm1iJBedTeBVeWNag8JaVfLgu+/W7oJVlPO32Po7pnvHp8iJ3b4K
|
||||
zXyVHaTX4S6Em+6LV8855TYrShzlAgMBAAGjgaEwgZ4wHQYDVR0OBBYEFEVldWDZ
|
||||
nm00ixZ5aMEsTLbLMpswMGIGA1UdIwRbMFmAFEVldWDZnm00ixZ5aMEsTLbLMpsw
|
||||
oSukKTAnMSUwIwYDVQQDDBxNYXR0ZXJtb3N0LCBJbmMuIEludGVybmFsIENBghQS
|
||||
J8ZQ4z5sgVmjW2SNkHlgYGvTETAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjAN
|
||||
BgkqhkiG9w0BAQsFAAOCAQEAPiCWFmopyAkY2T3Zyo4yaRPhX1+VOTMKJtY6EUhq
|
||||
/GHz6kzEyvCUBf0N892cibGxekrEoItY9NqO6RQRfowg+Gn5kc13z4NyL2W8/eoT
|
||||
Xy0ZvfaQbU++fQ6pVtWtMblDMU9xiYd7/MDvJpO328l1Vhcdp8kEi+lCvpy0sCRc
|
||||
PxzPhbgCMAbZEGx+4TMQd4SZKzlRxW/2fflpReh6v1Dv0VDUSYQWwsUnaLpdKHfh
|
||||
a5k0vuySYcszE4YKlY0zakeFlJfp7fBp1xTwcdW8aTfw15EicPMwTc6xxA4JJUJx
|
||||
cddu817n1nayK5u6r9Qh1oIVkr0nC9YELMMy4dpPgJ88SA==
|
||||
-----END CERTIFICATE-----
|
||||
33
vendor/github.com/mattermost/logr/v2/targets/utils.go
сгенерированный
поставляемый
Обычный файл
33
vendor/github.com/mattermost/logr/v2/targets/utils.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,33 @@
|
||||
package targets
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
// GetCertPool returns a x509.CertPool containing the cert(s)
|
||||
// from `cert`, which can be a path to a .pem or .crt file,
|
||||
// or a base64 encoded cert.
|
||||
func GetCertPool(cert string) (*x509.CertPool, error) {
|
||||
if cert == "" {
|
||||
return nil, errors.New("no cert provided")
|
||||
}
|
||||
|
||||
// first treat as a file and try to read.
|
||||
serverCert, err := ioutil.ReadFile(cert)
|
||||
if err != nil {
|
||||
// maybe it's a base64 encoded cert
|
||||
serverCert, err = base64.StdEncoding.DecodeString(cert)
|
||||
if err != nil {
|
||||
return nil, errors.New("cert cannot be read")
|
||||
}
|
||||
}
|
||||
|
||||
pool := x509.NewCertPool()
|
||||
if ok := pool.AppendCertsFromPEM(serverCert); ok {
|
||||
return pool, nil
|
||||
}
|
||||
return nil, errors.New("cannot parse cert")
|
||||
}
|
||||
38
vendor/github.com/mattermost/logr/v2/targets/writer.go
сгенерированный
поставляемый
Обычный файл
38
vendor/github.com/mattermost/logr/v2/targets/writer.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,38 @@
|
||||
package targets
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/mattermost/logr/v2"
|
||||
)
|
||||
|
||||
// Writer outputs log records to any `io.Writer`.
|
||||
type Writer struct {
|
||||
out io.Writer
|
||||
}
|
||||
|
||||
// NewWriterTarget creates a target capable of outputting log records to an io.Writer.
|
||||
func NewWriterTarget(out io.Writer) *Writer {
|
||||
if out == nil {
|
||||
out = ioutil.Discard
|
||||
}
|
||||
w := &Writer{out: out}
|
||||
return w
|
||||
}
|
||||
|
||||
// Init is called once to initialize the target.
|
||||
func (w *Writer) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write outputs bytes to this file target.
|
||||
func (w *Writer) Write(p []byte, rec *logr.LogRec) (int, error) {
|
||||
return w.out.Write(p)
|
||||
}
|
||||
|
||||
// Shutdown is called once to free/close any resources.
|
||||
// Target queue is already drained when this is called.
|
||||
func (w *Writer) Shutdown() error {
|
||||
return nil
|
||||
}
|
||||
0
vendor/github.com/mattermost/logr/timeout.go → vendor/github.com/mattermost/logr/v2/timeout.go
сгенерированный
поставляемый
0
vendor/github.com/mattermost/logr/timeout.go → vendor/github.com/mattermost/logr/v2/timeout.go
сгенерированный
поставляемый
12
vendor/github.com/wiggin77/cfg/.gitignore
сгенерированный
поставляемый
12
vendor/github.com/wiggin77/cfg/.gitignore
сгенерированный
поставляемый
@@ -1,12 +0,0 @@
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
5
vendor/github.com/wiggin77/cfg/.travis.yml
сгенерированный
поставляемый
5
vendor/github.com/wiggin77/cfg/.travis.yml
сгенерированный
поставляемый
@@ -1,5 +0,0 @@
|
||||
language: go
|
||||
sudo: false
|
||||
before_script:
|
||||
- go vet ./...
|
||||
|
||||
21
vendor/github.com/wiggin77/cfg/LICENSE
сгенерированный
поставляемый
21
vendor/github.com/wiggin77/cfg/LICENSE
сгенерированный
поставляемый
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 wiggin77
|
||||
|
||||
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.
|
||||
43
vendor/github.com/wiggin77/cfg/README.md
сгенерированный
поставляемый
43
vendor/github.com/wiggin77/cfg/README.md
сгенерированный
поставляемый
@@ -1,43 +0,0 @@
|
||||
# cfg
|
||||
|
||||
[](https://godoc.org/github.com/wiggin77/cfg)
|
||||
[](https://travis-ci.org/wiggin77/cfg)
|
||||
|
||||
Go package for app configuration. Supports chained configuration sources for multiple levels of defaults.
|
||||
Includes APIs for loading Linux style configuration files (name/value pairs) or INI files, map based properties,
|
||||
or easily create new configuration sources (e.g. load from database).
|
||||
|
||||
Supports monitoring configuration sources for changes, hot loading properties, and notifying listeners of changes.
|
||||
|
||||
## Usage
|
||||
|
||||
```Go
|
||||
config := &cfg.Config{}
|
||||
defer config.Shutdown() // stops monitoring
|
||||
|
||||
// load file via filespec string, os.File
|
||||
src, err := Config.NewSrcFileFromFilespec("./myfile.conf")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// add src to top of chain, meaning first searched
|
||||
cfg.PrependSource(src)
|
||||
|
||||
// fetch prop 'retries', default to 3 if not found
|
||||
val := config.Int("retries", 3)
|
||||
```
|
||||
|
||||
See [example](./example_test.go) for more complete example, including listening for configuration changes.
|
||||
|
||||
Config API parses the following data types:
|
||||
|
||||
| type | method | example property values |
|
||||
| ------- | ------ | -------- |
|
||||
| string | Config.String | test, "" |
|
||||
| int | Config.Int | -1, 77, 0 |
|
||||
| int64 | Config.Int64 | -9223372036854775, 372036854775808 |
|
||||
| float64 | Config.Float64 | -77.3456, 95642331.1 |
|
||||
| bool | Config.Bool | T,t,true,True,1,0,False,false,f,F |
|
||||
| time.Duration | Config.Duration | "10ms", "2 hours", "5 min" * |
|
||||
|
||||
\* Units of measure supported: ms, sec, min, hour, day, week, year.
|
||||
366
vendor/github.com/wiggin77/cfg/config.go
сгенерированный
поставляемый
366
vendor/github.com/wiggin77/cfg/config.go
сгенерированный
поставляемый
@@ -1,366 +0,0 @@
|
||||
package cfg
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/wiggin77/cfg/timeconv"
|
||||
)
|
||||
|
||||
// ErrNotFound returned when an operation is attempted on a
|
||||
// resource that doesn't exist, such as fetching a non-existing
|
||||
// property name.
|
||||
var ErrNotFound = errors.New("not found")
|
||||
|
||||
type sourceEntry struct {
|
||||
src Source
|
||||
props map[string]string
|
||||
}
|
||||
|
||||
// Config provides methods for retrieving property values from one or more
|
||||
// configuration sources.
|
||||
type Config struct {
|
||||
mutexSrc sync.RWMutex
|
||||
mutexListeners sync.RWMutex
|
||||
srcs []*sourceEntry
|
||||
chgListeners []ChangedListener
|
||||
shutdown chan interface{}
|
||||
wantPanicOnError bool
|
||||
}
|
||||
|
||||
// PrependSource inserts one or more `Sources` at the beginning of
|
||||
// the list of sources such that the first source will be the
|
||||
// source checked first when resolving a property value.
|
||||
func (config *Config) PrependSource(srcs ...Source) {
|
||||
arr := config.wrapSources(srcs...)
|
||||
|
||||
config.mutexSrc.Lock()
|
||||
if config.shutdown == nil {
|
||||
config.shutdown = make(chan interface{})
|
||||
}
|
||||
config.srcs = append(arr, config.srcs...)
|
||||
config.mutexSrc.Unlock()
|
||||
|
||||
for _, se := range arr {
|
||||
if _, ok := se.src.(SourceMonitored); ok {
|
||||
config.monitor(se)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AppendSource appends one or more `Sources` at the end of
|
||||
// the list of sources such that the last source will be the
|
||||
// source checked last when resolving a property value.
|
||||
func (config *Config) AppendSource(srcs ...Source) {
|
||||
arr := config.wrapSources(srcs...)
|
||||
|
||||
config.mutexSrc.Lock()
|
||||
if config.shutdown == nil {
|
||||
config.shutdown = make(chan interface{})
|
||||
}
|
||||
config.srcs = append(config.srcs, arr...)
|
||||
config.mutexSrc.Unlock()
|
||||
|
||||
for _, se := range arr {
|
||||
if _, ok := se.src.(SourceMonitored); ok {
|
||||
config.monitor(se)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// wrapSources wraps one or more Source's and returns
|
||||
// them as an array of `sourceEntry`.
|
||||
func (config *Config) wrapSources(srcs ...Source) []*sourceEntry {
|
||||
arr := make([]*sourceEntry, 0, len(srcs))
|
||||
for _, src := range srcs {
|
||||
se := &sourceEntry{src: src}
|
||||
config.reloadProps(se)
|
||||
arr = append(arr, se)
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
// SetWantPanicOnError sets the flag determining if Config
|
||||
// should panic when `GetProps` or `GetLastModified` errors
|
||||
// for a `Source`.
|
||||
func (config *Config) SetWantPanicOnError(b bool) {
|
||||
config.mutexSrc.Lock()
|
||||
config.wantPanicOnError = b
|
||||
config.mutexSrc.Unlock()
|
||||
}
|
||||
|
||||
// ShouldPanicOnError gets the flag determining if Config
|
||||
// should panic when `GetProps` or `GetLastModified` errors
|
||||
// for a `Source`.
|
||||
func (config *Config) ShouldPanicOnError() (b bool) {
|
||||
config.mutexSrc.RLock()
|
||||
b = config.wantPanicOnError
|
||||
config.mutexSrc.RUnlock()
|
||||
return b
|
||||
}
|
||||
|
||||
// getProp returns the value of a named property.
|
||||
// Each `Source` is checked, in the order created by adding via
|
||||
// `AppendSource` and `PrependSource`, until a value for the
|
||||
// property is found.
|
||||
func (config *Config) getProp(name string) (val string, ok bool) {
|
||||
config.mutexSrc.RLock()
|
||||
defer config.mutexSrc.RUnlock()
|
||||
|
||||
var s string
|
||||
for _, se := range config.srcs {
|
||||
if se.props != nil {
|
||||
if s, ok = se.props[name]; ok {
|
||||
val = strings.TrimSpace(s)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// String returns the value of the named prop as a string.
|
||||
// If the property is not found then the supplied default `def`
|
||||
// and `ErrNotFound` are returned.
|
||||
func (config *Config) String(name string, def string) (val string, err error) {
|
||||
if v, ok := config.getProp(name); ok {
|
||||
val = v
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
|
||||
err = ErrNotFound
|
||||
val = def
|
||||
return
|
||||
}
|
||||
|
||||
// Int returns the value of the named prop as an `int`.
|
||||
// If the property is not found then the supplied default `def`
|
||||
// and `ErrNotFound` are returned.
|
||||
//
|
||||
// See config.String
|
||||
func (config *Config) Int(name string, def int) (val int, err error) {
|
||||
var s string
|
||||
if s, err = config.String(name, ""); err == nil {
|
||||
var i int64
|
||||
if i, err = strconv.ParseInt(s, 10, 32); err == nil {
|
||||
val = int(i)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
val = def
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Int64 returns the value of the named prop as an `int64`.
|
||||
// If the property is not found then the supplied default `def`
|
||||
// and `ErrNotFound` are returned.
|
||||
//
|
||||
// See config.String
|
||||
func (config *Config) Int64(name string, def int64) (val int64, err error) {
|
||||
var s string
|
||||
if s, err = config.String(name, ""); err == nil {
|
||||
val, err = strconv.ParseInt(s, 10, 64)
|
||||
}
|
||||
if err != nil {
|
||||
val = def
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Float64 returns the value of the named prop as a `float64`.
|
||||
// If the property is not found then the supplied default `def`
|
||||
// and `ErrNotFound` are returned.
|
||||
//
|
||||
// See config.String
|
||||
func (config *Config) Float64(name string, def float64) (val float64, err error) {
|
||||
var s string
|
||||
if s, err = config.String(name, ""); err == nil {
|
||||
val, err = strconv.ParseFloat(s, 64)
|
||||
}
|
||||
if err != nil {
|
||||
val = def
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Bool returns the value of the named prop as a `bool`.
|
||||
// If the property is not found then the supplied default `def`
|
||||
// and `ErrNotFound` are returned.
|
||||
//
|
||||
// Supports (t, true, 1, y, yes) for true, and (f, false, 0, n, no) for false,
|
||||
// all case-insensitive.
|
||||
//
|
||||
// See config.String
|
||||
func (config *Config) Bool(name string, def bool) (val bool, err error) {
|
||||
var s string
|
||||
if s, err = config.String(name, ""); err == nil {
|
||||
switch strings.ToLower(s) {
|
||||
case "t", "true", "1", "y", "yes":
|
||||
val = true
|
||||
case "f", "false", "0", "n", "no":
|
||||
val = false
|
||||
default:
|
||||
err = errors.New("invalid syntax")
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
val = def
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Duration returns the value of the named prop as a `time.Duration`, representing
|
||||
// a span of time.
|
||||
//
|
||||
// Units of measure are supported: ms, sec, min, hour, day, week, year.
|
||||
// See config.UnitsToMillis for a complete list of units supported.
|
||||
//
|
||||
// If the property is not found then the supplied default `def`
|
||||
// and `ErrNotFound` are returned.
|
||||
//
|
||||
// See config.String
|
||||
func (config *Config) Duration(name string, def time.Duration) (val time.Duration, err error) {
|
||||
var s string
|
||||
if s, err = config.String(name, ""); err == nil {
|
||||
var ms int64
|
||||
ms, err = timeconv.ParseMilliseconds(s)
|
||||
val = time.Duration(ms) * time.Millisecond
|
||||
}
|
||||
if err != nil {
|
||||
val = def
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// AddChangedListener adds a listener that will receive notifications
|
||||
// whenever one or more property values change within the config.
|
||||
func (config *Config) AddChangedListener(l ChangedListener) {
|
||||
config.mutexListeners.Lock()
|
||||
defer config.mutexListeners.Unlock()
|
||||
|
||||
config.chgListeners = append(config.chgListeners, l)
|
||||
}
|
||||
|
||||
// RemoveChangedListener removes all instances of a ChangedListener.
|
||||
// Returns `ErrNotFound` if the listener was not present.
|
||||
func (config *Config) RemoveChangedListener(l ChangedListener) error {
|
||||
config.mutexListeners.Lock()
|
||||
defer config.mutexListeners.Unlock()
|
||||
|
||||
dest := make([]ChangedListener, 0, len(config.chgListeners))
|
||||
err := ErrNotFound
|
||||
|
||||
// Remove all instances of the listener by
|
||||
// copying list while filtering.
|
||||
for _, s := range config.chgListeners {
|
||||
if s != l {
|
||||
dest = append(dest, s)
|
||||
} else {
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
config.chgListeners = dest
|
||||
return err
|
||||
}
|
||||
|
||||
// Shutdown can be called to stop monitoring of all config sources.
|
||||
func (config *Config) Shutdown() {
|
||||
config.mutexSrc.RLock()
|
||||
defer config.mutexSrc.RUnlock()
|
||||
if config.shutdown != nil {
|
||||
close(config.shutdown)
|
||||
}
|
||||
}
|
||||
|
||||
// onSourceChanged is called whenever one or more properties of a
|
||||
// config source has changed.
|
||||
func (config *Config) onSourceChanged(src SourceMonitored) {
|
||||
defer func() {
|
||||
if p := recover(); p != nil {
|
||||
fmt.Println(p)
|
||||
}
|
||||
}()
|
||||
config.mutexListeners.RLock()
|
||||
defer config.mutexListeners.RUnlock()
|
||||
for _, l := range config.chgListeners {
|
||||
l.ConfigChanged(config, src)
|
||||
}
|
||||
}
|
||||
|
||||
// monitor periodically checks a config source for changes.
|
||||
func (config *Config) monitor(se *sourceEntry) {
|
||||
go func(se *sourceEntry, shutdown <-chan interface{}) {
|
||||
var src SourceMonitored
|
||||
var ok bool
|
||||
if src, ok = se.src.(SourceMonitored); !ok {
|
||||
return
|
||||
}
|
||||
paused := false
|
||||
last := time.Time{}
|
||||
freq := src.GetMonitorFreq()
|
||||
if freq <= 0 {
|
||||
paused = true
|
||||
freq = 10
|
||||
last, _ = src.GetLastModified()
|
||||
}
|
||||
timer := time.NewTimer(freq)
|
||||
for {
|
||||
select {
|
||||
case <-timer.C:
|
||||
if !paused {
|
||||
if latest, err := src.GetLastModified(); err != nil {
|
||||
if config.ShouldPanicOnError() {
|
||||
panic(fmt.Sprintf("error <%v> getting last modified for %v", err, src))
|
||||
}
|
||||
} else {
|
||||
if last.Before(latest) {
|
||||
last = latest
|
||||
config.reloadProps(se)
|
||||
// TODO: calc diff and provide detailed changes
|
||||
config.onSourceChanged(src)
|
||||
}
|
||||
}
|
||||
}
|
||||
freq = src.GetMonitorFreq()
|
||||
if freq <= 0 {
|
||||
paused = true
|
||||
freq = 10
|
||||
} else {
|
||||
paused = false
|
||||
}
|
||||
timer.Reset(freq)
|
||||
case <-shutdown:
|
||||
// stop the timer and exit
|
||||
if !timer.Stop() {
|
||||
<-timer.C
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}(se, config.shutdown)
|
||||
}
|
||||
|
||||
// reloadProps causes a Source to reload its properties.
|
||||
func (config *Config) reloadProps(se *sourceEntry) {
|
||||
config.mutexSrc.Lock()
|
||||
defer config.mutexSrc.Unlock()
|
||||
|
||||
m, err := se.src.GetProps()
|
||||
if err != nil {
|
||||
if config.wantPanicOnError {
|
||||
panic(fmt.Sprintf("GetProps error for %v", se.src))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
se.props = make(map[string]string)
|
||||
for k, v := range m {
|
||||
se.props[k] = v
|
||||
}
|
||||
}
|
||||
5
vendor/github.com/wiggin77/cfg/go.mod
сгенерированный
поставляемый
5
vendor/github.com/wiggin77/cfg/go.mod
сгенерированный
поставляемый
@@ -1,5 +0,0 @@
|
||||
module github.com/wiggin77/cfg
|
||||
|
||||
go 1.12
|
||||
|
||||
require github.com/wiggin77/merror v1.0.2
|
||||
2
vendor/github.com/wiggin77/cfg/go.sum
сгенерированный
поставляемый
2
vendor/github.com/wiggin77/cfg/go.sum
сгенерированный
поставляемый
@@ -1,2 +0,0 @@
|
||||
github.com/wiggin77/merror v1.0.2 h1:V0nH9eFp64ASyaXC+pB5WpvBoCg7NUwvaCSKdzlcHqw=
|
||||
github.com/wiggin77/merror v1.0.2/go.mod h1:uQTcIU0Z6jRK4OwqganPYerzQxSFJ4GSHM3aurxxQpg=
|
||||
167
vendor/github.com/wiggin77/cfg/ini/ini.go
сгенерированный
поставляемый
167
vendor/github.com/wiggin77/cfg/ini/ini.go
сгенерированный
поставляемый
@@ -1,167 +0,0 @@
|
||||
package ini
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Ini provides parsing and querying of INI format or simple name/value pairs
|
||||
// such as a simple config file.
|
||||
// A name/value pair format is just an INI with no sections, and properties can
|
||||
// be queried using an empty section name.
|
||||
type Ini struct {
|
||||
mutex sync.RWMutex
|
||||
m map[string]*Section
|
||||
lm time.Time
|
||||
}
|
||||
|
||||
// LoadFromFilespec loads an INI file from string containing path and filename.
|
||||
func (ini *Ini) LoadFromFilespec(filespec string) error {
|
||||
f, err := os.Open(filespec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ini.LoadFromFile(f)
|
||||
}
|
||||
|
||||
// LoadFromFile loads an INI file from `os.File`.
|
||||
func (ini *Ini) LoadFromFile(file *os.File) error {
|
||||
|
||||
fi, err := file.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lm := fi.ModTime()
|
||||
|
||||
if err := ini.LoadFromReader(file); err != nil {
|
||||
return err
|
||||
}
|
||||
ini.lm = lm
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadFromReader loads an INI file from an `io.Reader`.
|
||||
func (ini *Ini) LoadFromReader(reader io.Reader) error {
|
||||
data, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ini.LoadFromString(string(data))
|
||||
}
|
||||
|
||||
// LoadFromString parses an INI from a string .
|
||||
func (ini *Ini) LoadFromString(s string) error {
|
||||
m, err := getSections(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ini.mutex.Lock()
|
||||
ini.m = m
|
||||
ini.lm = time.Now()
|
||||
ini.mutex.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetLastModified returns the last modified timestamp of the
|
||||
// INI contents.
|
||||
func (ini *Ini) GetLastModified() time.Time {
|
||||
return ini.lm
|
||||
}
|
||||
|
||||
// GetSectionNames returns the names of all sections in this INI.
|
||||
// Note, the returned section names are a snapshot in time, meaning
|
||||
// other goroutines may change the contents of this INI as soon as
|
||||
// the method returns.
|
||||
func (ini *Ini) GetSectionNames() []string {
|
||||
ini.mutex.RLock()
|
||||
defer ini.mutex.RUnlock()
|
||||
|
||||
arr := make([]string, 0, len(ini.m))
|
||||
for key := range ini.m {
|
||||
arr = append(arr, key)
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
// GetKeys returns the names of all keys in the specified section.
|
||||
// Note, the returned key names are a snapshot in time, meaning other
|
||||
// goroutines may change the contents of this INI as soon as the
|
||||
// method returns.
|
||||
func (ini *Ini) GetKeys(sectionName string) ([]string, error) {
|
||||
sec, err := ini.getSection(sectionName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sec.getKeys(), nil
|
||||
}
|
||||
|
||||
// getSection returns the named section.
|
||||
func (ini *Ini) getSection(sectionName string) (*Section, error) {
|
||||
ini.mutex.RLock()
|
||||
defer ini.mutex.RUnlock()
|
||||
|
||||
sec, ok := ini.m[sectionName]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("section '%s' not found", sectionName)
|
||||
}
|
||||
return sec, nil
|
||||
}
|
||||
|
||||
// GetFlattenedKeys returns all section names plus keys as one
|
||||
// flattened array.
|
||||
func (ini *Ini) GetFlattenedKeys() []string {
|
||||
ini.mutex.RLock()
|
||||
defer ini.mutex.RUnlock()
|
||||
|
||||
arr := make([]string, 0, len(ini.m)*2)
|
||||
for _, section := range ini.m {
|
||||
keys := section.getKeys()
|
||||
for _, key := range keys {
|
||||
name := section.GetName()
|
||||
if name != "" {
|
||||
key = name + "." + key
|
||||
}
|
||||
arr = append(arr, key)
|
||||
}
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
// GetProp returns the value of the specified key in the named section.
|
||||
func (ini *Ini) GetProp(section string, key string) (val string, ok bool) {
|
||||
sec, err := ini.getSection(section)
|
||||
if err != nil {
|
||||
return val, false
|
||||
}
|
||||
return sec.GetProp(key)
|
||||
}
|
||||
|
||||
// ToMap returns a flattened map of the section name plus keys mapped
|
||||
// to values.
|
||||
func (ini *Ini) ToMap() map[string]string {
|
||||
m := make(map[string]string)
|
||||
|
||||
ini.mutex.RLock()
|
||||
defer ini.mutex.RUnlock()
|
||||
|
||||
for _, section := range ini.m {
|
||||
for _, key := range section.getKeys() {
|
||||
val, ok := section.GetProp(key)
|
||||
if ok {
|
||||
name := section.GetName()
|
||||
var mapkey string
|
||||
if name != "" {
|
||||
mapkey = name + "." + key
|
||||
} else {
|
||||
mapkey = key
|
||||
}
|
||||
m[mapkey] = val
|
||||
}
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
142
vendor/github.com/wiggin77/cfg/ini/parser.go
сгенерированный
поставляемый
142
vendor/github.com/wiggin77/cfg/ini/parser.go
сгенерированный
поставляемый
@@ -1,142 +0,0 @@
|
||||
package ini
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/wiggin77/merror"
|
||||
)
|
||||
|
||||
// LF is linefeed
|
||||
const LF byte = 0x0A
|
||||
|
||||
// CR is carriage return
|
||||
const CR byte = 0x0D
|
||||
|
||||
// getSections parses an INI formatted string, or string containing just name/value pairs,
|
||||
// returns map of `Section`'s.
|
||||
//
|
||||
// Any name/value pairs appearing before a section name are added to the section named
|
||||
// with an empty string (""). Also true for Linux-style config files where all props
|
||||
// are outside a named section.
|
||||
//
|
||||
// Any errors encountered are aggregated and returned, along with the partially parsed
|
||||
// sections.
|
||||
func getSections(str string) (map[string]*Section, error) {
|
||||
merr := merror.New()
|
||||
mapSections := make(map[string]*Section)
|
||||
lines := buildLineArray(str)
|
||||
section := newSection("")
|
||||
|
||||
for _, line := range lines {
|
||||
name, ok := parseSection(line)
|
||||
if ok {
|
||||
// A section name encountered. Stop processing the current one.
|
||||
// Don't add the current section to the map if the section name is blank
|
||||
// and the prop map is empty.
|
||||
nameCurr := section.GetName()
|
||||
if nameCurr != "" || section.hasKeys() {
|
||||
mapSections[nameCurr] = section
|
||||
}
|
||||
// Start processing a new section.
|
||||
section = newSection(name)
|
||||
} else {
|
||||
// Parse the property and add to the current section, or ignore if comment.
|
||||
if k, v, comment, err := parseProp(line); !comment && err == nil {
|
||||
section.setProp(k, v)
|
||||
} else if err != nil {
|
||||
merr.Append(err) // aggregate errors
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// If the current section is not empty, add it.
|
||||
if section.hasKeys() {
|
||||
mapSections[section.GetName()] = section
|
||||
}
|
||||
return mapSections, merr.ErrorOrNil()
|
||||
}
|
||||
|
||||
// buildLineArray parses the given string buffer and creates a list of strings,
|
||||
// one for each line in the string buffer.
|
||||
//
|
||||
// A line is considered to be terminated by any one of a line feed ('\n'),
|
||||
// a carriage return ('\r'), or a carriage return followed immediately by a
|
||||
// linefeed.
|
||||
//
|
||||
// Lines prefixed with ';' or '#' are considered comments and skipped.
|
||||
func buildLineArray(str string) []string {
|
||||
arr := make([]string, 0, 10)
|
||||
str = str + "\n"
|
||||
|
||||
iLen := len(str)
|
||||
iPos, iBegin := 0, 0
|
||||
var ch byte
|
||||
|
||||
for iPos < iLen {
|
||||
ch = str[iPos]
|
||||
if ch == LF || ch == CR {
|
||||
sub := str[iBegin:iPos]
|
||||
sub = strings.TrimSpace(sub)
|
||||
if sub != "" && !strings.HasPrefix(sub, ";") && !strings.HasPrefix(sub, "#") {
|
||||
arr = append(arr, sub)
|
||||
}
|
||||
iPos++
|
||||
if ch == CR && iPos < iLen && str[iPos] == LF {
|
||||
iPos++
|
||||
}
|
||||
iBegin = iPos
|
||||
} else {
|
||||
iPos++
|
||||
}
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
// parseSection parses the specified string for a section name enclosed in square brackets.
|
||||
// Returns the section name found, or `ok=false` if `str` is not a section header.
|
||||
func parseSection(str string) (name string, ok bool) {
|
||||
str = strings.TrimSpace(str)
|
||||
if !strings.HasPrefix(str, "[") {
|
||||
return "", false
|
||||
}
|
||||
iCloser := strings.Index(str, "]")
|
||||
if iCloser == -1 {
|
||||
return "", false
|
||||
}
|
||||
return strings.TrimSpace(str[1:iCloser]), true
|
||||
}
|
||||
|
||||
// parseProp parses the specified string and extracts a key/value pair.
|
||||
//
|
||||
// If the string is a comment (prefixed with ';' or '#') then `comment=true`
|
||||
// and key will be empty.
|
||||
func parseProp(str string) (key string, val string, comment bool, err error) {
|
||||
iLen := len(str)
|
||||
iEqPos := strings.Index(str, "=")
|
||||
if iEqPos == -1 {
|
||||
return "", "", false, fmt.Errorf("not a key/value pair:'%s'", str)
|
||||
}
|
||||
|
||||
key = str[0:iEqPos]
|
||||
key = strings.TrimSpace(key)
|
||||
if iEqPos+1 < iLen {
|
||||
val = str[iEqPos+1:]
|
||||
val = strings.TrimSpace(val)
|
||||
}
|
||||
|
||||
// Check that the key has at least 1 char.
|
||||
if key == "" {
|
||||
return "", "", false, fmt.Errorf("key is empty for '%s'", str)
|
||||
}
|
||||
|
||||
// Check if this line is a comment that just happens
|
||||
// to have an equals sign in it. Not an error, but not a
|
||||
// useable line either.
|
||||
if strings.HasPrefix(key, ";") || strings.HasPrefix(key, "#") {
|
||||
key = ""
|
||||
val = ""
|
||||
comment = true
|
||||
}
|
||||
return key, val, comment, err
|
||||
}
|
||||
109
vendor/github.com/wiggin77/cfg/ini/section.go
сгенерированный
поставляемый
109
vendor/github.com/wiggin77/cfg/ini/section.go
сгенерированный
поставляемый
@@ -1,109 +0,0 @@
|
||||
package ini
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Section represents a section in an INI file. The section has a name, which is
|
||||
// enclosed in square brackets in the file. The section also has an array of
|
||||
// key/value pairs.
|
||||
type Section struct {
|
||||
name string
|
||||
props map[string]string
|
||||
mtx sync.RWMutex
|
||||
}
|
||||
|
||||
func newSection(name string) *Section {
|
||||
sec := &Section{}
|
||||
sec.name = name
|
||||
sec.props = make(map[string]string)
|
||||
return sec
|
||||
}
|
||||
|
||||
// addLines addes an array of strings containing name/value pairs
|
||||
// of the format `key=value`.
|
||||
//func addLines(lines []string) {
|
||||
// TODO
|
||||
//}
|
||||
|
||||
// GetName returns the name of the section.
|
||||
func (sec *Section) GetName() (name string) {
|
||||
sec.mtx.RLock()
|
||||
name = sec.name
|
||||
sec.mtx.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// GetProp returns the value associated with the given key, or
|
||||
// `ok=false` if key does not exist.
|
||||
func (sec *Section) GetProp(key string) (val string, ok bool) {
|
||||
sec.mtx.RLock()
|
||||
val, ok = sec.props[key]
|
||||
sec.mtx.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// SetProp sets the value associated with the given key.
|
||||
func (sec *Section) setProp(key string, val string) {
|
||||
sec.mtx.Lock()
|
||||
sec.props[key] = val
|
||||
sec.mtx.Unlock()
|
||||
}
|
||||
|
||||
// hasKeys returns true if there are one or more properties in
|
||||
// this section.
|
||||
func (sec *Section) hasKeys() (b bool) {
|
||||
sec.mtx.RLock()
|
||||
b = len(sec.props) > 0
|
||||
sec.mtx.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// getKeys returns an array containing all keys in this section.
|
||||
func (sec *Section) getKeys() []string {
|
||||
sec.mtx.RLock()
|
||||
defer sec.mtx.RUnlock()
|
||||
|
||||
arr := make([]string, len(sec.props))
|
||||
idx := 0
|
||||
for k := range sec.props {
|
||||
arr[idx] = k
|
||||
idx++
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
// combine the given section with this one.
|
||||
func (sec *Section) combine(sec2 *Section) {
|
||||
sec.mtx.Lock()
|
||||
sec2.mtx.RLock()
|
||||
defer sec.mtx.Unlock()
|
||||
defer sec2.mtx.RUnlock()
|
||||
|
||||
for k, v := range sec2.props {
|
||||
sec.props[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// String returns a string representation of this section.
|
||||
func (sec *Section) String() string {
|
||||
return fmt.Sprintf("[%s]\n%s", sec.GetName(), sec.StringPropsOnly())
|
||||
}
|
||||
|
||||
// StringPropsOnly returns a string representation of this section
|
||||
// without the section header.
|
||||
func (sec *Section) StringPropsOnly() string {
|
||||
sec.mtx.RLock()
|
||||
defer sec.mtx.RUnlock()
|
||||
sb := &strings.Builder{}
|
||||
|
||||
for k, v := range sec.props {
|
||||
sb.WriteString(k)
|
||||
sb.WriteString("=")
|
||||
sb.WriteString(v)
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
11
vendor/github.com/wiggin77/cfg/listener.go
сгенерированный
поставляемый
11
vendor/github.com/wiggin77/cfg/listener.go
сгенерированный
поставляемый
@@ -1,11 +0,0 @@
|
||||
package cfg
|
||||
|
||||
// ChangedListener interface is for receiving notifications
|
||||
// when one or more properties within monitored config sources
|
||||
// (SourceMonitored) have changed values.
|
||||
type ChangedListener interface {
|
||||
|
||||
// Changed is called when one or more properties in a `SourceMonitored` has a
|
||||
// changed value.
|
||||
ConfigChanged(cfg *Config, src SourceMonitored)
|
||||
}
|
||||
11
vendor/github.com/wiggin77/cfg/nocopy.go
сгенерированный
поставляемый
11
vendor/github.com/wiggin77/cfg/nocopy.go
сгенерированный
поставляемый
@@ -1,11 +0,0 @@
|
||||
package cfg
|
||||
|
||||
// noCopy may be embedded into structs which must not be copied
|
||||
// after the first use.
|
||||
//
|
||||
// See https://golang.org/issues/8005#issuecomment-190753527
|
||||
// for details.
|
||||
type noCopy struct{}
|
||||
|
||||
// Lock is a no-op used by -copylocks checker from `go vet`.
|
||||
func (*noCopy) Lock() {}
|
||||
58
vendor/github.com/wiggin77/cfg/source.go
сгенерированный
поставляемый
58
vendor/github.com/wiggin77/cfg/source.go
сгенерированный
поставляемый
@@ -1,58 +0,0 @@
|
||||
package cfg
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Source is the interface required for any source of name/value pairs.
|
||||
type Source interface {
|
||||
|
||||
// GetProps fetches all the properties from a source and returns
|
||||
// them as a map.
|
||||
GetProps() (map[string]string, error)
|
||||
}
|
||||
|
||||
// SourceMonitored is the interface required for any config source that is
|
||||
// monitored for changes.
|
||||
type SourceMonitored interface {
|
||||
Source
|
||||
|
||||
// GetLastModified returns the time of the latest modification to any
|
||||
// property value within the source. If a source does not support
|
||||
// modifying properties at runtime then the zero value for `Time`
|
||||
// should be returned to ensure reload events are not generated.
|
||||
GetLastModified() (time.Time, error)
|
||||
|
||||
// GetMonitorFreq returns the frequency as a `time.Duration` between
|
||||
// checks for changes to this config source.
|
||||
//
|
||||
// Returning zero (or less) will temporarily suspend calls to `GetLastModified`
|
||||
// and `GetMonitorFreq` will be called every 10 seconds until resumed, after which
|
||||
// `GetMontitorFreq` will be called at a frequency roughly equal to the `time.Duration`
|
||||
// returned.
|
||||
GetMonitorFreq() time.Duration
|
||||
}
|
||||
|
||||
// AbstractSourceMonitor can be embedded in a custom `Source` to provide the
|
||||
// basic plumbing for monitor frequency.
|
||||
type AbstractSourceMonitor struct {
|
||||
mutex sync.RWMutex
|
||||
freq time.Duration
|
||||
}
|
||||
|
||||
// GetMonitorFreq returns the frequency as a `time.Duration` between
|
||||
// checks for changes to this config source.
|
||||
func (asm *AbstractSourceMonitor) GetMonitorFreq() (freq time.Duration) {
|
||||
asm.mutex.RLock()
|
||||
freq = asm.freq
|
||||
asm.mutex.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// SetMonitorFreq sets the frequency between checks for changes to this config source.
|
||||
func (asm *AbstractSourceMonitor) SetMonitorFreq(freq time.Duration) {
|
||||
asm.mutex.Lock()
|
||||
asm.freq = freq
|
||||
asm.mutex.Unlock()
|
||||
}
|
||||
63
vendor/github.com/wiggin77/cfg/srcfile.go
сгенерированный
поставляемый
63
vendor/github.com/wiggin77/cfg/srcfile.go
сгенерированный
поставляемый
@@ -1,63 +0,0 @@
|
||||
package cfg
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/wiggin77/cfg/ini"
|
||||
)
|
||||
|
||||
// SrcFile is a configuration `Source` backed by a file containing
|
||||
// name/value pairs or INI format.
|
||||
type SrcFile struct {
|
||||
AbstractSourceMonitor
|
||||
ini ini.Ini
|
||||
file *os.File
|
||||
}
|
||||
|
||||
// NewSrcFileFromFilespec creates a new SrcFile with the specified filespec.
|
||||
func NewSrcFileFromFilespec(filespec string) (*SrcFile, error) {
|
||||
file, err := os.Open(filespec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewSrcFile(file)
|
||||
}
|
||||
|
||||
// NewSrcFile creates a new SrcFile with the specified os.File.
|
||||
func NewSrcFile(file *os.File) (*SrcFile, error) {
|
||||
sf := &SrcFile{}
|
||||
sf.freq = time.Minute
|
||||
sf.file = file
|
||||
if err := sf.ini.LoadFromFile(file); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sf, nil
|
||||
}
|
||||
|
||||
// GetProps fetches all the properties from a source and returns
|
||||
// them as a map.
|
||||
func (sf *SrcFile) GetProps() (map[string]string, error) {
|
||||
lm, err := sf.GetLastModified()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Check if we need to reload.
|
||||
if sf.ini.GetLastModified() != lm {
|
||||
if err := sf.ini.LoadFromFile(sf.file); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return sf.ini.ToMap(), nil
|
||||
}
|
||||
|
||||
// GetLastModified returns the time of the latest modification to any
|
||||
// property value within the source.
|
||||
func (sf *SrcFile) GetLastModified() (time.Time, error) {
|
||||
fi, err := sf.file.Stat()
|
||||
if err != nil {
|
||||
return time.Now(), err
|
||||
}
|
||||
return fi.ModTime(), nil
|
||||
}
|
||||
78
vendor/github.com/wiggin77/cfg/srcmap.go
сгенерированный
поставляемый
78
vendor/github.com/wiggin77/cfg/srcmap.go
сгенерированный
поставляемый
@@ -1,78 +0,0 @@
|
||||
package cfg
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// SrcMap is a configuration `Source` backed by a simple map.
|
||||
type SrcMap struct {
|
||||
AbstractSourceMonitor
|
||||
m map[string]string
|
||||
lm time.Time
|
||||
}
|
||||
|
||||
// NewSrcMap creates an empty `SrcMap`.
|
||||
func NewSrcMap() *SrcMap {
|
||||
sm := &SrcMap{}
|
||||
sm.m = make(map[string]string)
|
||||
sm.lm = time.Now()
|
||||
sm.freq = time.Minute
|
||||
return sm
|
||||
}
|
||||
|
||||
// NewSrcMapFromMap creates a `SrcMap` containing a copy of the
|
||||
// specified map.
|
||||
func NewSrcMapFromMap(mapIn map[string]string) *SrcMap {
|
||||
sm := NewSrcMap()
|
||||
sm.PutAll(mapIn)
|
||||
return sm
|
||||
}
|
||||
|
||||
// Put inserts or updates a value in the `SrcMap`.
|
||||
func (sm *SrcMap) Put(key string, val string) {
|
||||
sm.mutex.Lock()
|
||||
sm.m[key] = val
|
||||
sm.lm = time.Now()
|
||||
sm.mutex.Unlock()
|
||||
}
|
||||
|
||||
// PutAll inserts a copy of `mapIn` into the `SrcMap`
|
||||
func (sm *SrcMap) PutAll(mapIn map[string]string) {
|
||||
sm.mutex.Lock()
|
||||
defer sm.mutex.Unlock()
|
||||
|
||||
for k, v := range mapIn {
|
||||
sm.m[k] = v
|
||||
}
|
||||
sm.lm = time.Now()
|
||||
}
|
||||
|
||||
// GetProps fetches all the properties from a source and returns
|
||||
// them as a map.
|
||||
func (sm *SrcMap) GetProps() (m map[string]string, err error) {
|
||||
sm.mutex.RLock()
|
||||
m = sm.m
|
||||
sm.mutex.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// GetLastModified returns the time of the latest modification to any
|
||||
// property value within the source. If a source does not support
|
||||
// modifying properties at runtime then the zero value for `Time`
|
||||
// should be returned to ensure reload events are not generated.
|
||||
func (sm *SrcMap) GetLastModified() (last time.Time, err error) {
|
||||
sm.mutex.RLock()
|
||||
last = sm.lm
|
||||
sm.mutex.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// GetMonitorFreq returns the frequency as a `time.Duration` between
|
||||
// checks for changes to this config source. Defaults to 1 minute
|
||||
// unless changed with `SetMonitorFreq`.
|
||||
func (sm *SrcMap) GetMonitorFreq() (freq time.Duration) {
|
||||
sm.mutex.RLock()
|
||||
freq = sm.freq
|
||||
sm.mutex.RUnlock()
|
||||
return
|
||||
}
|
||||
108
vendor/github.com/wiggin77/cfg/timeconv/parse.go
сгенерированный
поставляемый
108
vendor/github.com/wiggin77/cfg/timeconv/parse.go
сгенерированный
поставляемый
@@ -1,108 +0,0 @@
|
||||
package timeconv
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// MillisPerSecond is the number of millseconds per second.
|
||||
const MillisPerSecond int64 = 1000
|
||||
|
||||
// MillisPerMinute is the number of millseconds per minute.
|
||||
const MillisPerMinute int64 = MillisPerSecond * 60
|
||||
|
||||
// MillisPerHour is the number of millseconds per hour.
|
||||
const MillisPerHour int64 = MillisPerMinute * 60
|
||||
|
||||
// MillisPerDay is the number of millseconds per day.
|
||||
const MillisPerDay int64 = MillisPerHour * 24
|
||||
|
||||
// MillisPerWeek is the number of millseconds per week.
|
||||
const MillisPerWeek int64 = MillisPerDay * 7
|
||||
|
||||
// MillisPerYear is the approximate number of millseconds per year.
|
||||
const MillisPerYear int64 = MillisPerDay*365 + int64((float64(MillisPerDay) * 0.25))
|
||||
|
||||
// ParseMilliseconds parses a string containing a number plus
|
||||
// a unit of measure for time and returns the number of milliseconds
|
||||
// it represents.
|
||||
//
|
||||
// Example:
|
||||
// * "1 second" returns 1000
|
||||
// * "1 minute" returns 60000
|
||||
// * "1 hour" returns 3600000
|
||||
//
|
||||
// See config.UnitsToMillis for a list of supported units of measure.
|
||||
func ParseMilliseconds(str string) (int64, error) {
|
||||
s := strings.TrimSpace(str)
|
||||
reg := regexp.MustCompile("([0-9\\.\\-+]*)(.*)")
|
||||
matches := reg.FindStringSubmatch(s)
|
||||
if matches == nil || len(matches) < 1 || matches[1] == "" {
|
||||
return 0, fmt.Errorf("invalid syntax - '%s'", s)
|
||||
}
|
||||
digits := matches[1]
|
||||
units := "ms"
|
||||
if len(matches) > 1 && matches[2] != "" {
|
||||
units = matches[2]
|
||||
}
|
||||
|
||||
fDigits, err := strconv.ParseFloat(digits, 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
msPerUnit, err := UnitsToMillis(units)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Check for overflow.
|
||||
fms := float64(msPerUnit) * fDigits
|
||||
if fms > math.MaxInt64 || fms < math.MinInt64 {
|
||||
return 0, fmt.Errorf("out of range - '%s' overflows", s)
|
||||
}
|
||||
ms := int64(fms)
|
||||
return ms, nil
|
||||
}
|
||||
|
||||
// UnitsToMillis returns the number of milliseconds represented by the specified unit of measure.
|
||||
//
|
||||
// Example:
|
||||
// * "second" returns 1000 <br/>
|
||||
// * "minute" returns 60000 <br/>
|
||||
// * "hour" returns 3600000 <br/>
|
||||
//
|
||||
// Supported units of measure:
|
||||
// * "milliseconds", "millis", "ms", "millisecond"
|
||||
// * "seconds", "sec", "s", "second"
|
||||
// * "minutes", "mins", "min", "m", "minute"
|
||||
// * "hours", "h", "hour"
|
||||
// * "days", "d", "day"
|
||||
// * "weeks", "w", "week"
|
||||
// * "years", "y", "year"
|
||||
func UnitsToMillis(units string) (ms int64, err error) {
|
||||
u := strings.TrimSpace(units)
|
||||
u = strings.ToLower(u)
|
||||
switch u {
|
||||
case "milliseconds", "millisecond", "millis", "ms":
|
||||
ms = 1
|
||||
case "seconds", "second", "sec", "s":
|
||||
ms = MillisPerSecond
|
||||
case "minutes", "minute", "mins", "min", "m":
|
||||
ms = MillisPerMinute
|
||||
case "hours", "hour", "h":
|
||||
ms = MillisPerHour
|
||||
case "days", "day", "d":
|
||||
ms = MillisPerDay
|
||||
case "weeks", "week", "w":
|
||||
ms = MillisPerWeek
|
||||
case "years", "year", "y":
|
||||
ms = MillisPerYear
|
||||
default:
|
||||
err = fmt.Errorf("invalid syntax - '%s' not a supported unit of measure", u)
|
||||
}
|
||||
return
|
||||
}
|
||||
15
vendor/go.uber.org/multierr/.codecov.yml
сгенерированный
поставляемый
15
vendor/go.uber.org/multierr/.codecov.yml
сгенерированный
поставляемый
@@ -1,15 +0,0 @@
|
||||
coverage:
|
||||
range: 80..100
|
||||
round: down
|
||||
precision: 2
|
||||
|
||||
status:
|
||||
project: # measuring the overall project coverage
|
||||
default: # context, you can create multiple ones with custom titles
|
||||
enabled: yes # must be yes|true to enable this status
|
||||
target: 100 # specify the target coverage for each commit status
|
||||
# option: "auto" (must increase from parent commit or pull request base)
|
||||
# option: "X%" a static target percentage to hit
|
||||
if_not_found: success # if parent is not found report status as success, error, or failure
|
||||
if_ci_failed: error # if ci fails report status as success, error, or failure
|
||||
|
||||
4
vendor/go.uber.org/multierr/.gitignore
сгенерированный
поставляемый
4
vendor/go.uber.org/multierr/.gitignore
сгенерированный
поставляемый
@@ -1,4 +0,0 @@
|
||||
/vendor
|
||||
cover.html
|
||||
cover.out
|
||||
/bin
|
||||
66
vendor/go.uber.org/multierr/CHANGELOG.md
сгенерированный
поставляемый
66
vendor/go.uber.org/multierr/CHANGELOG.md
сгенерированный
поставляемый
@@ -1,66 +0,0 @@
|
||||
Releases
|
||||
========
|
||||
|
||||
v1.7.0 (2021-05-06)
|
||||
===================
|
||||
|
||||
- Add `AppendInvoke` to append into errors from `defer` blocks.
|
||||
|
||||
|
||||
v1.6.0 (2020-09-14)
|
||||
===================
|
||||
|
||||
- Actually drop library dependency on development-time tooling.
|
||||
|
||||
|
||||
v1.5.0 (2020-02-24)
|
||||
===================
|
||||
|
||||
- Drop library dependency on development-time tooling.
|
||||
|
||||
|
||||
v1.4.0 (2019-11-04)
|
||||
===================
|
||||
|
||||
- Add `AppendInto` function to more ergonomically build errors inside a
|
||||
loop.
|
||||
|
||||
|
||||
v1.3.0 (2019-10-29)
|
||||
===================
|
||||
|
||||
- Switch to Go modules.
|
||||
|
||||
|
||||
v1.2.0 (2019-09-26)
|
||||
===================
|
||||
|
||||
- Support extracting and matching against wrapped errors with `errors.As`
|
||||
and `errors.Is`.
|
||||
|
||||
|
||||
v1.1.0 (2017-06-30)
|
||||
===================
|
||||
|
||||
- Added an `Errors(error) []error` function to extract the underlying list of
|
||||
errors for a multierr error.
|
||||
|
||||
|
||||
v1.0.0 (2017-05-31)
|
||||
===================
|
||||
|
||||
No changes since v0.2.0. This release is committing to making no breaking
|
||||
changes to the current API in the 1.X series.
|
||||
|
||||
|
||||
v0.2.0 (2017-04-11)
|
||||
===================
|
||||
|
||||
- Repeatedly appending to the same error is now faster due to fewer
|
||||
allocations.
|
||||
|
||||
|
||||
v0.1.0 (2017-31-03)
|
||||
===================
|
||||
|
||||
- Initial release
|
||||
19
vendor/go.uber.org/multierr/LICENSE.txt
сгенерированный
поставляемый
19
vendor/go.uber.org/multierr/LICENSE.txt
сгенерированный
поставляемый
@@ -1,19 +0,0 @@
|
||||
Copyright (c) 2017-2021 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.
|
||||
38
vendor/go.uber.org/multierr/Makefile
сгенерированный
поставляемый
38
vendor/go.uber.org/multierr/Makefile
сгенерированный
поставляемый
@@ -1,38 +0,0 @@
|
||||
# Directory to put `go install`ed binaries in.
|
||||
export GOBIN ?= $(shell pwd)/bin
|
||||
|
||||
GO_FILES := $(shell \
|
||||
find . '(' -path '*/.*' -o -path './vendor' ')' -prune \
|
||||
-o -name '*.go' -print | cut -b3-)
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
go build ./...
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
go test -race ./...
|
||||
|
||||
.PHONY: gofmt
|
||||
gofmt:
|
||||
$(eval FMT_LOG := $(shell mktemp -t gofmt.XXXXX))
|
||||
@gofmt -e -s -l $(GO_FILES) > $(FMT_LOG) || true
|
||||
@[ ! -s "$(FMT_LOG)" ] || (echo "gofmt failed:" | cat - $(FMT_LOG) && false)
|
||||
|
||||
.PHONY: golint
|
||||
golint:
|
||||
@cd tools && go install golang.org/x/lint/golint
|
||||
@$(GOBIN)/golint ./...
|
||||
|
||||
.PHONY: staticcheck
|
||||
staticcheck:
|
||||
@cd tools && go install honnef.co/go/tools/cmd/staticcheck
|
||||
@$(GOBIN)/staticcheck ./...
|
||||
|
||||
.PHONY: lint
|
||||
lint: gofmt golint staticcheck
|
||||
|
||||
.PHONY: cover
|
||||
cover:
|
||||
go test -race -coverprofile=cover.out -coverpkg=./... -v ./...
|
||||
go tool cover -html=cover.out -o cover.html
|
||||
23
vendor/go.uber.org/multierr/README.md
сгенерированный
поставляемый
23
vendor/go.uber.org/multierr/README.md
сгенерированный
поставляемый
@@ -1,23 +0,0 @@
|
||||
# multierr [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov]
|
||||
|
||||
`multierr` allows combining one or more Go `error`s together.
|
||||
|
||||
## Installation
|
||||
|
||||
go get -u go.uber.org/multierr
|
||||
|
||||
## Status
|
||||
|
||||
Stable: No breaking changes will be made before 2.0.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
Released under the [MIT License].
|
||||
|
||||
[MIT License]: LICENSE.txt
|
||||
[doc-img]: https://pkg.go.dev/badge/go.uber.org/multierr
|
||||
[doc]: https://pkg.go.dev/go.uber.org/multierr
|
||||
[ci-img]: https://github.com/uber-go/multierr/actions/workflows/go.yml/badge.svg
|
||||
[cov-img]: https://codecov.io/gh/uber-go/multierr/branch/master/graph/badge.svg
|
||||
[ci]: https://github.com/uber-go/multierr/actions/workflows/go.yml
|
||||
[cov]: https://codecov.io/gh/uber-go/multierr
|
||||
639
vendor/go.uber.org/multierr/error.go
сгенерированный
поставляемый
639
vendor/go.uber.org/multierr/error.go
сгенерированный
поставляемый
@@ -1,639 +0,0 @@
|
||||
// Copyright (c) 2017-2021 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 multierr allows combining one or more errors together.
|
||||
//
|
||||
// Overview
|
||||
//
|
||||
// Errors can be combined with the use of the Combine function.
|
||||
//
|
||||
// multierr.Combine(
|
||||
// reader.Close(),
|
||||
// writer.Close(),
|
||||
// conn.Close(),
|
||||
// )
|
||||
//
|
||||
// If only two errors are being combined, the Append function may be used
|
||||
// instead.
|
||||
//
|
||||
// err = multierr.Append(reader.Close(), writer.Close())
|
||||
//
|
||||
// The underlying list of errors for a returned error object may be retrieved
|
||||
// with the Errors function.
|
||||
//
|
||||
// errors := multierr.Errors(err)
|
||||
// if len(errors) > 0 {
|
||||
// fmt.Println("The following errors occurred:", errors)
|
||||
// }
|
||||
//
|
||||
// Appending from a loop
|
||||
//
|
||||
// You sometimes need to append into an error from a loop.
|
||||
//
|
||||
// var err error
|
||||
// for _, item := range items {
|
||||
// err = multierr.Append(err, process(item))
|
||||
// }
|
||||
//
|
||||
// Cases like this may require knowledge of whether an individual instance
|
||||
// failed. This usually requires introduction of a new variable.
|
||||
//
|
||||
// var err error
|
||||
// for _, item := range items {
|
||||
// if perr := process(item); perr != nil {
|
||||
// log.Warn("skipping item", item)
|
||||
// err = multierr.Append(err, perr)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// multierr includes AppendInto to simplify cases like this.
|
||||
//
|
||||
// var err error
|
||||
// for _, item := range items {
|
||||
// if multierr.AppendInto(&err, process(item)) {
|
||||
// log.Warn("skipping item", item)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// This will append the error into the err variable, and return true if that
|
||||
// individual error was non-nil.
|
||||
//
|
||||
// See AppendInto for more information.
|
||||
//
|
||||
// Deferred Functions
|
||||
//
|
||||
// Go makes it possible to modify the return value of a function in a defer
|
||||
// block if the function was using named returns. This makes it possible to
|
||||
// record resource cleanup failures from deferred blocks.
|
||||
//
|
||||
// func sendRequest(req Request) (err error) {
|
||||
// conn, err := openConnection()
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// defer func() {
|
||||
// err = multierr.Append(err, conn.Close())
|
||||
// }()
|
||||
// // ...
|
||||
// }
|
||||
//
|
||||
// multierr provides the Invoker type and AppendInvoke function to make cases
|
||||
// like the above simpler and obviate the need for a closure. The following is
|
||||
// roughly equivalent to the example above.
|
||||
//
|
||||
// func sendRequest(req Request) (err error) {
|
||||
// conn, err := openConnection()
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// defer multierr.AppendInvoke(err, multierr.Close(conn))
|
||||
// // ...
|
||||
// }
|
||||
//
|
||||
// See AppendInvoke and Invoker for more information.
|
||||
//
|
||||
// Advanced Usage
|
||||
//
|
||||
// Errors returned by Combine and Append MAY implement the following
|
||||
// interface.
|
||||
//
|
||||
// type errorGroup interface {
|
||||
// // Returns a slice containing the underlying list of errors.
|
||||
// //
|
||||
// // This slice MUST NOT be modified by the caller.
|
||||
// Errors() []error
|
||||
// }
|
||||
//
|
||||
// Note that if you need access to list of errors behind a multierr error, you
|
||||
// should prefer using the Errors function. That said, if you need cheap
|
||||
// read-only access to the underlying errors slice, you can attempt to cast
|
||||
// the error to this interface. You MUST handle the failure case gracefully
|
||||
// because errors returned by Combine and Append are not guaranteed to
|
||||
// implement this interface.
|
||||
//
|
||||
// var errors []error
|
||||
// group, ok := err.(errorGroup)
|
||||
// if ok {
|
||||
// errors = group.Errors()
|
||||
// } else {
|
||||
// errors = []error{err}
|
||||
// }
|
||||
package multierr // import "go.uber.org/multierr"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"go.uber.org/atomic"
|
||||
)
|
||||
|
||||
var (
|
||||
// Separator for single-line error messages.
|
||||
_singlelineSeparator = []byte("; ")
|
||||
|
||||
// Prefix for multi-line messages
|
||||
_multilinePrefix = []byte("the following errors occurred:")
|
||||
|
||||
// Prefix for the first and following lines of an item in a list of
|
||||
// multi-line error messages.
|
||||
//
|
||||
// For example, if a single item is:
|
||||
//
|
||||
// foo
|
||||
// bar
|
||||
//
|
||||
// It will become,
|
||||
//
|
||||
// - foo
|
||||
// bar
|
||||
_multilineSeparator = []byte("\n - ")
|
||||
_multilineIndent = []byte(" ")
|
||||
)
|
||||
|
||||
// _bufferPool is a pool of bytes.Buffers.
|
||||
var _bufferPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &bytes.Buffer{}
|
||||
},
|
||||
}
|
||||
|
||||
type errorGroup interface {
|
||||
Errors() []error
|
||||
}
|
||||
|
||||
// Errors returns a slice containing zero or more errors that the supplied
|
||||
// error is composed of. If the error is nil, a nil slice is returned.
|
||||
//
|
||||
// err := multierr.Append(r.Close(), w.Close())
|
||||
// errors := multierr.Errors(err)
|
||||
//
|
||||
// If the error is not composed of other errors, the returned slice contains
|
||||
// just the error that was passed in.
|
||||
//
|
||||
// Callers of this function are free to modify the returned slice.
|
||||
func Errors(err error) []error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Note that we're casting to multiError, not errorGroup. Our contract is
|
||||
// that returned errors MAY implement errorGroup. Errors, however, only
|
||||
// has special behavior for multierr-specific error objects.
|
||||
//
|
||||
// This behavior can be expanded in the future but I think it's prudent to
|
||||
// start with as little as possible in terms of contract and possibility
|
||||
// of misuse.
|
||||
eg, ok := err.(*multiError)
|
||||
if !ok {
|
||||
return []error{err}
|
||||
}
|
||||
|
||||
errors := eg.Errors()
|
||||
result := make([]error, len(errors))
|
||||
copy(result, errors)
|
||||
return result
|
||||
}
|
||||
|
||||
// multiError is an error that holds one or more errors.
|
||||
//
|
||||
// An instance of this is guaranteed to be non-empty and flattened. That is,
|
||||
// none of the errors inside multiError are other multiErrors.
|
||||
//
|
||||
// multiError formats to a semi-colon delimited list of error messages with
|
||||
// %v and with a more readable multi-line format with %+v.
|
||||
type multiError struct {
|
||||
copyNeeded atomic.Bool
|
||||
errors []error
|
||||
}
|
||||
|
||||
var _ errorGroup = (*multiError)(nil)
|
||||
|
||||
// Errors returns the list of underlying errors.
|
||||
//
|
||||
// This slice MUST NOT be modified.
|
||||
func (merr *multiError) Errors() []error {
|
||||
if merr == nil {
|
||||
return nil
|
||||
}
|
||||
return merr.errors
|
||||
}
|
||||
|
||||
// As attempts to find the first error in the error list that matches the type
|
||||
// of the value that target points to.
|
||||
//
|
||||
// This function allows errors.As to traverse the values stored on the
|
||||
// multierr error.
|
||||
func (merr *multiError) As(target interface{}) bool {
|
||||
for _, err := range merr.Errors() {
|
||||
if errors.As(err, target) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Is attempts to match the provided error against errors in the error list.
|
||||
//
|
||||
// This function allows errors.Is to traverse the values stored on the
|
||||
// multierr error.
|
||||
func (merr *multiError) Is(target error) bool {
|
||||
for _, err := range merr.Errors() {
|
||||
if errors.Is(err, target) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (merr *multiError) Error() string {
|
||||
if merr == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
buff := _bufferPool.Get().(*bytes.Buffer)
|
||||
buff.Reset()
|
||||
|
||||
merr.writeSingleline(buff)
|
||||
|
||||
result := buff.String()
|
||||
_bufferPool.Put(buff)
|
||||
return result
|
||||
}
|
||||
|
||||
func (merr *multiError) Format(f fmt.State, c rune) {
|
||||
if c == 'v' && f.Flag('+') {
|
||||
merr.writeMultiline(f)
|
||||
} else {
|
||||
merr.writeSingleline(f)
|
||||
}
|
||||
}
|
||||
|
||||
func (merr *multiError) writeSingleline(w io.Writer) {
|
||||
first := true
|
||||
for _, item := range merr.errors {
|
||||
if first {
|
||||
first = false
|
||||
} else {
|
||||
w.Write(_singlelineSeparator)
|
||||
}
|
||||
io.WriteString(w, item.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (merr *multiError) writeMultiline(w io.Writer) {
|
||||
w.Write(_multilinePrefix)
|
||||
for _, item := range merr.errors {
|
||||
w.Write(_multilineSeparator)
|
||||
writePrefixLine(w, _multilineIndent, fmt.Sprintf("%+v", item))
|
||||
}
|
||||
}
|
||||
|
||||
// Writes s to the writer with the given prefix added before each line after
|
||||
// the first.
|
||||
func writePrefixLine(w io.Writer, prefix []byte, s string) {
|
||||
first := true
|
||||
for len(s) > 0 {
|
||||
if first {
|
||||
first = false
|
||||
} else {
|
||||
w.Write(prefix)
|
||||
}
|
||||
|
||||
idx := strings.IndexByte(s, '\n')
|
||||
if idx < 0 {
|
||||
idx = len(s) - 1
|
||||
}
|
||||
|
||||
io.WriteString(w, s[:idx+1])
|
||||
s = s[idx+1:]
|
||||
}
|
||||
}
|
||||
|
||||
type inspectResult struct {
|
||||
// Number of top-level non-nil errors
|
||||
Count int
|
||||
|
||||
// Total number of errors including multiErrors
|
||||
Capacity int
|
||||
|
||||
// Index of the first non-nil error in the list. Value is meaningless if
|
||||
// Count is zero.
|
||||
FirstErrorIdx int
|
||||
|
||||
// Whether the list contains at least one multiError
|
||||
ContainsMultiError bool
|
||||
}
|
||||
|
||||
// Inspects the given slice of errors so that we can efficiently allocate
|
||||
// space for it.
|
||||
func inspect(errors []error) (res inspectResult) {
|
||||
first := true
|
||||
for i, err := range errors {
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
res.Count++
|
||||
if first {
|
||||
first = false
|
||||
res.FirstErrorIdx = i
|
||||
}
|
||||
|
||||
if merr, ok := err.(*multiError); ok {
|
||||
res.Capacity += len(merr.errors)
|
||||
res.ContainsMultiError = true
|
||||
} else {
|
||||
res.Capacity++
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// fromSlice converts the given list of errors into a single error.
|
||||
func fromSlice(errors []error) error {
|
||||
res := inspect(errors)
|
||||
switch res.Count {
|
||||
case 0:
|
||||
return nil
|
||||
case 1:
|
||||
// only one non-nil entry
|
||||
return errors[res.FirstErrorIdx]
|
||||
case len(errors):
|
||||
if !res.ContainsMultiError {
|
||||
// already flat
|
||||
return &multiError{errors: errors}
|
||||
}
|
||||
}
|
||||
|
||||
nonNilErrs := make([]error, 0, res.Capacity)
|
||||
for _, err := range errors[res.FirstErrorIdx:] {
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if nested, ok := err.(*multiError); ok {
|
||||
nonNilErrs = append(nonNilErrs, nested.errors...)
|
||||
} else {
|
||||
nonNilErrs = append(nonNilErrs, err)
|
||||
}
|
||||
}
|
||||
|
||||
return &multiError{errors: nonNilErrs}
|
||||
}
|
||||
|
||||
// Combine combines the passed errors into a single error.
|
||||
//
|
||||
// If zero arguments were passed or if all items are nil, a nil error is
|
||||
// returned.
|
||||
//
|
||||
// Combine(nil, nil) // == nil
|
||||
//
|
||||
// If only a single error was passed, it is returned as-is.
|
||||
//
|
||||
// Combine(err) // == err
|
||||
//
|
||||
// Combine skips over nil arguments so this function may be used to combine
|
||||
// together errors from operations that fail independently of each other.
|
||||
//
|
||||
// multierr.Combine(
|
||||
// reader.Close(),
|
||||
// writer.Close(),
|
||||
// pipe.Close(),
|
||||
// )
|
||||
//
|
||||
// If any of the passed errors is a multierr error, it will be flattened along
|
||||
// with the other errors.
|
||||
//
|
||||
// multierr.Combine(multierr.Combine(err1, err2), err3)
|
||||
// // is the same as
|
||||
// multierr.Combine(err1, err2, err3)
|
||||
//
|
||||
// The returned error formats into a readable multi-line error message if
|
||||
// formatted with %+v.
|
||||
//
|
||||
// fmt.Sprintf("%+v", multierr.Combine(err1, err2))
|
||||
func Combine(errors ...error) error {
|
||||
return fromSlice(errors)
|
||||
}
|
||||
|
||||
// Append appends the given errors together. Either value may be nil.
|
||||
//
|
||||
// This function is a specialization of Combine for the common case where
|
||||
// there are only two errors.
|
||||
//
|
||||
// err = multierr.Append(reader.Close(), writer.Close())
|
||||
//
|
||||
// The following pattern may also be used to record failure of deferred
|
||||
// operations without losing information about the original error.
|
||||
//
|
||||
// func doSomething(..) (err error) {
|
||||
// f := acquireResource()
|
||||
// defer func() {
|
||||
// err = multierr.Append(err, f.Close())
|
||||
// }()
|
||||
func Append(left error, right error) error {
|
||||
switch {
|
||||
case left == nil:
|
||||
return right
|
||||
case right == nil:
|
||||
return left
|
||||
}
|
||||
|
||||
if _, ok := right.(*multiError); !ok {
|
||||
if l, ok := left.(*multiError); ok && !l.copyNeeded.Swap(true) {
|
||||
// Common case where the error on the left is constantly being
|
||||
// appended to.
|
||||
errs := append(l.errors, right)
|
||||
return &multiError{errors: errs}
|
||||
} else if !ok {
|
||||
// Both errors are single errors.
|
||||
return &multiError{errors: []error{left, right}}
|
||||
}
|
||||
}
|
||||
|
||||
// Either right or both, left and right, are multiErrors. Rely on usual
|
||||
// expensive logic.
|
||||
errors := [2]error{left, right}
|
||||
return fromSlice(errors[0:])
|
||||
}
|
||||
|
||||
// AppendInto appends an error into the destination of an error pointer and
|
||||
// returns whether the error being appended was non-nil.
|
||||
//
|
||||
// var err error
|
||||
// multierr.AppendInto(&err, r.Close())
|
||||
// multierr.AppendInto(&err, w.Close())
|
||||
//
|
||||
// The above is equivalent to,
|
||||
//
|
||||
// err := multierr.Append(r.Close(), w.Close())
|
||||
//
|
||||
// As AppendInto reports whether the provided error was non-nil, it may be
|
||||
// used to build a multierr error in a loop more ergonomically. For example:
|
||||
//
|
||||
// var err error
|
||||
// for line := range lines {
|
||||
// var item Item
|
||||
// if multierr.AppendInto(&err, parse(line, &item)) {
|
||||
// continue
|
||||
// }
|
||||
// items = append(items, item)
|
||||
// }
|
||||
//
|
||||
// Compare this with a version that relies solely on Append:
|
||||
//
|
||||
// var err error
|
||||
// for line := range lines {
|
||||
// var item Item
|
||||
// if parseErr := parse(line, &item); parseErr != nil {
|
||||
// err = multierr.Append(err, parseErr)
|
||||
// continue
|
||||
// }
|
||||
// items = append(items, item)
|
||||
// }
|
||||
func AppendInto(into *error, err error) (errored bool) {
|
||||
if into == nil {
|
||||
// We panic if 'into' is nil. This is not documented above
|
||||
// because suggesting that the pointer must be non-nil may
|
||||
// confuse users into thinking that the error that it points
|
||||
// to must be non-nil.
|
||||
panic("misuse of multierr.AppendInto: into pointer must not be nil")
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
*into = Append(*into, err)
|
||||
return true
|
||||
}
|
||||
|
||||
// Invoker is an operation that may fail with an error. Use it with
|
||||
// AppendInvoke to append the result of calling the function into an error.
|
||||
// This allows you to conveniently defer capture of failing operations.
|
||||
//
|
||||
// See also, Close and Invoke.
|
||||
type Invoker interface {
|
||||
Invoke() error
|
||||
}
|
||||
|
||||
// Invoke wraps a function which may fail with an error to match the Invoker
|
||||
// interface. Use it to supply functions matching this signature to
|
||||
// AppendInvoke.
|
||||
//
|
||||
// For example,
|
||||
//
|
||||
// func processReader(r io.Reader) (err error) {
|
||||
// scanner := bufio.NewScanner(r)
|
||||
// defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err))
|
||||
// for scanner.Scan() {
|
||||
// // ...
|
||||
// }
|
||||
// // ...
|
||||
// }
|
||||
//
|
||||
// In this example, the following line will construct the Invoker right away,
|
||||
// but defer the invocation of scanner.Err() until the function returns.
|
||||
//
|
||||
// defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err))
|
||||
type Invoke func() error
|
||||
|
||||
// Invoke calls the supplied function and returns its result.
|
||||
func (i Invoke) Invoke() error { return i() }
|
||||
|
||||
// Close builds an Invoker that closes the provided io.Closer. Use it with
|
||||
// AppendInvoke to close io.Closers and append their results into an error.
|
||||
//
|
||||
// For example,
|
||||
//
|
||||
// func processFile(path string) (err error) {
|
||||
// f, err := os.Open(path)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// defer multierr.AppendInvoke(&err, multierr.Close(f))
|
||||
// return processReader(f)
|
||||
// }
|
||||
//
|
||||
// In this example, multierr.Close will construct the Invoker right away, but
|
||||
// defer the invocation of f.Close until the function returns.
|
||||
//
|
||||
// defer multierr.AppendInvoke(&err, multierr.Close(f))
|
||||
func Close(closer io.Closer) Invoker {
|
||||
return Invoke(closer.Close)
|
||||
}
|
||||
|
||||
// AppendInvoke appends the result of calling the given Invoker into the
|
||||
// provided error pointer. Use it with named returns to safely defer
|
||||
// invocation of fallible operations until a function returns, and capture the
|
||||
// resulting errors.
|
||||
//
|
||||
// func doSomething(...) (err error) {
|
||||
// // ...
|
||||
// f, err := openFile(..)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// // multierr will call f.Close() when this function returns and
|
||||
// // if the operation fails, its append its error into the
|
||||
// // returned error.
|
||||
// defer multierr.AppendInvoke(&err, multierr.Close(f))
|
||||
//
|
||||
// scanner := bufio.NewScanner(f)
|
||||
// // Similarly, this scheduled scanner.Err to be called and
|
||||
// // inspected when the function returns and append its error
|
||||
// // into the returned error.
|
||||
// defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err))
|
||||
//
|
||||
// // ...
|
||||
// }
|
||||
//
|
||||
// Without defer, AppendInvoke behaves exactly like AppendInto.
|
||||
//
|
||||
// err := // ...
|
||||
// multierr.AppendInvoke(&err, mutltierr.Invoke(foo))
|
||||
//
|
||||
// // ...is roughly equivalent to...
|
||||
//
|
||||
// err := // ...
|
||||
// multierr.AppendInto(&err, foo())
|
||||
//
|
||||
// The advantage of the indirection introduced by Invoker is to make it easy
|
||||
// to defer the invocation of a function. Without this indirection, the
|
||||
// invoked function will be evaluated at the time of the defer block rather
|
||||
// than when the function returns.
|
||||
//
|
||||
// // BAD: This is likely not what the caller intended. This will evaluate
|
||||
// // foo() right away and append its result into the error when the
|
||||
// // function returns.
|
||||
// defer multierr.AppendInto(&err, foo())
|
||||
//
|
||||
// // GOOD: This will defer invocation of foo unutil the function returns.
|
||||
// defer multierr.AppendInvoke(&err, multierr.Invoke(foo))
|
||||
//
|
||||
// multierr provides a few Invoker implementations out of the box for
|
||||
// convenience. See Invoker for more information.
|
||||
func AppendInvoke(into *error, invoker Invoker) {
|
||||
AppendInto(into, invoker.Invoke())
|
||||
}
|
||||
8
vendor/go.uber.org/multierr/glide.yaml
сгенерированный
поставляемый
8
vendor/go.uber.org/multierr/glide.yaml
сгенерированный
поставляемый
@@ -1,8 +0,0 @@
|
||||
package: go.uber.org/multierr
|
||||
import:
|
||||
- package: go.uber.org/atomic
|
||||
version: ^1
|
||||
testImport:
|
||||
- package: github.com/stretchr/testify
|
||||
subpackages:
|
||||
- assert
|
||||
9
vendor/go.uber.org/multierr/go.mod
сгенерированный
поставляемый
9
vendor/go.uber.org/multierr/go.mod
сгенерированный
поставляемый
@@ -1,9 +0,0 @@
|
||||
module go.uber.org/multierr
|
||||
|
||||
go 1.14
|
||||
|
||||
require (
|
||||
github.com/stretchr/testify v1.7.0
|
||||
go.uber.org/atomic v1.7.0
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
)
|
||||
16
vendor/go.uber.org/multierr/go.sum
сгенерированный
поставляемый
16
vendor/go.uber.org/multierr/go.sum
сгенерированный
поставляемый
@@ -1,16 +0,0 @@
|
||||
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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
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.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
17
vendor/go.uber.org/zap/.codecov.yml
сгенерированный
поставляемый
17
vendor/go.uber.org/zap/.codecov.yml
сгенерированный
поставляемый
@@ -1,17 +0,0 @@
|
||||
coverage:
|
||||
range: 80..100
|
||||
round: down
|
||||
precision: 2
|
||||
|
||||
status:
|
||||
project: # measuring the overall project coverage
|
||||
default: # context, you can create multiple ones with custom titles
|
||||
enabled: yes # must be yes|true to enable this status
|
||||
target: 95% # specify the target coverage for each commit status
|
||||
# option: "auto" (must increase from parent commit or pull request base)
|
||||
# option: "X%" a static target percentage to hit
|
||||
if_not_found: success # if parent is not found report status as success, error, or failure
|
||||
if_ci_failed: error # if ci fails report status as success, error, or failure
|
||||
ignore:
|
||||
- internal/readme/readme.go
|
||||
|
||||
32
vendor/go.uber.org/zap/.gitignore
сгенерированный
поставляемый
32
vendor/go.uber.org/zap/.gitignore
сгенерированный
поставляемый
@@ -1,32 +0,0 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
vendor
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
*.pprof
|
||||
*.out
|
||||
*.log
|
||||
|
||||
/bin
|
||||
cover.out
|
||||
cover.html
|
||||
109
vendor/go.uber.org/zap/.readme.tmpl
сгенерированный
поставляемый
109
vendor/go.uber.org/zap/.readme.tmpl
сгенерированный
поставляемый
@@ -1,109 +0,0 @@
|
||||
# :zap: zap [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov]
|
||||
|
||||
Blazing fast, structured, leveled logging in Go.
|
||||
|
||||
## Installation
|
||||
|
||||
`go get -u go.uber.org/zap`
|
||||
|
||||
Note that zap only supports the two most recent minor versions of Go.
|
||||
|
||||
## Quick Start
|
||||
|
||||
In contexts where performance is nice, but not critical, use the
|
||||
`SugaredLogger`. It's 4-10x faster than other structured logging
|
||||
packages and includes both structured and `printf`-style APIs.
|
||||
|
||||
```go
|
||||
logger, _ := zap.NewProduction()
|
||||
defer logger.Sync() // flushes buffer, if any
|
||||
sugar := logger.Sugar()
|
||||
sugar.Infow("failed to fetch URL",
|
||||
// Structured context as loosely typed key-value pairs.
|
||||
"url", url,
|
||||
"attempt", 3,
|
||||
"backoff", time.Second,
|
||||
)
|
||||
sugar.Infof("Failed to fetch URL: %s", url)
|
||||
```
|
||||
|
||||
When performance and type safety are critical, use the `Logger`. It's even
|
||||
faster than the `SugaredLogger` and allocates far less, but it only supports
|
||||
structured logging.
|
||||
|
||||
```go
|
||||
logger, _ := zap.NewProduction()
|
||||
defer logger.Sync()
|
||||
logger.Info("failed to fetch URL",
|
||||
// Structured context as strongly typed Field values.
|
||||
zap.String("url", url),
|
||||
zap.Int("attempt", 3),
|
||||
zap.Duration("backoff", time.Second),
|
||||
)
|
||||
```
|
||||
|
||||
See the [documentation][doc] and [FAQ](FAQ.md) for more details.
|
||||
|
||||
## Performance
|
||||
|
||||
For applications that log in the hot path, reflection-based serialization and
|
||||
string formatting are prohibitively expensive — they're CPU-intensive
|
||||
and make many small allocations. Put differently, using `encoding/json` and
|
||||
`fmt.Fprintf` to log tons of `interface{}`s makes your application slow.
|
||||
|
||||
Zap takes a different approach. It includes a reflection-free, zero-allocation
|
||||
JSON encoder, and the base `Logger` strives to avoid serialization overhead
|
||||
and allocations wherever possible. By building the high-level `SugaredLogger`
|
||||
on that foundation, zap lets users *choose* when they need to count every
|
||||
allocation and when they'd prefer a more familiar, loosely typed API.
|
||||
|
||||
As measured by its own [benchmarking suite][], not only is zap more performant
|
||||
than comparable structured logging packages — it's also faster than the
|
||||
standard library. Like all benchmarks, take these with a grain of salt.<sup
|
||||
id="anchor-versions">[1](#footnote-versions)</sup>
|
||||
|
||||
Log a message and 10 fields:
|
||||
|
||||
{{.BenchmarkAddingFields}}
|
||||
|
||||
Log a message with a logger that already has 10 fields of context:
|
||||
|
||||
{{.BenchmarkAccumulatedContext}}
|
||||
|
||||
Log a static string, without any context or `printf`-style templating:
|
||||
|
||||
{{.BenchmarkWithoutFields}}
|
||||
|
||||
## Development Status: Stable
|
||||
|
||||
All APIs are finalized, and no breaking changes will be made in the 1.x series
|
||||
of releases. Users of semver-aware dependency management systems should pin
|
||||
zap to `^1`.
|
||||
|
||||
## Contributing
|
||||
|
||||
We encourage and support an active, healthy community of contributors —
|
||||
including you! Details are in the [contribution guide](CONTRIBUTING.md) and
|
||||
the [code of conduct](CODE_OF_CONDUCT.md). The zap maintainers keep an eye on
|
||||
issues and pull requests, but you can also report any negative conduct to
|
||||
oss-conduct@uber.com. That email list is a private, safe space; even the zap
|
||||
maintainers don't have access, so don't hesitate to hold us to a high
|
||||
standard.
|
||||
|
||||
<hr>
|
||||
|
||||
Released under the [MIT License](LICENSE.txt).
|
||||
|
||||
<sup id="footnote-versions">1</sup> In particular, keep in mind that we may be
|
||||
benchmarking against slightly older versions of other packages. Versions are
|
||||
pinned in zap's [glide.lock][] file. [↩](#anchor-versions)
|
||||
|
||||
[doc-img]: https://godoc.org/go.uber.org/zap?status.svg
|
||||
[doc]: https://godoc.org/go.uber.org/zap
|
||||
[ci-img]: https://travis-ci.com/uber-go/zap.svg?branch=master
|
||||
[ci]: https://travis-ci.com/uber-go/zap
|
||||
[cov-img]: https://codecov.io/gh/uber-go/zap/branch/master/graph/badge.svg
|
||||
[cov]: https://codecov.io/gh/uber-go/zap
|
||||
[benchmarking suite]: https://github.com/uber-go/zap/tree/master/benchmarks
|
||||
[glide.lock]: https://github.com/uber-go/zap/blob/master/glide.lock
|
||||
|
||||
460
vendor/go.uber.org/zap/CHANGELOG.md
сгенерированный
поставляемый
460
vendor/go.uber.org/zap/CHANGELOG.md
сгенерированный
поставляемый
@@ -1,460 +0,0 @@
|
||||
# Changelog
|
||||
|
||||
## 1.17.0 (25 May 2021)
|
||||
|
||||
Bugfixes:
|
||||
* [#867][]: Encode `<nil>` for nil `error` instead of a panic.
|
||||
* [#931][], [#936][]: Update minimum version constraints to address
|
||||
vulnerabilities in dependencies.
|
||||
|
||||
Enhancements:
|
||||
* [#865][]: Improve alignment of fields of the Logger struct, reducing its
|
||||
size from 96 to 80 bytes.
|
||||
* [#881][]: Support `grpclog.LoggerV2` in zapgrpc.
|
||||
* [#903][]: Support URL-encoded POST requests to the AtomicLevel HTTP handler
|
||||
with the `application/x-www-form-urlencoded` content type.
|
||||
* [#912][]: Support multi-field encoding with `zap.Inline`.
|
||||
* [#913][]: Speed up SugaredLogger for calls with a single string.
|
||||
* [#928][]: Add support for filtering by field name to `zaptest/observer`.
|
||||
|
||||
Thanks to @ash2k, @FMLS, @jimmystewpot, @Oncilla, @tsoslow, @tylitianrui, @withshubh, and @wziww for their contributions to this release.
|
||||
|
||||
## 1.16.0 (1 Sep 2020)
|
||||
|
||||
Bugfixes:
|
||||
* [#828][]: Fix missing newline in IncreaseLevel error messages.
|
||||
* [#835][]: Fix panic in JSON encoder when encoding times or durations
|
||||
without specifying a time or duration encoder.
|
||||
* [#843][]: Honor CallerSkip when taking stack traces.
|
||||
* [#862][]: Fix the default file permissions to use `0666` and rely on the umask instead.
|
||||
* [#854][]: Encode `<nil>` for nil `Stringer` instead of a panic error log.
|
||||
|
||||
Enhancements:
|
||||
* [#629][]: Added `zapcore.TimeEncoderOfLayout` to easily create time encoders
|
||||
for custom layouts.
|
||||
* [#697][]: Added support for a configurable delimiter in the console encoder.
|
||||
* [#852][]: Optimize console encoder by pooling the underlying JSON encoder.
|
||||
* [#844][]: Add ability to include the calling function as part of logs.
|
||||
* [#843][]: Add `StackSkip` for including truncated stacks as a field.
|
||||
* [#861][]: Add options to customize Fatal behaviour for better testability.
|
||||
|
||||
Thanks to @SteelPhase, @tmshn, @lixingwang, @wyxloading, @moul, @segevfiner, @andy-retailnext and @jcorbin for their contributions to this release.
|
||||
|
||||
## 1.15.0 (23 Apr 2020)
|
||||
|
||||
Bugfixes:
|
||||
* [#804][]: Fix handling of `Time` values out of `UnixNano` range.
|
||||
* [#812][]: Fix `IncreaseLevel` being reset after a call to `With`.
|
||||
|
||||
Enhancements:
|
||||
* [#806][]: Add `WithCaller` option to supersede the `AddCaller` option. This
|
||||
allows disabling annotation of log entries with caller information if
|
||||
previously enabled with `AddCaller`.
|
||||
* [#813][]: Deprecate `NewSampler` constructor in favor of
|
||||
`NewSamplerWithOptions` which supports a `SamplerHook` option. This option
|
||||
adds support for monitoring sampling decisions through a hook.
|
||||
|
||||
Thanks to @danielbprice for their contributions to this release.
|
||||
|
||||
## 1.14.1 (14 Mar 2020)
|
||||
|
||||
Bugfixes:
|
||||
* [#791][]: Fix panic on attempting to build a logger with an invalid Config.
|
||||
* [#795][]: Vendoring Zap with `go mod vendor` no longer includes Zap's
|
||||
development-time dependencies.
|
||||
* [#799][]: Fix issue introduced in 1.14.0 that caused invalid JSON output to
|
||||
be generated for arrays of `time.Time` objects when using string-based time
|
||||
formats.
|
||||
|
||||
Thanks to @YashishDua for their contributions to this release.
|
||||
|
||||
## 1.14.0 (20 Feb 2020)
|
||||
|
||||
Enhancements:
|
||||
* [#771][]: Optimize calls for disabled log levels.
|
||||
* [#773][]: Add millisecond duration encoder.
|
||||
* [#775][]: Add option to increase the level of a logger.
|
||||
* [#786][]: Optimize time formatters using `Time.AppendFormat` where possible.
|
||||
|
||||
Thanks to @caibirdme for their contributions to this release.
|
||||
|
||||
## 1.13.0 (13 Nov 2019)
|
||||
|
||||
Enhancements:
|
||||
* [#758][]: Add `Intp`, `Stringp`, and other similar `*p` field constructors
|
||||
to log pointers to primitives with support for `nil` values.
|
||||
|
||||
Thanks to @jbizzle for their contributions to this release.
|
||||
|
||||
## 1.12.0 (29 Oct 2019)
|
||||
|
||||
Enhancements:
|
||||
* [#751][]: Migrate to Go modules.
|
||||
|
||||
## 1.11.0 (21 Oct 2019)
|
||||
|
||||
Enhancements:
|
||||
* [#725][]: Add `zapcore.OmitKey` to omit keys in an `EncoderConfig`.
|
||||
* [#736][]: Add `RFC3339` and `RFC3339Nano` time encoders.
|
||||
|
||||
Thanks to @juicemia, @uhthomas for their contributions to this release.
|
||||
|
||||
## 1.10.0 (29 Apr 2019)
|
||||
|
||||
Bugfixes:
|
||||
* [#657][]: Fix `MapObjectEncoder.AppendByteString` not adding value as a
|
||||
string.
|
||||
* [#706][]: Fix incorrect call depth to determine caller in Go 1.12.
|
||||
|
||||
Enhancements:
|
||||
* [#610][]: Add `zaptest.WrapOptions` to wrap `zap.Option` for creating test
|
||||
loggers.
|
||||
* [#675][]: Don't panic when encoding a String field.
|
||||
* [#704][]: Disable HTML escaping for JSON objects encoded using the
|
||||
reflect-based encoder.
|
||||
|
||||
Thanks to @iaroslav-ciupin, @lelenanam, @joa, @NWilson for their contributions
|
||||
to this release.
|
||||
|
||||
## v1.9.1 (06 Aug 2018)
|
||||
|
||||
Bugfixes:
|
||||
|
||||
* [#614][]: MapObjectEncoder should not ignore empty slices.
|
||||
|
||||
## v1.9.0 (19 Jul 2018)
|
||||
|
||||
Enhancements:
|
||||
* [#602][]: Reduce number of allocations when logging with reflection.
|
||||
* [#572][], [#606][]: Expose a registry for third-party logging sinks.
|
||||
|
||||
Thanks to @nfarah86, @AlekSi, @JeanMertz, @philippgille, @etsangsplk, and
|
||||
@dimroc for their contributions to this release.
|
||||
|
||||
## v1.8.0 (13 Apr 2018)
|
||||
|
||||
Enhancements:
|
||||
* [#508][]: Make log level configurable when redirecting the standard
|
||||
library's logger.
|
||||
* [#518][]: Add a logger that writes to a `*testing.TB`.
|
||||
* [#577][]: Add a top-level alias for `zapcore.Field` to clean up GoDoc.
|
||||
|
||||
Bugfixes:
|
||||
* [#574][]: Add a missing import comment to `go.uber.org/zap/buffer`.
|
||||
|
||||
Thanks to @DiSiqueira and @djui for their contributions to this release.
|
||||
|
||||
## v1.7.1 (25 Sep 2017)
|
||||
|
||||
Bugfixes:
|
||||
* [#504][]: Store strings when using AddByteString with the map encoder.
|
||||
|
||||
## v1.7.0 (21 Sep 2017)
|
||||
|
||||
Enhancements:
|
||||
|
||||
* [#487][]: Add `NewStdLogAt`, which extends `NewStdLog` by allowing the user
|
||||
to specify the level of the logged messages.
|
||||
|
||||
## v1.6.0 (30 Aug 2017)
|
||||
|
||||
Enhancements:
|
||||
|
||||
* [#491][]: Omit zap stack frames from stacktraces.
|
||||
* [#490][]: Add a `ContextMap` method to observer logs for simpler
|
||||
field validation in tests.
|
||||
|
||||
## v1.5.0 (22 Jul 2017)
|
||||
|
||||
Enhancements:
|
||||
|
||||
* [#460][] and [#470][]: Support errors produced by `go.uber.org/multierr`.
|
||||
* [#465][]: Support user-supplied encoders for logger names.
|
||||
|
||||
Bugfixes:
|
||||
|
||||
* [#477][]: Fix a bug that incorrectly truncated deep stacktraces.
|
||||
|
||||
Thanks to @richard-tunein and @pavius for their contributions to this release.
|
||||
|
||||
## v1.4.1 (08 Jun 2017)
|
||||
|
||||
This release fixes two bugs.
|
||||
|
||||
Bugfixes:
|
||||
|
||||
* [#435][]: Support a variety of case conventions when unmarshaling levels.
|
||||
* [#444][]: Fix a panic in the observer.
|
||||
|
||||
## v1.4.0 (12 May 2017)
|
||||
|
||||
This release adds a few small features and is fully backward-compatible.
|
||||
|
||||
Enhancements:
|
||||
|
||||
* [#424][]: Add a `LineEnding` field to `EncoderConfig`, allowing users to
|
||||
override the Unix-style default.
|
||||
* [#425][]: Preserve time zones when logging times.
|
||||
* [#431][]: Make `zap.AtomicLevel` implement `fmt.Stringer`, which makes a
|
||||
variety of operations a bit simpler.
|
||||
|
||||
## v1.3.0 (25 Apr 2017)
|
||||
|
||||
This release adds an enhancement to zap's testing helpers as well as the
|
||||
ability to marshal an AtomicLevel. It is fully backward-compatible.
|
||||
|
||||
Enhancements:
|
||||
|
||||
* [#415][]: Add a substring-filtering helper to zap's observer. This is
|
||||
particularly useful when testing the `SugaredLogger`.
|
||||
* [#416][]: Make `AtomicLevel` implement `encoding.TextMarshaler`.
|
||||
|
||||
## v1.2.0 (13 Apr 2017)
|
||||
|
||||
This release adds a gRPC compatibility wrapper. It is fully backward-compatible.
|
||||
|
||||
Enhancements:
|
||||
|
||||
* [#402][]: Add a `zapgrpc` package that wraps zap's Logger and implements
|
||||
`grpclog.Logger`.
|
||||
|
||||
## v1.1.0 (31 Mar 2017)
|
||||
|
||||
This release fixes two bugs and adds some enhancements to zap's testing helpers.
|
||||
It is fully backward-compatible.
|
||||
|
||||
Bugfixes:
|
||||
|
||||
* [#385][]: Fix caller path trimming on Windows.
|
||||
* [#396][]: Fix a panic when attempting to use non-existent directories with
|
||||
zap's configuration struct.
|
||||
|
||||
Enhancements:
|
||||
|
||||
* [#386][]: Add filtering helpers to zaptest's observing logger.
|
||||
|
||||
Thanks to @moitias for contributing to this release.
|
||||
|
||||
## v1.0.0 (14 Mar 2017)
|
||||
|
||||
This is zap's first stable release. All exported APIs are now final, and no
|
||||
further breaking changes will be made in the 1.x release series. Anyone using a
|
||||
semver-aware dependency manager should now pin to `^1`.
|
||||
|
||||
Breaking changes:
|
||||
|
||||
* [#366][]: Add byte-oriented APIs to encoders to log UTF-8 encoded text without
|
||||
casting from `[]byte` to `string`.
|
||||
* [#364][]: To support buffering outputs, add `Sync` methods to `zapcore.Core`,
|
||||
`zap.Logger`, and `zap.SugaredLogger`.
|
||||
* [#371][]: Rename the `testutils` package to `zaptest`, which is less likely to
|
||||
clash with other testing helpers.
|
||||
|
||||
Bugfixes:
|
||||
|
||||
* [#362][]: Make the ISO8601 time formatters fixed-width, which is friendlier
|
||||
for tab-separated console output.
|
||||
* [#369][]: Remove the automatic locks in `zapcore.NewCore`, which allows zap to
|
||||
work with concurrency-safe `WriteSyncer` implementations.
|
||||
* [#347][]: Stop reporting errors when trying to `fsync` standard out on Linux
|
||||
systems.
|
||||
* [#373][]: Report the correct caller from zap's standard library
|
||||
interoperability wrappers.
|
||||
|
||||
Enhancements:
|
||||
|
||||
* [#348][]: Add a registry allowing third-party encodings to work with zap's
|
||||
built-in `Config`.
|
||||
* [#327][]: Make the representation of logger callers configurable (like times,
|
||||
levels, and durations).
|
||||
* [#376][]: Allow third-party encoders to use their own buffer pools, which
|
||||
removes the last performance advantage that zap's encoders have over plugins.
|
||||
* [#346][]: Add `CombineWriteSyncers`, a convenience function to tee multiple
|
||||
`WriteSyncer`s and lock the result.
|
||||
* [#365][]: Make zap's stacktraces compatible with mid-stack inlining (coming in
|
||||
Go 1.9).
|
||||
* [#372][]: Export zap's observing logger as `zaptest/observer`. This makes it
|
||||
easier for particularly punctilious users to unit test their application's
|
||||
logging.
|
||||
|
||||
Thanks to @suyash, @htrendev, @flisky, @Ulexus, and @skipor for their
|
||||
contributions to this release.
|
||||
|
||||
## v1.0.0-rc.3 (7 Mar 2017)
|
||||
|
||||
This is the third release candidate for zap's stable release. There are no
|
||||
breaking changes.
|
||||
|
||||
Bugfixes:
|
||||
|
||||
* [#339][]: Byte slices passed to `zap.Any` are now correctly treated as binary blobs
|
||||
rather than `[]uint8`.
|
||||
|
||||
Enhancements:
|
||||
|
||||
* [#307][]: Users can opt into colored output for log levels.
|
||||
* [#353][]: In addition to hijacking the output of the standard library's
|
||||
package-global logging functions, users can now construct a zap-backed
|
||||
`log.Logger` instance.
|
||||
* [#311][]: Frames from common runtime functions and some of zap's internal
|
||||
machinery are now omitted from stacktraces.
|
||||
|
||||
Thanks to @ansel1 and @suyash for their contributions to this release.
|
||||
|
||||
## v1.0.0-rc.2 (21 Feb 2017)
|
||||
|
||||
This is the second release candidate for zap's stable release. It includes two
|
||||
breaking changes.
|
||||
|
||||
Breaking changes:
|
||||
|
||||
* [#316][]: Zap's global loggers are now fully concurrency-safe
|
||||
(previously, users had to ensure that `ReplaceGlobals` was called before the
|
||||
loggers were in use). However, they must now be accessed via the `L()` and
|
||||
`S()` functions. Users can update their projects with
|
||||
|
||||
```
|
||||
gofmt -r "zap.L -> zap.L()" -w .
|
||||
gofmt -r "zap.S -> zap.S()" -w .
|
||||
```
|
||||
* [#309][] and [#317][]: RC1 was mistakenly shipped with invalid
|
||||
JSON and YAML struct tags on all config structs. This release fixes the tags
|
||||
and adds static analysis to prevent similar bugs in the future.
|
||||
|
||||
Bugfixes:
|
||||
|
||||
* [#321][]: Redirecting the standard library's `log` output now
|
||||
correctly reports the logger's caller.
|
||||
|
||||
Enhancements:
|
||||
|
||||
* [#325][] and [#333][]: Zap now transparently supports non-standard, rich
|
||||
errors like those produced by `github.com/pkg/errors`.
|
||||
* [#326][]: Though `New(nil)` continues to return a no-op logger, `NewNop()` is
|
||||
now preferred. Users can update their projects with `gofmt -r 'zap.New(nil) ->
|
||||
zap.NewNop()' -w .`.
|
||||
* [#300][]: Incorrectly importing zap as `github.com/uber-go/zap` now returns a
|
||||
more informative error.
|
||||
|
||||
Thanks to @skipor and @chapsuk for their contributions to this release.
|
||||
|
||||
## v1.0.0-rc.1 (14 Feb 2017)
|
||||
|
||||
This is the first release candidate for zap's stable release. There are multiple
|
||||
breaking changes and improvements from the pre-release version. Most notably:
|
||||
|
||||
* **Zap's import path is now "go.uber.org/zap"** — all users will
|
||||
need to update their code.
|
||||
* User-facing types and functions remain in the `zap` package. Code relevant
|
||||
largely to extension authors is now in the `zapcore` package.
|
||||
* The `zapcore.Core` type makes it easy for third-party packages to use zap's
|
||||
internals but provide a different user-facing API.
|
||||
* `Logger` is now a concrete type instead of an interface.
|
||||
* A less verbose (though slower) logging API is included by default.
|
||||
* Package-global loggers `L` and `S` are included.
|
||||
* A human-friendly console encoder is included.
|
||||
* A declarative config struct allows common logger configurations to be managed
|
||||
as configuration instead of code.
|
||||
* Sampling is more accurate, and doesn't depend on the standard library's shared
|
||||
timer heap.
|
||||
|
||||
## v0.1.0-beta.1 (6 Feb 2017)
|
||||
|
||||
This is a minor version, tagged to allow users to pin to the pre-1.0 APIs and
|
||||
upgrade at their leisure. Since this is the first tagged release, there are no
|
||||
backward compatibility concerns and all functionality is new.
|
||||
|
||||
Early zap adopters should pin to the 0.1.x minor version until they're ready to
|
||||
upgrade to the upcoming stable release.
|
||||
|
||||
[#316]: https://github.com/uber-go/zap/pull/316
|
||||
[#309]: https://github.com/uber-go/zap/pull/309
|
||||
[#317]: https://github.com/uber-go/zap/pull/317
|
||||
[#321]: https://github.com/uber-go/zap/pull/321
|
||||
[#325]: https://github.com/uber-go/zap/pull/325
|
||||
[#333]: https://github.com/uber-go/zap/pull/333
|
||||
[#326]: https://github.com/uber-go/zap/pull/326
|
||||
[#300]: https://github.com/uber-go/zap/pull/300
|
||||
[#339]: https://github.com/uber-go/zap/pull/339
|
||||
[#307]: https://github.com/uber-go/zap/pull/307
|
||||
[#353]: https://github.com/uber-go/zap/pull/353
|
||||
[#311]: https://github.com/uber-go/zap/pull/311
|
||||
[#366]: https://github.com/uber-go/zap/pull/366
|
||||
[#364]: https://github.com/uber-go/zap/pull/364
|
||||
[#371]: https://github.com/uber-go/zap/pull/371
|
||||
[#362]: https://github.com/uber-go/zap/pull/362
|
||||
[#369]: https://github.com/uber-go/zap/pull/369
|
||||
[#347]: https://github.com/uber-go/zap/pull/347
|
||||
[#373]: https://github.com/uber-go/zap/pull/373
|
||||
[#348]: https://github.com/uber-go/zap/pull/348
|
||||
[#327]: https://github.com/uber-go/zap/pull/327
|
||||
[#376]: https://github.com/uber-go/zap/pull/376
|
||||
[#346]: https://github.com/uber-go/zap/pull/346
|
||||
[#365]: https://github.com/uber-go/zap/pull/365
|
||||
[#372]: https://github.com/uber-go/zap/pull/372
|
||||
[#385]: https://github.com/uber-go/zap/pull/385
|
||||
[#396]: https://github.com/uber-go/zap/pull/396
|
||||
[#386]: https://github.com/uber-go/zap/pull/386
|
||||
[#402]: https://github.com/uber-go/zap/pull/402
|
||||
[#415]: https://github.com/uber-go/zap/pull/415
|
||||
[#416]: https://github.com/uber-go/zap/pull/416
|
||||
[#424]: https://github.com/uber-go/zap/pull/424
|
||||
[#425]: https://github.com/uber-go/zap/pull/425
|
||||
[#431]: https://github.com/uber-go/zap/pull/431
|
||||
[#435]: https://github.com/uber-go/zap/pull/435
|
||||
[#444]: https://github.com/uber-go/zap/pull/444
|
||||
[#477]: https://github.com/uber-go/zap/pull/477
|
||||
[#465]: https://github.com/uber-go/zap/pull/465
|
||||
[#460]: https://github.com/uber-go/zap/pull/460
|
||||
[#470]: https://github.com/uber-go/zap/pull/470
|
||||
[#487]: https://github.com/uber-go/zap/pull/487
|
||||
[#490]: https://github.com/uber-go/zap/pull/490
|
||||
[#491]: https://github.com/uber-go/zap/pull/491
|
||||
[#504]: https://github.com/uber-go/zap/pull/504
|
||||
[#508]: https://github.com/uber-go/zap/pull/508
|
||||
[#518]: https://github.com/uber-go/zap/pull/518
|
||||
[#577]: https://github.com/uber-go/zap/pull/577
|
||||
[#574]: https://github.com/uber-go/zap/pull/574
|
||||
[#602]: https://github.com/uber-go/zap/pull/602
|
||||
[#572]: https://github.com/uber-go/zap/pull/572
|
||||
[#606]: https://github.com/uber-go/zap/pull/606
|
||||
[#614]: https://github.com/uber-go/zap/pull/614
|
||||
[#657]: https://github.com/uber-go/zap/pull/657
|
||||
[#706]: https://github.com/uber-go/zap/pull/706
|
||||
[#610]: https://github.com/uber-go/zap/pull/610
|
||||
[#675]: https://github.com/uber-go/zap/pull/675
|
||||
[#704]: https://github.com/uber-go/zap/pull/704
|
||||
[#725]: https://github.com/uber-go/zap/pull/725
|
||||
[#736]: https://github.com/uber-go/zap/pull/736
|
||||
[#751]: https://github.com/uber-go/zap/pull/751
|
||||
[#758]: https://github.com/uber-go/zap/pull/758
|
||||
[#771]: https://github.com/uber-go/zap/pull/771
|
||||
[#773]: https://github.com/uber-go/zap/pull/773
|
||||
[#775]: https://github.com/uber-go/zap/pull/775
|
||||
[#786]: https://github.com/uber-go/zap/pull/786
|
||||
[#791]: https://github.com/uber-go/zap/pull/791
|
||||
[#795]: https://github.com/uber-go/zap/pull/795
|
||||
[#799]: https://github.com/uber-go/zap/pull/799
|
||||
[#804]: https://github.com/uber-go/zap/pull/804
|
||||
[#812]: https://github.com/uber-go/zap/pull/812
|
||||
[#806]: https://github.com/uber-go/zap/pull/806
|
||||
[#813]: https://github.com/uber-go/zap/pull/813
|
||||
[#629]: https://github.com/uber-go/zap/pull/629
|
||||
[#697]: https://github.com/uber-go/zap/pull/697
|
||||
[#828]: https://github.com/uber-go/zap/pull/828
|
||||
[#835]: https://github.com/uber-go/zap/pull/835
|
||||
[#843]: https://github.com/uber-go/zap/pull/843
|
||||
[#844]: https://github.com/uber-go/zap/pull/844
|
||||
[#852]: https://github.com/uber-go/zap/pull/852
|
||||
[#854]: https://github.com/uber-go/zap/pull/854
|
||||
[#861]: https://github.com/uber-go/zap/pull/861
|
||||
[#862]: https://github.com/uber-go/zap/pull/862
|
||||
[#865]: https://github.com/uber-go/zap/pull/865
|
||||
[#867]: https://github.com/uber-go/zap/pull/867
|
||||
[#881]: https://github.com/uber-go/zap/pull/881
|
||||
[#903]: https://github.com/uber-go/zap/pull/903
|
||||
[#912]: https://github.com/uber-go/zap/pull/912
|
||||
[#913]: https://github.com/uber-go/zap/pull/913
|
||||
[#928]: https://github.com/uber-go/zap/pull/928
|
||||
[#931]: https://github.com/uber-go/zap/pull/931
|
||||
[#936]: https://github.com/uber-go/zap/pull/936
|
||||
75
vendor/go.uber.org/zap/CODE_OF_CONDUCT.md
сгенерированный
поставляемый
75
vendor/go.uber.org/zap/CODE_OF_CONDUCT.md
сгенерированный
поставляемый
@@ -1,75 +0,0 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age,
|
||||
body size, disability, ethnicity, gender identity and expression, level of
|
||||
experience, nationality, personal appearance, race, religion, or sexual
|
||||
identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an
|
||||
appointed representative at an online or offline event. Representation of a
|
||||
project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at oss-conduct@uber.com. The project
|
||||
team will review and investigate all complaints, and will respond in a way
|
||||
that it deems appropriate to the circumstances. The project team is obligated
|
||||
to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 1.4, available at
|
||||
[http://contributor-covenant.org/version/1/4][version].
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
||||
75
vendor/go.uber.org/zap/CONTRIBUTING.md
сгенерированный
поставляемый
75
vendor/go.uber.org/zap/CONTRIBUTING.md
сгенерированный
поставляемый
@@ -1,75 +0,0 @@
|
||||
# Contributing
|
||||
|
||||
We'd love your help making zap the very best structured logging library in Go!
|
||||
|
||||
If you'd like to add new exported APIs, please [open an issue][open-issue]
|
||||
describing your proposal — discussing API changes ahead of time makes
|
||||
pull request review much smoother. In your issue, pull request, and any other
|
||||
communications, please remember to treat your fellow contributors with
|
||||
respect! We take our [code of conduct](CODE_OF_CONDUCT.md) seriously.
|
||||
|
||||
Note that you'll need to sign [Uber's Contributor License Agreement][cla]
|
||||
before we can accept any of your contributions. If necessary, a bot will remind
|
||||
you to accept the CLA when you open your pull request.
|
||||
|
||||
## Setup
|
||||
|
||||
[Fork][fork], then clone the repository:
|
||||
|
||||
```
|
||||
mkdir -p $GOPATH/src/go.uber.org
|
||||
cd $GOPATH/src/go.uber.org
|
||||
git clone git@github.com:your_github_username/zap.git
|
||||
cd zap
|
||||
git remote add upstream https://github.com/uber-go/zap.git
|
||||
git fetch upstream
|
||||
```
|
||||
|
||||
Make sure that the tests and the linters pass:
|
||||
|
||||
```
|
||||
make test
|
||||
make lint
|
||||
```
|
||||
|
||||
If you're not using the minor version of Go specified in the Makefile's
|
||||
`LINTABLE_MINOR_VERSIONS` variable, `make lint` doesn't do anything. This is
|
||||
fine, but it means that you'll only discover lint failures after you open your
|
||||
pull request.
|
||||
|
||||
## Making Changes
|
||||
|
||||
Start by creating a new branch for your changes:
|
||||
|
||||
```
|
||||
cd $GOPATH/src/go.uber.org/zap
|
||||
git checkout master
|
||||
git fetch upstream
|
||||
git rebase upstream/master
|
||||
git checkout -b cool_new_feature
|
||||
```
|
||||
|
||||
Make your changes, then ensure that `make lint` and `make test` still pass. If
|
||||
you're satisfied with your changes, push them to your fork.
|
||||
|
||||
```
|
||||
git push origin cool_new_feature
|
||||
```
|
||||
|
||||
Then use the GitHub UI to open a pull request.
|
||||
|
||||
At this point, you're waiting on us to review your changes. We *try* to respond
|
||||
to issues and pull requests within a few business days, and we may suggest some
|
||||
improvements or alternatives. Once your changes are approved, one of the
|
||||
project maintainers will merge them.
|
||||
|
||||
We're much more likely to approve your changes if you:
|
||||
|
||||
* Add tests for new functionality.
|
||||
* Write a [good commit message][commit-message].
|
||||
* Maintain backward compatibility.
|
||||
|
||||
[fork]: https://github.com/uber-go/zap/fork
|
||||
[open-issue]: https://github.com/uber-go/zap/issues/new
|
||||
[cla]: https://cla-assistant.io/uber-go/zap
|
||||
[commit-message]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
|
||||
164
vendor/go.uber.org/zap/FAQ.md
сгенерированный
поставляемый
164
vendor/go.uber.org/zap/FAQ.md
сгенерированный
поставляемый
@@ -1,164 +0,0 @@
|
||||
# Frequently Asked Questions
|
||||
|
||||
## Design
|
||||
|
||||
### Why spend so much effort on logger performance?
|
||||
|
||||
Of course, most applications won't notice the impact of a slow logger: they
|
||||
already take tens or hundreds of milliseconds for each operation, so an extra
|
||||
millisecond doesn't matter.
|
||||
|
||||
On the other hand, why *not* make structured logging fast? The `SugaredLogger`
|
||||
isn't any harder to use than other logging packages, and the `Logger` makes
|
||||
structured logging possible in performance-sensitive contexts. Across a fleet
|
||||
of Go microservices, making each application even slightly more efficient adds
|
||||
up quickly.
|
||||
|
||||
### Why aren't `Logger` and `SugaredLogger` interfaces?
|
||||
|
||||
Unlike the familiar `io.Writer` and `http.Handler`, `Logger` and
|
||||
`SugaredLogger` interfaces would include *many* methods. As [Rob Pike points
|
||||
out][go-proverbs], "The bigger the interface, the weaker the abstraction."
|
||||
Interfaces are also rigid — *any* change requires releasing a new major
|
||||
version, since it breaks all third-party implementations.
|
||||
|
||||
Making the `Logger` and `SugaredLogger` concrete types doesn't sacrifice much
|
||||
abstraction, and it lets us add methods without introducing breaking changes.
|
||||
Your applications should define and depend upon an interface that includes
|
||||
just the methods you use.
|
||||
|
||||
### Why are some of my logs missing?
|
||||
|
||||
Logs are dropped intentionally by zap when sampling is enabled. The production
|
||||
configuration (as returned by `NewProductionConfig()` enables sampling which will
|
||||
cause repeated logs within a second to be sampled. See more details on why sampling
|
||||
is enabled in [Why sample application logs](https://github.com/uber-go/zap/blob/master/FAQ.md#why-sample-application-logs).
|
||||
|
||||
### Why sample application logs?
|
||||
|
||||
Applications often experience runs of errors, either because of a bug or
|
||||
because of a misbehaving user. Logging errors is usually a good idea, but it
|
||||
can easily make this bad situation worse: not only is your application coping
|
||||
with a flood of errors, it's also spending extra CPU cycles and I/O logging
|
||||
those errors. Since writes are typically serialized, logging limits throughput
|
||||
when you need it most.
|
||||
|
||||
Sampling fixes this problem by dropping repetitive log entries. Under normal
|
||||
conditions, your application writes out every entry. When similar entries are
|
||||
logged hundreds or thousands of times each second, though, zap begins dropping
|
||||
duplicates to preserve throughput.
|
||||
|
||||
### Why do the structured logging APIs take a message in addition to fields?
|
||||
|
||||
Subjectively, we find it helpful to accompany structured context with a brief
|
||||
description. This isn't critical during development, but it makes debugging
|
||||
and operating unfamiliar systems much easier.
|
||||
|
||||
More concretely, zap's sampling algorithm uses the message to identify
|
||||
duplicate entries. In our experience, this is a practical middle ground
|
||||
between random sampling (which often drops the exact entry that you need while
|
||||
debugging) and hashing the complete entry (which is prohibitively expensive).
|
||||
|
||||
### Why include package-global loggers?
|
||||
|
||||
Since so many other logging packages include a global logger, many
|
||||
applications aren't designed to accept loggers as explicit parameters.
|
||||
Changing function signatures is often a breaking change, so zap includes
|
||||
global loggers to simplify migration.
|
||||
|
||||
Avoid them where possible.
|
||||
|
||||
### Why include dedicated Panic and Fatal log levels?
|
||||
|
||||
In general, application code should handle errors gracefully instead of using
|
||||
`panic` or `os.Exit`. However, every rule has exceptions, and it's common to
|
||||
crash when an error is truly unrecoverable. To avoid losing any information
|
||||
— especially the reason for the crash — the logger must flush any
|
||||
buffered entries before the process exits.
|
||||
|
||||
Zap makes this easy by offering `Panic` and `Fatal` logging methods that
|
||||
automatically flush before exiting. Of course, this doesn't guarantee that
|
||||
logs will never be lost, but it eliminates a common error.
|
||||
|
||||
See the discussion in uber-go/zap#207 for more details.
|
||||
|
||||
### What's `DPanic`?
|
||||
|
||||
`DPanic` stands for "panic in development." In development, it logs at
|
||||
`PanicLevel`; otherwise, it logs at `ErrorLevel`. `DPanic` makes it easier to
|
||||
catch errors that are theoretically possible, but shouldn't actually happen,
|
||||
*without* crashing in production.
|
||||
|
||||
If you've ever written code like this, you need `DPanic`:
|
||||
|
||||
```go
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("shouldn't ever get here: %v", err))
|
||||
}
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
### What does the error `expects import "go.uber.org/zap"` mean?
|
||||
|
||||
Either zap was installed incorrectly or you're referencing the wrong package
|
||||
name in your code.
|
||||
|
||||
Zap's source code happens to be hosted on GitHub, but the [import
|
||||
path][import-path] is `go.uber.org/zap`. This gives us, the project
|
||||
maintainers, the freedom to move the source code if necessary. However, it
|
||||
means that you need to take a little care when installing and using the
|
||||
package.
|
||||
|
||||
If you follow two simple rules, everything should work: install zap with `go
|
||||
get -u go.uber.org/zap`, and always import it in your code with `import
|
||||
"go.uber.org/zap"`. Your code shouldn't contain *any* references to
|
||||
`github.com/uber-go/zap`.
|
||||
|
||||
## Usage
|
||||
|
||||
### Does zap support log rotation?
|
||||
|
||||
Zap doesn't natively support rotating log files, since we prefer to leave this
|
||||
to an external program like `logrotate`.
|
||||
|
||||
However, it's easy to integrate a log rotation package like
|
||||
[`gopkg.in/natefinch/lumberjack.v2`][lumberjack] as a `zapcore.WriteSyncer`.
|
||||
|
||||
```go
|
||||
// lumberjack.Logger is already safe for concurrent use, so we don't need to
|
||||
// lock it.
|
||||
w := zapcore.AddSync(&lumberjack.Logger{
|
||||
Filename: "/var/log/myapp/foo.log",
|
||||
MaxSize: 500, // megabytes
|
||||
MaxBackups: 3,
|
||||
MaxAge: 28, // days
|
||||
})
|
||||
core := zapcore.NewCore(
|
||||
zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()),
|
||||
w,
|
||||
zap.InfoLevel,
|
||||
)
|
||||
logger := zap.New(core)
|
||||
```
|
||||
|
||||
## Extensions
|
||||
|
||||
We'd love to support every logging need within zap itself, but we're only
|
||||
familiar with a handful of log ingestion systems, flag-parsing packages, and
|
||||
the like. Rather than merging code that we can't effectively debug and
|
||||
support, we'd rather grow an ecosystem of zap extensions.
|
||||
|
||||
We're aware of the following extensions, but haven't used them ourselves:
|
||||
|
||||
| Package | Integration |
|
||||
| --- | --- |
|
||||
| `github.com/tchap/zapext` | Sentry, syslog |
|
||||
| `github.com/fgrosse/zaptest` | Ginkgo |
|
||||
| `github.com/blendle/zapdriver` | Stackdriver |
|
||||
| `github.com/moul/zapgorm` | Gorm |
|
||||
| `github.com/moul/zapfilter` | Advanced filtering rules |
|
||||
|
||||
[go-proverbs]: https://go-proverbs.github.io/
|
||||
[import-path]: https://golang.org/cmd/go/#hdr-Remote_import_paths
|
||||
[lumberjack]: https://godoc.org/gopkg.in/natefinch/lumberjack.v2
|
||||
19
vendor/go.uber.org/zap/LICENSE.txt
сгенерированный
поставляемый
19
vendor/go.uber.org/zap/LICENSE.txt
сгенерированный
поставляемый
@@ -1,19 +0,0 @@
|
||||
Copyright (c) 2016-2017 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.
|
||||
73
vendor/go.uber.org/zap/Makefile
сгенерированный
поставляемый
73
vendor/go.uber.org/zap/Makefile
сгенерированный
поставляемый
@@ -1,73 +0,0 @@
|
||||
export GOBIN ?= $(shell pwd)/bin
|
||||
|
||||
GOLINT = $(GOBIN)/golint
|
||||
STATICCHECK = $(GOBIN)/staticcheck
|
||||
BENCH_FLAGS ?= -cpuprofile=cpu.pprof -memprofile=mem.pprof -benchmem
|
||||
|
||||
# Directories containing independent Go modules.
|
||||
#
|
||||
# We track coverage only for the main module.
|
||||
MODULE_DIRS = . ./benchmarks ./zapgrpc/internal/test
|
||||
|
||||
# Many Go tools take file globs or directories as arguments instead of packages.
|
||||
GO_FILES := $(shell \
|
||||
find . '(' -path '*/.*' -o -path './vendor' ')' -prune \
|
||||
-o -name '*.go' -print | cut -b3-)
|
||||
|
||||
.PHONY: all
|
||||
all: lint test
|
||||
|
||||
.PHONY: lint
|
||||
lint: $(GOLINT) $(STATICCHECK)
|
||||
@rm -rf lint.log
|
||||
@echo "Checking formatting..."
|
||||
@gofmt -d -s $(GO_FILES) 2>&1 | tee lint.log
|
||||
@echo "Checking vet..."
|
||||
@$(foreach dir,$(MODULE_DIRS),(cd $(dir) && go vet ./... 2>&1) &&) true | tee -a lint.log
|
||||
@echo "Checking lint..."
|
||||
@$(foreach dir,$(MODULE_DIRS),(cd $(dir) && $(GOLINT) ./... 2>&1) &&) true | tee -a lint.log
|
||||
@echo "Checking staticcheck..."
|
||||
@$(foreach dir,$(MODULE_DIRS),(cd $(dir) && $(STATICCHECK) ./... 2>&1) &&) true | tee -a lint.log
|
||||
@echo "Checking for unresolved FIXMEs..."
|
||||
@git grep -i fixme | grep -v -e Makefile | tee -a lint.log
|
||||
@echo "Checking for license headers..."
|
||||
@./checklicense.sh | tee -a lint.log
|
||||
@[ ! -s lint.log ]
|
||||
@echo "Checking 'go mod tidy'..."
|
||||
@make tidy
|
||||
@if ! git diff --quiet; then \
|
||||
echo "'go mod tidy' resulted in changes or working tree is dirty:"; \
|
||||
git --no-pager diff; \
|
||||
fi
|
||||
|
||||
$(GOLINT):
|
||||
cd tools && go install golang.org/x/lint/golint
|
||||
|
||||
$(STATICCHECK):
|
||||
cd tools && go install honnef.co/go/tools/cmd/staticcheck
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
@$(foreach dir,$(MODULE_DIRS),(cd $(dir) && go test -race ./...) &&) true
|
||||
|
||||
.PHONY: cover
|
||||
cover:
|
||||
go test -race -coverprofile=cover.out -coverpkg=./... ./...
|
||||
go tool cover -html=cover.out -o cover.html
|
||||
|
||||
.PHONY: bench
|
||||
BENCH ?= .
|
||||
bench:
|
||||
@$(foreach dir,$(MODULE_DIRS), ( \
|
||||
cd $(dir) && \
|
||||
go list ./... | xargs -n1 go test -bench=$(BENCH) -run="^$$" $(BENCH_FLAGS) \
|
||||
) &&) true
|
||||
|
||||
.PHONY: updatereadme
|
||||
updatereadme:
|
||||
rm -f README.md
|
||||
cat .readme.tmpl | go run internal/readme/readme.go > README.md
|
||||
|
||||
.PHONY: tidy
|
||||
tidy:
|
||||
@$(foreach dir,$(MODULE_DIRS),(cd $(dir) && go mod tidy) &&) true
|
||||
134
vendor/go.uber.org/zap/README.md
сгенерированный
поставляемый
134
vendor/go.uber.org/zap/README.md
сгенерированный
поставляемый
@@ -1,134 +0,0 @@
|
||||
# :zap: zap [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov]
|
||||
|
||||
Blazing fast, structured, leveled logging in Go.
|
||||
|
||||
## Installation
|
||||
|
||||
`go get -u go.uber.org/zap`
|
||||
|
||||
Note that zap only supports the two most recent minor versions of Go.
|
||||
|
||||
## Quick Start
|
||||
|
||||
In contexts where performance is nice, but not critical, use the
|
||||
`SugaredLogger`. It's 4-10x faster than other structured logging
|
||||
packages and includes both structured and `printf`-style APIs.
|
||||
|
||||
```go
|
||||
logger, _ := zap.NewProduction()
|
||||
defer logger.Sync() // flushes buffer, if any
|
||||
sugar := logger.Sugar()
|
||||
sugar.Infow("failed to fetch URL",
|
||||
// Structured context as loosely typed key-value pairs.
|
||||
"url", url,
|
||||
"attempt", 3,
|
||||
"backoff", time.Second,
|
||||
)
|
||||
sugar.Infof("Failed to fetch URL: %s", url)
|
||||
```
|
||||
|
||||
When performance and type safety are critical, use the `Logger`. It's even
|
||||
faster than the `SugaredLogger` and allocates far less, but it only supports
|
||||
structured logging.
|
||||
|
||||
```go
|
||||
logger, _ := zap.NewProduction()
|
||||
defer logger.Sync()
|
||||
logger.Info("failed to fetch URL",
|
||||
// Structured context as strongly typed Field values.
|
||||
zap.String("url", url),
|
||||
zap.Int("attempt", 3),
|
||||
zap.Duration("backoff", time.Second),
|
||||
)
|
||||
```
|
||||
|
||||
See the [documentation][doc] and [FAQ](FAQ.md) for more details.
|
||||
|
||||
## Performance
|
||||
|
||||
For applications that log in the hot path, reflection-based serialization and
|
||||
string formatting are prohibitively expensive — they're CPU-intensive
|
||||
and make many small allocations. Put differently, using `encoding/json` and
|
||||
`fmt.Fprintf` to log tons of `interface{}`s makes your application slow.
|
||||
|
||||
Zap takes a different approach. It includes a reflection-free, zero-allocation
|
||||
JSON encoder, and the base `Logger` strives to avoid serialization overhead
|
||||
and allocations wherever possible. By building the high-level `SugaredLogger`
|
||||
on that foundation, zap lets users *choose* when they need to count every
|
||||
allocation and when they'd prefer a more familiar, loosely typed API.
|
||||
|
||||
As measured by its own [benchmarking suite][], not only is zap more performant
|
||||
than comparable structured logging packages — it's also faster than the
|
||||
standard library. Like all benchmarks, take these with a grain of salt.<sup
|
||||
id="anchor-versions">[1](#footnote-versions)</sup>
|
||||
|
||||
Log a message and 10 fields:
|
||||
|
||||
| Package | Time | Time % to zap | Objects Allocated |
|
||||
| :------ | :--: | :-----------: | :---------------: |
|
||||
| :zap: zap | 862 ns/op | +0% | 5 allocs/op
|
||||
| :zap: zap (sugared) | 1250 ns/op | +45% | 11 allocs/op
|
||||
| zerolog | 4021 ns/op | +366% | 76 allocs/op
|
||||
| go-kit | 4542 ns/op | +427% | 105 allocs/op
|
||||
| apex/log | 26785 ns/op | +3007% | 115 allocs/op
|
||||
| logrus | 29501 ns/op | +3322% | 125 allocs/op
|
||||
| log15 | 29906 ns/op | +3369% | 122 allocs/op
|
||||
|
||||
Log a message with a logger that already has 10 fields of context:
|
||||
|
||||
| Package | Time | Time % to zap | Objects Allocated |
|
||||
| :------ | :--: | :-----------: | :---------------: |
|
||||
| :zap: zap | 126 ns/op | +0% | 0 allocs/op
|
||||
| :zap: zap (sugared) | 187 ns/op | +48% | 2 allocs/op
|
||||
| zerolog | 88 ns/op | -30% | 0 allocs/op
|
||||
| go-kit | 5087 ns/op | +3937% | 103 allocs/op
|
||||
| log15 | 18548 ns/op | +14621% | 73 allocs/op
|
||||
| apex/log | 26012 ns/op | +20544% | 104 allocs/op
|
||||
| logrus | 27236 ns/op | +21516% | 113 allocs/op
|
||||
|
||||
Log a static string, without any context or `printf`-style templating:
|
||||
|
||||
| Package | Time | Time % to zap | Objects Allocated |
|
||||
| :------ | :--: | :-----------: | :---------------: |
|
||||
| :zap: zap | 118 ns/op | +0% | 0 allocs/op
|
||||
| :zap: zap (sugared) | 191 ns/op | +62% | 2 allocs/op
|
||||
| zerolog | 93 ns/op | -21% | 0 allocs/op
|
||||
| go-kit | 280 ns/op | +137% | 11 allocs/op
|
||||
| standard library | 499 ns/op | +323% | 2 allocs/op
|
||||
| apex/log | 1990 ns/op | +1586% | 10 allocs/op
|
||||
| logrus | 3129 ns/op | +2552% | 24 allocs/op
|
||||
| log15 | 3887 ns/op | +3194% | 23 allocs/op
|
||||
|
||||
## Development Status: Stable
|
||||
|
||||
All APIs are finalized, and no breaking changes will be made in the 1.x series
|
||||
of releases. Users of semver-aware dependency management systems should pin
|
||||
zap to `^1`.
|
||||
|
||||
## Contributing
|
||||
|
||||
We encourage and support an active, healthy community of contributors —
|
||||
including you! Details are in the [contribution guide](CONTRIBUTING.md) and
|
||||
the [code of conduct](CODE_OF_CONDUCT.md). The zap maintainers keep an eye on
|
||||
issues and pull requests, but you can also report any negative conduct to
|
||||
oss-conduct@uber.com. That email list is a private, safe space; even the zap
|
||||
maintainers don't have access, so don't hesitate to hold us to a high
|
||||
standard.
|
||||
|
||||
<hr>
|
||||
|
||||
Released under the [MIT License](LICENSE.txt).
|
||||
|
||||
<sup id="footnote-versions">1</sup> In particular, keep in mind that we may be
|
||||
benchmarking against slightly older versions of other packages. Versions are
|
||||
pinned in the [benchmarks/go.mod][] file. [↩](#anchor-versions)
|
||||
|
||||
[doc-img]: https://pkg.go.dev/badge/go.uber.org/zap
|
||||
[doc]: https://pkg.go.dev/go.uber.org/zap
|
||||
[ci-img]: https://github.com/uber-go/zap/actions/workflows/go.yml/badge.svg
|
||||
[ci]: https://github.com/uber-go/zap/actions/workflows/go.yml
|
||||
[cov-img]: https://codecov.io/gh/uber-go/zap/branch/master/graph/badge.svg
|
||||
[cov]: https://codecov.io/gh/uber-go/zap
|
||||
[benchmarking suite]: https://github.com/uber-go/zap/tree/master/benchmarks
|
||||
[benchmarks/go.mod]: https://github.com/uber-go/zap/blob/master/benchmarks/go.mod
|
||||
|
||||
320
vendor/go.uber.org/zap/array.go
сгенерированный
поставляемый
320
vendor/go.uber.org/zap/array.go
сгенерированный
поставляемый
@@ -1,320 +0,0 @@
|
||||
// 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 zap
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
// Array constructs a field with the given key and ArrayMarshaler. It provides
|
||||
// a flexible, but still type-safe and efficient, way to add array-like types
|
||||
// to the logging context. The struct's MarshalLogArray method is called lazily.
|
||||
func Array(key string, val zapcore.ArrayMarshaler) Field {
|
||||
return Field{Key: key, Type: zapcore.ArrayMarshalerType, Interface: val}
|
||||
}
|
||||
|
||||
// Bools constructs a field that carries a slice of bools.
|
||||
func Bools(key string, bs []bool) Field {
|
||||
return Array(key, bools(bs))
|
||||
}
|
||||
|
||||
// ByteStrings constructs a field that carries a slice of []byte, each of which
|
||||
// must be UTF-8 encoded text.
|
||||
func ByteStrings(key string, bss [][]byte) Field {
|
||||
return Array(key, byteStringsArray(bss))
|
||||
}
|
||||
|
||||
// Complex128s constructs a field that carries a slice of complex numbers.
|
||||
func Complex128s(key string, nums []complex128) Field {
|
||||
return Array(key, complex128s(nums))
|
||||
}
|
||||
|
||||
// Complex64s constructs a field that carries a slice of complex numbers.
|
||||
func Complex64s(key string, nums []complex64) Field {
|
||||
return Array(key, complex64s(nums))
|
||||
}
|
||||
|
||||
// Durations constructs a field that carries a slice of time.Durations.
|
||||
func Durations(key string, ds []time.Duration) Field {
|
||||
return Array(key, durations(ds))
|
||||
}
|
||||
|
||||
// Float64s constructs a field that carries a slice of floats.
|
||||
func Float64s(key string, nums []float64) Field {
|
||||
return Array(key, float64s(nums))
|
||||
}
|
||||
|
||||
// Float32s constructs a field that carries a slice of floats.
|
||||
func Float32s(key string, nums []float32) Field {
|
||||
return Array(key, float32s(nums))
|
||||
}
|
||||
|
||||
// Ints constructs a field that carries a slice of integers.
|
||||
func Ints(key string, nums []int) Field {
|
||||
return Array(key, ints(nums))
|
||||
}
|
||||
|
||||
// Int64s constructs a field that carries a slice of integers.
|
||||
func Int64s(key string, nums []int64) Field {
|
||||
return Array(key, int64s(nums))
|
||||
}
|
||||
|
||||
// Int32s constructs a field that carries a slice of integers.
|
||||
func Int32s(key string, nums []int32) Field {
|
||||
return Array(key, int32s(nums))
|
||||
}
|
||||
|
||||
// Int16s constructs a field that carries a slice of integers.
|
||||
func Int16s(key string, nums []int16) Field {
|
||||
return Array(key, int16s(nums))
|
||||
}
|
||||
|
||||
// Int8s constructs a field that carries a slice of integers.
|
||||
func Int8s(key string, nums []int8) Field {
|
||||
return Array(key, int8s(nums))
|
||||
}
|
||||
|
||||
// Strings constructs a field that carries a slice of strings.
|
||||
func Strings(key string, ss []string) Field {
|
||||
return Array(key, stringArray(ss))
|
||||
}
|
||||
|
||||
// Times constructs a field that carries a slice of time.Times.
|
||||
func Times(key string, ts []time.Time) Field {
|
||||
return Array(key, times(ts))
|
||||
}
|
||||
|
||||
// Uints constructs a field that carries a slice of unsigned integers.
|
||||
func Uints(key string, nums []uint) Field {
|
||||
return Array(key, uints(nums))
|
||||
}
|
||||
|
||||
// Uint64s constructs a field that carries a slice of unsigned integers.
|
||||
func Uint64s(key string, nums []uint64) Field {
|
||||
return Array(key, uint64s(nums))
|
||||
}
|
||||
|
||||
// Uint32s constructs a field that carries a slice of unsigned integers.
|
||||
func Uint32s(key string, nums []uint32) Field {
|
||||
return Array(key, uint32s(nums))
|
||||
}
|
||||
|
||||
// Uint16s constructs a field that carries a slice of unsigned integers.
|
||||
func Uint16s(key string, nums []uint16) Field {
|
||||
return Array(key, uint16s(nums))
|
||||
}
|
||||
|
||||
// Uint8s constructs a field that carries a slice of unsigned integers.
|
||||
func Uint8s(key string, nums []uint8) Field {
|
||||
return Array(key, uint8s(nums))
|
||||
}
|
||||
|
||||
// Uintptrs constructs a field that carries a slice of pointer addresses.
|
||||
func Uintptrs(key string, us []uintptr) Field {
|
||||
return Array(key, uintptrs(us))
|
||||
}
|
||||
|
||||
// Errors constructs a field that carries a slice of errors.
|
||||
func Errors(key string, errs []error) Field {
|
||||
return Array(key, errArray(errs))
|
||||
}
|
||||
|
||||
type bools []bool
|
||||
|
||||
func (bs bools) MarshalLogArray(arr zapcore.ArrayEncoder) error {
|
||||
for i := range bs {
|
||||
arr.AppendBool(bs[i])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type byteStringsArray [][]byte
|
||||
|
||||
func (bss byteStringsArray) MarshalLogArray(arr zapcore.ArrayEncoder) error {
|
||||
for i := range bss {
|
||||
arr.AppendByteString(bss[i])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type complex128s []complex128
|
||||
|
||||
func (nums complex128s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
|
||||
for i := range nums {
|
||||
arr.AppendComplex128(nums[i])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type complex64s []complex64
|
||||
|
||||
func (nums complex64s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
|
||||
for i := range nums {
|
||||
arr.AppendComplex64(nums[i])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type durations []time.Duration
|
||||
|
||||
func (ds durations) MarshalLogArray(arr zapcore.ArrayEncoder) error {
|
||||
for i := range ds {
|
||||
arr.AppendDuration(ds[i])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type float64s []float64
|
||||
|
||||
func (nums float64s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
|
||||
for i := range nums {
|
||||
arr.AppendFloat64(nums[i])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type float32s []float32
|
||||
|
||||
func (nums float32s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
|
||||
for i := range nums {
|
||||
arr.AppendFloat32(nums[i])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ints []int
|
||||
|
||||
func (nums ints) MarshalLogArray(arr zapcore.ArrayEncoder) error {
|
||||
for i := range nums {
|
||||
arr.AppendInt(nums[i])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type int64s []int64
|
||||
|
||||
func (nums int64s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
|
||||
for i := range nums {
|
||||
arr.AppendInt64(nums[i])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type int32s []int32
|
||||
|
||||
func (nums int32s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
|
||||
for i := range nums {
|
||||
arr.AppendInt32(nums[i])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type int16s []int16
|
||||
|
||||
func (nums int16s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
|
||||
for i := range nums {
|
||||
arr.AppendInt16(nums[i])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type int8s []int8
|
||||
|
||||
func (nums int8s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
|
||||
for i := range nums {
|
||||
arr.AppendInt8(nums[i])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type stringArray []string
|
||||
|
||||
func (ss stringArray) MarshalLogArray(arr zapcore.ArrayEncoder) error {
|
||||
for i := range ss {
|
||||
arr.AppendString(ss[i])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type times []time.Time
|
||||
|
||||
func (ts times) MarshalLogArray(arr zapcore.ArrayEncoder) error {
|
||||
for i := range ts {
|
||||
arr.AppendTime(ts[i])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type uints []uint
|
||||
|
||||
func (nums uints) MarshalLogArray(arr zapcore.ArrayEncoder) error {
|
||||
for i := range nums {
|
||||
arr.AppendUint(nums[i])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type uint64s []uint64
|
||||
|
||||
func (nums uint64s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
|
||||
for i := range nums {
|
||||
arr.AppendUint64(nums[i])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type uint32s []uint32
|
||||
|
||||
func (nums uint32s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
|
||||
for i := range nums {
|
||||
arr.AppendUint32(nums[i])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type uint16s []uint16
|
||||
|
||||
func (nums uint16s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
|
||||
for i := range nums {
|
||||
arr.AppendUint16(nums[i])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type uint8s []uint8
|
||||
|
||||
func (nums uint8s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
|
||||
for i := range nums {
|
||||
arr.AppendUint8(nums[i])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type uintptrs []uintptr
|
||||
|
||||
func (nums uintptrs) MarshalLogArray(arr zapcore.ArrayEncoder) error {
|
||||
for i := range nums {
|
||||
arr.AppendUintptr(nums[i])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
123
vendor/go.uber.org/zap/buffer/buffer.go
сгенерированный
поставляемый
123
vendor/go.uber.org/zap/buffer/buffer.go
сгенерированный
поставляемый
@@ -1,123 +0,0 @@
|
||||
// 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 buffer provides a thin wrapper around a byte slice. Unlike the
|
||||
// standard library's bytes.Buffer, it supports a portion of the strconv
|
||||
// package's zero-allocation formatters.
|
||||
package buffer // import "go.uber.org/zap/buffer"
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
const _size = 1024 // by default, create 1 KiB buffers
|
||||
|
||||
// Buffer is a thin wrapper around a byte slice. It's intended to be pooled, so
|
||||
// the only way to construct one is via a Pool.
|
||||
type Buffer struct {
|
||||
bs []byte
|
||||
pool Pool
|
||||
}
|
||||
|
||||
// AppendByte writes a single byte to the Buffer.
|
||||
func (b *Buffer) AppendByte(v byte) {
|
||||
b.bs = append(b.bs, v)
|
||||
}
|
||||
|
||||
// AppendString writes a string to the Buffer.
|
||||
func (b *Buffer) AppendString(s string) {
|
||||
b.bs = append(b.bs, s...)
|
||||
}
|
||||
|
||||
// AppendInt appends an integer to the underlying buffer (assuming base 10).
|
||||
func (b *Buffer) AppendInt(i int64) {
|
||||
b.bs = strconv.AppendInt(b.bs, i, 10)
|
||||
}
|
||||
|
||||
// AppendTime appends the time formatted using the specified layout.
|
||||
func (b *Buffer) AppendTime(t time.Time, layout string) {
|
||||
b.bs = t.AppendFormat(b.bs, layout)
|
||||
}
|
||||
|
||||
// AppendUint appends an unsigned integer to the underlying buffer (assuming
|
||||
// base 10).
|
||||
func (b *Buffer) AppendUint(i uint64) {
|
||||
b.bs = strconv.AppendUint(b.bs, i, 10)
|
||||
}
|
||||
|
||||
// AppendBool appends a bool to the underlying buffer.
|
||||
func (b *Buffer) AppendBool(v bool) {
|
||||
b.bs = strconv.AppendBool(b.bs, v)
|
||||
}
|
||||
|
||||
// AppendFloat appends a float to the underlying buffer. It doesn't quote NaN
|
||||
// or +/- Inf.
|
||||
func (b *Buffer) AppendFloat(f float64, bitSize int) {
|
||||
b.bs = strconv.AppendFloat(b.bs, f, 'f', -1, bitSize)
|
||||
}
|
||||
|
||||
// Len returns the length of the underlying byte slice.
|
||||
func (b *Buffer) Len() int {
|
||||
return len(b.bs)
|
||||
}
|
||||
|
||||
// Cap returns the capacity of the underlying byte slice.
|
||||
func (b *Buffer) Cap() int {
|
||||
return cap(b.bs)
|
||||
}
|
||||
|
||||
// Bytes returns a mutable reference to the underlying byte slice.
|
||||
func (b *Buffer) Bytes() []byte {
|
||||
return b.bs
|
||||
}
|
||||
|
||||
// String returns a string copy of the underlying byte slice.
|
||||
func (b *Buffer) String() string {
|
||||
return string(b.bs)
|
||||
}
|
||||
|
||||
// Reset resets the underlying byte slice. Subsequent writes re-use the slice's
|
||||
// backing array.
|
||||
func (b *Buffer) Reset() {
|
||||
b.bs = b.bs[:0]
|
||||
}
|
||||
|
||||
// Write implements io.Writer.
|
||||
func (b *Buffer) Write(bs []byte) (int, error) {
|
||||
b.bs = append(b.bs, bs...)
|
||||
return len(bs), nil
|
||||
}
|
||||
|
||||
// TrimNewline trims any final "\n" byte from the end of the buffer.
|
||||
func (b *Buffer) TrimNewline() {
|
||||
if i := len(b.bs) - 1; i >= 0 {
|
||||
if b.bs[i] == '\n' {
|
||||
b.bs = b.bs[:i]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Free returns the Buffer to its Pool.
|
||||
//
|
||||
// Callers must not retain references to the Buffer after calling Free.
|
||||
func (b *Buffer) Free() {
|
||||
b.pool.put(b)
|
||||
}
|
||||
49
vendor/go.uber.org/zap/buffer/pool.go
сгенерированный
поставляемый
49
vendor/go.uber.org/zap/buffer/pool.go
сгенерированный
поставляемый
@@ -1,49 +0,0 @@
|
||||
// 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 buffer
|
||||
|
||||
import "sync"
|
||||
|
||||
// A Pool is a type-safe wrapper around a sync.Pool.
|
||||
type Pool struct {
|
||||
p *sync.Pool
|
||||
}
|
||||
|
||||
// NewPool constructs a new Pool.
|
||||
func NewPool() Pool {
|
||||
return Pool{p: &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &Buffer{bs: make([]byte, 0, _size)}
|
||||
},
|
||||
}}
|
||||
}
|
||||
|
||||
// Get retrieves a Buffer from the pool, creating one if necessary.
|
||||
func (p Pool) Get() *Buffer {
|
||||
buf := p.p.Get().(*Buffer)
|
||||
buf.Reset()
|
||||
buf.pool = p
|
||||
return buf
|
||||
}
|
||||
|
||||
func (p Pool) put(buf *Buffer) {
|
||||
p.p.Put(buf)
|
||||
}
|
||||
17
vendor/go.uber.org/zap/checklicense.sh
сгенерированный
поставляемый
17
vendor/go.uber.org/zap/checklicense.sh
сгенерированный
поставляемый
@@ -1,17 +0,0 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
ERROR_COUNT=0
|
||||
while read -r file
|
||||
do
|
||||
case "$(head -1 "${file}")" in
|
||||
*"Copyright (c) "*" Uber Technologies, Inc.")
|
||||
# everything's cool
|
||||
;;
|
||||
*)
|
||||
echo "$file is missing license header."
|
||||
(( ERROR_COUNT++ ))
|
||||
;;
|
||||
esac
|
||||
done < <(git ls-files "*\.go")
|
||||
|
||||
exit $ERROR_COUNT
|
||||
264
vendor/go.uber.org/zap/config.go
сгенерированный
поставляемый
264
vendor/go.uber.org/zap/config.go
сгенерированный
поставляемый
@@ -1,264 +0,0 @@
|
||||
// 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 zap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
// SamplingConfig sets a sampling strategy for the logger. Sampling caps the
|
||||
// global CPU and I/O load that logging puts on your process while attempting
|
||||
// to preserve a representative subset of your logs.
|
||||
//
|
||||
// If specified, the Sampler will invoke the Hook after each decision.
|
||||
//
|
||||
// Values configured here are per-second. See zapcore.NewSamplerWithOptions for
|
||||
// details.
|
||||
type SamplingConfig struct {
|
||||
Initial int `json:"initial" yaml:"initial"`
|
||||
Thereafter int `json:"thereafter" yaml:"thereafter"`
|
||||
Hook func(zapcore.Entry, zapcore.SamplingDecision) `json:"-" yaml:"-"`
|
||||
}
|
||||
|
||||
// Config offers a declarative way to construct a logger. It doesn't do
|
||||
// anything that can't be done with New, Options, and the various
|
||||
// zapcore.WriteSyncer and zapcore.Core wrappers, but it's a simpler way to
|
||||
// toggle common options.
|
||||
//
|
||||
// Note that Config intentionally supports only the most common options. More
|
||||
// unusual logging setups (logging to network connections or message queues,
|
||||
// splitting output between multiple files, etc.) are possible, but require
|
||||
// direct use of the zapcore package. For sample code, see the package-level
|
||||
// BasicConfiguration and AdvancedConfiguration examples.
|
||||
//
|
||||
// For an example showing runtime log level changes, see the documentation for
|
||||
// AtomicLevel.
|
||||
type Config struct {
|
||||
// Level is the minimum enabled logging level. Note that this is a dynamic
|
||||
// level, so calling Config.Level.SetLevel will atomically change the log
|
||||
// level of all loggers descended from this config.
|
||||
Level AtomicLevel `json:"level" yaml:"level"`
|
||||
// Development puts the logger in development mode, which changes the
|
||||
// behavior of DPanicLevel and takes stacktraces more liberally.
|
||||
Development bool `json:"development" yaml:"development"`
|
||||
// DisableCaller stops annotating logs with the calling function's file
|
||||
// name and line number. By default, all logs are annotated.
|
||||
DisableCaller bool `json:"disableCaller" yaml:"disableCaller"`
|
||||
// DisableStacktrace completely disables automatic stacktrace capturing. By
|
||||
// default, stacktraces are captured for WarnLevel and above logs in
|
||||
// development and ErrorLevel and above in production.
|
||||
DisableStacktrace bool `json:"disableStacktrace" yaml:"disableStacktrace"`
|
||||
// Sampling sets a sampling policy. A nil SamplingConfig disables sampling.
|
||||
Sampling *SamplingConfig `json:"sampling" yaml:"sampling"`
|
||||
// Encoding sets the logger's encoding. Valid values are "json" and
|
||||
// "console", as well as any third-party encodings registered via
|
||||
// RegisterEncoder.
|
||||
Encoding string `json:"encoding" yaml:"encoding"`
|
||||
// EncoderConfig sets options for the chosen encoder. See
|
||||
// zapcore.EncoderConfig for details.
|
||||
EncoderConfig zapcore.EncoderConfig `json:"encoderConfig" yaml:"encoderConfig"`
|
||||
// OutputPaths is a list of URLs or file paths to write logging output to.
|
||||
// See Open for details.
|
||||
OutputPaths []string `json:"outputPaths" yaml:"outputPaths"`
|
||||
// ErrorOutputPaths is a list of URLs to write internal logger errors to.
|
||||
// The default is standard error.
|
||||
//
|
||||
// Note that this setting only affects internal errors; for sample code that
|
||||
// sends error-level logs to a different location from info- and debug-level
|
||||
// logs, see the package-level AdvancedConfiguration example.
|
||||
ErrorOutputPaths []string `json:"errorOutputPaths" yaml:"errorOutputPaths"`
|
||||
// InitialFields is a collection of fields to add to the root logger.
|
||||
InitialFields map[string]interface{} `json:"initialFields" yaml:"initialFields"`
|
||||
}
|
||||
|
||||
// NewProductionEncoderConfig returns an opinionated EncoderConfig for
|
||||
// production environments.
|
||||
func NewProductionEncoderConfig() zapcore.EncoderConfig {
|
||||
return zapcore.EncoderConfig{
|
||||
TimeKey: "ts",
|
||||
LevelKey: "level",
|
||||
NameKey: "logger",
|
||||
CallerKey: "caller",
|
||||
FunctionKey: zapcore.OmitKey,
|
||||
MessageKey: "msg",
|
||||
StacktraceKey: "stacktrace",
|
||||
LineEnding: zapcore.DefaultLineEnding,
|
||||
EncodeLevel: zapcore.LowercaseLevelEncoder,
|
||||
EncodeTime: zapcore.EpochTimeEncoder,
|
||||
EncodeDuration: zapcore.SecondsDurationEncoder,
|
||||
EncodeCaller: zapcore.ShortCallerEncoder,
|
||||
}
|
||||
}
|
||||
|
||||
// NewProductionConfig is a reasonable production logging configuration.
|
||||
// Logging is enabled at InfoLevel and above.
|
||||
//
|
||||
// It uses a JSON encoder, writes to standard error, and enables sampling.
|
||||
// Stacktraces are automatically included on logs of ErrorLevel and above.
|
||||
func NewProductionConfig() Config {
|
||||
return Config{
|
||||
Level: NewAtomicLevelAt(InfoLevel),
|
||||
Development: false,
|
||||
Sampling: &SamplingConfig{
|
||||
Initial: 100,
|
||||
Thereafter: 100,
|
||||
},
|
||||
Encoding: "json",
|
||||
EncoderConfig: NewProductionEncoderConfig(),
|
||||
OutputPaths: []string{"stderr"},
|
||||
ErrorOutputPaths: []string{"stderr"},
|
||||
}
|
||||
}
|
||||
|
||||
// NewDevelopmentEncoderConfig returns an opinionated EncoderConfig for
|
||||
// development environments.
|
||||
func NewDevelopmentEncoderConfig() zapcore.EncoderConfig {
|
||||
return zapcore.EncoderConfig{
|
||||
// Keys can be anything except the empty string.
|
||||
TimeKey: "T",
|
||||
LevelKey: "L",
|
||||
NameKey: "N",
|
||||
CallerKey: "C",
|
||||
FunctionKey: zapcore.OmitKey,
|
||||
MessageKey: "M",
|
||||
StacktraceKey: "S",
|
||||
LineEnding: zapcore.DefaultLineEnding,
|
||||
EncodeLevel: zapcore.CapitalLevelEncoder,
|
||||
EncodeTime: zapcore.ISO8601TimeEncoder,
|
||||
EncodeDuration: zapcore.StringDurationEncoder,
|
||||
EncodeCaller: zapcore.ShortCallerEncoder,
|
||||
}
|
||||
}
|
||||
|
||||
// NewDevelopmentConfig is a reasonable development logging configuration.
|
||||
// Logging is enabled at DebugLevel and above.
|
||||
//
|
||||
// It enables development mode (which makes DPanicLevel logs panic), uses a
|
||||
// console encoder, writes to standard error, and disables sampling.
|
||||
// Stacktraces are automatically included on logs of WarnLevel and above.
|
||||
func NewDevelopmentConfig() Config {
|
||||
return Config{
|
||||
Level: NewAtomicLevelAt(DebugLevel),
|
||||
Development: true,
|
||||
Encoding: "console",
|
||||
EncoderConfig: NewDevelopmentEncoderConfig(),
|
||||
OutputPaths: []string{"stderr"},
|
||||
ErrorOutputPaths: []string{"stderr"},
|
||||
}
|
||||
}
|
||||
|
||||
// Build constructs a logger from the Config and Options.
|
||||
func (cfg Config) Build(opts ...Option) (*Logger, error) {
|
||||
enc, err := cfg.buildEncoder()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sink, errSink, err := cfg.openSinks()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if cfg.Level == (AtomicLevel{}) {
|
||||
return nil, fmt.Errorf("missing Level")
|
||||
}
|
||||
|
||||
log := New(
|
||||
zapcore.NewCore(enc, sink, cfg.Level),
|
||||
cfg.buildOptions(errSink)...,
|
||||
)
|
||||
if len(opts) > 0 {
|
||||
log = log.WithOptions(opts...)
|
||||
}
|
||||
return log, nil
|
||||
}
|
||||
|
||||
func (cfg Config) buildOptions(errSink zapcore.WriteSyncer) []Option {
|
||||
opts := []Option{ErrorOutput(errSink)}
|
||||
|
||||
if cfg.Development {
|
||||
opts = append(opts, Development())
|
||||
}
|
||||
|
||||
if !cfg.DisableCaller {
|
||||
opts = append(opts, AddCaller())
|
||||
}
|
||||
|
||||
stackLevel := ErrorLevel
|
||||
if cfg.Development {
|
||||
stackLevel = WarnLevel
|
||||
}
|
||||
if !cfg.DisableStacktrace {
|
||||
opts = append(opts, AddStacktrace(stackLevel))
|
||||
}
|
||||
|
||||
if scfg := cfg.Sampling; scfg != nil {
|
||||
opts = append(opts, WrapCore(func(core zapcore.Core) zapcore.Core {
|
||||
var samplerOpts []zapcore.SamplerOption
|
||||
if scfg.Hook != nil {
|
||||
samplerOpts = append(samplerOpts, zapcore.SamplerHook(scfg.Hook))
|
||||
}
|
||||
return zapcore.NewSamplerWithOptions(
|
||||
core,
|
||||
time.Second,
|
||||
cfg.Sampling.Initial,
|
||||
cfg.Sampling.Thereafter,
|
||||
samplerOpts...,
|
||||
)
|
||||
}))
|
||||
}
|
||||
|
||||
if len(cfg.InitialFields) > 0 {
|
||||
fs := make([]Field, 0, len(cfg.InitialFields))
|
||||
keys := make([]string, 0, len(cfg.InitialFields))
|
||||
for k := range cfg.InitialFields {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, k := range keys {
|
||||
fs = append(fs, Any(k, cfg.InitialFields[k]))
|
||||
}
|
||||
opts = append(opts, Fields(fs...))
|
||||
}
|
||||
|
||||
return opts
|
||||
}
|
||||
|
||||
func (cfg Config) openSinks() (zapcore.WriteSyncer, zapcore.WriteSyncer, error) {
|
||||
sink, closeOut, err := Open(cfg.OutputPaths...)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
errSink, _, err := Open(cfg.ErrorOutputPaths...)
|
||||
if err != nil {
|
||||
closeOut()
|
||||
return nil, nil, err
|
||||
}
|
||||
return sink, errSink, nil
|
||||
}
|
||||
|
||||
func (cfg Config) buildEncoder() (zapcore.Encoder, error) {
|
||||
return newEncoder(cfg.Encoding, cfg.EncoderConfig)
|
||||
}
|
||||
113
vendor/go.uber.org/zap/doc.go
сгенерированный
поставляемый
113
vendor/go.uber.org/zap/doc.go
сгенерированный
поставляемый
@@ -1,113 +0,0 @@
|
||||
// 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 zap provides fast, structured, leveled logging.
|
||||
//
|
||||
// For applications that log in the hot path, reflection-based serialization
|
||||
// and string formatting are prohibitively expensive - they're CPU-intensive
|
||||
// and make many small allocations. Put differently, using json.Marshal and
|
||||
// fmt.Fprintf to log tons of interface{} makes your application slow.
|
||||
//
|
||||
// Zap takes a different approach. It includes a reflection-free,
|
||||
// zero-allocation JSON encoder, and the base Logger strives to avoid
|
||||
// serialization overhead and allocations wherever possible. By building the
|
||||
// high-level SugaredLogger on that foundation, zap lets users choose when
|
||||
// they need to count every allocation and when they'd prefer a more familiar,
|
||||
// loosely typed API.
|
||||
//
|
||||
// Choosing a Logger
|
||||
//
|
||||
// In contexts where performance is nice, but not critical, use the
|
||||
// SugaredLogger. It's 4-10x faster than other structured logging packages and
|
||||
// supports both structured and printf-style logging. Like log15 and go-kit,
|
||||
// the SugaredLogger's structured logging APIs are loosely typed and accept a
|
||||
// variadic number of key-value pairs. (For more advanced use cases, they also
|
||||
// accept strongly typed fields - see the SugaredLogger.With documentation for
|
||||
// details.)
|
||||
// sugar := zap.NewExample().Sugar()
|
||||
// defer sugar.Sync()
|
||||
// sugar.Infow("failed to fetch URL",
|
||||
// "url", "http://example.com",
|
||||
// "attempt", 3,
|
||||
// "backoff", time.Second,
|
||||
// )
|
||||
// sugar.Infof("failed to fetch URL: %s", "http://example.com")
|
||||
//
|
||||
// By default, loggers are unbuffered. However, since zap's low-level APIs
|
||||
// allow buffering, calling Sync before letting your process exit is a good
|
||||
// habit.
|
||||
//
|
||||
// In the rare contexts where every microsecond and every allocation matter,
|
||||
// use the Logger. It's even faster than the SugaredLogger and allocates far
|
||||
// less, but it only supports strongly-typed, structured logging.
|
||||
// logger := zap.NewExample()
|
||||
// defer logger.Sync()
|
||||
// logger.Info("failed to fetch URL",
|
||||
// zap.String("url", "http://example.com"),
|
||||
// zap.Int("attempt", 3),
|
||||
// zap.Duration("backoff", time.Second),
|
||||
// )
|
||||
//
|
||||
// Choosing between the Logger and SugaredLogger doesn't need to be an
|
||||
// application-wide decision: converting between the two is simple and
|
||||
// inexpensive.
|
||||
// logger := zap.NewExample()
|
||||
// defer logger.Sync()
|
||||
// sugar := logger.Sugar()
|
||||
// plain := sugar.Desugar()
|
||||
//
|
||||
// Configuring Zap
|
||||
//
|
||||
// The simplest way to build a Logger is to use zap's opinionated presets:
|
||||
// NewExample, NewProduction, and NewDevelopment. These presets build a logger
|
||||
// with a single function call:
|
||||
// logger, err := zap.NewProduction()
|
||||
// if err != nil {
|
||||
// log.Fatalf("can't initialize zap logger: %v", err)
|
||||
// }
|
||||
// defer logger.Sync()
|
||||
//
|
||||
// Presets are fine for small projects, but larger projects and organizations
|
||||
// naturally require a bit more customization. For most users, zap's Config
|
||||
// struct strikes the right balance between flexibility and convenience. See
|
||||
// the package-level BasicConfiguration example for sample code.
|
||||
//
|
||||
// More unusual configurations (splitting output between files, sending logs
|
||||
// to a message queue, etc.) are possible, but require direct use of
|
||||
// go.uber.org/zap/zapcore. See the package-level AdvancedConfiguration
|
||||
// example for sample code.
|
||||
//
|
||||
// Extending Zap
|
||||
//
|
||||
// The zap package itself is a relatively thin wrapper around the interfaces
|
||||
// in go.uber.org/zap/zapcore. Extending zap to support a new encoding (e.g.,
|
||||
// BSON), a new log sink (e.g., Kafka), or something more exotic (perhaps an
|
||||
// exception aggregation service, like Sentry or Rollbar) typically requires
|
||||
// implementing the zapcore.Encoder, zapcore.WriteSyncer, or zapcore.Core
|
||||
// interfaces. See the zapcore documentation for details.
|
||||
//
|
||||
// Similarly, package authors can use the high-performance Encoder and Core
|
||||
// implementations in the zapcore package to build their own loggers.
|
||||
//
|
||||
// Frequently Asked Questions
|
||||
//
|
||||
// An FAQ covering everything from installation errors to design decisions is
|
||||
// available at https://github.com/uber-go/zap/blob/master/FAQ.md.
|
||||
package zap // import "go.uber.org/zap"
|
||||
79
vendor/go.uber.org/zap/encoder.go
сгенерированный
поставляемый
79
vendor/go.uber.org/zap/encoder.go
сгенерированный
поставляемый
@@ -1,79 +0,0 @@
|
||||
// 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 zap
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
var (
|
||||
errNoEncoderNameSpecified = errors.New("no encoder name specified")
|
||||
|
||||
_encoderNameToConstructor = map[string]func(zapcore.EncoderConfig) (zapcore.Encoder, error){
|
||||
"console": func(encoderConfig zapcore.EncoderConfig) (zapcore.Encoder, error) {
|
||||
return zapcore.NewConsoleEncoder(encoderConfig), nil
|
||||
},
|
||||
"json": func(encoderConfig zapcore.EncoderConfig) (zapcore.Encoder, error) {
|
||||
return zapcore.NewJSONEncoder(encoderConfig), nil
|
||||
},
|
||||
}
|
||||
_encoderMutex sync.RWMutex
|
||||
)
|
||||
|
||||
// RegisterEncoder registers an encoder constructor, which the Config struct
|
||||
// can then reference. By default, the "json" and "console" encoders are
|
||||
// registered.
|
||||
//
|
||||
// Attempting to register an encoder whose name is already taken returns an
|
||||
// error.
|
||||
func RegisterEncoder(name string, constructor func(zapcore.EncoderConfig) (zapcore.Encoder, error)) error {
|
||||
_encoderMutex.Lock()
|
||||
defer _encoderMutex.Unlock()
|
||||
if name == "" {
|
||||
return errNoEncoderNameSpecified
|
||||
}
|
||||
if _, ok := _encoderNameToConstructor[name]; ok {
|
||||
return fmt.Errorf("encoder already registered for name %q", name)
|
||||
}
|
||||
_encoderNameToConstructor[name] = constructor
|
||||
return nil
|
||||
}
|
||||
|
||||
func newEncoder(name string, encoderConfig zapcore.EncoderConfig) (zapcore.Encoder, error) {
|
||||
if encoderConfig.TimeKey != "" && encoderConfig.EncodeTime == nil {
|
||||
return nil, fmt.Errorf("missing EncodeTime in EncoderConfig")
|
||||
}
|
||||
|
||||
_encoderMutex.RLock()
|
||||
defer _encoderMutex.RUnlock()
|
||||
if name == "" {
|
||||
return nil, errNoEncoderNameSpecified
|
||||
}
|
||||
constructor, ok := _encoderNameToConstructor[name]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no encoder registered for name %q", name)
|
||||
}
|
||||
return constructor(encoderConfig)
|
||||
}
|
||||
80
vendor/go.uber.org/zap/error.go
сгенерированный
поставляемый
80
vendor/go.uber.org/zap/error.go
сгенерированный
поставляемый
@@ -1,80 +0,0 @@
|
||||
// Copyright (c) 2017 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 zap
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
var _errArrayElemPool = sync.Pool{New: func() interface{} {
|
||||
return &errArrayElem{}
|
||||
}}
|
||||
|
||||
// Error is shorthand for the common idiom NamedError("error", err).
|
||||
func Error(err error) Field {
|
||||
return NamedError("error", err)
|
||||
}
|
||||
|
||||
// NamedError constructs a field that lazily stores err.Error() under the
|
||||
// provided key. Errors which also implement fmt.Formatter (like those produced
|
||||
// by github.com/pkg/errors) will also have their verbose representation stored
|
||||
// under key+"Verbose". If passed a nil error, the field is a no-op.
|
||||
//
|
||||
// For the common case in which the key is simply "error", the Error function
|
||||
// is shorter and less repetitive.
|
||||
func NamedError(key string, err error) Field {
|
||||
if err == nil {
|
||||
return Skip()
|
||||
}
|
||||
return Field{Key: key, Type: zapcore.ErrorType, Interface: err}
|
||||
}
|
||||
|
||||
type errArray []error
|
||||
|
||||
func (errs errArray) MarshalLogArray(arr zapcore.ArrayEncoder) error {
|
||||
for i := range errs {
|
||||
if errs[i] == nil {
|
||||
continue
|
||||
}
|
||||
// To represent each error as an object with an "error" attribute and
|
||||
// potentially an "errorVerbose" attribute, we need to wrap it in a
|
||||
// type that implements LogObjectMarshaler. To prevent this from
|
||||
// allocating, pool the wrapper type.
|
||||
elem := _errArrayElemPool.Get().(*errArrayElem)
|
||||
elem.error = errs[i]
|
||||
arr.AppendObject(elem)
|
||||
elem.error = nil
|
||||
_errArrayElemPool.Put(elem)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type errArrayElem struct {
|
||||
error
|
||||
}
|
||||
|
||||
func (e *errArrayElem) MarshalLogObject(enc zapcore.ObjectEncoder) error {
|
||||
// Re-use the error field's logic, which supports non-standard error types.
|
||||
Error(e.error).AddTo(enc)
|
||||
return nil
|
||||
}
|
||||
549
vendor/go.uber.org/zap/field.go
сгенерированный
поставляемый
549
vendor/go.uber.org/zap/field.go
сгенерированный
поставляемый
@@ -1,549 +0,0 @@
|
||||
// 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 zap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
// Field is an alias for Field. Aliasing this type dramatically
|
||||
// improves the navigability of this package's API documentation.
|
||||
type Field = zapcore.Field
|
||||
|
||||
var (
|
||||
_minTimeInt64 = time.Unix(0, math.MinInt64)
|
||||
_maxTimeInt64 = time.Unix(0, math.MaxInt64)
|
||||
)
|
||||
|
||||
// Skip constructs a no-op field, which is often useful when handling invalid
|
||||
// inputs in other Field constructors.
|
||||
func Skip() Field {
|
||||
return Field{Type: zapcore.SkipType}
|
||||
}
|
||||
|
||||
// nilField returns a field which will marshal explicitly as nil. See motivation
|
||||
// in https://github.com/uber-go/zap/issues/753 . If we ever make breaking
|
||||
// changes and add zapcore.NilType and zapcore.ObjectEncoder.AddNil, the
|
||||
// implementation here should be changed to reflect that.
|
||||
func nilField(key string) Field { return Reflect(key, nil) }
|
||||
|
||||
// Binary constructs a field that carries an opaque binary blob.
|
||||
//
|
||||
// Binary data is serialized in an encoding-appropriate format. For example,
|
||||
// zap's JSON encoder base64-encodes binary blobs. To log UTF-8 encoded text,
|
||||
// use ByteString.
|
||||
func Binary(key string, val []byte) Field {
|
||||
return Field{Key: key, Type: zapcore.BinaryType, Interface: val}
|
||||
}
|
||||
|
||||
// Bool constructs a field that carries a bool.
|
||||
func Bool(key string, val bool) Field {
|
||||
var ival int64
|
||||
if val {
|
||||
ival = 1
|
||||
}
|
||||
return Field{Key: key, Type: zapcore.BoolType, Integer: ival}
|
||||
}
|
||||
|
||||
// Boolp constructs a field that carries a *bool. The returned Field will safely
|
||||
// and explicitly represent `nil` when appropriate.
|
||||
func Boolp(key string, val *bool) Field {
|
||||
if val == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Bool(key, *val)
|
||||
}
|
||||
|
||||
// ByteString constructs a field that carries UTF-8 encoded text as a []byte.
|
||||
// To log opaque binary blobs (which aren't necessarily valid UTF-8), use
|
||||
// Binary.
|
||||
func ByteString(key string, val []byte) Field {
|
||||
return Field{Key: key, Type: zapcore.ByteStringType, Interface: val}
|
||||
}
|
||||
|
||||
// Complex128 constructs a field that carries a complex number. Unlike most
|
||||
// numeric fields, this costs an allocation (to convert the complex128 to
|
||||
// interface{}).
|
||||
func Complex128(key string, val complex128) Field {
|
||||
return Field{Key: key, Type: zapcore.Complex128Type, Interface: val}
|
||||
}
|
||||
|
||||
// Complex128p constructs a field that carries a *complex128. The returned Field will safely
|
||||
// and explicitly represent `nil` when appropriate.
|
||||
func Complex128p(key string, val *complex128) Field {
|
||||
if val == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Complex128(key, *val)
|
||||
}
|
||||
|
||||
// Complex64 constructs a field that carries a complex number. Unlike most
|
||||
// numeric fields, this costs an allocation (to convert the complex64 to
|
||||
// interface{}).
|
||||
func Complex64(key string, val complex64) Field {
|
||||
return Field{Key: key, Type: zapcore.Complex64Type, Interface: val}
|
||||
}
|
||||
|
||||
// Complex64p constructs a field that carries a *complex64. The returned Field will safely
|
||||
// and explicitly represent `nil` when appropriate.
|
||||
func Complex64p(key string, val *complex64) Field {
|
||||
if val == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Complex64(key, *val)
|
||||
}
|
||||
|
||||
// Float64 constructs a field that carries a float64. The way the
|
||||
// floating-point value is represented is encoder-dependent, so marshaling is
|
||||
// necessarily lazy.
|
||||
func Float64(key string, val float64) Field {
|
||||
return Field{Key: key, Type: zapcore.Float64Type, Integer: int64(math.Float64bits(val))}
|
||||
}
|
||||
|
||||
// Float64p constructs a field that carries a *float64. The returned Field will safely
|
||||
// and explicitly represent `nil` when appropriate.
|
||||
func Float64p(key string, val *float64) Field {
|
||||
if val == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Float64(key, *val)
|
||||
}
|
||||
|
||||
// Float32 constructs a field that carries a float32. The way the
|
||||
// floating-point value is represented is encoder-dependent, so marshaling is
|
||||
// necessarily lazy.
|
||||
func Float32(key string, val float32) Field {
|
||||
return Field{Key: key, Type: zapcore.Float32Type, Integer: int64(math.Float32bits(val))}
|
||||
}
|
||||
|
||||
// Float32p constructs a field that carries a *float32. The returned Field will safely
|
||||
// and explicitly represent `nil` when appropriate.
|
||||
func Float32p(key string, val *float32) Field {
|
||||
if val == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Float32(key, *val)
|
||||
}
|
||||
|
||||
// Int constructs a field with the given key and value.
|
||||
func Int(key string, val int) Field {
|
||||
return Int64(key, int64(val))
|
||||
}
|
||||
|
||||
// Intp constructs a field that carries a *int. The returned Field will safely
|
||||
// and explicitly represent `nil` when appropriate.
|
||||
func Intp(key string, val *int) Field {
|
||||
if val == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Int(key, *val)
|
||||
}
|
||||
|
||||
// Int64 constructs a field with the given key and value.
|
||||
func Int64(key string, val int64) Field {
|
||||
return Field{Key: key, Type: zapcore.Int64Type, Integer: val}
|
||||
}
|
||||
|
||||
// Int64p constructs a field that carries a *int64. The returned Field will safely
|
||||
// and explicitly represent `nil` when appropriate.
|
||||
func Int64p(key string, val *int64) Field {
|
||||
if val == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Int64(key, *val)
|
||||
}
|
||||
|
||||
// Int32 constructs a field with the given key and value.
|
||||
func Int32(key string, val int32) Field {
|
||||
return Field{Key: key, Type: zapcore.Int32Type, Integer: int64(val)}
|
||||
}
|
||||
|
||||
// Int32p constructs a field that carries a *int32. The returned Field will safely
|
||||
// and explicitly represent `nil` when appropriate.
|
||||
func Int32p(key string, val *int32) Field {
|
||||
if val == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Int32(key, *val)
|
||||
}
|
||||
|
||||
// Int16 constructs a field with the given key and value.
|
||||
func Int16(key string, val int16) Field {
|
||||
return Field{Key: key, Type: zapcore.Int16Type, Integer: int64(val)}
|
||||
}
|
||||
|
||||
// Int16p constructs a field that carries a *int16. The returned Field will safely
|
||||
// and explicitly represent `nil` when appropriate.
|
||||
func Int16p(key string, val *int16) Field {
|
||||
if val == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Int16(key, *val)
|
||||
}
|
||||
|
||||
// Int8 constructs a field with the given key and value.
|
||||
func Int8(key string, val int8) Field {
|
||||
return Field{Key: key, Type: zapcore.Int8Type, Integer: int64(val)}
|
||||
}
|
||||
|
||||
// Int8p constructs a field that carries a *int8. The returned Field will safely
|
||||
// and explicitly represent `nil` when appropriate.
|
||||
func Int8p(key string, val *int8) Field {
|
||||
if val == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Int8(key, *val)
|
||||
}
|
||||
|
||||
// String constructs a field with the given key and value.
|
||||
func String(key string, val string) Field {
|
||||
return Field{Key: key, Type: zapcore.StringType, String: val}
|
||||
}
|
||||
|
||||
// Stringp constructs a field that carries a *string. The returned Field will safely
|
||||
// and explicitly represent `nil` when appropriate.
|
||||
func Stringp(key string, val *string) Field {
|
||||
if val == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return String(key, *val)
|
||||
}
|
||||
|
||||
// Uint constructs a field with the given key and value.
|
||||
func Uint(key string, val uint) Field {
|
||||
return Uint64(key, uint64(val))
|
||||
}
|
||||
|
||||
// Uintp constructs a field that carries a *uint. The returned Field will safely
|
||||
// and explicitly represent `nil` when appropriate.
|
||||
func Uintp(key string, val *uint) Field {
|
||||
if val == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Uint(key, *val)
|
||||
}
|
||||
|
||||
// Uint64 constructs a field with the given key and value.
|
||||
func Uint64(key string, val uint64) Field {
|
||||
return Field{Key: key, Type: zapcore.Uint64Type, Integer: int64(val)}
|
||||
}
|
||||
|
||||
// Uint64p constructs a field that carries a *uint64. The returned Field will safely
|
||||
// and explicitly represent `nil` when appropriate.
|
||||
func Uint64p(key string, val *uint64) Field {
|
||||
if val == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Uint64(key, *val)
|
||||
}
|
||||
|
||||
// Uint32 constructs a field with the given key and value.
|
||||
func Uint32(key string, val uint32) Field {
|
||||
return Field{Key: key, Type: zapcore.Uint32Type, Integer: int64(val)}
|
||||
}
|
||||
|
||||
// Uint32p constructs a field that carries a *uint32. The returned Field will safely
|
||||
// and explicitly represent `nil` when appropriate.
|
||||
func Uint32p(key string, val *uint32) Field {
|
||||
if val == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Uint32(key, *val)
|
||||
}
|
||||
|
||||
// Uint16 constructs a field with the given key and value.
|
||||
func Uint16(key string, val uint16) Field {
|
||||
return Field{Key: key, Type: zapcore.Uint16Type, Integer: int64(val)}
|
||||
}
|
||||
|
||||
// Uint16p constructs a field that carries a *uint16. The returned Field will safely
|
||||
// and explicitly represent `nil` when appropriate.
|
||||
func Uint16p(key string, val *uint16) Field {
|
||||
if val == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Uint16(key, *val)
|
||||
}
|
||||
|
||||
// Uint8 constructs a field with the given key and value.
|
||||
func Uint8(key string, val uint8) Field {
|
||||
return Field{Key: key, Type: zapcore.Uint8Type, Integer: int64(val)}
|
||||
}
|
||||
|
||||
// Uint8p constructs a field that carries a *uint8. The returned Field will safely
|
||||
// and explicitly represent `nil` when appropriate.
|
||||
func Uint8p(key string, val *uint8) Field {
|
||||
if val == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Uint8(key, *val)
|
||||
}
|
||||
|
||||
// Uintptr constructs a field with the given key and value.
|
||||
func Uintptr(key string, val uintptr) Field {
|
||||
return Field{Key: key, Type: zapcore.UintptrType, Integer: int64(val)}
|
||||
}
|
||||
|
||||
// Uintptrp constructs a field that carries a *uintptr. The returned Field will safely
|
||||
// and explicitly represent `nil` when appropriate.
|
||||
func Uintptrp(key string, val *uintptr) Field {
|
||||
if val == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Uintptr(key, *val)
|
||||
}
|
||||
|
||||
// Reflect constructs a field with the given key and an arbitrary object. It uses
|
||||
// an encoding-appropriate, reflection-based function to lazily serialize nearly
|
||||
// any object into the logging context, but it's relatively slow and
|
||||
// allocation-heavy. Outside tests, Any is always a better choice.
|
||||
//
|
||||
// If encoding fails (e.g., trying to serialize a map[int]string to JSON), Reflect
|
||||
// includes the error message in the final log output.
|
||||
func Reflect(key string, val interface{}) Field {
|
||||
return Field{Key: key, Type: zapcore.ReflectType, Interface: val}
|
||||
}
|
||||
|
||||
// Namespace creates a named, isolated scope within the logger's context. All
|
||||
// subsequent fields will be added to the new namespace.
|
||||
//
|
||||
// This helps prevent key collisions when injecting loggers into sub-components
|
||||
// or third-party libraries.
|
||||
func Namespace(key string) Field {
|
||||
return Field{Key: key, Type: zapcore.NamespaceType}
|
||||
}
|
||||
|
||||
// Stringer constructs a field with the given key and the output of the value's
|
||||
// String method. The Stringer's String method is called lazily.
|
||||
func Stringer(key string, val fmt.Stringer) Field {
|
||||
return Field{Key: key, Type: zapcore.StringerType, Interface: val}
|
||||
}
|
||||
|
||||
// Time constructs a Field with the given key and value. The encoder
|
||||
// controls how the time is serialized.
|
||||
func Time(key string, val time.Time) Field {
|
||||
if val.Before(_minTimeInt64) || val.After(_maxTimeInt64) {
|
||||
return Field{Key: key, Type: zapcore.TimeFullType, Interface: val}
|
||||
}
|
||||
return Field{Key: key, Type: zapcore.TimeType, Integer: val.UnixNano(), Interface: val.Location()}
|
||||
}
|
||||
|
||||
// Timep constructs a field that carries a *time.Time. The returned Field will safely
|
||||
// and explicitly represent `nil` when appropriate.
|
||||
func Timep(key string, val *time.Time) Field {
|
||||
if val == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Time(key, *val)
|
||||
}
|
||||
|
||||
// Stack constructs a field that stores a stacktrace of the current goroutine
|
||||
// under provided key. Keep in mind that taking a stacktrace is eager and
|
||||
// expensive (relatively speaking); this function both makes an allocation and
|
||||
// takes about two microseconds.
|
||||
func Stack(key string) Field {
|
||||
return StackSkip(key, 1) // skip Stack
|
||||
}
|
||||
|
||||
// StackSkip constructs a field similarly to Stack, but also skips the given
|
||||
// number of frames from the top of the stacktrace.
|
||||
func StackSkip(key string, skip int) Field {
|
||||
// Returning the stacktrace as a string costs an allocation, but saves us
|
||||
// from expanding the zapcore.Field union struct to include a byte slice. Since
|
||||
// taking a stacktrace is already so expensive (~10us), the extra allocation
|
||||
// is okay.
|
||||
return String(key, takeStacktrace(skip+1)) // skip StackSkip
|
||||
}
|
||||
|
||||
// Duration constructs a field with the given key and value. The encoder
|
||||
// controls how the duration is serialized.
|
||||
func Duration(key string, val time.Duration) Field {
|
||||
return Field{Key: key, Type: zapcore.DurationType, Integer: int64(val)}
|
||||
}
|
||||
|
||||
// Durationp constructs a field that carries a *time.Duration. The returned Field will safely
|
||||
// and explicitly represent `nil` when appropriate.
|
||||
func Durationp(key string, val *time.Duration) Field {
|
||||
if val == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Duration(key, *val)
|
||||
}
|
||||
|
||||
// Object constructs a field with the given key and ObjectMarshaler. It
|
||||
// provides a flexible, but still type-safe and efficient, way to add map- or
|
||||
// struct-like user-defined types to the logging context. The struct's
|
||||
// MarshalLogObject method is called lazily.
|
||||
func Object(key string, val zapcore.ObjectMarshaler) Field {
|
||||
return Field{Key: key, Type: zapcore.ObjectMarshalerType, Interface: val}
|
||||
}
|
||||
|
||||
// Inline constructs a Field that is similar to Object, but it
|
||||
// will add the elements of the provided ObjectMarshaler to the
|
||||
// current namespace.
|
||||
func Inline(val zapcore.ObjectMarshaler) Field {
|
||||
return zapcore.Field{
|
||||
Type: zapcore.InlineMarshalerType,
|
||||
Interface: val,
|
||||
}
|
||||
}
|
||||
|
||||
// Any takes a key and an arbitrary value and chooses the best way to represent
|
||||
// them as a field, falling back to a reflection-based approach only if
|
||||
// necessary.
|
||||
//
|
||||
// Since byte/uint8 and rune/int32 are aliases, Any can't differentiate between
|
||||
// them. To minimize surprises, []byte values are treated as binary blobs, byte
|
||||
// values are treated as uint8, and runes are always treated as integers.
|
||||
func Any(key string, value interface{}) Field {
|
||||
switch val := value.(type) {
|
||||
case zapcore.ObjectMarshaler:
|
||||
return Object(key, val)
|
||||
case zapcore.ArrayMarshaler:
|
||||
return Array(key, val)
|
||||
case bool:
|
||||
return Bool(key, val)
|
||||
case *bool:
|
||||
return Boolp(key, val)
|
||||
case []bool:
|
||||
return Bools(key, val)
|
||||
case complex128:
|
||||
return Complex128(key, val)
|
||||
case *complex128:
|
||||
return Complex128p(key, val)
|
||||
case []complex128:
|
||||
return Complex128s(key, val)
|
||||
case complex64:
|
||||
return Complex64(key, val)
|
||||
case *complex64:
|
||||
return Complex64p(key, val)
|
||||
case []complex64:
|
||||
return Complex64s(key, val)
|
||||
case float64:
|
||||
return Float64(key, val)
|
||||
case *float64:
|
||||
return Float64p(key, val)
|
||||
case []float64:
|
||||
return Float64s(key, val)
|
||||
case float32:
|
||||
return Float32(key, val)
|
||||
case *float32:
|
||||
return Float32p(key, val)
|
||||
case []float32:
|
||||
return Float32s(key, val)
|
||||
case int:
|
||||
return Int(key, val)
|
||||
case *int:
|
||||
return Intp(key, val)
|
||||
case []int:
|
||||
return Ints(key, val)
|
||||
case int64:
|
||||
return Int64(key, val)
|
||||
case *int64:
|
||||
return Int64p(key, val)
|
||||
case []int64:
|
||||
return Int64s(key, val)
|
||||
case int32:
|
||||
return Int32(key, val)
|
||||
case *int32:
|
||||
return Int32p(key, val)
|
||||
case []int32:
|
||||
return Int32s(key, val)
|
||||
case int16:
|
||||
return Int16(key, val)
|
||||
case *int16:
|
||||
return Int16p(key, val)
|
||||
case []int16:
|
||||
return Int16s(key, val)
|
||||
case int8:
|
||||
return Int8(key, val)
|
||||
case *int8:
|
||||
return Int8p(key, val)
|
||||
case []int8:
|
||||
return Int8s(key, val)
|
||||
case string:
|
||||
return String(key, val)
|
||||
case *string:
|
||||
return Stringp(key, val)
|
||||
case []string:
|
||||
return Strings(key, val)
|
||||
case uint:
|
||||
return Uint(key, val)
|
||||
case *uint:
|
||||
return Uintp(key, val)
|
||||
case []uint:
|
||||
return Uints(key, val)
|
||||
case uint64:
|
||||
return Uint64(key, val)
|
||||
case *uint64:
|
||||
return Uint64p(key, val)
|
||||
case []uint64:
|
||||
return Uint64s(key, val)
|
||||
case uint32:
|
||||
return Uint32(key, val)
|
||||
case *uint32:
|
||||
return Uint32p(key, val)
|
||||
case []uint32:
|
||||
return Uint32s(key, val)
|
||||
case uint16:
|
||||
return Uint16(key, val)
|
||||
case *uint16:
|
||||
return Uint16p(key, val)
|
||||
case []uint16:
|
||||
return Uint16s(key, val)
|
||||
case uint8:
|
||||
return Uint8(key, val)
|
||||
case *uint8:
|
||||
return Uint8p(key, val)
|
||||
case []byte:
|
||||
return Binary(key, val)
|
||||
case uintptr:
|
||||
return Uintptr(key, val)
|
||||
case *uintptr:
|
||||
return Uintptrp(key, val)
|
||||
case []uintptr:
|
||||
return Uintptrs(key, val)
|
||||
case time.Time:
|
||||
return Time(key, val)
|
||||
case *time.Time:
|
||||
return Timep(key, val)
|
||||
case []time.Time:
|
||||
return Times(key, val)
|
||||
case time.Duration:
|
||||
return Duration(key, val)
|
||||
case *time.Duration:
|
||||
return Durationp(key, val)
|
||||
case []time.Duration:
|
||||
return Durations(key, val)
|
||||
case error:
|
||||
return NamedError(key, val)
|
||||
case []error:
|
||||
return Errors(key, val)
|
||||
case fmt.Stringer:
|
||||
return Stringer(key, val)
|
||||
default:
|
||||
return Reflect(key, val)
|
||||
}
|
||||
}
|
||||
39
vendor/go.uber.org/zap/flag.go
сгенерированный
поставляемый
39
vendor/go.uber.org/zap/flag.go
сгенерированный
поставляемый
@@ -1,39 +0,0 @@
|
||||
// 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 zap
|
||||
|
||||
import (
|
||||
"flag"
|
||||
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
// LevelFlag uses the standard library's flag.Var to declare a global flag
|
||||
// with the specified name, default, and usage guidance. The returned value is
|
||||
// a pointer to the value of the flag.
|
||||
//
|
||||
// If you don't want to use the flag package's global state, you can use any
|
||||
// non-nil *Level as a flag.Value with your own *flag.FlagSet.
|
||||
func LevelFlag(name string, defaultLevel zapcore.Level, usage string) *zapcore.Level {
|
||||
lvl := defaultLevel
|
||||
flag.Var(&lvl, name, usage)
|
||||
return &lvl
|
||||
}
|
||||
34
vendor/go.uber.org/zap/glide.yaml
сгенерированный
поставляемый
34
vendor/go.uber.org/zap/glide.yaml
сгенерированный
поставляемый
@@ -1,34 +0,0 @@
|
||||
package: go.uber.org/zap
|
||||
license: MIT
|
||||
import:
|
||||
- package: go.uber.org/atomic
|
||||
version: ^1
|
||||
- package: go.uber.org/multierr
|
||||
version: ^1
|
||||
testImport:
|
||||
- package: github.com/satori/go.uuid
|
||||
- package: github.com/sirupsen/logrus
|
||||
- package: github.com/apex/log
|
||||
subpackages:
|
||||
- handlers/json
|
||||
- package: github.com/go-kit/kit
|
||||
subpackages:
|
||||
- log
|
||||
- package: github.com/stretchr/testify
|
||||
subpackages:
|
||||
- assert
|
||||
- require
|
||||
- package: gopkg.in/inconshreveable/log15.v2
|
||||
- package: github.com/mattn/goveralls
|
||||
- package: github.com/pborman/uuid
|
||||
- package: github.com/pkg/errors
|
||||
- package: github.com/rs/zerolog
|
||||
- package: golang.org/x/tools
|
||||
subpackages:
|
||||
- cover
|
||||
- package: golang.org/x/lint
|
||||
subpackages:
|
||||
- golint
|
||||
- package: github.com/axw/gocov
|
||||
subpackages:
|
||||
- gocov
|
||||
168
vendor/go.uber.org/zap/global.go
сгенерированный
поставляемый
168
vendor/go.uber.org/zap/global.go
сгенерированный
поставляемый
@@ -1,168 +0,0 @@
|
||||
// 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 zap
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
const (
|
||||
_loggerWriterDepth = 2
|
||||
_programmerErrorTemplate = "You've found a bug in zap! Please file a bug at " +
|
||||
"https://github.com/uber-go/zap/issues/new and reference this error: %v"
|
||||
)
|
||||
|
||||
var (
|
||||
_globalMu sync.RWMutex
|
||||
_globalL = NewNop()
|
||||
_globalS = _globalL.Sugar()
|
||||
)
|
||||
|
||||
// L returns the global Logger, which can be reconfigured with ReplaceGlobals.
|
||||
// It's safe for concurrent use.
|
||||
func L() *Logger {
|
||||
_globalMu.RLock()
|
||||
l := _globalL
|
||||
_globalMu.RUnlock()
|
||||
return l
|
||||
}
|
||||
|
||||
// S returns the global SugaredLogger, which can be reconfigured with
|
||||
// ReplaceGlobals. It's safe for concurrent use.
|
||||
func S() *SugaredLogger {
|
||||
_globalMu.RLock()
|
||||
s := _globalS
|
||||
_globalMu.RUnlock()
|
||||
return s
|
||||
}
|
||||
|
||||
// ReplaceGlobals replaces the global Logger and SugaredLogger, and returns a
|
||||
// function to restore the original values. It's safe for concurrent use.
|
||||
func ReplaceGlobals(logger *Logger) func() {
|
||||
_globalMu.Lock()
|
||||
prev := _globalL
|
||||
_globalL = logger
|
||||
_globalS = logger.Sugar()
|
||||
_globalMu.Unlock()
|
||||
return func() { ReplaceGlobals(prev) }
|
||||
}
|
||||
|
||||
// NewStdLog returns a *log.Logger which writes to the supplied zap Logger at
|
||||
// InfoLevel. To redirect the standard library's package-global logging
|
||||
// functions, use RedirectStdLog instead.
|
||||
func NewStdLog(l *Logger) *log.Logger {
|
||||
logger := l.WithOptions(AddCallerSkip(_stdLogDefaultDepth + _loggerWriterDepth))
|
||||
f := logger.Info
|
||||
return log.New(&loggerWriter{f}, "" /* prefix */, 0 /* flags */)
|
||||
}
|
||||
|
||||
// NewStdLogAt returns *log.Logger which writes to supplied zap logger at
|
||||
// required level.
|
||||
func NewStdLogAt(l *Logger, level zapcore.Level) (*log.Logger, error) {
|
||||
logger := l.WithOptions(AddCallerSkip(_stdLogDefaultDepth + _loggerWriterDepth))
|
||||
logFunc, err := levelToFunc(logger, level)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return log.New(&loggerWriter{logFunc}, "" /* prefix */, 0 /* flags */), nil
|
||||
}
|
||||
|
||||
// RedirectStdLog redirects output from the standard library's package-global
|
||||
// logger to the supplied logger at InfoLevel. Since zap already handles caller
|
||||
// annotations, timestamps, etc., it automatically disables the standard
|
||||
// library's annotations and prefixing.
|
||||
//
|
||||
// It returns a function to restore the original prefix and flags and reset the
|
||||
// standard library's output to os.Stderr.
|
||||
func RedirectStdLog(l *Logger) func() {
|
||||
f, err := redirectStdLogAt(l, InfoLevel)
|
||||
if err != nil {
|
||||
// Can't get here, since passing InfoLevel to redirectStdLogAt always
|
||||
// works.
|
||||
panic(fmt.Sprintf(_programmerErrorTemplate, err))
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
// RedirectStdLogAt redirects output from the standard library's package-global
|
||||
// logger to the supplied logger at the specified level. Since zap already
|
||||
// handles caller annotations, timestamps, etc., it automatically disables the
|
||||
// standard library's annotations and prefixing.
|
||||
//
|
||||
// It returns a function to restore the original prefix and flags and reset the
|
||||
// standard library's output to os.Stderr.
|
||||
func RedirectStdLogAt(l *Logger, level zapcore.Level) (func(), error) {
|
||||
return redirectStdLogAt(l, level)
|
||||
}
|
||||
|
||||
func redirectStdLogAt(l *Logger, level zapcore.Level) (func(), error) {
|
||||
flags := log.Flags()
|
||||
prefix := log.Prefix()
|
||||
log.SetFlags(0)
|
||||
log.SetPrefix("")
|
||||
logger := l.WithOptions(AddCallerSkip(_stdLogDefaultDepth + _loggerWriterDepth))
|
||||
logFunc, err := levelToFunc(logger, level)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.SetOutput(&loggerWriter{logFunc})
|
||||
return func() {
|
||||
log.SetFlags(flags)
|
||||
log.SetPrefix(prefix)
|
||||
log.SetOutput(os.Stderr)
|
||||
}, nil
|
||||
}
|
||||
|
||||
func levelToFunc(logger *Logger, lvl zapcore.Level) (func(string, ...Field), error) {
|
||||
switch lvl {
|
||||
case DebugLevel:
|
||||
return logger.Debug, nil
|
||||
case InfoLevel:
|
||||
return logger.Info, nil
|
||||
case WarnLevel:
|
||||
return logger.Warn, nil
|
||||
case ErrorLevel:
|
||||
return logger.Error, nil
|
||||
case DPanicLevel:
|
||||
return logger.DPanic, nil
|
||||
case PanicLevel:
|
||||
return logger.Panic, nil
|
||||
case FatalLevel:
|
||||
return logger.Fatal, nil
|
||||
}
|
||||
return nil, fmt.Errorf("unrecognized level: %q", lvl)
|
||||
}
|
||||
|
||||
type loggerWriter struct {
|
||||
logFunc func(msg string, fields ...Field)
|
||||
}
|
||||
|
||||
func (l *loggerWriter) Write(p []byte) (int, error) {
|
||||
p = bytes.TrimSpace(p)
|
||||
l.logFunc(string(p))
|
||||
return len(p), nil
|
||||
}
|
||||
26
vendor/go.uber.org/zap/global_go112.go
сгенерированный
поставляемый
26
vendor/go.uber.org/zap/global_go112.go
сгенерированный
поставляемый
@@ -1,26 +0,0 @@
|
||||
// Copyright (c) 2019 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.
|
||||
|
||||
// See #682 for more information.
|
||||
// +build go1.12
|
||||
|
||||
package zap
|
||||
|
||||
const _stdLogDefaultDepth = 1
|
||||
26
vendor/go.uber.org/zap/global_prego112.go
сгенерированный
поставляемый
26
vendor/go.uber.org/zap/global_prego112.go
сгенерированный
поставляемый
@@ -1,26 +0,0 @@
|
||||
// Copyright (c) 2019 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.
|
||||
|
||||
// See #682 for more information.
|
||||
// +build !go1.12
|
||||
|
||||
package zap
|
||||
|
||||
const _stdLogDefaultDepth = 2
|
||||
12
vendor/go.uber.org/zap/go.mod
сгенерированный
поставляемый
12
vendor/go.uber.org/zap/go.mod
сгенерированный
поставляемый
@@ -1,12 +0,0 @@
|
||||
module go.uber.org/zap
|
||||
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/stretchr/testify v1.7.0
|
||||
go.uber.org/atomic v1.7.0
|
||||
go.uber.org/multierr v1.6.0
|
||||
gopkg.in/yaml.v2 v2.2.8
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
)
|
||||
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Ссылка в новой задаче
Block a user