...

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

Documentation: github.com/dsoprea/go-logging

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

View as plain text