add history trace command

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
Tonis Tiigi
2025-01-09 16:57:01 -08:00
parent ba2d3692a6
commit cfeea34b2d
19 changed files with 33406 additions and 0 deletions

View File

@@ -0,0 +1,224 @@
package jaeger
import (
"encoding/json"
"time"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/sdk/resource"
tracesdk "go.opentelemetry.io/otel/sdk/trace"
"go.opentelemetry.io/otel/trace"
)
const (
keyInstrumentationLibraryName = "otel.library.name"
keyInstrumentationLibraryVersion = "otel.library.version"
keyError = "error"
keySpanKind = "span.kind"
keyStatusCode = "otel.status_code"
keyStatusMessage = "otel.status_description"
keyDroppedAttributeCount = "otel.event.dropped_attributes_count"
keyEventName = "event"
)
func ResourceToProcess(res *resource.Resource, defaultServiceName string) Process {
var process Process
var serviceName attribute.KeyValue
if res != nil {
for iter := res.Iter(); iter.Next(); {
if iter.Attribute().Key == attribute.Key("service.name") {
serviceName = iter.Attribute()
// Don't convert service.name into tag.
continue
}
if tag := keyValueToJaegerTag(iter.Attribute()); tag != nil {
process.Tags = append(process.Tags, *tag)
}
}
}
// If no service.name is contained in a Span's Resource,
// that field MUST be populated from the default Resource.
if serviceName.Value.AsString() == "" {
serviceName = attribute.Key("service.version").String(defaultServiceName)
}
process.ServiceName = serviceName.Value.AsString()
return process
}
func ConvertSpan(ss tracesdk.ReadOnlySpan) Span {
attr := ss.Attributes()
tags := make([]KeyValue, 0, len(attr))
for _, kv := range attr {
tag := keyValueToJaegerTag(kv)
if tag != nil {
tags = append(tags, *tag)
}
}
if is := ss.InstrumentationScope(); is.Name != "" {
tags = append(tags, getStringTag(keyInstrumentationLibraryName, is.Name))
if is.Version != "" {
tags = append(tags, getStringTag(keyInstrumentationLibraryVersion, is.Version))
}
}
if ss.SpanKind() != trace.SpanKindInternal {
tags = append(tags,
getStringTag(keySpanKind, ss.SpanKind().String()),
)
}
if ss.Status().Code != codes.Unset {
switch ss.Status().Code {
case codes.Ok:
tags = append(tags, getStringTag(keyStatusCode, "OK"))
case codes.Error:
tags = append(tags, getBoolTag(keyError, true))
tags = append(tags, getStringTag(keyStatusCode, "ERROR"))
}
if ss.Status().Description != "" {
tags = append(tags, getStringTag(keyStatusMessage, ss.Status().Description))
}
}
var logs []Log
for _, a := range ss.Events() {
nTags := len(a.Attributes)
if a.Name != "" {
nTags++
}
if a.DroppedAttributeCount != 0 {
nTags++
}
fields := make([]KeyValue, 0, nTags)
if a.Name != "" {
// If an event contains an attribute with the same key, it needs
// to be given precedence and overwrite this.
fields = append(fields, getStringTag(keyEventName, a.Name))
}
for _, kv := range a.Attributes {
tag := keyValueToJaegerTag(kv)
if tag != nil {
fields = append(fields, *tag)
}
}
if a.DroppedAttributeCount != 0 {
fields = append(fields, getInt64Tag(keyDroppedAttributeCount, int64(a.DroppedAttributeCount)))
}
logs = append(logs, Log{
Timestamp: timeAsEpochMicroseconds(a.Time),
Fields: fields,
})
}
var refs []Reference
for _, link := range ss.Links() {
refs = append(refs, Reference{
RefType: FollowsFrom,
TraceID: TraceID(link.SpanContext.TraceID().String()),
SpanID: SpanID(link.SpanContext.SpanID().String()),
})
}
refs = append(refs, Reference{
RefType: ChildOf,
TraceID: TraceID(ss.Parent().TraceID().String()),
SpanID: SpanID(ss.Parent().SpanID().String()),
})
return Span{
TraceID: TraceID(ss.SpanContext().TraceID().String()),
SpanID: SpanID(ss.SpanContext().SpanID().String()),
Flags: uint32(ss.SpanContext().TraceFlags()),
OperationName: ss.Name(),
References: refs,
StartTime: timeAsEpochMicroseconds(ss.StartTime()),
Duration: durationAsMicroseconds(ss.EndTime().Sub(ss.StartTime())),
Tags: tags,
Logs: logs,
}
}
func keyValueToJaegerTag(keyValue attribute.KeyValue) *KeyValue {
var tag *KeyValue
switch keyValue.Value.Type() {
case attribute.STRING:
s := keyValue.Value.AsString()
tag = &KeyValue{
Key: string(keyValue.Key),
Type: StringType,
Value: s,
}
case attribute.BOOL:
b := keyValue.Value.AsBool()
tag = &KeyValue{
Key: string(keyValue.Key),
Type: BoolType,
Value: b,
}
case attribute.INT64:
i := keyValue.Value.AsInt64()
tag = &KeyValue{
Key: string(keyValue.Key),
Type: Int64Type,
Value: i,
}
case attribute.FLOAT64:
f := keyValue.Value.AsFloat64()
tag = &KeyValue{
Key: string(keyValue.Key),
Type: Float64Type,
Value: f,
}
case attribute.BOOLSLICE,
attribute.INT64SLICE,
attribute.FLOAT64SLICE,
attribute.STRINGSLICE:
data, _ := json.Marshal(keyValue.Value.AsInterface())
a := (string)(data)
tag = &KeyValue{
Key: string(keyValue.Key),
Type: StringType,
Value: a,
}
}
return tag
}
func getInt64Tag(k string, i int64) KeyValue {
return KeyValue{
Key: k,
Type: Int64Type,
Value: i,
}
}
func getStringTag(k, s string) KeyValue {
return KeyValue{
Key: k,
Type: StringType,
Value: s,
}
}
func getBoolTag(k string, b bool) KeyValue {
return KeyValue{
Key: k,
Type: BoolType,
Value: b,
}
}
// timeAsEpochMicroseconds converts time.Time to microseconds since epoch,
// which is the format the StartTime field is stored in the Span.
func timeAsEpochMicroseconds(t time.Time) uint64 {
return uint64(t.UnixNano() / 1000)
}
// durationAsMicroseconds converts time.Duration to microseconds,
// which is the format the Duration field is stored in the Span.
func durationAsMicroseconds(d time.Duration) uint64 {
return uint64(d.Nanoseconds() / 1000)
}

View File

@@ -0,0 +1,102 @@
package jaeger
// ReferenceType is the reference type of one span to another
type ReferenceType string
// TraceID is the shared trace ID of all spans in the trace.
type TraceID string
// SpanID is the id of a span
type SpanID string
// ProcessID is a hashed value of the Process struct that is unique within the trace.
type ProcessID string
// ValueType is the type of a value stored in KeyValue struct.
type ValueType string
const (
// ChildOf means a span is the child of another span
ChildOf ReferenceType = "CHILD_OF"
// FollowsFrom means a span follows from another span
FollowsFrom ReferenceType = "FOLLOWS_FROM"
// StringType indicates a string value stored in KeyValue
StringType ValueType = "string"
// BoolType indicates a Boolean value stored in KeyValue
BoolType ValueType = "bool"
// Int64Type indicates a 64bit signed integer value stored in KeyValue
Int64Type ValueType = "int64"
// Float64Type indicates a 64bit float value stored in KeyValue
Float64Type ValueType = "float64"
// BinaryType indicates an arbitrary byte array stored in KeyValue
BinaryType ValueType = "binary"
)
// Trace is a list of spans
type Trace struct {
TraceID TraceID `json:"traceID"`
Spans []Span `json:"spans"`
Processes map[ProcessID]Process `json:"processes"`
Warnings []string `json:"warnings"`
}
// Span is a span denoting a piece of work in some infrastructure
// When converting to UI model, ParentSpanID and Process should be dereferenced into
// References and ProcessID, respectively.
// When converting to ES model, ProcessID and Warnings should be omitted. Even if
// included, ES with dynamic settings off will automatically ignore unneeded fields.
type Span struct {
TraceID TraceID `json:"traceID"`
SpanID SpanID `json:"spanID"`
ParentSpanID SpanID `json:"parentSpanID,omitempty"` // deprecated
Flags uint32 `json:"flags,omitempty"`
OperationName string `json:"operationName"`
References []Reference `json:"references"`
StartTime uint64 `json:"startTime"` // microseconds since Unix epoch
Duration uint64 `json:"duration"` // microseconds
Tags []KeyValue `json:"tags"`
Logs []Log `json:"logs"`
ProcessID ProcessID `json:"processID,omitempty"`
Process *Process `json:"process,omitempty"`
Warnings []string `json:"warnings"`
}
// Reference is a reference from one span to another
type Reference struct {
RefType ReferenceType `json:"refType"`
TraceID TraceID `json:"traceID"`
SpanID SpanID `json:"spanID"`
}
// Process is the process emitting a set of spans
type Process struct {
ServiceName string `json:"serviceName"`
Tags []KeyValue `json:"tags"`
}
// Log is a log emitted in a span
type Log struct {
Timestamp uint64 `json:"timestamp"`
Fields []KeyValue `json:"fields"`
}
// KeyValue is a key-value pair with typed value.
type KeyValue struct {
Key string `json:"key"`
Type ValueType `json:"type,omitempty"`
Value interface{} `json:"value"`
}
// DependencyLink shows dependencies between services
type DependencyLink struct {
Parent string `json:"parent"`
Child string `json:"child"`
CallCount uint64 `json:"callCount"`
}
// Operation defines the data in the operation response when query operation by service and span kind
type Operation struct {
Name string `json:"name"`
SpanKind string `json:"spanKind"`
}