parent
5db0804f1d
commit
2fd259c162
@ -0,0 +1,2 @@
|
|||||||
|
bin/
|
||||||
|
.vscode/
|
@ -0,0 +1,14 @@
|
|||||||
|
all: bindir
|
||||||
|
go build -o ./bin ./cmd/...
|
||||||
|
|
||||||
|
httpcat: bindir
|
||||||
|
go build -o ./bin ./cmd/httpcat/...
|
||||||
|
|
||||||
|
mdfmt: bindir
|
||||||
|
go build -o ./bin ./cmd/md-fmt/...
|
||||||
|
|
||||||
|
otelq: bindir
|
||||||
|
go build -o ./bin ./cmd/otelq/...
|
||||||
|
|
||||||
|
bindir:
|
||||||
|
mkdir ./bin; exit 0
|
@ -0,0 +1,3 @@
|
|||||||
|
# Tempo To OTLP
|
||||||
|
|
||||||
|
Converts a Tempo protobuf message to an OTLP protobuf message
|
@ -0,0 +1,181 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto" // NOTE: keep this, it's required to unmarshall the tempopb
|
||||||
|
"github.com/grafana/tempo/pkg/tempopb"
|
||||||
|
tempocommonv1 "github.com/grafana/tempo/pkg/tempopb/common/v1"
|
||||||
|
tempotracev1 "github.com/grafana/tempo/pkg/tempopb/trace/v1"
|
||||||
|
commonv1 "go.opentelemetry.io/proto/otlp/common/v1"
|
||||||
|
resourcev1 "go.opentelemetry.io/proto/otlp/resource/v1"
|
||||||
|
tracev1 "go.opentelemetry.io/proto/otlp/trace/v1"
|
||||||
|
newproto "google.golang.org/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
file = flag.String("file", "", "Tempo proto file. if set, will not use stdin.")
|
||||||
|
out = flag.String("out", "", "Output file destination. Defaults to stdout.")
|
||||||
|
// asJSON = flag.Bool("json", false, "Read input as jsonpb.") // TODO(wperron) implement this
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO(wperron) implement reading from stdin
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
var output io.Writer
|
||||||
|
if *out == "" {
|
||||||
|
output = os.Stdout
|
||||||
|
} else {
|
||||||
|
output = must(os.OpenFile(*out, os.O_CREATE+os.O_RDWR, 0o664))
|
||||||
|
}
|
||||||
|
|
||||||
|
h := must(os.Open(*file))
|
||||||
|
bs := must(io.ReadAll(h))
|
||||||
|
var trace tempopb.Trace
|
||||||
|
if err := proto.Unmarshal(bs, &trace); err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
td := must(convert(trace))
|
||||||
|
bs = must(newproto.Marshal(&td))
|
||||||
|
written := must(io.Copy(output, bytes.NewReader(bs)))
|
||||||
|
|
||||||
|
fmt.Fprintf(os.Stderr, "completed successfully, %d bytes written", written)
|
||||||
|
}
|
||||||
|
|
||||||
|
func convert(trace tempopb.Trace) (tracev1.TracesData, error) {
|
||||||
|
td := make([]*tracev1.ResourceSpans, 0, len(trace.Batches))
|
||||||
|
for _, resourceSpans := range trace.Batches {
|
||||||
|
rs := &tracev1.ResourceSpans{
|
||||||
|
Resource: &resourcev1.Resource{
|
||||||
|
Attributes: convertAttributes(resourceSpans.Resource.Attributes),
|
||||||
|
DroppedAttributesCount: resourceSpans.Resource.DroppedAttributesCount,
|
||||||
|
},
|
||||||
|
ScopeSpans: make([]*tracev1.ScopeSpans, 0),
|
||||||
|
}
|
||||||
|
for _, ils := range resourceSpans.InstrumentationLibrarySpans {
|
||||||
|
scope := &tracev1.ScopeSpans{
|
||||||
|
Spans: make([]*tracev1.Span, 0),
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.Scope = &commonv1.InstrumentationScope{
|
||||||
|
Name: ils.InstrumentationLibrary.Name,
|
||||||
|
Version: ils.InstrumentationLibrary.Version,
|
||||||
|
Attributes: convertAttributes(resourceSpans.Resource.Attributes), // TODO(wperron) is this right?
|
||||||
|
DroppedAttributesCount: resourceSpans.Resource.DroppedAttributesCount, // TODO(wperron) is this right?
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, span := range ils.Spans {
|
||||||
|
otelSpan := &tracev1.Span{
|
||||||
|
TraceId: span.TraceId,
|
||||||
|
SpanId: span.SpanId,
|
||||||
|
TraceState: span.TraceState,
|
||||||
|
ParentSpanId: span.ParentSpanId,
|
||||||
|
Name: span.Name,
|
||||||
|
Kind: tracev1.Span_SpanKind(span.Kind),
|
||||||
|
StartTimeUnixNano: span.StartTimeUnixNano,
|
||||||
|
EndTimeUnixNano: span.EndTimeUnixNano,
|
||||||
|
Attributes: convertAttributes(span.Attributes),
|
||||||
|
DroppedAttributesCount: span.DroppedAttributesCount,
|
||||||
|
Events: convertEvents(span.Events),
|
||||||
|
DroppedEventsCount: span.DroppedEventsCount,
|
||||||
|
Links: convertLinks(span.Links),
|
||||||
|
DroppedLinksCount: span.DroppedLinksCount,
|
||||||
|
Status: convertStatus(span.Status),
|
||||||
|
}
|
||||||
|
scope.Spans = append(scope.Spans, otelSpan)
|
||||||
|
}
|
||||||
|
|
||||||
|
rs.ScopeSpans = append(rs.ScopeSpans, scope)
|
||||||
|
}
|
||||||
|
td = append(td, rs)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tracev1.TracesData{
|
||||||
|
ResourceSpans: td,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertAttributes(attrs []*tempocommonv1.KeyValue) []*commonv1.KeyValue {
|
||||||
|
kvs := make([]*commonv1.KeyValue, 0, len(attrs))
|
||||||
|
for _, a := range attrs {
|
||||||
|
kvs = append(kvs, &commonv1.KeyValue{
|
||||||
|
Key: a.Key,
|
||||||
|
Value: convertAnyValue(a.Value),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return kvs
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertAnyValue(av *tempocommonv1.AnyValue) *commonv1.AnyValue {
|
||||||
|
inner := av.GetValue()
|
||||||
|
v := &commonv1.AnyValue{}
|
||||||
|
switch inner.(type) {
|
||||||
|
case *tempocommonv1.AnyValue_StringValue:
|
||||||
|
v.Value = &commonv1.AnyValue_StringValue{StringValue: av.GetStringValue()}
|
||||||
|
case *tempocommonv1.AnyValue_IntValue:
|
||||||
|
v.Value = &commonv1.AnyValue_IntValue{IntValue: av.GetIntValue()}
|
||||||
|
case *tempocommonv1.AnyValue_DoubleValue:
|
||||||
|
v.Value = &commonv1.AnyValue_DoubleValue{DoubleValue: av.GetDoubleValue()}
|
||||||
|
case *tempocommonv1.AnyValue_BoolValue:
|
||||||
|
v.Value = &commonv1.AnyValue_BoolValue{BoolValue: av.GetBoolValue()}
|
||||||
|
case *tempocommonv1.AnyValue_ArrayValue:
|
||||||
|
inner := av.GetArrayValue().Values
|
||||||
|
i := make([]*commonv1.AnyValue, 0, len(inner))
|
||||||
|
for _, val := range inner {
|
||||||
|
i = append(i, convertAnyValue(val))
|
||||||
|
}
|
||||||
|
v.Value = &commonv1.AnyValue_ArrayValue{ArrayValue: &commonv1.ArrayValue{Values: i}}
|
||||||
|
case *tempocommonv1.AnyValue_KvlistValue:
|
||||||
|
inner := av.GetKvlistValue().Values
|
||||||
|
v.Value = &commonv1.AnyValue_KvlistValue{KvlistValue: &commonv1.KeyValueList{Values: convertAttributes(inner)}}
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertEvents(events []*tempotracev1.Span_Event) []*tracev1.Span_Event {
|
||||||
|
ev := make([]*tracev1.Span_Event, 0, len(events))
|
||||||
|
for _, event := range events {
|
||||||
|
ev = append(ev, &tracev1.Span_Event{
|
||||||
|
TimeUnixNano: event.TimeUnixNano,
|
||||||
|
Name: event.Name,
|
||||||
|
DroppedAttributesCount: event.DroppedAttributesCount,
|
||||||
|
Attributes: convertAttributes(event.Attributes),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return ev
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertLinks(links []*tempotracev1.Span_Link) []*tracev1.Span_Link {
|
||||||
|
ls := make([]*tracev1.Span_Link, 0, len(links))
|
||||||
|
for _, link := range links {
|
||||||
|
ls = append(ls, &tracev1.Span_Link{
|
||||||
|
TraceId: link.TraceId,
|
||||||
|
SpanId: link.SpanId,
|
||||||
|
TraceState: link.TraceState,
|
||||||
|
DroppedAttributesCount: link.DroppedAttributesCount,
|
||||||
|
Attributes: convertAttributes(link.Attributes),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return ls
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertStatus(status *tempotracev1.Status) *tracev1.Status {
|
||||||
|
return &tracev1.Status{
|
||||||
|
Message: status.Message,
|
||||||
|
Code: tracev1.Status_StatusCode(status.Code),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func must[T any](v T, err error) T {
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
Loading…
Reference in new issue