-
Notifications
You must be signed in to change notification settings - Fork 4
add more helpers #4
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
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| // otelhelpers is a package of helper functions for dealing with otel | ||
| // traceparent propagation over files and environment variables. | ||
| package otelhelpers | ||
|
|
||
| import ( | ||
| "bytes" | ||
| "context" | ||
| "io/ioutil" | ||
| "os" | ||
|
|
||
| "go.opentelemetry.io/otel" | ||
| ) | ||
|
|
||
| // ContextWithEnvTraceparent is a helper that looks for the the TRACEPARENT | ||
| // environment variable and if it's set, it grabs the traceparent and | ||
| // adds it to the context it returns. When there is no envvar or it's | ||
| // empty, the original context is returned unmodified. | ||
| func ContextWithEnvTraceparent(ctx context.Context) context.Context { | ||
| traceparent := os.Getenv("TRACEPARENT") | ||
| if traceparent != "" { | ||
| return ContextWithTraceparentString(ctx, traceparent) | ||
| } | ||
| return ctx | ||
| } | ||
|
|
||
| // ContextWithLinuxCmdlineTraceparent looks in /proc/cmdline for a traceparent= | ||
| // command line option and returns the context with that value as traceparent | ||
| // if it's there. Does no validation. Returns the original context if there is | ||
| // no cmdline option or if there's an error doing the read. | ||
| // This is Linux-only but should be safe on other operating systems. | ||
| func ContextWithCmdlineTraceparent(ctx context.Context) context.Context { | ||
| tp, err := tpFromCmdline("/proc/cmdline") | ||
| if err != nil { | ||
| // what to do with error? is there a way to hit the otel error handler infra? | ||
| return ctx | ||
| } | ||
|
|
||
| return ContextWithTraceparentString(ctx, tp) | ||
| } | ||
|
|
||
| // ContextWithCmdlineOrEnvTraceparent checks the environment variable first, | ||
| // then /proc/cmdline and returns a context with them set, if available. When | ||
| // both are present, the cmdline is prioritized. When neither is present, | ||
| // the original context is returned as-is. | ||
| func ContextWithCmdlineOrEnvTraceparent(ctx context.Context) context.Context { | ||
| ctx = ContextWithEnvTraceparent(ctx) | ||
| return ContextWithCmdlineTraceparent(ctx) | ||
| } | ||
|
|
||
| // ContextWithTraceparentString takes a W3C traceparent string, uses the otel | ||
| // carrier code to get it into a context it returns ready to go. | ||
| func ContextWithTraceparentString(ctx context.Context, traceparent string) context.Context { | ||
| carrier := SimpleCarrier{} | ||
| carrier.Set("traceparent", traceparent) | ||
| prop := otel.GetTextMapPropagator() | ||
| return prop.Extract(ctx, carrier) | ||
| } | ||
|
|
||
| // TraceparentStringFromContext gets the current trace from the context and | ||
| // returns a W3C traceparent string. | ||
| func TraceparentStringFromContext(ctx context.Context) string { | ||
| carrier := SimpleCarrier{} | ||
| prop := otel.GetTextMapPropagator() | ||
| prop.Inject(ctx, carrier) | ||
| return carrier.Get("traceparent") | ||
| } | ||
|
|
||
| // tpFromCmdline reads a /proc/cmdline style file, parses it, and returns whatever | ||
| // value is present for "traceparent=". | ||
| func tpFromCmdline(file string) (string, error) { | ||
| data, err := ioutil.ReadFile(file) | ||
| if err != nil { | ||
| return "", err | ||
| } | ||
|
|
||
| if bytes.Contains(data, []byte("traceparent=")) { | ||
| kvpairs := bytes.Split(data, []byte(" ")) | ||
| for _, kv := range kvpairs { | ||
| parts := bytes.SplitN(kv, []byte("="), 2) | ||
| if string(parts[0]) == "traceparent" { | ||
| return string(parts[1]), nil | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return "", nil | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| package otelhelpers | ||
|
|
||
| import ( | ||
| "context" | ||
| "io/ioutil" | ||
| "os" | ||
| "testing" | ||
|
|
||
| "github.com/equinix-labs/otel-init-go/otelinit" | ||
| "go.opentelemetry.io/otel/trace" | ||
| ) | ||
|
|
||
| func TestMain(m *testing.M) { | ||
| // Many of the tests here won't work at all if otel is in non-recording mode | ||
| // so we set a default endpoint and let it fail in the background. If there | ||
| // happens to be a listener it will connnect but should not receive spans. | ||
| os.Setenv("OTEL_EXPORTER_OTLP_ENDPOINT", "localhost:4317") | ||
| otelinit.InitOpenTelemetry(context.Background(), "otel-init-go-helpers-test") | ||
|
|
||
| os.Exit(m.Run()) | ||
| } | ||
|
|
||
| func TestContextWithEnvTraceparent(t *testing.T) { | ||
| // make sure the environment variable isn't polluting test state | ||
| os.Unsetenv("TRACEPARENT") | ||
|
|
||
| // trace id should not change, because there's no envvar and no file | ||
| ctx := ContextWithEnvTraceparent(context.Background()) | ||
| sc := trace.SpanContextFromContext(ctx) | ||
| if sc.HasTraceID() { | ||
| t.Error("traceparent detected where there should be none") | ||
| } | ||
|
|
||
| os.Setenv("TRACEPARENT", "00-f61fc53f926e07a9c3893b1a722e1b65-7a2d6a804f3de137-01") | ||
| ctx = ContextWithEnvTraceparent(context.Background()) | ||
| sc = trace.SpanContextFromContext(ctx) | ||
| if sc.TraceID().String() != "f61fc53f926e07a9c3893b1a722e1b65" { | ||
| t.Errorf("no trace id where one is expected. got: %q", sc.TraceID().String()) | ||
| } | ||
| if sc.SpanID().String() != "7a2d6a804f3de137" { | ||
| t.Errorf("no span id where one is expected. got: %q", sc.SpanID().String()) | ||
| } | ||
| if !sc.IsSampled() { | ||
| t.Error("expected sampling to be enabled but it is not") | ||
| } | ||
| } | ||
|
|
||
| func TestTpFromCmdline(t *testing.T) { | ||
| testTp := "00-f61fc53f926e07a9c3893b1a722e1b65-7a2d6a804f3de137-01" | ||
| testCmdlines := []string{ | ||
| "traceparent=00-f61fc53f926e07a9c3893b1a722e1b65-7a2d6a804f3de137-01", | ||
| "foo=bar initrd=lol root=wheeeeeeeeeeee-fun traceparent=00-f61fc53f926e07a9c3893b1a722e1b65-7a2d6a804f3de137-01", | ||
| "traceparent=00-f61fc53f926e07a9c3893b1a722e1b65-7a2d6a804f3de137-01 foo=bar initrd=lol root=wheeeeeeeeeeee-fun", | ||
| "kimi=ga baka=desu traceparent=00-f61fc53f926e07a9c3893b1a722e1b65-7a2d6a804f3de137-01 foo=bar initrd=lol root=wheeeeeeeeeeee-fun", | ||
| } | ||
|
|
||
| for _, cmdline := range testCmdlines { | ||
| file, err := ioutil.TempFile(t.TempDir(), "go-test-otel-init-go") | ||
| if err != nil { | ||
| t.Fatalf("unable to create tempfile for testing: %s", err) | ||
| } | ||
| defer os.Remove(file.Name()) | ||
|
|
||
| // write out a cmdline file for test | ||
| file.WriteString(cmdline) | ||
| file.Close() | ||
|
|
||
| got, err := tpFromCmdline(file.Name()) | ||
| if err != nil { | ||
| t.Errorf("reading cmdline test file failed unexpectedly: %s", err) | ||
| } | ||
| if got != testTp { | ||
| t.Errorf("tpFromCmdline comparison failed, expected '%s', got '%s'", testTp, got) | ||
| } | ||
| } | ||
| } |
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| package otelhelpers | ||
|
|
||
| import ( | ||
| "context" | ||
| "testing" | ||
|
|
||
| "go.opentelemetry.io/otel" | ||
| ) | ||
|
|
||
| func TestSimpleCarrier(t *testing.T) { | ||
| carrier := SimpleCarrier{} | ||
| carrier.Clear() // clean up after other tests | ||
|
|
||
| // traceparent is the only key supported by SimpleCarrier | ||
| got := carrier.Get("traceparent") | ||
| if got != "" { | ||
| t.Errorf("got a non-empty traceparent value '%s' where empty string was expected", got) | ||
| } | ||
|
|
||
| carrier.Set("foobar", "baz") | ||
| if carrier.Get("foobar") != "baz" { | ||
| t.Error("did not get the expected value back in Set/Get test") | ||
| } | ||
|
|
||
| // traceparent is supported so this should work fine | ||
| tp := "00-b122b620341449410b9cd900c96d459d-aa21cda35388b694-01" | ||
| carrier.Set("traceparent", tp) | ||
|
|
||
| // we've set 2 keys so far, and both should get returned | ||
| keys := carrier.Keys() | ||
| if len(keys) != 2 { | ||
| t.Errorf("expected exactly 2 keys from Keys() but instead got %q", keys) | ||
| } | ||
|
|
||
| // make sure the value round-trips in one piece | ||
| got = carrier.Get("traceparent") | ||
| if got != tp { | ||
| t.Errorf("expected traceparent value '%s' but got '%s'", tp, got) | ||
| } | ||
|
|
||
| // it's impractical to test the internal state of otel-go, so the next best | ||
| // thing is to round-trip our traceparent through it and make sure it comes | ||
| // back as expected | ||
| prop := otel.GetTextMapPropagator() | ||
| ctx := prop.Extract(context.Background(), carrier) | ||
| if ctx == nil { | ||
| t.Errorf("expected a context but got nil, likely a problem in otel? this shouldn't happen...") | ||
| } | ||
|
|
||
| // try to round trip the traceparent back out of that context ^^ | ||
| rtCarrier := SimpleCarrier{} | ||
| prop.Inject(ctx, rtCarrier) | ||
| got = carrier.Get("traceparent") | ||
| if got != tp { | ||
| t.Errorf("round-tripping traceparent through a context failed, expected '%s', got '%s'", tp, got) | ||
| } | ||
|
|
||
| carrier.Clear() // clean up for other tests | ||
| } |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.