Dynamic Configuration Refactoring

This commit is contained in:
Ludovic Fernandez 2018-11-14 10:18:03 +01:00 committed by Traefiker Bot
parent d3ae88f108
commit a09dfa3ce1
452 changed files with 21023 additions and 9419 deletions

80
log/deprecated.go Normal file
View file

@ -0,0 +1,80 @@
package log
import "github.com/sirupsen/logrus"
// Debug logs a message at level Debug on the standard logger.
// Deprecated
func Debug(args ...interface{}) {
mainLogger.Debug(args...)
}
// Debugf logs a message at level Debug on the standard logger.
// Deprecated
func Debugf(format string, args ...interface{}) {
mainLogger.Debugf(format, args...)
}
// Info logs a message at level Info on the standard logger.
// Deprecated
func Info(args ...interface{}) {
mainLogger.Info(args...)
}
// Infof logs a message at level Info on the standard logger.
// Deprecated
func Infof(format string, args ...interface{}) {
mainLogger.Infof(format, args...)
}
// Warn logs a message at level Warn on the standard logger.
// Deprecated
func Warn(args ...interface{}) {
mainLogger.Warn(args...)
}
// Warnf logs a message at level Warn on the standard logger.
// Deprecated
func Warnf(format string, args ...interface{}) {
mainLogger.Warnf(format, args...)
}
// Error logs a message at level Error on the standard logger.
// Deprecated
func Error(args ...interface{}) {
mainLogger.Error(args...)
}
// Errorf logs a message at level Error on the standard logger.
// Deprecated
func Errorf(format string, args ...interface{}) {
mainLogger.Errorf(format, args...)
}
// Panic logs a message at level Panic on the standard logger.
// Deprecated
func Panic(args ...interface{}) {
mainLogger.Panic(args...)
}
// Panicf logs a message at level Panic on the standard logger.
// Deprecated
func Panicf(format string, args ...interface{}) {
mainLogger.Panicf(format, args...)
}
// Fatal logs a message at level Fatal on the standard logger.
// Deprecated
func Fatal(args ...interface{}) {
mainLogger.Fatal(args...)
}
// Fatalf logs a message at level Fatal on the standard logger.
// Deprecated
func Fatalf(format string, args ...interface{}) {
mainLogger.Fatalf(format, args...)
}
// AddHook adds a hook to the standard logger hooks.
func AddHook(hook logrus.Hook) {
logrus.AddHook(hook)
}

14
log/fields.go Normal file
View file

@ -0,0 +1,14 @@
package log
// Log entry name
const (
EntryPointName = "entryPointName"
RouterName = "routerName"
MiddlewareName = "middlewareName"
MiddlewareType = "middlewareType"
ProviderName = "providerName"
ServiceName = "serviceName"
MetricsProviderName = "metricsProviderName"
TracingProviderName = "tracingProviderName"
ServerName = "serverName"
)

145
log/log.go Normal file
View file

