github.com/facebookincubator/go-belt@v0.0.0-20230703220935-39cd348f1a38/tool/experimental/tracer/implementation/zipkin/span.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 zipkin 14 15 import ( 16 "context" 17 "encoding/json" 18 "fmt" 19 "strings" 20 "sync" 21 "time" 22 23 "github.com/facebookincubator/go-belt" 24 "github.com/facebookincubator/go-belt/pkg/field" 25 "github.com/facebookincubator/go-belt/tool/experimental/tracer" 26 "github.com/openzipkin/zipkin-go" 27 "github.com/openzipkin/zipkin-go/model" 28 ) 29 30 func init() { 31 if tracer.Default == nil { 32 tracer.Default = func() tracer.Tracer { 33 return Default() 34 } 35 } 36 } 37 38 // SpanImpl is the implementation of tracer.Span on top of zipkin.Span. 39 type SpanImpl struct { 40 sendOnce sync.Once 41 parent tracer.Span 42 name string 43 tracer *TracerImpl 44 zipkinSpan zipkin.Span 45 fields *field.FieldsChain 46 } 47 48 var _ tracer.Span = (*SpanImpl)(nil) 49 50 func (t *TracerImpl) newSpanBelt( 51 belt *belt.Belt, 52 name string, 53 parent tracer.Span, 54 options ...tracer.SpanOption, 55 ) (tracer.Span, *belt.Belt) { 56 if tracer.IsNoopSpan(parent) { 57 return tracer.NewNoopSpan(name, parent, time.Now()), belt 58 } 59 60 span := &SpanImpl{ 61 parent: parent, 62 tracer: t, 63 fields: t.ContextFields, 64 name: name, 65 } 66 67 if !t.PreHooks.ProcessSpan(span) { 68 return tracer.NewNoopSpan(name, parent, time.Now()), belt 69 } 70 71 t.compileFields() 72 73 zipkinOptions := spanZipkinOptions(t.compiledFields, options...) 74 span.zipkinSpan = t.ZipkinTracer.StartSpan(name, zipkinOptions...) 75 76 var returnSpan tracer.Span = span 77 if zipkin.IsNoop(span.zipkinSpan) { 78 returnSpan = tracer.NewNoopSpan(name, parent, time.Now()) 79 } 80 81 if belt == nil { 82 return returnSpan, nil 83 } 84 return returnSpan, tracer.BeltWithSpan(belt, span) 85 } 86 87 func (t *TracerImpl) newSpanCtx( 88 ctx context.Context, 89 name string, 90 parent tracer.Span, 91 options ...tracer.SpanOption, 92 ) (tracer.Span, context.Context) { 93 if tracer.IsNoopSpan(parent) { 94 return tracer.NewNoopSpan(name, parent, time.Now()), ctx 95 } 96 97 span := &SpanImpl{ 98 parent: parent, 99 tracer: t, 100 fields: t.ContextFields, 101 name: name, 102 } 103 104 if !t.PreHooks.ProcessSpan(span) { 105 return tracer.NewNoopSpan(name, parent, time.Now()), ctx 106 } 107 108 t.compileFields() 109 110 zipkinOptions := spanZipkinOptions(t.compiledFields, options...) 111 var resultCtx context.Context 112 if ctx != nil { 113 span.zipkinSpan, resultCtx = t.ZipkinTracer.StartSpanFromContext(ctx, name, zipkinOptions...) 114 } else { 115 span.zipkinSpan = t.ZipkinTracer.StartSpan(name, zipkinOptions...) 116 resultCtx = zipkin.NewContext(context.Background(), span.zipkinSpan) 117 } 118 119 var returnSpan tracer.Span = span 120 if zipkin.IsNoop(span.zipkinSpan) { 121 returnSpan = tracer.NewNoopSpan(name, parent, time.Now()) 122 } 123 resultCtx = tracer.CtxWithSpan(resultCtx, returnSpan) 124 125 return returnSpan, resultCtx 126 } 127 128 func spanZipkinOptions(fields map[string]string, opts ...tracer.SpanOption) []zipkin.SpanOption { 129 lastResetFieldsIdx := -1 130 for idx, opt := range opts { 131 if _, isResetFields := opt.(tracer.SpanOptionResetFields); isResetFields { 132 lastResetFieldsIdx = idx 133 continue 134 } 135 } 136 zipkinOptions := make([]zipkin.SpanOption, 0, len(opts)+1) 137 if fields != nil && lastResetFieldsIdx < 0 { 138 zipkinOptions = append(zipkinOptions, zipkin.Tags(fields)) 139 } 140 for idx, opt := range opts { 141 if _, isAddFields := opt.(tracer.SpanOptionAddFields); isAddFields && idx < lastResetFieldsIdx { 142 continue 143 } 144 zipkinOption := spanOptionToZipkin(opt) 145 if zipkinOption == nil { 146 continue 147 } 148 zipkinOptions = append(zipkinOptions, zipkinOption) 149 } 150 151 return zipkinOptions 152 } 153 154 func spanOptionToZipkin(opt tracer.SpanOption) zipkin.SpanOption { 155 switch opt := opt.(type) { 156 case tracer.SpanOptionAddFields: 157 tags := make(map[string]string, len(opt)) 158 field.Fields(opt).ForEachField(func(f *field.Field) bool { 159 tags[f.Key] = fmt.Sprint(f.Value) 160 return true 161 }) 162 return zipkin.Tags(tags) 163 case tracer.SpanOptionResetFields: 164 return nil 165 case tracer.SpanOptionRole: 166 switch opt { 167 case tracer.RoleClient: 168 return zipkin.Kind(model.Client) 169 case tracer.RoleServer: 170 return zipkin.Kind(model.Server) 171 default: 172 return zipkin.Kind(model.Kind(strings.ToUpper(string(opt)))) 173 } 174 case tracer.SpanOptionStart: 175 return zipkin.StartTime(time.Time(opt)) 176 } 177 return nil 178 } 179 180 // ID implements tracer.Span. 181 func (span *SpanImpl) ID() any { 182 return span.zipkinSpan.Context().ID 183 } 184 185 // Name implements tracer.Span. 186 func (span *SpanImpl) Name() string { 187 return span.name 188 } 189 190 // TraceIDs implements tracer.Span. 191 func (span *SpanImpl) TraceIDs() belt.TraceIDs { 192 return span.tracer.TraceIDs 193 } 194 195 // StartTS implements tracer.Span. 196 func (span *SpanImpl) StartTS() time.Time { 197 b, err := json.Marshal(span.zipkinSpan) 198 if err != nil { 199 return time.Time{} 200 } 201 202 var spanModel model.SpanModel 203 err = json.Unmarshal(b, &spanModel) 204 if err != nil { 205 return time.Time{} 206 } 207 208 return spanModel.Timestamp 209 } 210 211 // Fields implements tracer.Span. 212 func (span *SpanImpl) Fields() field.AbstractFields { 213 return span.fields 214 } 215 216 // Parent implements tracer.Span. 217 func (span *SpanImpl) Parent() tracer.Span { 218 return span.parent 219 } 220 221 // SetName implements tracer.Span. 222 func (span *SpanImpl) SetName(name string) { 223 span.zipkinSpan.SetName(name) 224 } 225 226 // Annotate implements tracer.Span. 227 func (span *SpanImpl) Annotate(ts time.Time, event string) { 228 span.zipkinSpan.Annotate(ts, event) 229 } 230 231 // SetField implements tracer.Span. 232 func (span *SpanImpl) SetField(k field.Key, v field.Value) { 233 span.setFieldInZipkin(k, v) 234 } 235 236 func (span *SpanImpl) setFieldInZipkin(k field.Key, v field.Value) { 237 span.fields = span.fields.WithField(k, v) 238 span.zipkinSpan.Tag(k, fmt.Sprint(v)) 239 } 240 241 // SetFields implements tracer.Span. 242 func (span *SpanImpl) SetFields(fields field.AbstractFields) { 243 span.fields = span.fields.WithFields(fields) 244 fields.ForEachField(func(f *field.Field) bool { 245 span.setFieldInZipkin(f.Key, f.Value) 246 return true 247 }) 248 } 249 250 // Finish implements tracer.Span. 251 func (span *SpanImpl) Finish() { 252 span.sendOnce.Do(func() { 253 if !span.tracer.Hooks.ProcessSpan(span) { 254 return 255 } 256 span.zipkinSpan.Finish() 257 }) 258 } 259 260 // FinishWithDuration implements tracer.Span. 261 func (span *SpanImpl) FinishWithDuration(duration time.Duration) { 262 span.sendOnce.Do(func() { 263 if !span.tracer.Hooks.ProcessSpan(span) { 264 return 265 } 266 span.zipkinSpan.FinishedWithDuration(duration) 267 }) 268 } 269 270 // SendAsIs implements tracer.Span. 271 func (span *SpanImpl) SendAsIs() { 272 span.sendOnce.Do(span.zipkinSpan.Flush) 273 } 274 275 // Flush implements tracer.Span. 276 func (*SpanImpl) Flush() {}