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

How can I add tags per request? #441

Open
jeffthompson1971 opened this issue Aug 16, 2019 · 16 comments
Open

How can I add tags per request? #441

jeffthompson1971 opened this issue Aug 16, 2019 · 16 comments

Comments

@jeffthompson1971
Copy link
Contributor

jeffthompson1971 commented Aug 16, 2019

I have this working, but i need to pass in a couple specific tags like username etc so i know who the trace is for. I don't see a way to do that. I am referring to the express implementation. i will have custom fields added to the request object...

was thinking maybe I could do a pull request that looks for a 'tags' object on the request object, and if that's there it adds to the span? if not then nobody breaks and it's backwards compatible?

@codefromthecrypt
Copy link
Member

are these tags http headers?

@codefromthecrypt
Copy link
Member

or are you looking for a way for us to propagate something beyond the traceId?

@jeffthompson1971
Copy link
Contributor Author

yes.. this are like username and an organization identifier.. i add them to every trace on client-side with the axios implementation. but some of our node services will not have their corresponding web apps instrumented... but still want to get those tags in the express traces

@jeffthompson1971
Copy link
Contributor Author

i can fork this and try some stuff out?

@codefromthecrypt
Copy link
Member

consider stuffing it in the traceID object because you aren't guaranteed to not have any spans between http in and http out. that's my advice.

in the java side, we have this thing called "extra" which is basically a field inside TraceId to store stuff. it is intentionally opaque list to allow multiple plugins to not clobber eachother. one plugin is called "extra fields" which allows you to propagate something from headers to the other side.

@jeffthompson1971
Copy link
Contributor Author

hmm.. so i already just use the defaultTags so that the fields are stand-alone and can be queried. if I have stuff in a traceID object how could i query for all spans from user [email protected] for example?
this is what i have.
`const tracer = new Tracer({
sampler: theSampler,
ctxImpl: new CLSContext('zipkin'),
recorder: new BatchRecorder({
logger: new HttpLogger({
endpoint: TRACER_ENDPOINT,
jsonEncoder: jsonEncoder.JSON_V2,
}),
}),
localServiceName: SERVICE_NAME,
defaultTags: { component: 'TracerMiddleware', project: 'aip-core-ui' },
});

export const TracerMiddleware = zipkinMiddleware.expressMiddleware({ tracer });`

@codefromthecrypt
Copy link
Member

I maybe guessed wrong on what you need. you are not asking to propagate tags, rather just add request-specific tags (ex parsing). I think this would be a change to httpServerInstrumentation, to add a parsing function which will then sink the tags into the span.

@jcchavezs
Copy link
Contributor

jcchavezs commented Aug 16, 2019 via email

@jeffthompson1971
Copy link
Contributor Author

Yes definately what I need are tags that are per-request and NOT global. basically it's simple - we just want to know that user and tenantID (the customer identifier in our system)...

So what I would prefer to do is have some upstream express middlware that sets the name=value pairs i need ONTO the request object (in header?) and then we can have zipkin (where I need help with) look for a known header element and if it's there it adds the tags to the span.
Why not have a header key like 'trace-tags' and that value can be a JSON string like
{user: '[email protected]', tenantId: '23423432432432'}

then it's very simple and innocuous change in zipkin-js somewhere that just looks for this header and then just do this right before sending the span:
a) convert the JSON string element in the header 'trace-tags' to a JSON object
b) iterate over the keys and log an annotation on the span for each one

thoughts?

@codefromthecrypt
Copy link
Member

codefromthecrypt commented Aug 19, 2019 via email

@jeffthompson1971
Copy link
Contributor Author

Thanks for the help Adrian. I'm trying to wrap my head completely around it. I'm kinda new - like for example I'm not even clear what the distinction is between a tag and an annotation ;) .

It is a bit abstract - can you like jot down a little psuedo-code for how this API will be extended etc.? That would help me better understand what you are proposing. are you saying you will add a new parameter that will be a function that does the parsing of the request object that I need - be it header or other bits? can you spec that out a little?

@codefromthecrypt
Copy link
Member

@jeffthompson1971 sorry.. yeah we are years overdue getting this api overhauled. One main reason is that annotation jargon is so confusing. In real life people almost never need annotations in the v2 model. tags are for lookup etc https://github.com/openzipkin/zipkin-api/blob/master/zipkin2-api.yaml#L307

