github.com/v2pro/plz@v0.0.0-20221028024117-e5f9aec5b631/countlog/root.go (about)

     1  package countlog
     2  
     3  import (
     4  	"github.com/v2pro/plz/countlog/spi"
     5  	"runtime"
     6  	"fmt"
     7  	"runtime/debug"
     8  	"github.com/v2pro/plz/countlog/loglog"
     9  )
    10  
    11  type panicHandler func(recovered interface{}, event *spi.Event, site *spi.LogSite)
    12  
    13  func newRootHandler(site *spi.LogSite, onPanic panicHandler) spi.EventHandler {
    14  	statsHandler := EventAggregator.HandlerOf(site)
    15  	if statsHandler == nil {
    16  		return &oneHandler{
    17  			site:    site,
    18  			handler: EventWriter.HandlerOf(site),
    19  			onPanic: onPanic,
    20  		}
    21  	}
    22  	return &statsAndOutput{
    23  		site:          site,
    24  		statsHandler:  statsHandler,
    25  		outputHandler: EventWriter.HandlerOf(site),
    26  		onPanic:       onPanic,
    27  	}
    28  }
    29  
    30  type oneHandler struct {
    31  	site    *spi.LogSite
    32  	handler spi.EventHandler
    33  	onPanic panicHandler
    34  }
    35  
    36  func (handler *oneHandler) Handle(event *spi.Event) {
    37  	defer func() {
    38  		recovered := recover()
    39  		if recovered != nil {
    40  			handler.onPanic(recovered, event, handler.site)
    41  		}
    42  	}()
    43  	handler.handler.Handle(event)
    44  }
    45  
    46  func (handler *oneHandler) LogSite() *spi.LogSite {
    47  	return handler.site
    48  }
    49  
    50  type statsAndOutput struct {
    51  	site          *spi.LogSite
    52  	statsHandler  spi.EventHandler
    53  	outputHandler spi.EventHandler
    54  	onPanic       panicHandler
    55  }
    56  
    57  func (handler *statsAndOutput) Handle(event *spi.Event) {
    58  	defer func() {
    59  		recovered := recover()
    60  		if recovered != nil {
    61  			handler.onPanic(recovered, event, handler.site)
    62  		}
    63  	}()
    64  	if event.Level >= spi.MinCallLevel {
    65  		handler.outputHandler.Handle(event)
    66  	}
    67  	handler.statsHandler.Handle(event)
    68  }
    69  
    70  func (handler *statsAndOutput) LogSite() *spi.LogSite {
    71  	return handler.site
    72  }
    73  
    74  func nomalModeOnPanic(recovered interface{}, event *spi.Event, site *spi.LogSite) {
    75  	redirector := &redirector{
    76  		site:            *site,
    77  	}
    78  	handlerCache.Store(site.Event, redirector)
    79  	newSite := *site
    80  	newSite.File = "unknown"
    81  	newSite.Line = 0
    82  	newSite.Sample = event.Properties
    83  	newRootHandler(&newSite, fallbackModeOnPanic).Handle(event)
    84  }
    85  
    86  func fallbackModeOnPanic(recovered interface{}, event *spi.Event, site *spi.LogSite) {
    87  	loglog.Error(fmt.Errorf("%v", recovered))
    88  	if spi.MinLevel <= spi.LevelDebug {
    89  		debug.PrintStack()
    90  	}
    91  }
    92  
    93  type redirector struct {
    94  	site spi.LogSite
    95  }
    96  
    97  func (redirector *redirector) Handle(event *spi.Event) {
    98  	_, callerFile, callerLine, _ := runtime.Caller(3)
    99  	key := accurateHandlerKey{callerFile, callerLine}
   100  	handlerObj, found := handlerCache.Load(key)
   101  	if found {
   102  		handlerObj.(spi.EventHandler).Handle(event)
   103  		return
   104  	}
   105  	site := redirector.site
   106  	site.File = callerFile
   107  	site.Line = callerLine
   108  	site.Sample = event.Properties
   109  	handler := newRootHandler(&site, fallbackModeOnPanic)
   110  	handlerCache.Store(key, handler)
   111  	handler.Handle(event)
   112  	return
   113  }
   114  
   115  type accurateHandlerKey struct {
   116  	File string
   117  	Line int
   118  }