...

Source file src/github.com/prometheus/common/promlog/log.go

Documentation: github.com/prometheus/common/promlog

     1  // Copyright 2017 The Prometheus Authors
     2  // Licensed under the Apache License, Version 2.0 (the "License");
     3  // you may not use this file except in compliance with the License.
     4  // You may obtain a copy of the License at
     5  //
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  // Package promlog defines standardised ways to initialize Go kit loggers
    15  // across Prometheus components.
    16  // It should typically only ever be imported by main packages.
    17  package promlog
    18  
    19  import (
    20  	"fmt"
    21  	"os"
    22  	"sync"
    23  	"time"
    24  
    25  	"github.com/go-kit/log"
    26  	"github.com/go-kit/log/level"
    27  )
    28  
    29  var (
    30  	// This timestamp format differs from RFC3339Nano by using .000 instead
    31  	// of .999999999 which changes the timestamp from 9 variable to 3 fixed
    32  	// decimals (.130 instead of .130987456).
    33  	timestampFormat = log.TimestampFormat(
    34  		func() time.Time { return time.Now().UTC() },
    35  		"2006-01-02T15:04:05.000Z07:00",
    36  	)
    37  
    38  	LevelFlagOptions  = []string{"debug", "info", "warn", "error"}
    39  	FormatFlagOptions = []string{"logfmt", "json"}
    40  )
    41  
    42  // AllowedLevel is a settable identifier for the minimum level a log entry
    43  // must be have.
    44  type AllowedLevel struct {
    45  	s string
    46  	o level.Option
    47  }
    48  
    49  func (l *AllowedLevel) UnmarshalYAML(unmarshal func(interface{}) error) error {
    50  	var s string
    51  	type plain string
    52  	if err := unmarshal((*plain)(&s)); err != nil {
    53  		return err
    54  	}
    55  	if s == "" {
    56  		return nil
    57  	}
    58  	lo := &AllowedLevel{}
    59  	if err := lo.Set(s); err != nil {
    60  		return err
    61  	}
    62  	*l = *lo
    63  	return nil
    64  }
    65  
    66  func (l *AllowedLevel) String() string {
    67  	return l.s
    68  }
    69  
    70  // Set updates the value of the allowed level.
    71  func (l *AllowedLevel) Set(s string) error {
    72  	switch s {
    73  	case "debug":
    74  		l.o = level.AllowDebug()
    75  	case "info":
    76  		l.o = level.AllowInfo()
    77  	case "warn":
    78  		l.o = level.AllowWarn()
    79  	case "error":
    80  		l.o = level.AllowError()
    81  	default:
    82  		return fmt.Errorf("unrecognized log level %q", s)
    83  	}
    84  	l.s = s
    85  	return nil
    86  }
    87  
    88  // AllowedFormat is a settable identifier for the output format that the logger can have.
    89  type AllowedFormat struct {
    90  	s string
    91  }
    92  
    93  func (f *AllowedFormat) String() string {
    94  	return f.s
    95  }
    96  
    97  // Set updates the value of the allowed format.
    98  func (f *AllowedFormat) Set(s string) error {
    99  	switch s {
   100  	case "logfmt", "json":
   101  		f.s = s
   102  	default:
   103  		return fmt.Errorf("unrecognized log format %q", s)
   104  	}
   105  	return nil
   106  }
   107  
   108  // Config is a struct containing configurable settings for the logger
   109  type Config struct {
   110  	Level  *AllowedLevel
   111  	Format *AllowedFormat
   112  }
   113  
   114  // New returns a new leveled oklog logger. Each logged line will be annotated
   115  // with a timestamp. The output always goes to stderr.
   116  func New(config *Config) log.Logger {
   117  	if config.Format != nil && config.Format.s == "json" {
   118  		return NewWithLogger(log.NewJSONLogger(log.NewSyncWriter(os.Stderr)), config)
   119  	}
   120  
   121  	return NewWithLogger(log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr)), config)
   122  }
   123  
   124  // NewWithLogger returns a new leveled oklog logger with a custom log.Logger.
   125  // Each logged line will be annotated with a timestamp.
   126  func NewWithLogger(l log.Logger, config *Config) log.Logger {
   127  	if config.Level != nil {
   128  		l = log.With(l, "ts", timestampFormat, "caller", log.Caller(5))
   129  		l = level.NewFilter(l, config.Level.o)
   130  	} else {
   131  		l = log.With(l, "ts", timestampFormat, "caller", log.DefaultCaller)
   132  	}
   133  	return l
   134  }
   135  
   136  // NewDynamic returns a new leveled logger. Each logged line will be annotated
   137  // with a timestamp. The output always goes to stderr. Some properties can be
   138  // changed, like the level.
   139  func NewDynamic(config *Config) *logger {
   140  	if config.Format != nil && config.Format.s == "json" {
   141  		return NewDynamicWithLogger(log.NewJSONLogger(log.NewSyncWriter(os.Stderr)), config)
   142  	}
   143  
   144  	return NewDynamicWithLogger(log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr)), config)
   145  }
   146  
   147  // NewDynamicWithLogger returns a new leveled logger with a custom io.Writer.
   148  // Each logged line will be annotated with a timestamp.
   149  // Some properties can be changed, like the level.
   150  func NewDynamicWithLogger(l log.Logger, config *Config) *logger {
   151  	lo := &logger{
   152  		base:    l,
   153  		leveled: l,
   154  	}
   155  
   156  	if config.Level != nil {
   157  		lo.SetLevel(config.Level)
   158  	}
   159  
   160  	return lo
   161  }
   162  
   163  type logger struct {
   164  	base         log.Logger
   165  	leveled      log.Logger
   166  	currentLevel *AllowedLevel
   167  	mtx          sync.Mutex
   168  }
   169  
   170  // Log implements logger.Log.
   171  func (l *logger) Log(keyvals ...interface{}) error {
   172  	l.mtx.Lock()
   173  	defer l.mtx.Unlock()
   174  	return l.leveled.Log(keyvals...)
   175  }
   176  
   177  // SetLevel changes the log level.
   178  func (l *logger) SetLevel(lvl *AllowedLevel) {
   179  	l.mtx.Lock()
   180  	defer l.mtx.Unlock()
   181  	if lvl == nil {
   182  		l.leveled = log.With(l.base, "ts", timestampFormat, "caller", log.DefaultCaller)
   183  		l.currentLevel = nil
   184  		return
   185  	}
   186  
   187  	if l.currentLevel != nil && l.currentLevel.s != lvl.s {
   188  		_ = l.base.Log("msg", "Log level changed", "prev", l.currentLevel, "current", lvl)
   189  	}
   190  	l.currentLevel = lvl
   191  	l.leveled = level.NewFilter(log.With(l.base, "ts", timestampFormat, "caller", log.Caller(5)), lvl.o)
   192  }
   193  

View as plain text