-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
Zipkin json encoding via HTTP #334
Conversation
How are we going to write integration tests for this?
|
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 didn't do a completely thorough review, had a few starter questions. Overall direction looks good.
cc @black-adder
if err != nil { | ||
http.Error(w, fmt.Sprintf(app.UnableToReadBodyErrFormat, err), http.StatusBadRequest) | ||
return | ||
} | ||
} else { | ||
http.Error(w, "Only Content-Type:application/x-thrift is supported at the moment", http.StatusBadRequest) |
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.
extend err msg
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.
The comment is odd, but why custom error
? I don't see a value for it
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 meant err msg says only x-thrift is supported, not json, so we can extend it to mention both
@@ -148,7 +187,7 @@ func TestGzipBadBody(t *testing.T) { | |||
func TestUnsupportedContentType(t *testing.T) { | |||
server, _ := initializeTestServer(nil) | |||
defer server.Close() | |||
statusCode, _, err := postBytes(server.URL+`/api/v1/spans`, []byte{}, createHeader("application/json")) | |||
statusCode, _, err := postBytes(server.URL+`/api/v1/spans`, []byte{}, createHeader("text/html")) |
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.
xml would be funnier
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.
video/mp4
:P
type binaryAnnotation struct { | ||
Endpoint endpoint `json:"endpoint"` | ||
Key string `json:"key"` | ||
Value string `json:"value"` |
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.
why did Adrian refer to oneOf
? Is this the right JSON format?
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 oneOf
refers to bool
, string
num
so on. In the current https://github.com/openzipkin/zipkin-api/blob/master/zipkin-api.yaml it's defined as string
} | ||
|
||
for _, span := range spans { | ||
if len(span.TraceID) != 16 { |
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.
how are we dealing with 128bit IDs? Does Zipkin JSON encode them as one string, or hi/lo?
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.
a good point, It's hi/low. New thrift it supports https://github.com/openzipkin/zipkin-api/blob/master/thrift/zipkinCore.thrift#L493. For now we could do only one part and once #335 is merged do both.
https://github.com/openzipkin/zipkin/blob/cf4df43952f2b82496c2e4c730765e7b70eba22b/zipkin/src/main/java/zipkin/internal/Util.java#L114
https://github.com/openzipkin/zipkin/blob/ba025306985da1f253665ac053d290a0f21f841c/zipkin/src/main/java/zipkin/internal/JsonCodec.java#L348-L351
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.
Currently parsing only traceId[16:]
(https://github.com/openzipkin/zipkin/blob/cf4df43952f2b82496c2e4c730765e7b70eba22b/zipkin/src/main/java/zipkin/internal/Util.java#L105). Once new thrift is updated traceIdHigh
will be set with [:16]
if len(span.ID) != 16 && len(span.ID) != 32 { | ||
return nil, errSpanID | ||
} | ||
if len(span.ParentID) > 0 && len(span.ParentID) != 16 { |
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 never understood this restriction in Zipkin to have the exact length - what if some leading zeroes were dropped for whatever reason, we can still parse the hex string. Especially for parsing out-of-band span data, I am not sure why we need to be this strict.
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.
Honestly, I don't know, it's defined in api definition.
@adriancole hi, would you give us some insights here?
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.
@yurishkuro updated to align with your comment. It seems that zipkin sever does the same.
}, nil | ||
} | ||
|
||
func parseIpv4(str string) (int32, error) { |
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.
so we're only parsing IPv4 from Zipkin, not IPv6?
Note that our zipkin.thrift (in jaeger-idl) is probably behind the official Zipkin IDL, e.g. we don't have IPv6 or TraceIdHigh fields.
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.
yes, the reason is that our zipkin.thrift does not support ipv6. I will leave TODO comment there.
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.
Yeah, we should sync it up with official IDL (should be backwards compatible) - #335
I think we might be able to use the same xdock driver, but a different "client" that's implemented with Zipkin's JSON and Thrift official senders (two clients, really) @black-adder - can we come up with a unified end-to-end testing framework? Ultimately it boils down to two parts
|
a152d1d
to
990f499
Compare
} | ||
|
||
var ( | ||
errIstUnsignedLog = errors.New("id is not an unsigned long") |
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.
nit: s/ist/is
@@ -122,6 +122,45 @@ func TestThriftFormat(t *testing.T) { | |||
assert.EqualValues(t, "Cannot submit Zipkin batch: Bad times ahead\n", resBodyStr) | |||
} | |||
|
|||
func TestJsonFormat(t *testing.T) { |
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.
nit: could we use a fixture for this test instead which defines the test cases and then run the cases at once?
} else { | ||
http.Error(w, "Only Content-Type:application/x-thrift is supported at the moment", http.StatusBadRequest) | ||
http.Error(w, "Not supported Content-Type", http.StatusBadRequest) |
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.
nit: s/Not Supported/Unsupported/ we can literally save bytes
@@ -0,0 +1,234 @@ | |||
// Copyright (c) 2017 Uber Technologies, Inc. |
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.
file can be json.go since it's already namespaced under zipkin.
} | ||
|
||
func decode(body []byte) ([]zipkinSpan, error) { | ||
type zipkinSpans []zipkinSpan |
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.
is this intermediate type definition necessary?
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.
it can be defined in global scope, is that better?
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.
sgtm
return tSpan, nil | ||
} | ||
|
||
func endpointToThrift(endp endpoint) (*zipkincore.Endpoint, error) { |
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.
go prefers incomprehensible variable names for short functions: https://talks.golang.org/2014/names.slide#7
so here s/endp/e
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.
whaaa?
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.
incomprehensible 😃 then I will rename all local vars to i
import ( | ||
"encoding/json" | ||
"fmt" | ||
"github.com/stretchr/testify/assert" |
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.
import ordering
assert.Equal(t, "foo", endp.ServiceName) | ||
assert.Equal(t, "127.0.0.1", endp.IPv4) | ||
assert.Equal(t, "2001:db8::c001", endp.IPv6) | ||
// only service name |
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.
this test and a couple of other tests might be slightly more readable with fixtures
This can be easily done with in the all-in-one integration test, however doing it via crossdock tests does seem more semantically correct. The all-in-one integration test is supposed to be a simple test that ensures the pipeline works, I don't want to conflate the test with zipkin testing logic. Creating a test zipkin client which sends http and thrift spans to the collectors and making this run with the xdock tests seems the best approach. But I'm not entirely sure how to get this to work without creating another docker image just for the zipkin client, I'll have to experiment a bit. |
btw, their new format with Go: https://github.com/openzipkin/zipkin-api-example/tree/master/go |
6a83968
to
2ada3b2
Compare
23e2b4d
to
cce519b
Compare
This is updated and should be ready for re-review. |
expected string | ||
statusCode int | ||
}{ | ||
{spanJSON, "Cannot submit Zipkin batch: Bad times ahead\n", http.StatusInternalServerError}, |
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.
please explicitly define the variable names in the struct: ie {payload: spanJSON, etc.}
we've been bad about this in the past but should do it for everything going forward
} | ||
|
||
var result uint64 | ||
for i := start; i < len; i++ { |
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.
Could we use official the builtin golang functions for this: https://play.golang.org/p/0GUjRvy0kw or would it not work?
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.
it produces different numbers
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.
why not use model functions like model/TraceIDFromString
?
https://github.com/uber/jaeger/blob/master/model/span.go#L146
It automatically takes care of high/low.
}, nil | ||
} | ||
|
||
func parseIpv4(str string) (int32, error) { |
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.
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.
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.
ParseIP returns IP, which is an alias to []byte. If nil return 0 else pack bytes into long
} else { | ||
http.Error(w, "Only Content-Type:application/x-thrift is supported at the moment", http.StatusBadRequest) | ||
http.Error(w, "Unsupported Content-Type", http.StatusBadRequest) |
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.
missing return?
var tSpans []*zipkincore.Span | ||
if contentType == "application/x-thrift" { | ||
tSpans, err = deserializeThrift(bodyBytes) | ||
if err != nil { |
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.
can do this outside of the if contentType==
expected string | ||
statusCode int | ||
}{ | ||
{payload: spanJSON, expected: "Cannot submit Zipkin batch: Bad times ahead\n", statusCode: http.StatusInternalServerError}, |
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.
nit: this is more readable
{
payload: spanJSON,
expected: "Cannot submit Zipkin batch: Bad times ahead\n",
statusCode: http.StatusInternalServerError,
},
Annotations []annotation `json:"annotations"` | ||
BinaryAnnotations []binaryAnnotation `json:"binaryAnnotations"` | ||
} | ||
type zipkinSpans []zipkinSpan |
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.
only used once, better to inline: var spans []zipkinSpan
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.
It was like that before. @black-adder wanted to move it here. https://github.com/uber/jaeger/pull/334#discussion_r133217513
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 it's fine to define it here as long as it's used in multiple places (which looks like it can). Every reference to []zipkinSpan
can be replaced with zipkinSpans
. I count at least 3 instances where this happens.
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 prefer to use []zipkinSpan
directly, less obfuscation. The type alias is useful when you add extra behavior to it, like we do with []model.Tag
(sorting, etc.)
}, nil | ||
} | ||
|
||
func parseIpv4(str string) (int32, error) { |
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.
ParseIP returns IP, which is an alias to []byte. If nil return 0 else pack bytes into long
} | ||
|
||
var result uint64 | ||
for i := start; i < len; i++ { |
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.
why not use model functions like model/TraceIDFromString
?
https://github.com/uber/jaeger/blob/master/model/span.go#L146
It automatically takes care of high/low.
@yurishkuro I am on PTO until 28th. I don't want to block other people.. If there are no serious blockers I can submit fix PR to address latest comments:
Once this is in #342 could be merged (if it's ok). |
Fixes https://github.com/uber/jaeger/issues/225
Zipkin API definition: https://github.com/openzipkin/zipkin-api/blob/master/zipkin-api.yaml
Docker Image to try it: https://hub.docker.com/r/pavolloffay/tmp-jaeger-zipkin-json/