-
Notifications
You must be signed in to change notification settings - Fork 295
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
implemented JSON distributed trace converter #335
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,7 @@ import ( | |
"errors" | ||
"fmt" | ||
"net/http" | ||
"reflect" | ||
"strings" | ||
"testing" | ||
|
||
|
@@ -46,6 +47,47 @@ type TraceContextTestCase struct { | |
} `json:"intrinsics"` | ||
} | ||
|
||
func TestJsonDTHeaders(t *testing.T) { | ||
type testcase struct { | ||
in string | ||
out http.Header | ||
err bool | ||
} | ||
|
||
for i, test := range []testcase{ | ||
{"", http.Header{}, false}, | ||
{"{}", http.Header{}, false}, | ||
{" invalid ", http.Header{}, true}, | ||
{`"foo"`, http.Header{}, true}, | ||
{`{"foo": "bar"}`, map[string][]string{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. http.Header is more correct to use, even though they're (currently) equivalent. |
||
"Foo": {"bar"}, | ||
}, false}, | ||
{`{ | ||
"foo": "bar", | ||
"baz": "quux", | ||
"multiple": [ | ||
"alpha", | ||
"beta", | ||
"gamma" | ||
] | ||
}`, map[string][]string{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here with |
||
"Foo": {"bar"}, | ||
"Baz": {"quux"}, | ||
"Multiple": {"alpha", "beta", "gamma"}, | ||
}, false}, | ||
} { | ||
h, err := DistributedTraceHeadersFromJson(test.in) | ||
|
||
if err != nil { | ||
if !test.err { | ||
t.Errorf("case %d: %v: error expected but not generated", i, test.in) | ||
} | ||
} else if !reflect.DeepEqual(test.out, h) { | ||
t.Errorf("case %d, %v -> %v but expected %v", i, test.in, h, test.out) | ||
} | ||
} | ||
} | ||
|
||
func TestCrossAgentW3CTraceContext(t *testing.T) { | ||
var tcs []TraceContextTestCase | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,8 @@ | |
package newrelic | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"net/http" | ||
"net/url" | ||
"strings" | ||
|
@@ -269,6 +271,76 @@ func (txn *Transaction) AcceptDistributedTraceHeaders(t TransportType, hdrs http | |
txn.thread.logAPIError(txn.thread.AcceptDistributedTraceHeaders(t, hdrs), "accept trace payload", nil) | ||
} | ||
|
||
// | ||
// AcceptDistributedTraceHeadersFromJson() works just like AcceptDistributedTraceHeaders(), except | ||
// that it takes the header data as a JSON string à la DistributedTraceHeadersFromJson(). Additionally | ||
// (unlike AcceptDistributedTraceHeaders()) it returns an error if it was unable to successfully | ||
// convert the JSON string to http headers. There is no guarantee that the header data found in JSON | ||
// is correct beyond conforming to the expected types and syntax. | ||
// | ||
func (txn *Transaction) AcceptDistributedTraceHeadersFromJson(t TransportType, jsondata string) error { | ||
hdrs, err := DistributedTraceHeadersFromJson(jsondata) | ||
if err != nil { | ||
return err | ||
} | ||
txn.AcceptDistributedTraceHeaders(t, hdrs) | ||
return nil | ||
} | ||
|
||
// | ||
// DistributedTraceHeadersFromJson() takes a set of distributed trace headers as a JSON-encoded string | ||
// and emits a http.Header value suitable for passing on to the | ||
// txn.AcceptDistributedTraceHeaders() function. | ||
// | ||
// For example, given the input string | ||
// `{"traceparent": "frob", "tracestate": "blorfl", "newrelic": "xyzzy"}` | ||
// This will emit an http.Header value with headers "traceparent", "tracestate", and "newrelic". | ||
// | ||
// This is a convenience function provided for cases where you receive the trace header data | ||
// already as a JSON string and want to avoid manually converting that to an http.Header. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if we want to add that this is likely when working with trace headers with different language agents? Maybe that would be too wordy. I just want to make sure somebody like Jo Ann would see this and use it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not a bad idea. I don't mind leaning to more verbosity when it comes to documentation. You never know when that extra sentence or paragraph was just what someone was looking for. |
||
// | ||
// The JSON string must be a single object whose values may be strings or arrays of strings. | ||
// These are translated directly to http headers with singleton or multiple values. | ||
// | ||
func DistributedTraceHeadersFromJson(jsondata string) (hdrs http.Header, err error) { | ||
var raw interface{} | ||
hdrs = http.Header{} | ||
if jsondata == "" { | ||
RichVanderwal marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return | ||
} | ||
err = json.Unmarshal([]byte(jsondata), &raw) | ||
if err != nil { | ||
return | ||
} | ||
|
||
switch d := raw.(type) { | ||
case map[string]interface{}: | ||
for k, v := range d { | ||
switch hval := v.(type) { | ||
case string: | ||
hdrs.Set(k, hval) | ||
case []interface{}: | ||
for _, subval := range hval { | ||
switch sval := subval.(type) { | ||
case string: | ||
hdrs.Add(k, sval) | ||
default: | ||
err = fmt.Errorf("JSON object must have only strings or arrays of strings.") | ||
return | ||
} | ||
} | ||
default: | ||
err = fmt.Errorf("JSON object must have only strings or arrays of strings.") | ||
return | ||
} | ||
} | ||
default: | ||
err = fmt.Errorf("JSON string must be a single object.") | ||
return | ||
} | ||
return | ||
} | ||
|
||
// Application returns the Application which started the transaction. | ||
func (txn *Transaction) Application() *Application { | ||
if nil == txn { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we're requiring new versions of these libraries, will this affect our customers in any way?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can move the version back. That was automatically bumped by my go runtime, not because I purposefully upgraded it.