-
Notifications
You must be signed in to change notification settings - Fork 99
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
[Q] How to trace microservices? #1076
Comments
Hi @mrdulin, I assume you are using Google Cloud Functions. GCF has been working on supporting this library but I believe this support is not yet released. Unfortunately because of how the platform works, using In the meantime, you can try this workaround that uses the custom span API: https://gist.github.com/kjin/3b86f1eaf6c8ee9c458df8708e77ae53 Note two things: that (1) you would be manually be creating root spans via Let me know how that works for you! |
@kjin Thanks for replying. Your example is very helpful, but what about the background cloud function? Should I pass I am trying to use const tracer = require('@google-cloud/trace-agent').start({
samplingRate: 0,
bufferSize: 1,
serviceContext: { service: 'TestCloudTracePubSubService' }
});
const fetch = require('node-fetch');
const CLOUD_FUNCTION_HTTP_SERVICE_URL = process.env.CLOUD_FUNCTION_HTTP_SERVICE_URL || '';
console.log('CLOUD_FUNCTION_HTTP_SERVICE_URL: ', CLOUD_FUNCTION_HTTP_SERVICE_URL);
exports.TestCloudTracePubSubService = function TestCloudTrace(data, context, callback) {
const pubSubMessage = data;
const jsonString = pubSubMessage.data ? Buffer.from(pubSubMessage.data, 'base64').toString() : '{}';
const message = JSON.parse(jsonString);
console.log(`message: ${JSON.stringify(message, null, 2)}`);
const traceContext = parseContextFromHeader(message.traceContext);
tracer.runInRootSpan(
{
name: 'TestCloudTracePubSubService', // Your function name here
traceContext
},
async (rootSpan) => {
// Your logic here. Just be sure to call endSpan() after sending a response.
const fetchChildSpan = tracer.createChildSpan({ name: 'fetch data from http cloud function' });
const fetchChildSpanTraceContext = fetchChildSpan.getTraceContext();
console.log('fetchChildSpanTraceContext: ', fetchChildSpanTraceContext);
const body = await fetch(CLOUD_FUNCTION_HTTP_SERVICE_URL).then((res) => res.json());
console.log(`body: ${JSON.stringify(body, null, 2)}`);
fetchChildSpan.endSpan();
rootSpan.endSpan();
callback();
}
);
};
function parseContextFromHeader(str) {
if (!str) {
return null;
}
const matches = str.match(/^([0-9a-fA-F]+)(?:\/([0-9]+))(?:;o=(.*))?/);
if (!matches || matches.length !== 4 || matches[0] !== str || (matches[2] && isNaN(Number(matches[2])))) {
return null;
}
return {
traceId: matches[1],
spanId: matches[2],
options: isNaN(Number(matches[3])) ? undefined : Number(matches[3])
};
} The trace timeline for background cloud function: update Here is my nodejs web service (service A): The trace timeline: Here is my HTTP cloud function (service B): The trace timeline: As you can see, they are separated into two traces. I want to associate them with the same trace context. How can I do this? I am trying to set For a business transaction, the They aren't same, so I think that's why they are separated into two traces. I think if service A and service B use the same If there are examples for using cross-service trace context and. context propagation, that will be great. |
So it seems like there are two separate issues at play here:
|
I made a demo for tracing cross services. Here are my services: .
├── 01-web-api
├── 02-create-budget
├── 03-create-campaign
├── 04-create-adgroup
├── 05-create-ad
├── 06-google-adwords-api
├── deploy.sh
├── docs
└── utils.js The workflow is from 01-web-api to 06-google-adwords-api 02 - 05 services are background cloud functions Here is the timeline, it seems work. But I am not sure does it make sense. The key point for trace cross services is to use the same I pass the The question is, can you tell me does this make sense? I found an issue that the cold start time of cloud function will affect the latency. |
I think it makes sense. If I understand correctly, this is how I would guess things work:
And that you were able to get 06 to show up because you passed the trace context from 01 to 06 via each of 02-05. You receive the value of the trace context in each of these four background functions 02-05 via the input data attribute, but only pass it onto 06 rather than using it in these functions. So the one missing part of this diagram, if any, is the spans that represent the running time of each of 02-05 (which should be technically possible since you have that information). |
Hi @mrdulin -- I'm going to close this issue. Please comment if you have any other Q's and I'll re-open. Thanks! |
@kjin I think, The problem is in root span. If I close root span in the first microservice, I can not see all the time of work my microservices. But If I do not close root span in the first microservice, I will get a memory leak. I think If we have the feature: send trace data from children span and clear memory without close root span(We can do it in another service), it will be a good point for trace microservices. |
@klerick Thanks for the suggestion. I don't think we'll have the bandwidth to implement such a feature, though. (I'm also not sure it's possible to start a span in one service and end it in another, the Stackdriver Trace API may not allow that.) |
@kjin I think, the Stackdriver Trace API does not depend on it because this behavior is common for Stackdriver. I tried to extend the class RootSpanData but the filed "children" is private. |
@kjin I think If will add the feature, create root span from existing data. We can use the trace agent for microservice. Sends new traces to Stackdriver Trace or updates existing traces. If the ID of a trace that you send matches that of an existing trace, any fields in the existing trace and its spans are overwritten by the provided values, and any new fields provided are merged with the existing trace data. If the ID does not match, a new trace is created. We will have the opportunity to change endTime for root span and we will have correctly charts) |
Can someone share a workflow for implementing distributed tracing in cloud functions/cloud run? |
@bcoe @mrdulin Can you guys confirm if its still the same way we need to do? Right now I just have imported at start of each service alone and don't have any further code. Do I need to pass the context from each service and pass it to others(pubsub subscriptions/other services)? |
My application is designed as microservice. Here is my application arch:
service A Web API => event-driven cloud function => service B API
I want to use Cross-Service Trace Contexts to trace a completed business transaction which means I expect below trace span.
Here is a demo for Cross-Service Trace Contexts feature. But I use the local nodejs web server. There is no background cloud function.
I deployed my real application services above on GCP. But the cross-service trace contexts doesn't work well. I use trace context in my background cloud function like this:
I try to create child span with
traceContext
passed by event message. But it doesn't work.As you can see, there is only a root span. Not only for background cloud function, but also for HTTP cloud function.
So, how can I trace a whole business transaction? The microservice arch is designed like this:
GKE service A API => background cloud function => GKE service B API
I can't even add a label to cloud trace within HTTP and background functions.
I got below error in my HTTP cloud function:
I am thinking use trace REST API within HTTP and backgroud Cloud function, but I am not sure that how can I use
traceContext
generated from cloud trace library with trace REST API.I want to generate a trace and spans like below:
I want to trace a whole business transaction, even some microservice is called asynchronously(background cloud function).
Is this possible? Any advice will be good!
The text was updated successfully, but these errors were encountered: