@redthreadlabs/tracelog 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.d.ts CHANGED
@@ -293,6 +293,15 @@ declare namespace apm {
293
293
  spanStackTraceMinDuration?: string;
294
294
  stackTraceLimit?: number;
295
295
  traceContinuationStrategy?: TraceContinuationStrategy;
296
+ /**
297
+ * Route auto-instrumented transactions (and their spans and breakdown
298
+ * metricsets) to a named channel when the transaction name matches a
299
+ * wildcard pattern. First matching rule wins; unmatched records go to
300
+ * the default channel.
301
+ *
302
+ * Example: `[{ pattern: '* unknown route*', channel: 'unknown-route' }]`
303
+ */
304
+ transactionChannels?: Array<TransactionChannelRule>;
296
305
  transactionIgnoreUrls?: Array<string>;
297
306
  transactionMaxSpans?: number;
298
307
  transactionSampleRate?: number;
@@ -329,6 +338,13 @@ declare namespace apm {
329
338
  writeSpan (span: object): void;
330
339
  }
331
340
 
341
+ interface TransactionChannelRule {
342
+ /** Wildcard pattern matched against the transaction name. */
343
+ pattern: string;
344
+ /** Channel to route matching transactions, spans, and breakdown metricsets to. */
345
+ channel: string;
346
+ }
347
+
332
348
  interface CaptureEventOptions {
333
349
  message?: string;
334
350
  level?: 'debug' | 'info' | 'warn' | 'error' | 'fatal';
package/lib/agent.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright Shaxpir Inc. and Elasticsearch B.V. and other contributors where applicable.
2
+ * Copyright Red Thread Labs LLC. and Elasticsearch B.V. and other contributors where applicable.
3
3
  * Licensed under the BSD 2-Clause License; you may not use this file except in
4
4
  * compliance with the BSD 2-Clause License.
5
5
  */
@@ -831,6 +831,26 @@ Agent.prototype.writeEvents = function (events, cb) {
831
831
  // the default channel ('server'). Named channels are created lazily via
832
832
  // getChannel(name) and write to separate files (e.g. tracelog-client-*.jsonl).
833
833
 
834
+ /**
835
+ * Resolve the channel that records for the named transaction should be
836
+ * routed to, per the `transactionChannels` config rules (first matching
837
+ * rule wins). Returns null when no rule matches, i.e. the record belongs
838
+ * on the default channel. Used for auto-instrumented transactions, their
839
+ * spans, and their breakdown metricsets.
840
+ */
841
+ Agent.prototype._channelForTransactionName = function (name) {
842
+ const rules = this._conf && this._conf.transactionChannelRules;
843
+ if (!rules || rules.length === 0 || typeof name !== 'string') {
844
+ return null;
845
+ }
846
+ for (let i = 0; i < rules.length; i++) {
847
+ if (rules[i].re.test(name)) {
848
+ return rules[i].channel;
849
+ }
850
+ }
851
+ return null;
852
+ };
853
+
834
854
  /**
835
855
  * Get a named channel. Returns an object with writeEvent, writeEvents,
836
856
  * writeError, writeTransaction, and writeSpan methods that route to the
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * Copyright Elasticsearch B.V. and other contributors where applicable.
3
- * Copyright Shaxpir Inc. All rights reserved.
3
+ * Copyright Red Thread Labs LLC. All rights reserved.
4
4
  * Licensed under the BSD 2-Clause License; you may not use this file except in
5
5
  * compliance with the BSD 2-Clause License.
6
6
  */
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright Shaxpir Inc. All rights reserved.
2
+ * Copyright Red Thread Labs LLC. All rights reserved.
3
3
  * Licensed under the BSD 2-Clause License; you may not use this file except in
4
4
  * compliance with the BSD 2-Clause License.
5
5
  */
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * Copyright Elasticsearch B.V. and other contributors where applicable.
3
- * Copyright Shaxpir Inc. All rights reserved.
3
+ * Copyright Red Thread Labs LLC. All rights reserved.
4
4
  * Licensed under the BSD 2-Clause License; you may not use this file except in
5
5
  * compliance with the BSD 2-Clause License.
6
6
  */
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright Shaxpir Inc. All rights reserved.
2
+ * Copyright Red Thread Labs LLC. All rights reserved.
3
3
  * Licensed under the BSD 2-Clause License; you may not use this file except in
4
4
  * compliance with the BSD 2-Clause License.
5
5
  */
@@ -48,6 +48,7 @@ const {
48
48
  normalizeSanitizeFieldNames,
49
49
  normalizeCloudProvider,
50
50
  normalizeCustomMetricsHistogramBoundaries,
51
+ normalizeTransactionChannels,
51
52
  normalizeTransactionSampleRate,
52
53
  normalizeTraceContinuationStrategy,
53
54
  normalizeContextManager,
@@ -396,6 +397,7 @@ function normalize(opts, logger) {
396
397
  normalizeDurationOptions(opts, DURATION_OPTS, defaults, logger);
397
398
  normalizeBools(opts, BOOL_OPTS, defaults, logger);
398
399
  normalizeIgnoreOptions(opts);
400
+ normalizeTransactionChannels(opts, [], defaults, logger);
399
401
  normalizeElasticsearchCaptureBodyUrls(opts);
400
402
  normalizeDisableMetrics(opts);
401
403
  normalizeSanitizeFieldNames(opts);
@@ -427,6 +427,46 @@ function normalizeIgnoreOptions(opts, fields, defaults, logger) {
427
427
  }
428
428
  }
429
429
 
430
+ /**
431
+ * Normalizes transactionChannels rules into compiled wildcard matchers.
432
+ *
433
+ * Input is an array of { pattern, channel } objects, where `pattern` is a
434
+ * wildcard expression matched against the transaction name (e.g.
435
+ * '* unknown route*') and `channel` is the name of the channel to route
436
+ * matching transactions (and their spans and breakdown metricsets) to.
437
+ * Compiles to opts.transactionChannelRules: [{ re, channel }]. Invalid
438
+ * rules are dropped with a warning.
439
+ *
440
+ * @param {Record<String, unknown>} opts the configuration options to normalize
441
+ * @param {String[]} fields the list of fields to normalize (unused)
442
+ * @param {Record<String, unknown>} defaults the configuration defaults (unused)
443
+ * @param {import('../logging.js').Logger} logger
444
+ */
445
+ function normalizeTransactionChannels(opts, fields, defaults, logger) {
446
+ if (opts.transactionChannels) {
447
+ opts.transactionChannelRules = [];
448
+ const wildcard = new WildcardMatcher();
449
+ for (const rule of opts.transactionChannels) {
450
+ if (
451
+ !rule ||
452
+ typeof rule.pattern !== 'string' ||
453
+ typeof rule.channel !== 'string' ||
454
+ !rule.channel
455
+ ) {
456
+ logger.warn(
457
+ 'invalid transactionChannels rule (expected { pattern, channel } strings): %j',
458
+ rule,
459
+ );
460
+ continue;
461
+ }
462
+ opts.transactionChannelRules.push({
463
+ re: wildcard.compile(rule.pattern),
464
+ channel: rule.channel,
465
+ });
466
+ }
467
+ }
468
+ }
469
+
430
470
  /**
431
471
  * Normalizes the wildcard matchers of sanitizeFieldNames and thansforms the into RegExps
432
472
  *
@@ -640,6 +680,7 @@ module.exports = {
640
680
  normalizeKeyValuePairs,
641
681
  normalizeNumbers,
642
682
  normalizeSanitizeFieldNames,
683
+ normalizeTransactionChannels,
643
684
  normalizeTransactionSampleRate,
644
685
  secondsFromDuration,
645
686
  normalizeTraceContinuationStrategy,
@@ -361,6 +361,17 @@ const CONFIG_SCHEMA = [
361
361
  centralConfigName: 'trace_continuation_strategy',
362
362
  crossAgentName: 'trace_continuation_strategy',
363
363
  },
364
+ {
365
+ name: 'transactionChannels',
366
+ configType: 'transactionChannelArray',
367
+ defaultValue: [],
368
+ },
369
+ {
370
+ name: 'transactionChannelRules',
371
+ configType: 'wildcardChannelRuleArray',
372
+ defaultValue: [],
373
+ deps: ['transactionChannels'],
374
+ },
364
375
  {
365
376
  name: 'transactionIgnoreUrls',
366
377
  configType: 'stringArray',
@@ -717,7 +717,12 @@ Instrumentation.prototype.addEndedTransaction = function (transaction) {
717
717
  trans: transaction.id,
718
718
  trace: transaction.traceId,
719
719
  });
720
- agent._apmClient.sendTransaction(payload);
720
+ const channel = agent._channelForTransactionName(payload.name);
721
+ if (channel && typeof agent._apmClient.sendToChannel === 'function') {
722
+ agent._apmClient.sendToChannel(channel, 'transaction', payload);
723
+ } else {
724
+ agent._apmClient.sendTransaction(payload);
725
+ }
721
726
  };
722
727
 
723
728
  Instrumentation.prototype.addEndedSpan = function (span) {
@@ -841,7 +846,17 @@ Instrumentation.prototype._encodeAndSendSpan = function (span) {
841
846
  type: span.type,
842
847
  });
843
848
  if (agent._apmClient) {
844
- agent._apmClient.sendSpan(payload);
849
+ // Spans follow their transaction's channel. The transaction name
850
+ // may not be final until the transaction ends, so this routing is
851
+ // best-effort for spans that end before the route is resolved.
852
+ const channel = agent._channelForTransactionName(
853
+ span.transaction && span.transaction.name,
854
+ );
855
+ if (channel && typeof agent._apmClient.sendToChannel === 'function') {
856
+ agent._apmClient.sendToChannel(channel, 'span', payload);
857
+ } else {
858
+ agent._apmClient.sendSpan(payload);
859
+ }
845
860
  }
846
861
  }
847
862
  }
@@ -67,7 +67,19 @@ class MetricsReporter extends Reporter {
67
67
 
68
68
  if (this._agent._apmClient) {
69
69
  for (const metric of seen.values()) {
70
- this._agent._apmClient.sendMetricSet(metric);
70
+ // Breakdown metricsets carry the name of the transaction they
71
+ // were aggregated for; route them to that transaction's channel.
72
+ const channel = this._agent._channelForTransactionName(
73
+ metric.transaction && metric.transaction.name,
74
+ );
75
+ if (
76
+ channel &&
77
+ typeof this._agent._apmClient.sendToChannel === 'function'
78
+ ) {
79
+ this._agent._apmClient.sendToChannel(channel, 'metricset', metric);
80
+ } else {
81
+ this._agent._apmClient.sendMetricSet(metric);
82
+ }
71
83
  }
72
84
  }
73
85
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redthreadlabs/tracelog",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "Node.js APM instrumentation that writes traces to JSONL files",
5
5
  "publishConfig": {
6
6
  "access": "public"