Refactor mlog
- simplify mlog by removing redundant code
- remove Zap dependency
- update unit test helpers
- update logging config
- update auditing
Этот коммит содержится в:
Doug Lauder
2021-08-17 16:08:04 -04:00
коммит произвёл GitHub
родитель 04b27ce93c
Коммит a4507327a7
216 изменённых файлов: 4940 добавлений и 14674 удалений

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 сгенерированный поставляемый
Просмотреть файл

@@ -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 сгенерированный поставляемый
Просмотреть файл

@@ -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 сгенерированный поставляемый
Просмотреть файл

@@ -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 сгенерированный поставляемый
Просмотреть файл

@@ -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 сгенерированный поставляемый
Просмотреть файл

@@ -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 сгенерированный поставляемый
Просмотреть файл

@@ -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 сгенерированный поставляемый
Просмотреть файл

@@ -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 сгенерированный поставляемый
Просмотреть файл

@@ -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 сгенерированный поставляемый
Просмотреть файл

@@ -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 сгенерированный поставляемый
Просмотреть файл

@@ -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 сгенерированный поставляемый
Просмотреть файл

@@ -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
}

Просмотреть файл

@@ -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/LICENSE → vendor/github.com/mattermost/logr/v2/LICENSE сгенерированный поставляемый
Просмотреть файл

Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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
}
}

Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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 сгенерированный поставляемый
Просмотреть файл

@@ -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 сгенерированный поставляемый
Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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
}

Просмотреть файл

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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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{}{}
}

Просмотреть файл

@@ -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 ""
}

Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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
}
}
}

Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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
}

Просмотреть файл