Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TraceQL] Add Support for Kind #2217

Merged
merged 10 commits into from
Mar 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
docs/
example/
integration/
operations/
opentelemetry-proto/
tools/
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* [FEATURE] Add support for Azure Workload Identity authentication [#2195](https://github.com/grafana/tempo/pull/2195) (@LambArchie)
* [FEATURE] Add flag to check configuration [#2131](https://github.com/grafana/tempo/issues/2131) (@robertscherbarth @agrib-01)
* [FEATURE] Add flag to optionally enable all available Go runtime metrics [#2005](https://github.com/grafana/tempo/pull/2005) (@andreasgerstmayr)
* [FEATURE] Add support for span `kind` to TraceQL [#2217](https://github.com/grafana/tempo/pull/2217) (@joe-elliott)
* [CHANGE] Add support for s3 session token in static config [#2093](https://github.com/grafana/tempo/pull/2093) (@farodin91)
* [CHANGE] **Breaking Change** Remove support for search on v2 blocks. [#2159](https://github.com/grafana/tempo/pull/2062) (@joe-elliott)
Removed config options:
Expand Down
12 changes: 6 additions & 6 deletions docs/sources/tempo/traceql/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,12 @@ Intrinsic fields are fundamental to spans. These fields can be referenced when s

The following table shows the current intrinsic fields:


| **Operation** | **Type** | **Definition** | **Example** |
|---------------|----------|---------------------------------------|------------------------|
| status | string | status values are error, ok, or unset | { status = ok } |
| duration | duration | end - start time of the span | { duration > 100ms } |
| name | string | operation or span name | { name = "HTTP POST" } |
| **Field** | **Type** | **Definition** | **Example** |
|---------------|-------------|-----------------------------------------------------------------|------------------------|
| status | status enum | status: error, ok, or unset | { status = ok } |
| duration | duration | end - start time of the span | { duration > 100ms } |
| name | string | operation or span name | { name = "HTTP POST" } |
| kind | kind enum | kind: server, client, producer, consumer, internal, unspecified | { kind = server } |

### Attribute fields

Expand Down
19 changes: 19 additions & 0 deletions pkg/model/trace/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,22 @@ func StatusToString(s v1trace.Status_StatusCode) string {
}
return status
}

func KindToString(k v1trace.Span_SpanKind) string {
var kind string
switch k {
case v1trace.Span_SPAN_KIND_UNSPECIFIED:
kind = "unspecified"
case v1trace.Span_SPAN_KIND_INTERNAL:
kind = "internal"
case v1trace.Span_SPAN_KIND_SERVER:
kind = "server"
case v1trace.Span_SPAN_KIND_CLIENT:
kind = "client"
case v1trace.Span_SPAN_KIND_PRODUCER:
kind = "producer"
case v1trace.Span_SPAN_KIND_CONSUMER:
kind = "consumer"
}
return kind
}
9 changes: 9 additions & 0 deletions pkg/search/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,15 @@ func GetVirtualTagValuesV2(tagName string) []tempopb.TagValue {
{Type: "keyword", Value: traceql.StatusError.String()},
{Type: "keyword", Value: traceql.StatusUnset.String()},
}
case traceql.IntrinsicKind.String():
return []tempopb.TagValue{
{Type: "keyword", Value: traceql.KindClient.String()},
{Type: "keyword", Value: traceql.KindServer.String()},
{Type: "keyword", Value: traceql.KindProducer.String()},
{Type: "keyword", Value: traceql.KindConsumer.String()},
{Type: "keyword", Value: traceql.KindInternal.String()},
{Type: "keyword", Value: traceql.KindUnspecified.String()},
}
}

return nil
Expand Down
12 changes: 11 additions & 1 deletion pkg/traceql/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,8 @@ type Static struct {
S string
B bool
D time.Duration
Status Status
Status Status // todo: can we just use the N member for status and kind?
Kind Kind
}

// nolint: revive
Expand Down Expand Up @@ -503,6 +504,13 @@ func NewStaticStatus(s Status) Static {
}
}

func NewStaticKind(k Kind) Static {
return Static{
Type: TypeKind,
Kind: k,
}
}

// **********************
// Attributes
// **********************
Expand Down Expand Up @@ -537,6 +545,8 @@ func (a Attribute) impliedType() StaticType {
return TypeString
case IntrinsicStatus:
return TypeStatus
case IntrinsicKind:
return TypeKind
case IntrinsicParent:
return TypeNil
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/traceql/ast_stringer.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ func (n Static) EncodeToString(quotes bool) string {
return n.D.String()
case TypeStatus:
return n.Status.String()
case TypeKind:
return n.Kind.String()
}

return fmt.Sprintf("static(%d)", n.Type)
Expand Down
2 changes: 2 additions & 0 deletions pkg/traceql/ast_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ func TestStatic_Equals(t *testing.T) {
{NewStaticBool(true), NewStaticBool(true)},
{NewStaticDuration(1 * time.Second), NewStaticDuration(1000 * time.Millisecond)},
{NewStaticStatus(StatusOk), NewStaticStatus(StatusOk)},
{NewStaticKind(KindClient), NewStaticKind(KindClient)},
{NewStaticDuration(0), NewStaticInt(0)},
// Status and int comparison
{NewStaticStatus(StatusError), NewStaticInt(0)},
Expand All @@ -32,6 +33,7 @@ func TestStatic_Equals(t *testing.T) {
{NewStaticInt(1), NewStaticInt(2)},
{NewStaticBool(true), NewStaticInt(1)},
{NewStaticString("foo"), NewStaticString("bar")},
{NewStaticKind(KindClient), NewStaticKind(KindConsumer)},
{NewStaticStatus(StatusError), NewStaticStatus(StatusOk)},
{NewStaticStatus(StatusOk), NewStaticInt(0)},
{NewStaticStatus(StatusError), NewStaticFloat(0)},
Expand Down
6 changes: 6 additions & 0 deletions pkg/traceql/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,12 @@ func (s Static) asAnyValue() *common_v1.AnyValue {
StringValue: "nil",
},
}
case TypeKind:
return &common_v1.AnyValue{
Value: &common_v1.AnyValue_StringValue{
StringValue: s.Kind.String(),
},
}
}

return &common_v1.AnyValue{
Expand Down
10 changes: 10 additions & 0 deletions pkg/traceql/engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ func TestEngine_asTraceSearchMetadata(t *testing.T) {
attributes: map[Attribute]Static{
NewIntrinsic(IntrinsicName): NewStaticString("HTTP GET"),
NewIntrinsic(IntrinsicStatus): NewStaticStatus(StatusOk),
NewIntrinsic(IntrinsicKind): NewStaticKind(KindClient),
NewAttribute("cluster"): NewStaticString("prod"),
NewAttribute("count"): NewStaticInt(5),
NewAttribute("count_but_float"): NewStaticFloat(5.0),
Expand Down Expand Up @@ -225,6 +226,14 @@ func TestEngine_asTraceSearchMetadata(t *testing.T) {
},
},
},
{
Key: "kind",
Value: &v1.AnyValue{
Value: &v1.AnyValue_StringValue{
StringValue: KindClient.String(),
},
},
},
{
Key: "status",
Value: &v1.AnyValue{
Expand Down Expand Up @@ -319,6 +328,7 @@ func TestStatic_AsAnyValue(t *testing.T) {
{NewStaticBool(true), &v1.AnyValue{Value: &v1.AnyValue_BoolValue{BoolValue: true}}},
{NewStaticDuration(5 * time.Second), &v1.AnyValue{Value: &v1.AnyValue_StringValue{StringValue: "5s"}}},
{NewStaticStatus(StatusOk), &v1.AnyValue{Value: &v1.AnyValue_StringValue{StringValue: "ok"}}},
{NewStaticKind(KindInternal), &v1.AnyValue{Value: &v1.AnyValue_StringValue{StringValue: "internal"}}},
{NewStaticNil(), &v1.AnyValue{Value: &v1.AnyValue_StringValue{StringValue: "nil"}}},
}
for _, tc := range tt {
Expand Down
5 changes: 5 additions & 0 deletions pkg/traceql/enum_attributes.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const (
IntrinsicChildCount
IntrinsicName
IntrinsicStatus
IntrinsicKind
IntrinsicParent
)

Expand All @@ -44,6 +45,8 @@ func (i Intrinsic) String() string {
return "name"
case IntrinsicStatus:
return "status"
case IntrinsicKind:
return "kind"
case IntrinsicChildCount:
return "childCount"
case IntrinsicParent:
Expand All @@ -62,6 +65,8 @@ func intrinsicFromString(s string) Intrinsic {
return IntrinsicName
case "status":
return IntrinsicStatus
case "kind":
return IntrinsicKind
case "childCount":
return IntrinsicChildCount
case "parent":
Expand Down
2 changes: 2 additions & 0 deletions pkg/traceql/enum_operators.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ func binaryTypeValid(op Operator, t StaticType) bool {
fallthrough
case TypeStatus:
return op == OpEqual || op == OpNotEqual
case TypeKind:
return op == OpEqual || op == OpNotEqual
}

return false
Expand Down
5 changes: 5 additions & 0 deletions pkg/traceql/enum_operators_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ func TestOperatorBinaryTypesValid(t *testing.T) {
{OpMod, TypeStatus, false},
{OpMult, TypeNil, false},
{OpPower, TypeBoolean, false},
{OpMult, TypeKind, false},
// equality
{OpEqual, TypeDuration, true},
{OpNotEqual, TypeStatus, true},
Expand All @@ -71,6 +72,8 @@ func TestOperatorBinaryTypesValid(t *testing.T) {
{OpNotEqual, TypeAttribute, true},
{OpEqual, TypeBoolean, true},
{OpNotEqual, TypeFloat, true},
{OpEqual, TypeKind, true},
{OpNotEqual, TypeKind, true},

{OpEqual, TypeSpanset, false},
// range comparison
Expand All @@ -80,6 +83,7 @@ func TestOperatorBinaryTypesValid(t *testing.T) {
{OpLessEqual, TypeDuration, true},

{OpGreater, TypeStatus, false},
{OpLessEqual, TypeKind, false},
{OpGreaterEqual, TypeNil, false},
{OpLess, TypeString, false},
{OpLessEqual, TypeBoolean, false},
Expand All @@ -88,6 +92,7 @@ func TestOperatorBinaryTypesValid(t *testing.T) {
{OpNotRegex, TypeAttribute, true},
{OpRegex, TypeString, true},

{OpRegex, TypeKind, false},
{OpRegex, TypeInt, false},
{OpNotRegex, TypeInt, false},
// boolean
Expand Down
31 changes: 31 additions & 0 deletions pkg/traceql/enum_statics.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const (
TypeBoolean
TypeDuration
TypeStatus
TypeKind
)

// isMatchingOperand returns whether two types can be combined with a binary operator. the kind of operator is
Expand Down Expand Up @@ -60,3 +61,33 @@ func (s Status) String() string {

return fmt.Sprintf("status(%d)", s)
}

type Kind int

const (
KindUnspecified Kind = iota
KindInternal
KindClient
KindServer
KindProducer
KindConsumer
)

func (k Kind) String() string {
switch k {
case KindUnspecified:
return "unspecified"
case KindInternal:
return "internal"
case KindClient:
return "client"
case KindServer:
return "server"
case KindProducer:
return "producer"
case KindConsumer:
return "consumer"
}

return fmt.Sprintf("kind(%d)", k)
}
30 changes: 19 additions & 11 deletions pkg/traceql/expr.y
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ import (
%token <staticDuration> DURATION
%token <val> DOT OPEN_BRACE CLOSE_BRACE OPEN_PARENS CLOSE_PARENS
NIL TRUE FALSE STATUS_ERROR STATUS_OK STATUS_UNSET
IDURATION CHILDCOUNT NAME STATUS PARENT
KIND_UNSPECIFIED KIND_INTERNAL KIND_SERVER KIND_CLIENT KIND_PRODUCER KIND_CONSUMER
IDURATION CHILDCOUNT NAME STATUS PARENT KIND
PARENT_DOT RESOURCE_DOT SPAN_DOT
COUNT AVG MAX MIN SUM
BY COALESCE
Expand Down Expand Up @@ -236,23 +237,30 @@ fieldExpression:
// Statics
// **********************
static:
STRING { $$ = NewStaticString($1) }
| INTEGER { $$ = NewStaticInt($1) }
| FLOAT { $$ = NewStaticFloat($1) }
| TRUE { $$ = NewStaticBool(true) }
| FALSE { $$ = NewStaticBool(false) }
| NIL { $$ = NewStaticNil() }
| DURATION { $$ = NewStaticDuration($1) }
| STATUS_OK { $$ = NewStaticStatus(StatusOk) }
| STATUS_ERROR { $$ = NewStaticStatus(StatusError) }
| STATUS_UNSET { $$ = NewStaticStatus(StatusUnset) }
STRING { $$ = NewStaticString($1) }
| INTEGER { $$ = NewStaticInt($1) }
| FLOAT { $$ = NewStaticFloat($1) }
| TRUE { $$ = NewStaticBool(true) }
| FALSE { $$ = NewStaticBool(false) }
| NIL { $$ = NewStaticNil() }
| DURATION { $$ = NewStaticDuration($1) }
| STATUS_OK { $$ = NewStaticStatus(StatusOk) }
| STATUS_ERROR { $$ = NewStaticStatus(StatusError) }
| STATUS_UNSET { $$ = NewStaticStatus(StatusUnset) }
| KIND_UNSPECIFIED { $$ = NewStaticKind(KindUnspecified)}
| KIND_INTERNAL { $$ = NewStaticKind(KindInternal) }
| KIND_SERVER { $$ = NewStaticKind(KindServer) }
| KIND_CLIENT { $$ = NewStaticKind(KindClient) }
| KIND_PRODUCER { $$ = NewStaticKind(KindProducer) }
| KIND_CONSUMER { $$ = NewStaticKind(KindConsumer) }
;

intrinsicField:
IDURATION { $$ = NewIntrinsic(IntrinsicDuration) }
| CHILDCOUNT { $$ = NewIntrinsic(IntrinsicChildCount) }
| NAME { $$ = NewIntrinsic(IntrinsicName) }
| STATUS { $$ = NewIntrinsic(IntrinsicStatus) }
| KIND { $$ = NewIntrinsic(IntrinsicKind) }
| PARENT { $$ = NewIntrinsic(IntrinsicParent) }
;

Expand Down
Loading