forked from signalfx/tracing-examples
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
160 lines (143 loc) · 5.49 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
const fetch = require("node-fetch");
const { initTracer } = require("jaeger-client");
const opentracing = require("opentracing");
const URL = require("url");
// This function creates and initializes a Jaeger tracer that is compatible
// with OpenTracing. This should only be created once per process and the
// instance returned should be shared everywhere it is needed.
function createTracer() {
// You can use the default value in most cases unless you are using our
// metricproxy.
let ingestUrl = URL.parse(process.env.SIGNALFX_INGEST_URL || "https://ingest.signalfx.com/v1/trace");
const accessToken = process.env.SIGNALFX_ACCESS_TOKEN;
if (!accessToken) {
console.error("You must set the envvar SIGNALFX_ACCESS_TOKEN");
process.exit(1);
}
const config = {
// This is the service name that will be used by default for the "local"
// side of spans emitted from this tracer.
serviceName: "signalfx-jaeger-node-example",
// This configures the tracer to send every span generated. You can also
// use a probabalistic sampler, among many others.
// See https://github.com/jaegertracing/jaeger-client-node/tree/master/src/samplers
sampler: {
type: "const",
param: 1
},
// This will configure the tracer to send spans via HTTP to the configured
// ingestUrl. The spans are sent in Jaeger's Thrift format.
reporter: {
logSpans: true,
collectorEndpoint: ingestUrl,
// You authenticate to SignalFx using Basic auth with a username of
// "auth" and a password that is your access token.
username: "auth",
password: process.env.SIGNALFX_ACCESS_TOKEN
}
};
const options = {
logger: console
};
const tracer = initTracer(config, options);
opentracing.initGlobalTracer(tracer);
return tracer;
}
// The tracer should only be instantiated once in the lifetime of the
// application. It should be treated as a singleton and imported in whatever
// module needs it.
const tracer = createTracer();
// This is a traced wrapper of fetch. It isn't terribly flexible as it doesn't
// allow for any customization of the HTTP requests and assumes that you want
// the response interpreted as JSON. But it shows the basic technique of
// starting a span, given a context, and attaching tags and events to it.
// "context", if not null, is the parent span of the current request.
function fetchTracedJSON(context, url) {
const span = tracer.startSpan(URL.parse(url).hostname, context ? { childOf: context } : undefined);
span.setTag(opentracing.Tags.HTTP_URL, url);
span.setTag(opentracing.Tags.HTTP_METHOD, "GET");
span.setTag(opentracing.Tags.SPAN_KIND, opentracing.Tags.SPAN_KIND_RPC_CLIENT);
return fetch(url)
.then(resp => {
span.setTag(opentracing.Tags.HTTP_STATUS_CODE, resp.status);
// You can set this envvar to something to simulate an error event on
// the span.
if (process.env.SIMULATE_ERROR) {
throw new Error("Could not get body of response");
}
// The span isn't logically finished until the body is done streaming.
return resp.json();
})
.catch(err => {
span.setTag(opentracing.Tags.ERROR, true);
span.log({
event: "error",
message: err.toString()
});
throw err;
})
.finally(() => {
span.finish();
});
}
function getBiggestGainers(context) {
return fetchTracedJSON(context, "https://api.iextrading.com/1.0/stock/market/list/gainers");
}
function getIndustryOfStock(context, stock) {
return fetchTracedJSON(context, "https://api.iextrading.com/1.0/stock/" + stock + "/company").then(
company => company.industry
);
}
function countIndustries(industries) {
const counts = {};
for (const i in industries) {
const ind = industries[i];
if (ind in counts) {
counts[ind]++;
} else {
counts[ind] = 1;
}
}
return counts;
}
// Look up the "biggest gainers" in the stock market currently and then find
// which industries they belong to and print out a count of those industries to
// the console. This involves several HTTP requests, all of which are traced.
function printMostGainingIndustries() {
// Create the "root" span of this line of processing.
const rootSpan = tracer.startSpan("industries-of-biggest-gainers");
// Span context generally must be passed explicitly in Javascript since
// everything runs in a single thread and is highly asynchronous. It is
// possible to use AsyncListeners and continuation-local-storage to propagate
// the context implicitly, but the Jaeger tracer and OpenTracing do not
// directly support that currently.
return getBiggestGainers(rootSpan)
.then(gainers => {
rootSpan.setTag("numResults", gainers.length);
return Promise.all(gainers.map(g => g.symbol).map(sym => getIndustryOfStock(rootSpan, sym)));
})
.then(countIndustries)
.then(industryCounts => {
for (k in industryCounts) {
console.log(k + ": " + industryCounts[k]);
}
})
.catch(err => {
// You should always set the "error" tag on spans that see errors.
rootSpan.setTag(opentracing.Tags.ERROR, true);
rootSpan.log({
event: "error",
message: err.toString()
});
})
.finally(() => {
// We need to make sure we finish the span on both the success and error
// paths.
rootSpan.finish();
});
}
printMostGainingIndustries().finally(() => {
// Make sure to close the tracer before the application finishes so that
// spans are not lost.
tracer.close();
});