github.com/facebookincubator/go-belt@v0.0.0-20230703220935-39cd348f1a38/tool/experimental/tracer/implementation/logger/tracer.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 logger 14 15 import ( 16 "context" 17 "time" 18 19 "github.com/facebookincubator/go-belt" 20 "github.com/facebookincubator/go-belt/pkg/field" 21 "github.com/facebookincubator/go-belt/tool/experimental/tracer" 22 "github.com/facebookincubator/go-belt/tool/logger" 23 loggertypes "github.com/facebookincubator/go-belt/tool/logger/types" 24 ) 25 26 // TracerImpl is the implementation of tracer.Tracer based on a given logger.Logger. 27 type TracerImpl struct { 28 Logger loggertypes.Logger 29 LevelFunc func(span *SpanImpl) loggertypes.Level 30 PreHooks tracer.Hooks 31 Hooks tracer.Hooks 32 TraceIDs belt.TraceIDs 33 Fields *field.FieldsChain 34 } 35 36 var _ tracer.Tracer = (*TracerImpl)(nil) 37 38 func (t TracerImpl) clone() *TracerImpl { 39 return &t 40 } 41 42 // WithContextFields implements tracer.Tracer. 43 func (t *TracerImpl) WithContextFields(allFields *field.FieldsChain, newFieldsCount int) belt.Tool { 44 clone := t.clone() 45 clone.Fields = allFields 46 return clone 47 } 48 49 // WithTraceIDs implements tracer.Tracer. 50 func (t *TracerImpl) WithTraceIDs(traceIDs belt.TraceIDs, newTraceIDsCount int) belt.Tool { 51 clone := t.clone() 52 clone.TraceIDs = traceIDs 53 clone.Logger = t.Logger.WithTraceIDs(traceIDs, newTraceIDsCount).(logger.Logger) 54 return clone 55 } 56 57 // WithPreHooks implements tracer.Tracer. 58 func (t *TracerImpl) WithPreHooks(hooks ...tracer.Hook) tracer.Tracer { 59 c := t.clone() 60 if hooks == nil { 61 c.PreHooks = nil 62 } else { 63 c.PreHooks = tracer.Hooks{c.PreHooks, tracer.Hooks(hooks)} 64 } 65 return c 66 } 67 68 // WithHooks implements tracer.Tracer. 69 func (t *TracerImpl) WithHooks(hooks ...tracer.Hook) tracer.Tracer { 70 c := t.clone() 71 if hooks == nil { 72 c.Hooks = nil 73 } else { 74 c.Hooks = tracer.Hooks{c.Hooks, tracer.Hooks(hooks)} 75 } 76 return c 77 } 78 79 // Start implements tracer.Tracer. 80 func (t *TracerImpl) Start(name string, parent tracer.Span, options ...tracer.SpanOption) tracer.Span { 81 span, _ := t.newSpanBelt(nil, name, parent, options...) 82 return span 83 } 84 85 // StartWithBelt implements tracer.Tracer. 86 func (t *TracerImpl) StartWithBelt(belt *belt.Belt, name string, options ...tracer.SpanOption) (tracer.Span, *belt.Belt) { 87 return t.newSpanBelt(belt, name, nil, options...) 88 89 } 90 91 // StartChildWithBelt implements tracer.Tracer. 92 func (t *TracerImpl) StartChildWithBelt(belt *belt.Belt, name string, options ...tracer.SpanOption) (tracer.Span, *belt.Belt) { 93 return t.newSpanBelt(belt, name, tracer.SpanFromBelt(belt), options...) 94 } 95 96 // StartWithCtx implements tracer.Tracer. 97 func (t *TracerImpl) StartWithCtx(ctx context.Context, name string, options ...tracer.SpanOption) (tracer.Span, context.Context) { 98 return t.newSpanCtx(ctx, name, nil, options...) 99 100 } 101 102 // StartChildWithCtx implements tracer.Tracer. 103 func (t *TracerImpl) StartChildWithCtx(ctx context.Context, name string, options ...tracer.SpanOption) (tracer.Span, context.Context) { 104 return t.newSpanCtx(ctx, name, tracer.SpanFromCtx(ctx), options...) 105 } 106 107 // Flush implements tracer.Tracer. 108 func (t *TracerImpl) Flush() { 109 t.Logger.Flush() 110 } 111 112 func (t *TracerImpl) send(span *SpanImpl) { 113 fields := span.FieldsValue. 114 WithField(FieldNameName, span.NameValue, FieldPropertyName). 115 WithField(FieldNameDuration, span.Duration, FieldPropertyDuration). 116 WithField(FieldNameStartTS, span.StartTSValue, FieldPropertyStartTS) 117 118 if span.ParentValue != nil { 119 fields = fields.WithField(FieldNameParent, span.ParentValue, FieldPropertyParent) 120 } 121 122 if len(span.Events) > 0 { 123 eventsAsFields := make(field.Fields, 0, len(span.Events)) 124 for _, ev := range span.Events { 125 eventsAsFields = append(eventsAsFields, field.Field{ 126 Key: FieldNamePrefixEvent + ev.Name, 127 Value: ev.Timestamp, 128 Properties: field.Properties{FieldPropertyEvent}, 129 }) 130 } 131 fields = fields.WithFields(eventsAsFields) 132 } 133 134 t.Logger.LogFields(t.LevelFunc(span), "timespan finished", fields) 135 } 136 137 type fieldPropertyEnum uint 138 139 const ( 140 // FieldPropertyStartTS is a field.Property which marks the field as a start timestamp. 141 FieldPropertyStartTS = fieldPropertyEnum(iota + 1) 142 143 // FieldPropertyDuration is a field.Property which marks the field as a duration. 144 FieldPropertyDuration 145 146 // FieldPropertyName is a field.Property which marks the field as the span name 147 FieldPropertyName 148 149 // FieldPropertyEvent is a field.Property which marks the field as the timestamp of an event. 150 FieldPropertyEvent 151 152 // FieldPropertyParent is a field.Property which marks the field as the parent span. 153 FieldPropertyParent 154 ) 155 156 // SpanFromLogEntry converts a logger Entry to a Span. 157 func SpanFromLogEntry(entry *loggertypes.Entry) *SpanImpl { 158 var ( 159 fields field.Fields 160 startTS time.Time 161 duration time.Duration 162 parent tracer.Span 163 name string 164 events []Event 165 ) 166 entry.Fields.ForEachField(func(f *field.Field) bool { 167 if len(f.Properties) != 1 { 168 fields = append(fields, *f) 169 return true 170 } 171 switch f.Properties[0] { 172 case FieldPropertyStartTS: 173 startTS = f.Value.(time.Time) 174 case FieldPropertyParent: 175 parent = f.Value.(tracer.Span) 176 case FieldPropertyDuration: 177 duration = f.Value.(time.Duration) 178 case FieldPropertyName: 179 name = f.Value.(string) 180 case FieldPropertyEvent: 181 events = append(events, Event{ 182 Timestamp: f.Value.(time.Time), 183 Name: f.Key[len(FieldNamePrefixEvent):], 184 }) 185 default: 186 fields = append(fields, *f) 187 } 188 return true 189 }) 190 span := &SpanImpl{ 191 StartTSValue: startTS, 192 Duration: duration, 193 ParentValue: parent, 194 FieldsValue: (*field.FieldsChain)(nil).WithFields(fields), 195 NameValue: name, 196 Events: events, 197 } 198 return span 199 } 200 201 var ( 202 // FieldNamePrefixEvent is the field name prefix for events reported through Annotate method. 203 FieldNamePrefixEvent = "event_" 204 205 // FieldNameName is the field name for the span name. 206 FieldNameName = "name" 207 208 // FieldNameDuration is the field name for the duration. 209 FieldNameDuration = "duration" 210 211 // FieldNameParent is the field name for the parent span. 212 FieldNameParent = "parent" 213 214 // FieldNameStartTS is the field name for the span start timestamp. 215 FieldNameStartTS = "start_ts" 216 ) 217 218 // Default is the overridable function which returns a logger-based Tracer 219 // with the default configuration. 220 var Default = func() tracer.Tracer { 221 return New(logger.Default(), func(_ *SpanImpl) loggertypes.Level { 222 return logger.LevelTrace 223 }) 224 } 225 226 // New returns a new instance of TracerImpl (a tracer.Tracer implementation based on a given logger.Logger). 227 func New(logger loggertypes.Logger, levelFunc func(span *SpanImpl) loggertypes.Level) *TracerImpl { 228 return &TracerImpl{ 229 Logger: logger, 230 LevelFunc: levelFunc, 231 } 232 }