In the java library, we expose a parser object people can override, only used to parse name and tags ex. https://github.com/openzipkin/brave/blob/master/instrumentation/http/src/main/java/brave/http/HttpParser.java#L59-L64

you can see we do similar in zipkin-js now, just we don't expose a parsing function

@poorejc
Copy link

poorejc commented Jun 5, 2020

Hey folks!

Great project. I want to add myself as an interested party in this feature. I'm willing to help, too.

Our use case: I represent Apache Flagon--we do systemic thin-client instrumentation for user behavioral analysis (https://github.com/apache/incubator-flagon-useralejs). We'd like to be able to correlate zipkin traces with user behavior at both the event and workflow model level. This means that at request, we need to parse and write data to traces to allow for cross-references with up and downstream behavioral logs. My thinking is that we need a few things:

  1. Germane to this ticket: get traceID and maybe spanID from a trace at request, and add that as a key value pair into our logs. Ideally, we are able to add a UID or IRI from our logs into either Zipkin annotations or tags (probably tags makes most sense). Looking at this thread, @adriancole seems to say that "we do similar in zipkin-js now, just we don't expose a parsing function"... does that mean that zipkin-js traces can be parsed, the function just isn't exposed through the package, or that a callback function needs to be written to parse and then expose. Give us a little more scaffolding (point us to relevant src), and we're willing to push a PR on this.

  2. An alternative we've explored is to add a customLog function that packages our logs in zipkin format to push directly to the zipkin API, in which case we might considered trying to use tracer.scope (referenced in How to get the traceId, parentId, spanId of current request to use outside the tracer? #128) to coerce both our logs and zipkin traces to have the same traceID. Do you have any docs on tracer.scope you can point us to? We're having trouble finding it. Also, another issue we're having with this method is that wrapping fetch calls, for example, with zipkin-instrumentation-fetch appears to affect event handling--namely this doesn't work as it would for a standard fetch call:

self.addEventListener('fetch', function () { console.log("fetch"); });

This makes triggering other logs off the same fetch call tricky. Any comment you have here would be really helpful. Fully willing to admit that it could be a scope issue, I haven't experimented a ton with this, but am using a modified example of the zipkin-js 'web' example you provide.

@codefromthecrypt
Copy link
Member

what I meant to say is we don't currently have at initialization time of http client or server instrumentation, a function of httprequest/response to parse that into tags. instead the parsing details are constant (not terrible to do this).

concretely the constructor to https://github.com/openzipkin/zipkin-js/blob/master/packages/zipkin/src/instrumentation/httpClient.js#L10

could take a function for parsing tags of request/response (probably that function can also handle the span name)

@jcchavezs
Copy link
Contributor

So two things to clarify here. First of all the original issue was to add tags based on a request on the server. This is, when a server receives the request, having a user defined function that parses that request, adds the desired tags and then continues with normal flow.

What @adriancole is pointing out is to do this in the client side (i.e. your service adding the tags to the span that represents the client request) which is a bit simpler. Main difference is that client recordRequest receives the request as first parameter: https://github.com/openzipkin/zipkin-js/blob/master/packages/zipkin/src/instrumentation/httpClient.js#L20 while server recordRequest receives details of the request: https://github.com/openzipkin/zipkin-js/blob/master/packages/zipkin/src/instrumentation/httpServer.js#L76.

From the details of the ticket I feel like you are looking for this to happen on server side but I might be wrong.

In client side, it is as easy as pass an argument in the constructor that is called in the recordRequest and there one can call this.tracer.recordBinary('my_tag', 'something_from_the_request'). A consistent design we have been implementing is the httpTracing component. Something like:

httpTracing = {
requestParser(request): void;
responseParser(response): void;
}

In client side it is straightforward because we have access to the request.

In server side, we don't

recordRequest(method, requestUrl, readHeader) {

So I'd say we do some sort of polymorfism where if the first argument is a request we use it and if it is a string we assume it is the method.

In any case I am happy to help with reviews and further discussions.

@jcchavezs
Copy link
Contributor

jcchavezs commented Jun 15, 2020

@poorejc anything else I can help with to get this started?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants