1 | "use strict";
|
2 | var __importDefault = (this && this.__importDefault) || function (mod) {
|
3 | return (mod && mod.__esModule) ? mod : { "default": mod };
|
4 | };
|
5 | Object.defineProperty(exports, "__esModule", { value: true });
|
6 | exports.createW3CTracestate = exports.setHttpHeader = exports.Span = void 0;
|
7 | const lodash_1 = __importDefault(require("lodash"));
|
8 | const serialize_error_1 = require("serialize-error");
|
9 | const traceparent_1 = __importDefault(require("traceparent"));
|
10 | const EventMessage_1 = require("./model/EventMessage");
|
11 | const Recorder_1 = require("./Recorder");
|
12 | const EventLoggingServiceClient_1 = require("./transport/EventLoggingServiceClient");
|
13 | const config_1 = __importDefault(require("./lib/config"));
|
14 | const util_1 = __importDefault(require("./lib/util"));
|
15 | const defaultRecorder = config_1.default.EVENT_LOGGER_SIDECAR_DISABLED
|
16 | ? new Recorder_1.DefaultLoggerRecorder()
|
17 | : new Recorder_1.DefaultSidecarRecorder(new EventLoggingServiceClient_1.EventLoggingServiceClient(config_1.default.EVENT_LOGGER_SERVER_HOST, config_1.default.EVENT_LOGGER_SERVER_PORT));
|
18 |
|
19 |
|
20 |
|
21 |
|
22 | const asyncOverrides = util_1.default.eventAsyncOverrides(config_1.default.ASYNC_OVERRIDE_EVENTS);
|
23 | class Span {
|
24 | spanContext;
|
25 | recorders;
|
26 | isFinished = false;
|
27 | |
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 | constructor(spanContext, recorders, defaultTagsSetter) {
|
34 | this.defaultTagsSetter = defaultTagsSetter ? defaultTagsSetter : this.defaultTagsSetter;
|
35 | this.recorders = recorders ? recorders : { defaultRecorder };
|
36 | if (!!spanContext.tags && !!spanContext.tags.tracestate) {
|
37 | spanContext.tracestates = util_1.default.getTracestateMap(config_1.default.EVENT_LOGGER_VENDOR_PREFIX, spanContext.tags.tracestate).tracestates;
|
38 | if (!spanContext.tracestates[config_1.default.EVENT_LOGGER_VENDOR_PREFIX]) {
|
39 | spanContext.tracestates[config_1.default.EVENT_LOGGER_VENDOR_PREFIX] = { spanId: spanContext.spanId };
|
40 | }
|
41 | }
|
42 | this.spanContext = spanContext;
|
43 | this.defaultTagsSetter();
|
44 | this.spanContext = Object.freeze(this.spanContext);
|
45 | return this;
|
46 | }
|
47 | |
48 |
|
49 |
|
50 |
|
51 | defaultTagsSetter(message) {
|
52 | const w3cHeaders = getTracestate(this.spanContext);
|
53 | if (w3cHeaders) {
|
54 | this.setTags(Object.assign(this.spanContext.tags, w3cHeaders));
|
55 | if (!(config_1.default.EVENT_LOGGER_VENDOR_PREFIX in this.getTracestates())) {
|
56 | this.setTracestates(Object.assign(this.spanContext.tracestates, util_1.default.getTracestateMap(config_1.default.EVENT_LOGGER_VENDOR_PREFIX, w3cHeaders.tracestate).tracestates));
|
57 | }
|
58 | }
|
59 | return this;
|
60 | }
|
61 | setTracestates(tracestates) {
|
62 | let newContext = new EventMessage_1.EventTraceMetadata(this.getContext());
|
63 | for (let key in tracestates) {
|
64 | newContext.tracestates[key] = tracestates[key];
|
65 | }
|
66 | this.spanContext = Object.freeze(new EventMessage_1.EventTraceMetadata(newContext));
|
67 | return this;
|
68 | }
|
69 | |
70 |
|
71 |
|
72 | getContext() {
|
73 | return Object.assign({}, this.spanContext, { tags: JSON.parse(JSON.stringify(this.spanContext.tags)) });
|
74 | }
|
75 | |
76 |
|
77 |
|
78 |
|
79 |
|
80 | getChild(service, recorders = this.recorders) {
|
81 | try {
|
82 | let inputTraceContext = this.getContext();
|
83 | return new Span(new EventMessage_1.EventTraceMetadata(Object.assign({}, inputTraceContext, {
|
84 | service,
|
85 | spanId: undefined,
|
86 | startTimestamp: undefined,
|
87 | finishTimestamp: undefined,
|
88 | parentSpanId: inputTraceContext.spanId
|
89 | })), recorders, this.defaultTagsSetter);
|
90 | }
|
91 | catch (e) {
|
92 | throw (e);
|
93 | }
|
94 | }
|
95 | |
96 |
|
97 |
|
98 |
|
99 |
|
100 | injectContextToMessage(carrier, injectOptions = {}) {
|
101 | let result = lodash_1.default.cloneDeep(carrier);
|
102 | let { path } = injectOptions;
|
103 | if (carrier instanceof EventMessage_1.EventMessage || (('metadata' in carrier)))
|
104 | path = 'metadata';
|
105 | else if (carrier instanceof EventMessage_1.EventTraceMetadata) {
|
106 | return Promise.resolve(this.spanContext);
|
107 | }
|
108 | if (!path) {
|
109 | Object.assign(result, { trace: this.spanContext });
|
110 | }
|
111 | else {
|
112 | lodash_1.default.merge(lodash_1.default.get(result, path), { trace: this.spanContext });
|
113 | }
|
114 | return result;
|
115 | }
|
116 | |
117 |
|
118 |
|
119 |
|
120 |
|
121 | injectContextToHttpRequest(request, type = EventMessage_1.HttpRequestOptions.w3c) {
|
122 | let result = lodash_1.default.cloneDeep(request);
|
123 | result.headers = setHttpHeader(this.spanContext, type, result.headers);
|
124 | return result;
|
125 | }
|
126 | |
127 |
|
128 |
|
129 |
|
130 | setTags(tags) {
|
131 | let newContext = new EventMessage_1.EventTraceMetadata(this.getContext());
|
132 | for (let key in tags) {
|
133 | if (key === 'tracestate' || key === 'traceparent')
|
134 | continue;
|
135 | newContext.tags[key] = tags[key];
|
136 | }
|
137 | this.spanContext = Object.freeze(new EventMessage_1.EventTraceMetadata(newContext));
|
138 | return this;
|
139 | }
|
140 | _setTagTracestate(tags) {
|
141 | let newContext = new EventMessage_1.EventTraceMetadata(this.getContext());
|
142 | newContext.tags.tracestate = tags.tracestate;
|
143 | this.spanContext = Object.freeze(new EventMessage_1.EventTraceMetadata(newContext));
|
144 | return this;
|
145 | }
|
146 | |
147 |
|
148 |
|
149 | getTags() {
|
150 | const { tags } = this.getContext();
|
151 | return !!tags ? tags : {};
|
152 | }
|
153 | |
154 |
|
155 |
|
156 |
|
157 | setTracestateTags(tags) {
|
158 | this.spanContext.tracestates[config_1.default.EVENT_LOGGER_VENDOR_PREFIX] = Object.assign(this.spanContext.tracestates[config_1.default.EVENT_LOGGER_VENDOR_PREFIX], tags);
|
159 | const { ownTraceStateString, restTraceStateString } = encodeTracestate(this.spanContext);
|
160 | this._setTagTracestate({ tracestate: `${ownTraceStateString}${restTraceStateString}` });
|
161 | return this;
|
162 | }
|
163 | |
164 |
|
165 |
|
166 | getTracestates() {
|
167 | return this.spanContext.tracestates;
|
168 | }
|
169 | |
170 |
|
171 |
|
172 | getTracestateTags() {
|
173 | if (config_1.default.EVENT_LOGGER_VENDOR_PREFIX in this.spanContext.tracestates) {
|
174 | return this.spanContext.tracestates[config_1.default.EVENT_LOGGER_VENDOR_PREFIX];
|
175 | }
|
176 | else {
|
177 | return {};
|
178 | }
|
179 | }
|
180 | |
181 |
|
182 |
|
183 |
|
184 |
|
185 | async finish(message, state, finishTimestamp) {
|
186 | if (this.spanContext.finishTimestamp) {
|
187 | return Promise.reject(new Error('span already finished'));
|
188 | }
|
189 | let spanContext = this._finishSpan(finishTimestamp).getContext();
|
190 | await this.trace(message, spanContext, state);
|
191 | return Promise.resolve(this);
|
192 | }
|
193 | |
194 |
|
195 |
|
196 |
|
197 | _finishSpan(finishTimestamp) {
|
198 | let newContext = Object.assign({}, this.spanContext);
|
199 | if (finishTimestamp instanceof Date) {
|
200 | newContext.finishTimestamp = finishTimestamp.toISOString();
|
201 | }
|
202 | else if (!finishTimestamp) {
|
203 | newContext.finishTimestamp = (new Date()).toISOString();
|
204 | }
|
205 | else {
|
206 | newContext.finishTimestamp = finishTimestamp;
|
207 | }
|
208 | this.spanContext = Object.freeze(new EventMessage_1.EventTraceMetadata(newContext));
|
209 | return this;
|
210 | }
|
211 | |
212 |
|
213 |
|
214 |
|
215 |
|
216 |
|
217 |
|
218 | async trace(message, spanContext = this.spanContext, state, action) {
|
219 | if (!message) {
|
220 | message = new EventMessage_1.EventMessage({
|
221 | type: 'application/json',
|
222 | content: spanContext
|
223 | });
|
224 | }
|
225 | try {
|
226 | await this.recordMessage(message, EventMessage_1.TraceEventTypeAction.getType(), action, state);
|
227 | this.isFinished = this.spanContext.finishTimestamp ? true : false;
|
228 | return this;
|
229 | }
|
230 | catch (e) {
|
231 | throw new Error(`Error when logging trace. ${JSON.stringify(e, null, 2)}`);
|
232 | }
|
233 | }
|
234 | |
235 |
|
236 |
|
237 |
|
238 |
|
239 |
|
240 | async audit(message, action = EventMessage_1.AuditEventAction.default, state) {
|
241 | let result = await this.recordMessage(message, EventMessage_1.AuditEventTypeAction.getType(), action, state);
|
242 | return result;
|
243 | }
|
244 | |
245 |
|
246 |
|
247 |
|
248 |
|
249 |
|
250 |
|
251 | async info(message, state) {
|
252 | let { action, type } = new EventMessage_1.LogEventTypeAction(EventMessage_1.LogEventAction.info);
|
253 | await this.recordMessage(message, type, action, state);
|
254 | }
|
255 | |
256 |
|
257 |
|
258 |
|
259 |
|
260 |
|
261 |
|
262 | async debug(message, state) {
|
263 | let { action, type } = new EventMessage_1.LogEventTypeAction(EventMessage_1.LogEventAction.debug);
|
264 | await this.recordMessage(message, type, action, state);
|
265 | }
|
266 | |
267 |
|
268 |
|
269 |
|
270 |
|
271 |
|
272 |
|
273 | async verbose(message, state) {
|
274 | let { action, type } = new EventMessage_1.LogEventTypeAction(EventMessage_1.LogEventAction.verbose);
|
275 | await this.recordMessage(message, type, action, state);
|
276 | }
|
277 | |
278 |
|
279 |
|
280 |
|
281 |
|
282 |
|
283 |
|
284 | async performance(message, state) {
|
285 | let { action, type } = new EventMessage_1.LogEventTypeAction(EventMessage_1.LogEventAction.performance);
|
286 | await this.recordMessage(message, type, action, state);
|
287 | }
|
288 | |
289 |
|
290 |
|
291 |
|
292 |
|
293 |
|
294 |
|
295 | async warning(message, state) {
|
296 | let { action, type } = new EventMessage_1.LogEventTypeAction(EventMessage_1.LogEventAction.warning);
|
297 | await this.recordMessage(message, type, action, state);
|
298 | }
|
299 | |
300 |
|
301 |
|
302 |
|
303 |
|
304 |
|
305 |
|
306 | async error(message, state) {
|
307 | let { action, type } = new EventMessage_1.LogEventTypeAction(EventMessage_1.LogEventAction.error);
|
308 | await this.recordMessage(message, type, action, state);
|
309 | }
|
310 | |
311 |
|
312 |
|
313 |
|
314 |
|
315 |
|
316 |
|
317 | async recordMessage(message, type, action, state) {
|
318 | if (this.isFinished) {
|
319 | throw new Error('span finished. no further actions allowed');
|
320 | }
|
321 | let newEnvelope = this.createEventMessage(message, type, action, state);
|
322 | let key = `${type}Recorder`;
|
323 | let recorder = this.recorders.defaultRecorder;
|
324 | if (this.recorders[key]) {
|
325 | recorder = this.recorders[key];
|
326 | }
|
327 | if (util_1.default.shouldOverrideEvent(asyncOverrides, type)) {
|
328 |
|
329 | recorder.record(newEnvelope, util_1.default.shouldLogToConsole(type, action));
|
330 | return true;
|
331 | }
|
332 | const logResult = await recorder.record(newEnvelope, util_1.default.shouldLogToConsole(type, action));
|
333 | if (logResult.status !== EventMessage_1.LogResponseStatus.accepted) {
|
334 | throw new Error(`Error when recording ${type}-${action} event. status: ${logResult.status}`);
|
335 | }
|
336 | return logResult;
|
337 | }
|
338 | |
339 |
|
340 |
|
341 | createEventMessage = (message, type, _action, state = EventMessage_1.EventStateMetadata.success()) => {
|
342 | let defaults = getDefaults(type);
|
343 | let action = _action ? _action : defaults.action;
|
344 | let messageToLog;
|
345 | if (message instanceof Error) {
|
346 |
|
347 |
|
348 | messageToLog = new EventMessage_1.EventMessage({
|
349 | content: { error: (0, serialize_error_1.serializeError)(message) },
|
350 | type: 'application/json'
|
351 | });
|
352 | }
|
353 | else if (typeof message === 'string') {
|
354 | messageToLog = new EventMessage_1.EventMessage({
|
355 | content: { payload: message },
|
356 | type: 'application/json'
|
357 | });
|
358 | }
|
359 | else {
|
360 | messageToLog = new EventMessage_1.EventMessage({
|
361 | content: message,
|
362 | type: 'application/json'
|
363 | });
|
364 |
|
365 |
|
366 | }
|
367 | return Object.assign(messageToLog, {
|
368 | metadata: {
|
369 | event: defaults.eventMetadataCreator({
|
370 | action,
|
371 | state
|
372 | }),
|
373 | trace: this.spanContext
|
374 | }
|
375 | });
|
376 | };
|
377 | }
|
378 | exports.Span = Span;
|
379 | const getDefaults = (type) => {
|
380 | switch (type) {
|
381 | case EventMessage_1.EventType.audit: {
|
382 | return {
|
383 | action: EventMessage_1.AuditEventAction.default,
|
384 | eventMetadataCreator: EventMessage_1.EventMetadata.audit
|
385 | };
|
386 | }
|
387 | case EventMessage_1.EventType.trace: {
|
388 | return {
|
389 | action: EventMessage_1.TraceEventAction.span,
|
390 | eventMetadataCreator: EventMessage_1.EventMetadata.trace
|
391 | };
|
392 | }
|
393 | case EventMessage_1.EventType.log: {
|
394 | return {
|
395 | action: EventMessage_1.LogEventAction.info,
|
396 | eventMetadataCreator: EventMessage_1.EventMetadata.log
|
397 | };
|
398 | }
|
399 | }
|
400 | return {
|
401 | action: EventMessage_1.NullEventAction.undefined,
|
402 | eventMetadataCreator: EventMessage_1.EventMetadata.log
|
403 | };
|
404 | };
|
405 | const setHttpHeader = (context, type, headers) => {
|
406 | const { traceId, parentSpanId, spanId, flags, sampled } = context;
|
407 | switch (type) {
|
408 | case EventMessage_1.HttpRequestOptions.xb3: {
|
409 | let XB3headers = {
|
410 | 'X-B3-TraceId': traceId,
|
411 | 'X-B3-SpanId': spanId,
|
412 | 'X-B3-Sampled': sampled,
|
413 | 'X-B3-Flags': flags,
|
414 | 'X-B3-Version': '0'
|
415 | };
|
416 | let result = parentSpanId ? Object.assign({ 'X-B3-ParentSpanId': parentSpanId }, XB3headers) : XB3headers;
|
417 | return Object.assign(headers, result);
|
418 | }
|
419 | case EventMessage_1.HttpRequestOptions.w3c:
|
420 | default: {
|
421 | const tracestate = headers.tracestate ? createW3CTracestate(context, headers.tracestate) : (context.tags && context.tags.tracestate) ? context.tags.tracestate : null;
|
422 | return lodash_1.default.pickBy({
|
423 | ...headers,
|
424 | ...{
|
425 | traceparent: createW3Ctreaceparent(context),
|
426 | tracestate
|
427 | }
|
428 | }, lodash_1.default.identity);
|
429 | }
|
430 | }
|
431 | };
|
432 | exports.setHttpHeader = setHttpHeader;
|
433 | const encodeTracestate = (context) => {
|
434 | const { spanId } = context;
|
435 | let tracestatesMap = {};
|
436 | tracestatesMap[config_1.default.EVENT_LOGGER_VENDOR_PREFIX] = {};
|
437 | let ownTraceStateString = '';
|
438 | let restTraceStateString = '';
|
439 | if ((!!context.tags && !!context.tags.tracestate)) {
|
440 | const { tracestates, ownTraceState, restTraceState } = util_1.default.getTracestateMap(config_1.default.EVENT_LOGGER_VENDOR_PREFIX, context.tags.tracestate);
|
441 | tracestatesMap = tracestates;
|
442 | ownTraceStateString = ownTraceState;
|
443 | restTraceStateString = restTraceState;
|
444 | }
|
445 | if (context.tracestates && context.tracestates[config_1.default.EVENT_LOGGER_VENDOR_PREFIX])
|
446 | tracestatesMap = context.tracestates;
|
447 | const newOpaqueValueMap = ((typeof tracestatesMap[config_1.default.EVENT_LOGGER_VENDOR_PREFIX]) === 'object')
|
448 | ? Object.assign(tracestatesMap[config_1.default.EVENT_LOGGER_VENDOR_PREFIX], { spanId })
|
449 | : null;
|
450 | let opaqueValue = newOpaqueValueMap ? JSON.stringify(newOpaqueValueMap) : `{"spanId":"${spanId}"}`;
|
451 | return { ownTraceStateString: `${config_1.default.EVENT_LOGGER_VENDOR_PREFIX}=${Buffer.from(opaqueValue).toString('base64')}`, restTraceStateString };
|
452 | };
|
453 | const createW3CTracestate = (spanContext, tracestate) => {
|
454 | const newTracestate = encodeTracestate(spanContext).ownTraceStateString;
|
455 | if (!tracestate && config_1.default.EVENT_LOGGER_TRACESTATE_HEADER_ENABLED) {
|
456 | return newTracestate;
|
457 | }
|
458 | let tracestateArray = (tracestate.split(','));
|
459 | let resultMap = new Map();
|
460 | let resultArray = [];
|
461 | let result;
|
462 | for (let rawStates of tracestateArray) {
|
463 | let states = rawStates.trim();
|
464 | let [vendorRaw] = states.split('=');
|
465 | resultMap.set(vendorRaw.trim(), states);
|
466 | }
|
467 | if (resultMap.has(config_1.default.EVENT_LOGGER_VENDOR_PREFIX)) {
|
468 | resultMap.delete(config_1.default.EVENT_LOGGER_VENDOR_PREFIX);
|
469 | for (let entry of resultMap.values()) {
|
470 | resultArray.push(entry);
|
471 | }
|
472 | resultArray.unshift(newTracestate);
|
473 | result = resultArray.join(',');
|
474 | }
|
475 | else {
|
476 | tracestateArray.unshift(newTracestate);
|
477 | result = tracestateArray.join(',');
|
478 | }
|
479 | return result;
|
480 | };
|
481 | exports.createW3CTracestate = createW3CTracestate;
|
482 | const createW3Ctreaceparent = (spanContext) => {
|
483 | const { traceId, parentSpanId, spanId, flags, sampled } = spanContext;
|
484 | const version = Buffer.alloc(1).fill(0);
|
485 | const flagsForBuff = (flags && sampled) ? (flags | sampled) : flags ? flags : sampled ? sampled : 0x00;
|
486 | const flagsBuffer = Buffer.alloc(1).fill(flagsForBuff);
|
487 | const traceIdBuff = Buffer.from(traceId, 'hex');
|
488 | const spanIdBuff = Buffer.from(spanId, 'hex');
|
489 | const parentSpanIdBuff = parentSpanId && Buffer.from(parentSpanId, 'hex');
|
490 | let W3CHeaders = parentSpanIdBuff
|
491 | ? new traceparent_1.default(Buffer.concat([version, traceIdBuff, spanIdBuff, flagsBuffer, parentSpanIdBuff]))
|
492 | : new traceparent_1.default(Buffer.concat([version, traceIdBuff, spanIdBuff, flagsBuffer]));
|
493 | return W3CHeaders.toString();
|
494 | };
|
495 | const getTracestate = (spanContext) => {
|
496 | let tracestate;
|
497 | if (!!config_1.default.EVENT_LOGGER_TRACESTATE_HEADER_ENABLED || (!!spanContext.tags && !!spanContext.tags.tracestate)) {
|
498 | let currentTracestate = undefined;
|
499 | if (!!spanContext.tags && !!spanContext.tags.tracestate)
|
500 | currentTracestate = spanContext.tags.tracestate;
|
501 | tracestate = createW3CTracestate(spanContext, currentTracestate);
|
502 | return { tracestate };
|
503 | }
|
504 | return false;
|
505 | };
|
506 |
|
\ | No newline at end of file |