github.com/machinefi/w3bstream@v1.6.5-rc9.0.20240426031326-b8c7c4876e72/pkg/depends/conf/log/logger_span_exporter.go (about) 1 package log 2 3 import ( 4 "context" 5 "fmt" 6 "os" 7 8 "github.com/sirupsen/logrus" 9 "go.opentelemetry.io/otel" 10 "go.opentelemetry.io/otel/codes" 11 "go.opentelemetry.io/otel/sdk/trace" 12 13 "github.com/machinefi/w3bstream/pkg/depends/kit/metax" 14 ) 15 16 type ( 17 SpanMapper func(trace.ReadOnlySpan) trace.ReadOnlySpan 18 WithExporter func(trace.SpanExporter) trace.SpanExporter 19 ) 20 21 type StdExporter struct{ formatter logrus.Formatter } 22 23 func (e *StdExporter) Shutdown(ctx context.Context) error { return nil } 24 25 func (e *StdExporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan) error { 26 for _, data := range spans { 27 for _, ev := range data.Events() { 28 if ev.Name == "" || ev.Name[0] != '@' { 29 continue 30 } 31 level, err := logrus.ParseLevel(ev.Name[1:]) 32 if err != nil { 33 continue 34 } 35 ent := logrus.NewEntry(logrus.StandardLogger()) 36 ent.Level = level 37 ent.Time = ev.Time 38 ent.Data = logrus.Fields{} 39 for _, kv := range ev.Attributes { 40 k := string(kv.Key) 41 switch k { 42 case "message": 43 ent.Message = kv.Value.AsString() 44 default: 45 ent.Data[k] = kv.Value.AsInterface() 46 } 47 } 48 for _, kv := range data.Attributes() { 49 k := string(kv.Key) 50 if k != "meta" { 51 ent.Data[k] = kv.Value.AsInterface() 52 continue 53 } 54 meta := metax.ParseMeta(kv.Value.AsString()) 55 for mk := range meta { 56 if mk == "_id" { 57 continue 58 } 59 ent.Data[mk] = meta[mk] 60 } 61 } 62 spanCtx := data.SpanContext() 63 ent.Data["span"] = data.Name() 64 ent.Data["traceID"] = spanCtx.TraceID() 65 if spanCtx.HasSpanID() { 66 ent.Data["spanID"] = spanCtx.SpanID() 67 } 68 if data.Parent().IsValid() { 69 ent.Data["parentSpanID"] = data.Parent().SpanID() 70 } 71 72 if content, err := e.formatter.Format(ent); err != nil { 73 fmt.Println(err) 74 } else { 75 _, _ = os.Stdout.Write(content) 76 } 77 } 78 } 79 return nil 80 } 81 82 type SpanMapExporter struct { 83 mappers []SpanMapper 84 trace.SpanExporter 85 } 86 87 func WithSpanMapExporter(mappers ...SpanMapper) WithExporter { 88 return func(exporters trace.SpanExporter) trace.SpanExporter { 89 return &SpanMapExporter{mappers, exporters} 90 } 91 } 92 93 func (e *SpanMapExporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan) error { 94 snapshots := make([]trace.ReadOnlySpan, 0) 95 96 for i := range spans { 97 data := spans[i] 98 for _, m := range e.mappers { 99 data = m(data) 100 } 101 if data != nil { 102 snapshots = append(snapshots, data) 103 } 104 } 105 106 if len(snapshots) == 0 { 107 return nil 108 } 109 return e.SpanExporter.ExportSpans(ctx, snapshots) 110 } 111 112 type ErrIgnoreExporter struct{ trace.SpanExporter } 113 114 func WithErrIgnoreExporter() WithExporter { 115 return func(exporter trace.SpanExporter) trace.SpanExporter { 116 return &ErrIgnoreExporter{exporter} 117 } 118 } 119 120 func (e *ErrIgnoreExporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan) error { 121 _ = e.SpanExporter.ExportSpans(ctx, spans) 122 return nil 123 } 124 125 func InstallPipeline(output LoggerOutputType, format LoggerFormatType, exporter trace.SpanExporter) error { 126 var stdexp *StdExporter 127 switch format { 128 case LOGGER_FORMAT_TYPE__JSON: 129 stdexp = &StdExporter{&logrus.JSONFormatter{}} 130 default: 131 stdexp = &StdExporter{&logrus.TextFormatter{}} 132 } 133 134 otel.SetTracerProvider( 135 trace.NewTracerProvider( 136 trace.WithSampler(trace.AlwaysSample()), 137 trace.WithSyncer( 138 WithSpanMapExporter( 139 OutputFilter(output), 140 )(stdexp)), 141 trace.WithBatcher( 142 WithSpanMapExporter( 143 OutputFilter(output), 144 SpanOnlyFilter(), 145 )(WithErrIgnoreExporter()(exporter)), 146 ), 147 ), 148 ) 149 150 return nil 151 } 152 153 func OutputFilter(output LoggerOutputType) SpanMapper { 154 return func(s trace.ReadOnlySpan) trace.ReadOnlySpan { 155 if output == LOGGER_OUTPUT_TYPE__NEVER { 156 return nil 157 } 158 if output == LOGGER_OUTPUT_TYPE__ON_FAILURE && s.Status().Code == codes.Ok { 159 return nil 160 } 161 return s 162 } 163 } 164 165 func SpanOnlyFilter() SpanMapper { 166 return func(s trace.ReadOnlySpan) trace.ReadOnlySpan { 167 if s == nil { 168 return nil 169 } 170 return s 171 } 172 }