...

Source file src/github.com/dsoprea/go-logging/v2/log.go

Documentation: github.com/dsoprea/go-logging/v2

     1  package log
     2  
     3  import (
     4  	"bytes"
     5  	e "errors"
     6  	"fmt"
     7  	"strings"
     8  	"sync"
     9  
    10  	"text/template"
    11  
    12  	"github.com/go-errors/errors"
    13  	"golang.org/x/net/context"
    14  )
    15  
    16  // LogLevel describes a log-level.
    17  type LogLevel int
    18  
    19  // Config severity integers.
    20  const (
    21  	// LevelDebug exposes debug logging an above. This exposes all logging.
    22  	LevelDebug LogLevel = iota
    23  
    24  	// LevelInfo exposes info logging and above.
    25  	LevelInfo LogLevel = iota
    26  
    27  	// LevelWarning exposes warning logging and above.
    28  	LevelWarning LogLevel = iota
    29  
    30  	// LevelError exposes error logging and above. This is the most restrictive.
    31  	LevelError LogLevel = iota
    32  )
    33  
    34  // Config severity names.
    35  
    36  // LogLevelName describes the name of a log-level.
    37  type LogLevelName string
    38  
    39  const (
    40  	levelNameDebug   LogLevelName = "debug"
    41  	levelNameInfo    LogLevelName = "info"
    42  	levelNameWarning LogLevelName = "warning"
    43  	levelNameError   LogLevelName = "error"
    44  )
    45  
    46  // Seveirty name->integer map.
    47  var (
    48  	levelNameMap = map[LogLevelName]LogLevel{
    49  		levelNameDebug:   LevelDebug,
    50  		levelNameInfo:    LevelInfo,
    51  		levelNameWarning: LevelWarning,
    52  		levelNameError:   LevelError,
    53  	}
    54  
    55  	levelNameMapR = map[LogLevel]LogLevelName{
    56  		LevelDebug:   levelNameDebug,
    57  		LevelInfo:    levelNameInfo,
    58  		LevelWarning: levelNameWarning,
    59  		LevelError:   levelNameError,
    60  	}
    61  )
    62  
    63  // Other
    64  var (
    65  	includeFilters    = make(map[string]bool)
    66  	useIncludeFilters = false
    67  	excludeFilters    = make(map[string]bool)
    68  	useExcludeFilters = false
    69  
    70  	adapters = make(map[string]LogAdapter)
    71  
    72  	// TODO(dustin): !! Finish implementing this.
    73  	excludeBypassLevel LogLevel = -1
    74  )
    75  
    76  // AddIncludeFilter adds global include filter.
    77  func AddIncludeFilter(noun string) {
    78  	includeFilters[noun] = true
    79  	useIncludeFilters = true
    80  }
    81  
    82  // RemoveIncludeFilter removes global include filter.
    83  func RemoveIncludeFilter(noun string) {
    84  	delete(includeFilters, noun)
    85  	if len(includeFilters) == 0 {
    86  		useIncludeFilters = false
    87  	}
    88  }
    89  
    90  // AddExcludeFilter adds global exclude filter.
    91  func AddExcludeFilter(noun string) {
    92  	excludeFilters[noun] = true
    93  	useExcludeFilters = true
    94  }
    95  
    96  // RemoveExcludeFilter removes global exclude filter.
    97  func RemoveExcludeFilter(noun string) {
    98  	delete(excludeFilters, noun)
    99  	if len(excludeFilters) == 0 {
   100  		useExcludeFilters = false
   101  	}
   102  }
   103  
   104  // AddAdapter registers a new adapter.
   105  func AddAdapter(name string, la LogAdapter) {
   106  	if _, found := adapters[name]; found == true {
   107  		Panic(e.New("adapter already registered"))
   108  	}
   109  
   110  	if la == nil {
   111  		Panic(e.New("adapter is nil"))
   112  	}
   113  
   114  	adapters[name] = la
   115  
   116  	if GetDefaultAdapterName() == "" {
   117  		SetDefaultAdapterName(name)
   118  	}
   119  }
   120  
   121  // ClearAdapters deregisters all adapters.
   122  func ClearAdapters() {
   123  	adapters = make(map[string]LogAdapter)
   124  	SetDefaultAdapterName("")
   125  }
   126  
   127  // LogAdapter describes minimal log-adapter functionality.
   128  type LogAdapter interface {
   129  	// Debugf logs a debug message.
   130  	Debugf(lc *LogContext, message *string) error
   131  
   132  	// Infof logs an info message.
   133  	Infof(lc *LogContext, message *string) error
   134  
   135  	// Warningf logs a warning message.
   136  	Warningf(lc *LogContext, message *string) error
   137  
   138  	// Errorf logs an error message.
   139  	Errorf(lc *LogContext, message *string) error
   140  }
   141  
   142  // TODO(dustin): !! Also populate whether we've bypassed an exception so that
   143  //                  we can add a template macro to prefix an exclamation of
   144  //                  some sort.
   145  
   146  // MessageContext describes the current logging context and can be used for
   147  // substitution in a template string.
   148  type MessageContext struct {
   149  	Level         *LogLevelName
   150  	Noun          *string
   151  	Message       *string
   152  	ExcludeBypass bool
   153  }
   154  
   155  // LogContext encapsulates the current context for passing to the adapter.
   156  type LogContext struct {
   157  	logger *Logger
   158  	ctx    context.Context
   159  }
   160  
   161  // Logger is the main logger type.
   162  type Logger struct {
   163  	isConfigured bool
   164  	an           string
   165  	la           LogAdapter
   166  	t            *template.Template
   167  	systemLevel  LogLevel
   168  	noun         string
   169  }
   170  
   171  // NewLoggerWithAdapterName initializes a logger struct to log to a specific
   172  // adapter.
   173  func NewLoggerWithAdapterName(noun string, adapterName string) (l *Logger) {
   174  	l = &Logger{
   175  		noun: noun,
   176  		an:   adapterName,
   177  	}
   178  
   179  	return l
   180  }
   181  
   182  // NewLogger returns a new logger struct.
   183  func NewLogger(noun string) (l *Logger) {
   184  	l = NewLoggerWithAdapterName(noun, "")
   185  
   186  	return l
   187  }
   188  
   189  // Noun returns the noun that this logger represents.
   190  func (l *Logger) Noun() string {
   191  	return l.noun
   192  }
   193  
   194  // Adapter returns the adapter used by this logger struct.
   195  func (l *Logger) Adapter() LogAdapter {
   196  	return l.la
   197  }
   198  
   199  var (
   200  	configureMutex sync.Mutex
   201  )
   202  
   203  func (l *Logger) doConfigure(force bool) {
   204  	configureMutex.Lock()
   205  	defer configureMutex.Unlock()
   206  
   207  	if l.isConfigured == true && force == false {
   208  		return
   209  	}
   210  
   211  	if IsConfigurationLoaded() == false {
   212  		Panic(e.New("can not configure because configuration is not loaded"))
   213  	}
   214  
   215  	if l.an == "" {
   216  		l.an = GetDefaultAdapterName()
   217  	}
   218  
   219  	// If this is empty, then no specific adapter was given or no system
   220  	// default was configured (which implies that no adapters were registered).
   221  	// All of our logging will be skipped.
   222  	if l.an != "" {
   223  		la, found := adapters[l.an]
   224  		if found == false {
   225  			Panic(fmt.Errorf("adapter is not valid: %s", l.an))
   226  		}
   227  
   228  		l.la = la
   229  	}
   230  
   231  	// Set the level.
   232  
   233  	systemLevel, found := levelNameMap[levelName]
   234  	if found == false {
   235  		Panic(fmt.Errorf("log-level not valid: [%s]", levelName))
   236  	}
   237  
   238  	l.systemLevel = systemLevel
   239  
   240  	// Set the form.
   241  
   242  	if format == "" {
   243  		Panic(e.New("format is empty"))
   244  	}
   245  
   246  	if t, err := template.New("logItem").Parse(format); err != nil {
   247  		Panic(err)
   248  	} else {
   249  		l.t = t
   250  	}
   251  
   252  	l.isConfigured = true
   253  }
   254  
   255  func (l *Logger) flattenMessage(lc *MessageContext, format *string, args []interface{}) (string, error) {
   256  	m := fmt.Sprintf(*format, args...)
   257  
   258  	lc.Message = &m
   259  
   260  	var b bytes.Buffer
   261  	if err := l.t.Execute(&b, *lc); err != nil {
   262  		return "", err
   263  	}
   264  
   265  	return b.String(), nil
   266  }
   267  
   268  func (l *Logger) allowMessage(noun string, level LogLevel) bool {
   269  	if _, found := includeFilters[noun]; found == true {
   270  		return true
   271  	}
   272  
   273  	// If we didn't hit an include filter and we *had* include filters, filter
   274  	// it out.
   275  	if useIncludeFilters == true {
   276  		return false
   277  	}
   278  
   279  	if _, found := excludeFilters[noun]; found == true {
   280  		return false
   281  	}
   282  
   283  	return true
   284  }
   285  
   286  func (l *Logger) makeLogContext(ctx context.Context) *LogContext {
   287  	return &LogContext{
   288  		ctx:    ctx,
   289  		logger: l,
   290  	}
   291  }
   292  
   293  type logMethod func(lc *LogContext, message *string) error
   294  
   295  func (l *Logger) log(ctx context.Context, level LogLevel, lm logMethod, format string, args []interface{}) error {
   296  	if l.systemLevel > level {
   297  		return nil
   298  	}
   299  
   300  	// Preempt the normal filter checks if we can unconditionally allow at a
   301  	// certain level and we've hit that level.
   302  	//
   303  	// Notice that this is only relevant if the system-log level is letting
   304  	// *anything* show logs at the level we came in with.
   305  	canExcludeBypass := level >= excludeBypassLevel && excludeBypassLevel != -1
   306  	didExcludeBypass := false
   307  
   308  	n := l.Noun()
   309  
   310  	if l.allowMessage(n, level) == false {
   311  		if canExcludeBypass == false {
   312  			return nil
   313  		}
   314  
   315  		didExcludeBypass = true
   316  	}
   317  
   318  	levelName, found := levelNameMapR[level]
   319  	if found == false {
   320  		Panicf("level not valid: (%d)", level)
   321  	}
   322  
   323  	levelName = LogLevelName(strings.ToUpper(string(levelName)))
   324  
   325  	mc := &MessageContext{
   326  		Level:         &levelName,
   327  		Noun:          &n,
   328  		ExcludeBypass: didExcludeBypass,
   329  	}
   330  
   331  	s, err := l.flattenMessage(mc, &format, args)
   332  	PanicIf(err)
   333  
   334  	lc := l.makeLogContext(ctx)
   335  
   336  	err = lm(lc, &s)
   337  	PanicIf(err)
   338  
   339  	if level == LevelError {
   340  		return e.New(s)
   341  	}
   342  
   343  	return nil
   344  }
   345  
   346  func (l *Logger) mergeStack(err interface{}, format string, args []interface{}) (string, []interface{}) {
   347  	if format != "" {
   348  		format += "\n%s"
   349  	} else {
   350  		format = "%s"
   351  	}
   352  
   353  	var stackified *errors.Error
   354  	stackified, ok := err.(*errors.Error)
   355  	if ok == false {
   356  		stackified = errors.Wrap(err, 2)
   357  	}
   358  
   359  	args = append(args, stackified.ErrorStack())
   360  
   361  	return format, args
   362  }
   363  
   364  // Debugf forwards debug-logging to the underlying adapter.
   365  func (l *Logger) Debugf(ctx context.Context, format string, args ...interface{}) {
   366  	l.doConfigure(false)
   367  
   368  	if l.la != nil {
   369  		l.log(ctx, LevelDebug, l.la.Debugf, format, args)
   370  	}
   371  }
   372  
   373  // Infof forwards debug-logging to the underlying adapter.
   374  func (l *Logger) Infof(ctx context.Context, format string, args ...interface{}) {
   375  	l.doConfigure(false)
   376  
   377  	if l.la != nil {
   378  		l.log(ctx, LevelInfo, l.la.Infof, format, args)
   379  	}
   380  }
   381  
   382  // Warningf forwards debug-logging to the underlying adapter.
   383  func (l *Logger) Warningf(ctx context.Context, format string, args ...interface{}) {
   384  	l.doConfigure(false)
   385  
   386  	if l.la != nil {
   387  		l.log(ctx, LevelWarning, l.la.Warningf, format, args)
   388  	}
   389  }
   390  
   391  // Errorf forwards debug-logging to the underlying adapter.
   392  func (l *Logger) Errorf(ctx context.Context, errRaw interface{}, format string, args ...interface{}) {
   393  	l.doConfigure(false)
   394  
   395  	var err interface{}
   396  
   397  	if errRaw != nil {
   398  		_, ok := errRaw.(*errors.Error)
   399  		if ok == true {
   400  			err = errRaw
   401  		} else {
   402  			err = errors.Wrap(errRaw, 1)
   403  		}
   404  	}
   405  
   406  	if l.la != nil {
   407  		if errRaw != nil {
   408  			format, args = l.mergeStack(err, format, args)
   409  		}
   410  
   411  		l.log(ctx, LevelError, l.la.Errorf, format, args)
   412  	}
   413  }
   414  
   415  // ErrorIff logs a string-substituted message if errRaw is non-nil.
   416  func (l *Logger) ErrorIff(ctx context.Context, errRaw interface{}, format string, args ...interface{}) {
   417  	if errRaw == nil {
   418  		return
   419  	}
   420  
   421  	var err interface{}
   422  
   423  	_, ok := errRaw.(*errors.Error)
   424  	if ok == true {
   425  		err = errRaw
   426  	} else {
   427  		err = errors.Wrap(errRaw, 1)
   428  	}
   429  
   430  	l.Errorf(ctx, err, format, args...)
   431  }
   432  
   433  // Panicf logs a string-substituted message.
   434  func (l *Logger) Panicf(ctx context.Context, errRaw interface{}, format string, args ...interface{}) {
   435  	l.doConfigure(false)
   436  
   437  	var wrapped interface{}
   438  
   439  	_, ok := errRaw.(*errors.Error)
   440  	if ok == true {
   441  		wrapped = errRaw
   442  	} else {
   443  		wrapped = errors.Wrap(errRaw, 1)
   444  	}
   445  
   446  	if l.la != nil {
   447  		format, args = l.mergeStack(wrapped, format, args)
   448  		wrapped = l.log(ctx, LevelError, l.la.Errorf, format, args)
   449  	}
   450  
   451  	Panic(wrapped)
   452  }
   453  
   454  // PanicIff panics with a string-substituted message if errRaw is non-nil.
   455  func (l *Logger) PanicIff(ctx context.Context, errRaw interface{}, format string, args ...interface{}) {
   456  	if errRaw == nil {
   457  		return
   458  	}
   459  
   460  	// We wrap the error here rather than rely on on Panicf because there will
   461  	// be one more stack-frame than expected and there'd be no way for that
   462  	// method to know whether it should drop one frame or two.
   463  
   464  	var err interface{}
   465  
   466  	_, ok := errRaw.(*errors.Error)
   467  	if ok == true {
   468  		err = errRaw
   469  	} else {
   470  		err = errors.Wrap(errRaw, 1)
   471  	}
   472  
   473  	l.Panicf(ctx, err.(error), format, args...)
   474  }
   475  
   476  // Wrap returns a stack-wrapped error. If already stack-wrapped this is a no-op.
   477  func Wrap(err interface{}) *errors.Error {
   478  	es, ok := err.(*errors.Error)
   479  	if ok == true {
   480  		return es
   481  	}
   482  
   483  	return errors.Wrap(err, 1)
   484  }
   485  
   486  // Errorf returns a stack-wrapped error with a string-substituted message.
   487  func Errorf(message string, args ...interface{}) *errors.Error {
   488  	err := fmt.Errorf(message, args...)
   489  	return errors.Wrap(err, 1)
   490  }
   491  
   492  // Panic panics with the error. Wrap if not already stack-wrapped.
   493  func Panic(err interface{}) {
   494  	_, ok := err.(*errors.Error)
   495  	if ok == true {
   496  		panic(err)
   497  	} else {
   498  		panic(errors.Wrap(err, 1))
   499  	}
   500  }
   501  
   502  // Panicf panics a stack-wrapped error with a string-substituted message.
   503  func Panicf(message string, args ...interface{}) {
   504  	err := Errorf(message, args...)
   505  	Panic(err)
   506  }
   507  
   508  // PanicIf panics if err is non-nil.
   509  func PanicIf(err interface{}) {
   510  	if err == nil {
   511  		return
   512  	}
   513  
   514  	_, ok := err.(*errors.Error)
   515  	if ok == true {
   516  		panic(err)
   517  	} else {
   518  		panic(errors.Wrap(err, 1))
   519  	}
   520  }
   521  
   522  // Is checks if the left ("actual") error equals the right ("against") error.
   523  // The right must be an unwrapped error (the kind that you'd initialize as a
   524  // global variable). The left can be a wrapped or unwrapped error.
   525  func Is(actual, against error) bool {
   526  	// If it's an unwrapped error.
   527  	if _, ok := actual.(*errors.Error); ok == false {
   528  		return actual == against
   529  	}
   530  
   531  	return errors.Is(actual, against)
   532  }
   533  
   534  // PrintError is a utility function to prevent the caller from having to import
   535  // the third-party library.
   536  func PrintError(err error) {
   537  	wrapped := Wrap(err)
   538  	fmt.Printf("Stack:\n\n%s\n", wrapped.ErrorStack())
   539  }
   540  
   541  // PrintErrorf is a utility function to prevent the caller from having to
   542  // import the third-party library.
   543  func PrintErrorf(err error, format string, args ...interface{}) {
   544  	wrapped := Wrap(err)
   545  
   546  	fmt.Printf(format, args...)
   547  	fmt.Printf("\n")
   548  	fmt.Printf("Stack:\n\n%s\n", wrapped.ErrorStack())
   549  }
   550  
   551  func init() {
   552  	if format == "" {
   553  		format = defaultFormat
   554  	}
   555  
   556  	if levelName == "" {
   557  		levelName = defaultLevelName
   558  	}
   559  
   560  	if includeNouns != "" {
   561  		for _, noun := range strings.Split(includeNouns, ",") {
   562  			AddIncludeFilter(noun)
   563  		}
   564  	}
   565  
   566  	if excludeNouns != "" {
   567  		for _, noun := range strings.Split(excludeNouns, ",") {
   568  			AddExcludeFilter(noun)
   569  		}
   570  	}
   571  
   572  	if excludeBypassLevelName != "" {
   573  		excludeBypassLevelName = LogLevelName(strings.ToLower(string(excludeBypassLevelName)))
   574  
   575  		var found bool
   576  		if excludeBypassLevel, found = levelNameMap[excludeBypassLevelName]; found == false {
   577  			panic(e.New("exclude bypass-level is invalid"))
   578  		}
   579  	}
   580  }
   581  

View as plain text