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() {}