@ -0,0 +1,145 @@
package log
import (
"context"
"fmt"
"io"
"os"
"github.com/sirupsen/logrus"
)
type contextKey int
const (
loggerKey contextKey = iota
)
// Logger the Traefik logger
type Logger interface {
logrus.FieldLogger
WriterLevel(logrus.Level) *io.PipeWriter
}
var (
mainLogger Logger
logFilePath string
logFile *os.File
)
func init() {
mainLogger = logrus.StandardLogger()
logrus.SetOutput(os.Stdout)
}
// SetLogger sets the logger.
func SetLogger(l Logger) {
mainLogger = l
}
// SetOutput sets the standard logger output.
func SetOutput(out io.Writer) {
logrus.SetOutput(out)
}
// SetFormatter sets the standard logger formatter.
func SetFormatter(formatter logrus.Formatter) {
logrus.SetFormatter(formatter)
}
// SetLevel sets the standard logger level.
func SetLevel(level logrus.Level) {
logrus.SetLevel(level)
}
// GetLevel returns the standard logger level.
func GetLevel() logrus.Level {
return logrus.GetLevel()
}
// Str adds a string field
func Str(key, value string) func(logrus.Fields) {
return func(fields logrus.Fields) {
fields[key] = value
}
}
// With Adds fields
func With(ctx context.Context, opts ...func(logrus.Fields)) context.Context {
logger := FromContext(ctx)
fields := make(logrus.Fields)
for _, opt := range opts {
opt(fields)
}
logger = logger.WithFields(fields)
return context.WithValue(ctx, loggerKey, logger)
}
// FromContext Gets the logger from context
func FromContext(ctx context.Context) Logger {
if ctx == nil {
panic("nil context")
}
logger, ok := ctx.Value(loggerKey).(Logger)
if !ok {
logger = mainLogger
}
return logger
}
// WithoutContext Gets the main logger
func WithoutContext() Logger {
return mainLogger
}
// OpenFile opens the log file using the specified path
func OpenFile(path string) error {
logFilePath = path
var err error
logFile, err = os.OpenFile(logFilePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
return err
}
SetOutput(logFile)
return nil
}
// CloseFile closes the log and sets the Output to stdout
func CloseFile() error {
logrus.SetOutput(os.Stdout)
if logFile != nil {
return logFile.Close()
}
return nil
}
// RotateFile closes and reopens the log file to allow for rotation
// by an external source. If the log isn't backed by a file then
// it does nothing.
func RotateFile() error {
logger := FromContext(context.Background())
if logFile == nil && logFilePath == "" {
logger.Debug("Traefik log is not writing to a file, ignoring rotate request")
return nil
}
if logFile != nil {
defer func(f *os.File) {
_ = f.Close()
}(logFile)
}
if err := OpenFile(logFilePath); err != nil {
return fmt.Errorf("error opening log file: %s", err)
}
return nil
}

58
log/log_test.go Normal file
View file

@ -0,0 +1,58 @@
package log
import (
"bytes"
"context"
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func TestLog(t *testing.T) {
testCases := []struct {
desc string
fields map[string]string
expected string
}{
{
desc: "Log with one field",
fields: map[string]string{
"foo": "bar",
},
expected: ` level=error msg="message test" foo=bar$`,
},
{
desc: "Log with two fields",
fields: map[string]string{
"foo": "bar",
"oof": "rab",
},
expected: ` level=error msg="message test" foo=bar oof=rab$`,
},
{
desc: "Log without field",
fields: map[string]string{},
expected: ` level=error msg="message test"$`,
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
var buffer bytes.Buffer
SetOutput(&buffer)
ctx := context.Background()
for key, value := range test.fields {
ctx = With(ctx, Str(key, value))
}
FromContext(ctx).Error("message test")
assert.Regexp(t, test.expected, strings.TrimSpace(buffer.String()))
})
}
}

View file

