Skip to content

Commit

Permalink
feat: add span status code/description cli and env var (#111)
Browse files Browse the repository at this point in the history
* feat: add span status code and span status description cli and env var support

* chore: remove shorthand syntax bc it causes conflicts
  • Loading branch information
ericmustin authored Oct 5, 2022
1 parent ff5a4eb commit e48e468
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 22 deletions.
34 changes: 18 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,22 +96,24 @@ otel-cli server json --dir $dir --timeout 60 --max-spans 5
Everything is configurable via CLI arguments and environment variables. If no endpoint
is specified, otel-cli will run in non-recording mode and not attempt to contact any servers.

| CLI argument | environment variable | config file key | example value |
| --------------- | ----------------------------- | --------------- | -------------- |
| --endpoint | OTEL_EXPORTER_OTLP_ENDPOINT | endpoint | localhost:4317 |
| --insecure | OTEL_EXPORTER_OTLP_INSECURE | insecure | false |
| --timeout | OTEL_EXPORTER_OTLP_TIMEOUT | timeout | 1s |
| --otlp-headers | OTEL_EXPORTER_OTLP_HEADERS | otlp-headers | k=v,a=b |
| --otlp-blocking | OTEL_EXPORTER_OTLP_BLOCKING | otlp-blocking | false |
| --service | OTEL_CLI_SERVICE_NAME | service | myapp |
| --kind | OTEL_CLI_TRACE_KIND | kind | server |
| --attrs | OTEL_CLI_ATTRIBUTES | attrs | k=v,a=b |
| --tp-required | OTEL_CLI_TRACEPARENT_REQUIRED | tp-required | false |
| --tp-carrier | OTEL_CLI_CARRIER_FILE | tp-carrier | filename.txt |
| --tp-ignore-env | OTEL_CLI_IGNORE_ENV | tp-ignore-env | false |
| --tp-print | OTEL_CLI_PRINT_TRACEPARENT | tp-print | false |
| --tp-export | OTEL_CLI_EXPORT_TRACEPARENT | tp-export | false |
| --no-tls-verify | OTEL_CLI_NO_TLS_VERIFY | no-tls-verify | false |
| CLI argument | environment variable | config file key | example value |
| -------------------- | ----------------------------- | ------------------ | -------------- |
| --endpoint | OTEL_EXPORTER_OTLP_ENDPOINT | endpoint | localhost:4317 |
| --insecure | OTEL_EXPORTER_OTLP_INSECURE | insecure | false |
| --timeout | OTEL_EXPORTER_OTLP_TIMEOUT | timeout | 1s |
| --otlp-headers | OTEL_EXPORTER_OTLP_HEADERS | otlp-headers | k=v,a=b |
| --otlp-blocking | OTEL_EXPORTER_OTLP_BLOCKING | otlp-blocking | false |
| --service | OTEL_CLI_SERVICE_NAME | service | myapp |
| --kind | OTEL_CLI_TRACE_KIND | kind | server |
| --status-code | OTEL_CLI_STATUS_CODE | status-code | error |
| --status-description | OTEL_CLI_STATUS_DESCRIPTION | status-description | cancelled |
| --attrs | OTEL_CLI_ATTRIBUTES | attrs | k=v,a=b |
| --tp-required | OTEL_CLI_TRACEPARENT_REQUIRED | tp-required | false |
| --tp-carrier | OTEL_CLI_CARRIER_FILE | tp-carrier | filename.txt |
| --tp-ignore-env | OTEL_CLI_IGNORE_ENV | tp-ignore-env | false |
| --tp-print | OTEL_CLI_PRINT_TRACEPARENT | tp-print | false |
| --tp-export | OTEL_CLI_EXPORT_TRACEPARENT | tp-export | false |
| --no-tls-verify | OTEL_CLI_NO_TLS_VERIFY | no-tls-verify | false |

[Valid timeout units](https://pkg.go.dev/time#ParseDuration) are "ns", "us"/"µs", "ms", "s", "m", "h".

Expand Down
26 changes: 22 additions & 4 deletions otelcli/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ func DefaultConfig() Config {
CfgFile: "",
Verbose: false,
Fail: false,
StatusCode: "unset",
StatusDescription: "",
}
}

Expand All @@ -57,10 +59,12 @@ type Config struct {
Blocking bool `json:"blocking"`
NoTlsVerify bool `json:"no_tls_verify"`

ServiceName string `json:"service_name"`
SpanName string `json:"span_name"`
Kind string `json:"span_kind"`
Attributes map[string]string `json:"span_attributes"`
ServiceName string `json:"service_name"`
SpanName string `json:"span_name"`
Kind string `json:"span_kind"`
Attributes map[string]string `json:"span_attributes"`
StatusCode string `json:"span_status_code"`
StatusDescription string `json:"span_status_description"`

TraceparentCarrierFile string `json:"traceparent_carrier_file"`
TraceparentIgnoreEnv bool `json:"traceparent_ignore_env"`
Expand Down Expand Up @@ -109,6 +113,8 @@ func (c Config) ToStringMap() map[string]string {
"span_name": c.SpanName,
"span_kind": c.Kind,
"span_attributes": flattenStringMap(c.Attributes, "{}"),
"span_status_code": c.StatusCode,
"span_status_description": c.StatusDescription,
"traceparent_carrier_file": c.TraceparentCarrierFile,
"traceparent_ignore_env": strconv.FormatBool(c.TraceparentIgnoreEnv),
"traceparent_print": strconv.FormatBool(c.TraceparentPrint),
Expand Down Expand Up @@ -186,6 +192,18 @@ func (c Config) WithAttributes(with map[string]string) Config {
return c
}

// WithStatusCode returns the config with StatusCode set to the provided value.
func (c Config) WithStatusCode(with string) Config {
c.StatusCode = with
return c
}

// WithStatusDescription returns the config with StatusDescription set to the provided value.
func (c Config) WithStatusDescription(with string) Config {
c.StatusDescription = with
return c
}

// WithTraceparentCarrierFile returns the config with TraceparentCarrierFile set to the provided value.
func (c Config) WithTraceparentCarrierFile(with string) Config {
c.TraceparentCarrierFile = with
Expand Down
21 changes: 21 additions & 0 deletions otelcli/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,27 @@ func TestWithAttributes(t *testing.T) {
t.Errorf("Attributes did not match (-want +got):\n%s", diff)
}
}

func TestWithStatusCode(t *testing.T) {
if DefaultConfig().WithStatusCode("unset").StatusCode != "unset" {
t.Fail()
}

if DefaultConfig().WithStatusCode("ok").StatusCode != "ok" {
t.Fail()
}

if DefaultConfig().WithStatusCode("error").StatusCode != "ok" {
t.Fail()
}
}

func TestWithStatusDescription(t *testing.T) {
if DefaultConfig().WithStatusDescription("Set SCE To AUX").StatusCode != "Set SCE To AUX" {
t.Fail()
}
}

func TestWithTraceparentCarrierFile(t *testing.T) {
if DefaultConfig().WithTraceparentCarrierFile("foobar").TraceparentCarrierFile != "foobar" {
t.Fail()
Expand Down
17 changes: 17 additions & 0 deletions otelcli/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"time"

"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/trace"
)

Expand Down Expand Up @@ -131,6 +132,22 @@ func otelSpanKind(kind string) trace.SpanKind {
}
}

// otelSpanStatus takes a supported string span status and returns the otel
// constant for it. Returns default of Unset on no match.
// TODO: figure out the best way to report invalid values
func otelSpanStatus(status string) codes.Code {
switch status {
case "unset":
return codes.Unset
case "ok":
return codes.Ok
case "error":
return codes.Error
default:
return codes.Unset
}
}

// propagateOtelCliSpan saves the traceparent to file if necessary, then prints
// span info to the console according to command-line args.
func propagateOtelCliSpan(ctx context.Context, span trace.Span, target io.Writer) {
Expand Down
33 changes: 33 additions & 0 deletions otelcli/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"time"

"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/trace"
)

Expand Down Expand Up @@ -164,6 +165,38 @@ func TestOtelSpanKind(t *testing.T) {
}
}

func TestOtelSpanStatus(t *testing.T) {

for _, testcase := range []struct {
name string
want codes.Code
}{
{
name: "unset",
want: codes.Unset,
},
{
name: "ok",
want: codes.Ok,
},
{
name: "error",
want: codes.Error,
},
{
name: "cromulent",
want: codes.Unset,
},
} {
t.Run(testcase.name, func(t *testing.T) {
out := otelSpanStatus(testcase.name)
if out != testcase.want {
t.Errorf("otelSpanStatus returned the wrong value, '%q', for '%s'", out, testcase.name)
}
})
}
}

func TestPropagateOtelCliSpan(t *testing.T) {
// TODO: should this noop the tracing backend?

Expand Down
10 changes: 8 additions & 2 deletions otelcli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,15 @@ func addSpanParams(cmd *cobra.Command) {
cmd.Flags().StringVarP(&config.ServiceName, "service", "n", defaults.ServiceName, "set the name of the application sent on the traces")
// --kind / -k
cmd.Flags().StringVarP(&config.Kind, "kind", "k", defaults.Kind, "set the trace kind, e.g. internal, server, client, producer, consumer")
// --status-code / -sc
cmd.Flags().StringVar(&config.StatusCode, "status-code", defaults.StatusCode, "set the span status code, e.g. unset|ok|error")
// --status-description / -sd
cmd.Flags().StringVar(&config.StatusDescription, "status-description", defaults.StatusDescription, "set the span status description when a span status code of error is set, e.g. 'cancelled'")
var span_env_flags = map[string]string{
"service": "OTEL_CLI_SERVICE_NAME",
"kind": "OTEL_CLI_TRACE_KIND",
"service": "OTEL_CLI_SERVICE_NAME",
"kind": "OTEL_CLI_TRACE_KIND",
"status-code": "OTEL_CLI_STATUS_CODE",
"status-description": "OTEL_CLI_STATUS_DESCRIPTION",
}
for config_key, env_value := range span_env_flags {
viper.BindPFlag(config_key, cmd.Flags().Lookup(config_key))
Expand Down
11 changes: 11 additions & 0 deletions otelcli/span.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/spf13/cobra"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/trace"
)

Expand Down Expand Up @@ -74,6 +75,16 @@ func startSpan() (context.Context, trace.Span, func()) {
ctx, span := tracer.Start(ctx, config.SpanName, startOpts...)
span.SetAttributes(cliAttrsToOtel(config.Attributes)...) // applies CLI attributes to the span

spanStatus := otelSpanStatus(config.StatusCode)

// Only set status description when an error status.
// https://github.com/open-telemetry/opentelemetry-specification/blob/480a19d702470563d32a870932be5ddae798079c/specification/trace/api.md#set-status
if spanStatus == codes.Error {
span.SetStatus(spanStatus, config.StatusDescription)
} else {
span.SetStatus(spanStatus, "")
}

return ctx, span, shutdown
}

Expand Down

0 comments on commit e48e468

Please sign in to comment.