-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat!: add support for chaining setups, by storing the setups in an i…
…ntermediary Contains breaking changes to the API, but hopefully not to any usages
- Loading branch information
1 parent
c52beb3
commit 959f2ff
Showing
6 changed files
with
247 additions
and
87 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package logger | ||
|
||
import ( | ||
"runtime" | ||
"strings" | ||
"sync" | ||
) | ||
|
||
var ( | ||
|
||
// qualified package name, cached at first use | ||
goLoggerPackage string | ||
|
||
// Positions in the call stack when tracing to report the calling method | ||
minimumCallerDepth int | ||
|
||
// Used for caller information initialisation | ||
callerInitOnce sync.Once | ||
) | ||
|
||
const ( | ||
maximumCallerDepth int = 25 | ||
knownGoLoggerFrames int = 4 | ||
) | ||
|
||
// getCaller retrieves the name of the first non-go-logger calling function | ||
func getCaller() *runtime.Frame { | ||
// cache this package's fully-qualified name | ||
callerInitOnce.Do(func() { | ||
pcs := make([]uintptr, maximumCallerDepth) | ||
_ = runtime.Callers(0, pcs) | ||
|
||
// dynamic get the package name and the minimum caller depth | ||
for i := 0; i < maximumCallerDepth; i++ { | ||
funcName := runtime.FuncForPC(pcs[i]).Name() | ||
if strings.Contains(funcName, "getCaller") { | ||
goLoggerPackage = getPackageName(funcName) | ||
break | ||
} | ||
} | ||
|
||
minimumCallerDepth = knownGoLoggerFrames | ||
}) | ||
|
||
// Restrict the lookback frames to avoid runaway lookups | ||
pcs := make([]uintptr, maximumCallerDepth) | ||
depth := runtime.Callers(minimumCallerDepth, pcs) | ||
frames := runtime.CallersFrames(pcs[:depth]) | ||
|
||
for f, again := frames.Next(); again; f, again = frames.Next() { | ||
pkg := getPackageName(f.Function) | ||
|
||
// If the caller isn't part of this package, we're done | ||
if pkg != goLoggerPackage { | ||
return &f //nolint:scopelint | ||
} | ||
} | ||
|
||
// if we got here, we failed to find the caller's context | ||
return nil | ||
} | ||
|
||
// getPackageName reduces a fully qualified function name to the package name | ||
// There really ought to be to be a better way... | ||
func getPackageName(f string) string { | ||
for { | ||
lastPeriod := strings.LastIndex(f, ".") | ||
lastSlash := strings.LastIndex(f, "/") | ||
if lastPeriod > lastSlash { | ||
f = f[:lastPeriod] | ||
} else { | ||
break | ||
} | ||
} | ||
|
||
return f | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,91 +1,101 @@ | ||
package logger | ||
|
||
import ( | ||
"runtime" | ||
"strings" | ||
"sync" | ||
"context" | ||
"time" | ||
|
||
"github.com/sirupsen/logrus" | ||
) | ||
|
||
// Entry represents a logging entry and all supported method we use | ||
type Entry interface { | ||
Debugf(format string, args ...interface{}) | ||
Debug(args ...interface{}) | ||
Infof(format string, args ...interface{}) | ||
Info(args ...interface{}) | ||
Warnf(format string, args ...interface{}) | ||
Warn(args ...interface{}) | ||
Errorf(format string, args ...interface{}) | ||
Error(args ...interface{}) | ||
Fatalf(format string, args ...interface{}) | ||
Fatal(args ...interface{}) | ||
type Entry struct { | ||
logger *Logger | ||
timestamp time.Time | ||
fields Fields | ||
context context.Context | ||
} | ||
|
||
var ( | ||
// WithError is a convenience wrapper for WithField("error", err) | ||
func (e *Entry) WithError(err error) *Entry { | ||
return e.WithField(errorKey, err) | ||
} | ||
|
||
// qualified package name, cached at first use | ||
goLoggerPackage string | ||
// WithField forwards a logging call with a field | ||
func (e *Entry) WithField(key string, value interface{}) *Entry { | ||
e.fields[key] = value | ||
return e | ||
} | ||
|
||
// Positions in the call stack when tracing to report the calling method | ||
minimumCallerDepth int | ||
// WithFields forwards a logging call with fields | ||
func (e *Entry) WithFields(fields Fields) *Entry { | ||
for k, v := range fields { | ||
e.fields[k] = v | ||
} | ||
return e | ||
} | ||
|
||
// Used for caller information initialisation | ||
callerInitOnce sync.Once | ||
) | ||
// WithContext sets the context for the log-message. Useful when using hooks. | ||
func (e *Entry) WithContext(ctx context.Context) *Entry { | ||
e.context = ctx | ||
return e | ||
} | ||
|
||
const ( | ||
maximumCallerDepth int = 25 | ||
knownGoLoggerFrames int = 4 | ||
) | ||
// Info forwards a logging call in the (format, args) format | ||
func (e *Entry) Info(args ...interface{}) { | ||
logrusFields := logrus.Fields(e.fields) | ||
e.logger.logrusLogger.WithContext(e.context).WithTime(e.timestamp).WithFields(logrusFields).Info(args...) | ||
} | ||
|
||
// getCaller retrieves the name of the first non-go-logger calling function | ||
func getCaller() *runtime.Frame { | ||
// cache this package's fully-qualified name | ||
callerInitOnce.Do(func() { | ||
pcs := make([]uintptr, maximumCallerDepth) | ||
_ = runtime.Callers(0, pcs) | ||
|
||
// dynamic get the package name and the minimum caller depth | ||
for i := 0; i < maximumCallerDepth; i++ { | ||
funcName := runtime.FuncForPC(pcs[i]).Name() | ||
if strings.Contains(funcName, "getCaller") { | ||
goLoggerPackage = getPackageName(funcName) | ||
break | ||
} | ||
} | ||
|
||
minimumCallerDepth = knownGoLoggerFrames | ||
}) | ||
|
||
// Restrict the lookback frames to avoid runaway lookups | ||
pcs := make([]uintptr, maximumCallerDepth) | ||
depth := runtime.Callers(minimumCallerDepth, pcs) | ||
frames := runtime.CallersFrames(pcs[:depth]) | ||
|
||
for f, again := frames.Next(); again; f, again = frames.Next() { | ||
pkg := getPackageName(f.Function) | ||
|
||
// If the caller isn't part of this package, we're done | ||
if pkg != goLoggerPackage { | ||
return &f //nolint:scopelint | ||
} | ||
} | ||
// Infof forwards a logging call in the (format, args) format | ||
func (e *Entry) Infof(format string, args ...interface{}) { | ||
logrusFields := logrus.Fields(e.fields) | ||
e.logger.logrusLogger.WithContext(e.context).WithTime(e.timestamp).WithFields(logrusFields).Infof(format, args...) | ||
} | ||
|
||
// if we got here, we failed to find the caller's context | ||
return nil | ||
// Error forwards an error logging call | ||
func (e *Entry) Error(args ...interface{}) { | ||
logrusFields := logrus.Fields(e.fields) | ||
e.logger.logrusLogger.WithContext(e.context).WithTime(e.timestamp).WithFields(logrusFields).Error(args...) | ||
} | ||
|
||
// getPackageName reduces a fully qualified function name to the package name | ||
// There really ought to be to be a better way... | ||
func getPackageName(f string) string { | ||
for { | ||
lastPeriod := strings.LastIndex(f, ".") | ||
lastSlash := strings.LastIndex(f, "/") | ||
if lastPeriod > lastSlash { | ||
f = f[:lastPeriod] | ||
} else { | ||
break | ||
} | ||
} | ||
// Errorf forwards an error logging call | ||
func (e *Entry) Errorf(format string, args ...interface{}) { | ||
logrusFields := logrus.Fields(e.fields) | ||
e.logger.logrusLogger.WithContext(e.context).WithTime(e.timestamp).WithFields(logrusFields).Errorf(format, args...) | ||
} | ||
|
||
// Debug forwards a debugging logging call | ||
func (e *Entry) Debug(args ...interface{}) { | ||
logrusFields := logrus.Fields(e.fields) | ||
e.logger.logrusLogger.WithContext(e.context).WithTime(e.timestamp).WithFields(logrusFields).Debug(args...) | ||
} | ||
|
||
// Debugf forwards a debugging logging call | ||
func (e *Entry) Debugf(format string, args ...interface{}) { | ||
logrusFields := logrus.Fields(e.fields) | ||
e.logger.logrusLogger.WithContext(e.context).WithTime(e.timestamp).WithFields(logrusFields).Debugf(format, args...) | ||
} | ||
|
||
// Warn forwards a warning logging call | ||
func (e *Entry) Warn(args ...interface{}) { | ||
logrusFields := logrus.Fields(e.fields) | ||
e.logger.logrusLogger.WithContext(e.context).WithTime(e.timestamp).WithFields(logrusFields).Warn(args...) | ||
} | ||
|
||
// Warnf forwards a warning logging call | ||
func (e *Entry) Warnf(format string, args ...interface{}) { | ||
logrusFields := logrus.Fields(e.fields) | ||
e.logger.logrusLogger.WithContext(e.context).WithTime(e.timestamp).WithFields(logrusFields).Warnf(format, args...) | ||
} | ||
|
||
// Fatal forwards a fatal logging call | ||
func (e *Entry) Fatal(args ...interface{}) { | ||
logrusFields := logrus.Fields(e.fields) | ||
e.logger.logrusLogger.WithContext(e.context).WithTime(e.timestamp).WithFields(logrusFields).Fatal(args...) | ||
} | ||
|
||
return f | ||
// Fatalf forwards a fatal logging call | ||
func (e *Entry) Fatalf(format string, args ...interface{}) { | ||
logrusFields := logrus.Fields(e.fields) | ||
e.logger.logrusLogger.WithContext(e.context).WithTime(e.timestamp).WithFields(logrusFields).Fatalf(format, args...) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.