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  }