gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

log.go (11120B)


      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 }