github.com/facebookincubator/go-belt@v0.0.0-20230703220935-39cd348f1a38/tool/experimental/errmon/adapter/generic_error_monitor.go (about)

     1  // Copyright 2022 Meta Platforms, Inc. and affiliates.
     2  //
     3  // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
     4  //
     5  // 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
     6  //
     7  // 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
     8  //
     9  // 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
    10  //
    11  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    12  
    13  package adapter
    14  
    15  import (
    16  	"time"
    17  
    18  	"github.com/facebookincubator/go-belt"
    19  	"github.com/facebookincubator/go-belt/pkg/field"
    20  	"github.com/facebookincubator/go-belt/pkg/runtime"
    21  	errmontypes "github.com/facebookincubator/go-belt/tool/experimental/errmon/types"
    22  	"github.com/facebookincubator/go-belt/tool/experimental/tracer"
    23  	loggertypes "github.com/facebookincubator/go-belt/tool/logger/types"
    24  	"github.com/go-ng/slices"
    25  )
    26  
    27  // ErrorMonitorFromEmitter wraps a Emitter with a generic implementation of of ErrorMonitor.
    28  func ErrorMonitorFromEmitter(
    29  	emitter Emitter,
    30  	callerFrameFilter runtime.PCFilter,
    31  ) ErrorMonitor {
    32  	return &GenericErrorMonitor{
    33  		EmitterValue:      emitter,
    34  		CallerFrameFilter: callerFrameFilter,
    35  	}
    36  }
    37  
    38  // GenericErrorMonitor implements ErrorMonitor given a Emitter.
    39  type GenericErrorMonitor struct {
    40  	EmitterValue      Emitter
    41  	CallerFrameFilter runtime.PCFilter
    42  	ContextFields     *FieldsChain
    43  	TraceIDs          TraceIDs
    44  	PreHooks          PreHooks
    45  	Hooks             Hooks
    46  }
    47  
    48  var _ ErrorMonitor = (*GenericErrorMonitor)(nil)
    49  
    50  func (h GenericErrorMonitor) clone() *GenericErrorMonitor {
    51  	return &h
    52  }
    53  
    54  func (h *GenericErrorMonitor) newEvent(belt *belt.Belt, extraFields field.AbstractFields) *Event {
    55  	fields := h.ContextFields
    56  	if extraFields != nil {
    57  		fields = fields.WithFields(extraFields)
    58  	}
    59  	ev := &Event{
    60  		Entry: loggertypes.Entry{
    61  			Timestamp: time.Now(),
    62  			Fields:    fields,
    63  			TraceIDs:  h.TraceIDs,
    64  		},
    65  		ID: errmontypes.RandomEventID(),
    66  	}
    67  
    68  	// stack trace
    69  	ev.Exception.StackTrace = runtime.CallerStackTrace(h.CallerFrameFilter)
    70  
    71  	// caller
    72  	var pcs [1]runtime.PC
    73  	if ev.StackTrace.ProgramCounters(pcs[:]) != 0 {
    74  		ev.Entry.Caller = pcs[0]
    75  	}
    76  
    77  	// goroutines
    78  	ev.Goroutines, ev.CurrentGoroutineID = getGoroutines()
    79  
    80  	// spans
    81  	span := tracer.SpanFromBelt(belt)
    82  	for span != nil {
    83  		ev.Spans = append(ev.Spans, span)
    84  		span = span.Parent()
    85  	}
    86  	slices.Reverse(ev.Spans)
    87  
    88  	return ev
    89  }
    90  
    91  // Emitter implements errmon.ErrorMonitor
    92  func (h *GenericErrorMonitor) Emitter() Emitter {
    93  	return h.EmitterValue
    94  }
    95  
    96  // WithPreHooks implements errmon.ErrorMonitor
    97  func (h *GenericErrorMonitor) WithPreHooks(preHooks ...PreHook) ErrorMonitor {
    98  	clone := h.clone()
    99  	clone.PreHooks = preHooks
   100  	return clone
   101  }
   102  
   103  // WithHooks implements errmon.ErrorMonitor
   104  func (h *GenericErrorMonitor) WithHooks(hooks ...Hook) ErrorMonitor {
   105  	clone := h.clone()
   106  	clone.Hooks = hooks
   107  	return clone
   108  }
   109  
   110  // ObserveError implements errmon.ErrorMonitor
   111  func (h *GenericErrorMonitor) ObserveError(belt *belt.Belt, err error) *Event {
   112  	if err == nil {
   113  		return nil
   114  	}
   115  	preHooksResult := h.PreHooks.ProcessInputError(h.TraceIDs, err)
   116  	if preHooksResult.Skip {
   117  		return nil
   118  	}
   119  	ev := h.newEvent(belt, preHooksResult.ExtraFields)
   120  	if ev == nil {
   121  		return nil
   122  	}
   123  	ev.Exception.Error = err
   124  	ev.Entry.Level = loggertypes.LevelError
   125  	ev.Entry.Properties = []loggertypes.EntryProperty{errmontypes.EntryPropertyErrorMonitoringEventEntry, errmontypes.EntryPropertyErrorEvent}
   126  	if !h.Hooks.Process(ev) {
   127  		return nil
   128  	}
   129  	h.EmitterValue.Emit(ev)
   130  	return ev
   131  }
   132  
   133  // ObserveRecover implements errmon.ErrorMonitor
   134  func (h *GenericErrorMonitor) ObserveRecover(belt *belt.Belt, recoverResult any) *Event {
   135  	if recoverResult == nil {
   136  		// no panic
   137  		return nil
   138  	}
   139  	preHooksResult := h.PreHooks.ProcessInputPanic(h.TraceIDs, recoverResult)
   140  	if preHooksResult.Skip {
   141  		return nil
   142  	}
   143  	ev := h.newEvent(belt, preHooksResult.ExtraFields)
   144  	if ev == nil {
   145  		return nil
   146  	}
   147  	ev.Exception.IsPanic = true
   148  	ev.Exception.PanicValue = recoverResult
   149  	ev.Entry.Level = loggertypes.LevelPanic
   150  	ev.Entry.Properties = []loggertypes.EntryProperty{errmontypes.EntryPropertyErrorMonitoringEventEntry, errmontypes.EntryPropertyPanicEvent}
   151  	if !h.Hooks.Process(ev) {
   152  		return nil
   153  	}
   154  	h.EmitterValue.Emit(ev)
   155  	return ev
   156  }
   157  
   158  // WithContextFields implements errmon.ErrorMonitor
   159  func (h *GenericErrorMonitor) WithContextFields(allFields *FieldsChain, _ int) belt.Tool {
   160  	clone := h.clone()
   161  	clone.ContextFields = allFields
   162  	return clone
   163  }
   164  
   165  // WithTraceIDs implements errmon.ErrorMonitor
   166  func (h *GenericErrorMonitor) WithTraceIDs(allTraceIDs TraceIDs, _ int) belt.Tool {
   167  	clone := h.clone()
   168  	clone.TraceIDs = allTraceIDs
   169  	return clone
   170  }
   171  
   172  // Flush implements metrics.Metrics (or more specifically belt.Tool).
   173  func (*GenericErrorMonitor) Flush() {}