@ -1,315 +0,0 @@
package log
import (
"bufio"
"fmt"
"io"
"os"
"runtime"
"github.com/sirupsen/logrus"
)
// Logger allows overriding the logrus logger behavior
type Logger interface {
logrus.FieldLogger
WriterLevel(logrus.Level) *io.PipeWriter
}
var (
logger Logger
logFilePath string
logFile *os.File
)
func init() {
logger = logrus.StandardLogger().WithFields(logrus.Fields{})
logrus.SetOutput(os.Stdout)
}
// Context sets the Context of the logger
func Context(context interface{}) *logrus.Entry {
return logger.WithField("context", context)
}
// SetOutput sets the standard logger output.
func SetOutput(out io.Writer) {
logrus.SetOutput(out)
}
// SetFormatter sets the standard logger formatter.
func SetFormatter(formatter logrus.Formatter) {
logrus.SetFormatter(formatter)
}
// SetLevel sets the standard logger level.
func SetLevel(level logrus.Level) {
logrus.SetLevel(level)
}
// SetLogger sets the logger.
func SetLogger(l Logger) {
logger = l
}
// GetLevel returns the standard logger level.
func GetLevel() logrus.Level {
return logrus.GetLevel()
}
// AddHook adds a hook to the standard logger hooks.
func AddHook(hook logrus.Hook) {
logrus.AddHook(hook)
}
// WithError creates an entry from the standard logger and adds an error to it, using the value defined in ErrorKey as key.
func WithError(err error) *logrus.Entry {
return logger.WithError(err)
}
// WithField creates an entry from the standard logger and adds a field to
// it. If you want multiple fields, use `WithFields`.
//
// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
// or Panic on the Entry it returns.
func WithField(key string, value interface{}) *logrus.Entry {
return logger.WithField(key, value)
}
// WithFields creates an entry from the standard logger and adds multiple
// fields to it. This is simply a helper for `WithField`, invoking it
// once for each field.
//
// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
// or Panic on the Entry it returns.
func WithFields(fields logrus.Fields) *logrus.Entry {
return logger.WithFields(fields)
}
// Debug logs a message at level Debug on the standard logger.
func Debug(args ...interface{}) {
logger.Debug(args...)
}
// Print logs a message at level Info on the standard logger.
func Print(args ...interface{}) {
logger.Print(args...)
}
// Info logs a message at level Info on the standard logger.
func Info(args ...interface{}) {
logger.Info(args...)
}
// Warn logs a message at level Warn on the standard logger.
func Warn(args ...interface{}) {
logger.Warn(args...)
}
// Warning logs a message at level Warn on the standard logger.
func Warning(args ...interface{}) {
logger.Warning(args...)
}
// Error logs a message at level Error on the standard logger.
func Error(args ...interface{}) {
logger.Error(args...)
}
// Panic logs a message at level Panic on the standard logger.
func Panic(args ...interface{}) {
logger.Panic(args...)
}
// Fatal logs a message at level Fatal on the standard logger.
func Fatal(args ...interface{}) {
logger.Fatal(args...)
}
// Debugf logs a message at level Debug on the standard logger.
func Debugf(format string, args ...interface{}) {
logger.Debugf(format, args...)
}
// Printf logs a message at level Info on the standard logger.
func Printf(format string, args ...interface{}) {
logger.Printf(format, args...)
}
// Infof logs a message at level Info on the standard logger.
func Infof(format string, args ...interface{}) {
logger.Infof(format, args...)
}
// Warnf logs a message at level Warn on the standard logger.
func Warnf(format string, args ...interface{}) {
logger.Warnf(format, args...)
}
// Warningf logs a message at level Warn on the standard logger.
func Warningf(format string, args ...interface{}) {
logger.Warningf(format, args...)
}
// Errorf logs a message at level Error on the standard logger.
func Errorf(format string, args ...interface{}) {
logger.Errorf(format, args...)
}
// Panicf logs a message at level Panic on the standard logger.
func Panicf(format string, args ...interface{}) {
logger.Panicf(format, args...)
}
// Fatalf logs a message at level Fatal on the standard logger.
func Fatalf(format string, args ...interface{}) {
logger.Fatalf(format, args...)
}
// Debugln logs a message at level Debug on the standard logger.
func Debugln(args ...interface{}) {
logger.Debugln(args...)
}
// Println logs a message at level Info on the standard logger.
func Println(args ...interface{}) {
logger.Println(args...)
}
// Infoln logs a message at level Info on the standard logger.
func Infoln(args ...interface{}) {
logger.Infoln(args...)
}
// Warnln logs a message at level Warn on the standard logger.
func Warnln(args ...interface{}) {
logger.Warnln(args...)
}
// Warningln logs a message at level Warn on the standard logger.
func Warningln(args ...interface{}) {
logger.Warningln(args...)
}
// Errorln logs a message at level Error on the standard logger.
func Errorln(args ...interface{}) {
logger.Errorln(args...)
}
// Panicln logs a message at level Panic on the standard logger.
func Panicln(args ...interface{}) {
logger.Panicln(args...)
}
// Fatalln logs a message at level Fatal on the standard logger.
func Fatalln(args ...interface{}) {
logger.Fatalln(args...)
}
// OpenFile opens the log file using the specified path
func OpenFile(path string) error {
logFilePath = path
var err error
logFile, err = os.OpenFile(logFilePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err == nil {
SetOutput(logFile)
}
return err
}
// CloseFile closes the log and sets the Output to stdout
func CloseFile() error {
logrus.SetOutput(os.Stdout)
if logFile != nil {
return logFile.Close()
}
return nil
}
// RotateFile closes and reopens the log file to allow for rotation
// by an external source. If the log isn't backed by a file then
// it does nothing.
func RotateFile() error {
if logFile == nil && logFilePath == "" {
Debug("Traefik log is not writing to a file, ignoring rotate request")
return nil
}
if logFile != nil {
defer func(f *os.File) {
f.Close()
}(logFile)
}
if err := OpenFile(logFilePath); err != nil {
return fmt.Errorf("error opening log file: %s", err)
}
return nil
}
// Writer logs writer (Level Info)
func Writer() *io.PipeWriter {
return WriterLevel(logrus.InfoLevel)
}
// WriterLevel logs writer for a specific level.
func WriterLevel(level logrus.Level) *io.PipeWriter {
return logger.WriterLevel(level)
}
// CustomWriterLevel logs writer for a specific level. (with a custom scanner buffer size.)
// adapted from github.com/Sirupsen/logrus/writer.go
func CustomWriterLevel(level logrus.Level, maxScanTokenSize int) *io.PipeWriter {
reader, writer := io.Pipe()
var printFunc func(args ...interface{})
switch level {
case logrus.DebugLevel:
printFunc = Debug
case logrus.InfoLevel:
printFunc = Info
case logrus.WarnLevel:
printFunc = Warn
case logrus.ErrorLevel:
printFunc = Error
case logrus.FatalLevel:
printFunc = Fatal
case logrus.PanicLevel:
printFunc = Panic
default:
printFunc = Print
}
go writerScanner(reader, maxScanTokenSize, printFunc)
runtime.SetFinalizer(writer, writerFinalizer)
return writer
}
// extract from github.com/Sirupsen/logrus/writer.go
// Hack the buffer size
func writerScanner(reader io.ReadCloser, scanTokenSize int, printFunc func(args ...interface{})) {
scanner := bufio.NewScanner(reader)
if scanTokenSize > bufio.MaxScanTokenSize {
buf := make([]byte, bufio.MaxScanTokenSize)
scanner.Buffer(buf, scanTokenSize)
}
for scanner.Scan() {
printFunc(scanner.Text())
}
if err := scanner.Err(); err != nil {
Errorf("Error while reading from Writer: %s", err)
}
reader.Close()
}
func writerFinalizer(writer *io.PipeWriter) {
writer.Close()
}

View file

@ -1,79 +0,0 @@
package log
import (
"io/ioutil"
"os"
"strings"
"testing"
"time"
)
func TestLogRotation(t *testing.T) {
tempDir, err := ioutil.TempDir("", "traefik_")
if err != nil {
t.Fatalf("Error setting up temporary directory: %s", err)
}
fileName := tempDir + "traefik.log"
if err := OpenFile(fileName); err != nil {
t.Fatalf("Error opening temporary file %s: %s", fileName, err)
}
defer CloseFile()
rotatedFileName := fileName + ".rotated"
iterations := 20
halfDone := make(chan bool)
writeDone := make(chan bool)
go func() {
for i := 0; i < iterations; i++ {
Println("Test log line")
if i == iterations/2 {
halfDone <- true
}
}
writeDone <- true
}()
<-halfDone
err = os.Rename(fileName, rotatedFileName)
if err != nil {
t.Fatalf("Error renaming file: %s", err)
}
err = RotateFile()
if err != nil {
t.Fatalf("Error rotating file: %s", err)
}
select {
case <-writeDone:
gotLineCount := lineCount(t, fileName) + lineCount(t, rotatedFileName)
if iterations != gotLineCount {
t.Errorf("Wanted %d written log lines, got %d", iterations, gotLineCount)
}
case <-time.After(500 * time.Millisecond):
t.Fatalf("test timed out")
}
close(halfDone)
close(writeDone)
}
func lineCount(t *testing.T, fileName string) int {
t.Helper()
fileContents, err := ioutil.ReadFile(fileName)
if err != nil {
t.Fatalf("Error reading from file %s: %s", fileName, err)
}
count := 0
for _, line := range strings.Split(string(fileContents), "\n") {
if strings.TrimSpace(line) == "" {
continue
}
count++
}
return count
}