appdynamics 22.2.0 → 22.7.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.
@@ -1,7 +1,7 @@
1
1
  {
2
- "version": "22.2.0.0",
2
+ "version": "22.7.0.0",
3
3
  "sha": "",
4
4
  "nodeVersion": "",
5
- "buildName": "8373",
5
+ "buildName": "8783",
6
6
  "compatibilityVersion": "4.4.1.0"
7
7
  }
package/lib/core/agent.js CHANGED
@@ -34,6 +34,7 @@ var CpuProfiler = require('../v8/cpu-profiler').CpuProfiler;
34
34
  var HeapProfiler = require('../v8/heap-profiler').HeapProfiler;
35
35
  var agentVersion = require('../../appdynamics_version.json');
36
36
  var appDNativeLoader = require('appdynamics-native');
37
+ var SecureApp = require('../secure_app/secure_app').SecureApp;
37
38
 
38
39
  var LibAgent = require('../libagent/libagent').LibAgent;
39
40
  var LibagentConnector = require('../libagent/libagent-connector').LibagentConnector;
@@ -88,6 +89,7 @@ function Agent() {
88
89
  this.gcStats = new GCStats(this);
89
90
  this.cpuProfiler = new CpuProfiler(this);
90
91
  this.heapProfiler = new HeapProfiler(this);
92
+ this.secureApp = new SecureApp(this);
91
93
 
92
94
  this.libagent = null;
93
95
 
@@ -282,6 +284,10 @@ Agent.prototype.init = function (opts) {
282
284
  self.cpuProfiler.init();
283
285
  self.heapProfiler.init();
284
286
 
287
+ if(self.opts.secureAppEnabled) {
288
+ self.secureApp.init(this);
289
+ }
290
+
285
291
  // Initialize libagent
286
292
  self.backendConnector.intializeAgentHelpers();
287
293
 
@@ -342,6 +348,12 @@ Agent.prototype.initializeOpts = function (opts) {
342
348
  if (self.opts.nodeName === undefined) {
343
349
  self.opts.nodeName = process.env.APPDYNAMICS_AGENT_NODE_NAME;
344
350
  }
351
+ if (self.opts.reuseNode === undefined) {
352
+ self.opts.reuseNode = process.env.APPDYNAMICS_AGENT_REUSE_NODE_NAME;
353
+ }
354
+ if (self.opts.reuseNodePrefix === undefined) {
355
+ self.opts.reuseNodePrefix = process.env.APPDYNAMICS_AGENT_REUSE_NODE_NAME_PREFIX;
356
+ }
345
357
  if (self.opts.analyticsMaxSegmentSizeInBytes === undefined && process.env.APPDYNAMICS_ANALYTICS_MAX_SEGMENT_SIZE) {
346
358
  self.opts.analyticsMaxSegmentSizeInBytes = parseInt(process.env.APPDYNAMICS_ANALYTICS_MAX_SEGMENT_SIZE, 10);
347
359
  }
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ const core_1 = require("@opentelemetry/core");
3
+
4
+ class AppdynamicsSpanExporter {
5
+ constructor(logger) {
6
+ this.logger = logger;
7
+ }
8
+
9
+ export(spans, resultCallback) {
10
+ return this._sendSpans(spans, resultCallback);
11
+ }
12
+
13
+ shutdown() {
14
+ this._sendSpans([]);
15
+ return Promise.resolve();
16
+ }
17
+
18
+ _exportInfo(span) {
19
+ return {
20
+ traceId: span.spanContext().traceId,
21
+ parentId: span.parentSpanId,
22
+ name: span.name,
23
+ id: span.spanContext().spanId,
24
+ kind: span.kind,
25
+ timestamp: core_1.hrTimeToMicroseconds(span.startTime),
26
+ duration: core_1.hrTimeToMicroseconds(span.duration),
27
+ attributes: span.attributes,
28
+ status: span.status,
29
+ events: span.events,
30
+ };
31
+ }
32
+
33
+ _sendSpans(spans, done) {
34
+ for (const span of spans) {
35
+ this.logger.trace("Exporting span : " + JSON.stringify(this._exportInfo(span)));
36
+ }
37
+ if (done) {
38
+ return done({ code: core_1.ExportResultCode.SUCCESS });
39
+ }
40
+ }
41
+ }
42
+
43
+ module.exports.AppdynamicsSpanExporter = AppdynamicsSpanExporter;
@@ -2,60 +2,91 @@
2
2
 
3
3
  const opentelemetry_api = require('@opentelemetry/api');
4
4
  const { ROOT_CONTEXT } = require('@opentelemetry/api');
5
- const { BatchSpanProcessor, ConsoleSpanExporter, BasicTracerProvider } = require('@opentelemetry/tracing');
6
- const { CollectorTraceExporter } = require("@opentelemetry/exporter-collector");
5
+ const { BatchSpanProcessor, BasicTracerProvider } = require('@opentelemetry/sdk-trace-base');
6
+ const { OTLPTraceExporter } = require("@opentelemetry/exporter-trace-otlp-proto");
7
7
  const { AsyncHooksContextManager, AsyncLocalStorageContextManager } = require("@opentelemetry/context-async-hooks");
8
- const { ParentBasedSampler, AlwaysOnSampler } = require("@opentelemetry/core");
8
+ const { ParentBasedSampler, AlwaysOnSampler, TraceIdRatioBasedSampler } = require("@opentelemetry/core");
9
9
  const { Resource } = require('@opentelemetry/resources');
10
10
  const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions');
11
+ const AppdynamicsSpanExporter = require('./appdynamics_span_exporter').AppdynamicsSpanExporter;
11
12
  const url = require('url');
12
13
 
13
14
  module.exports = TracerProvider;
14
15
 
15
16
  function TracerProvider(logger) {
16
17
  this.host = 'localhost';
17
- this.port = '55681';
18
+ this.port = '4318';
19
+ this.url = `http://${this.host}:${this.port}/v1/traces`;
18
20
  this.ot_api = opentelemetry_api;
19
21
  this.ROOT_CONTEXT = ROOT_CONTEXT;
20
22
  this.logger = logger;
21
23
  }
22
24
 
25
+ function getSamplerFromConfig(config) {
26
+ if(config.sampler && config.sampler.TraceIdRatioBasedSampler) {
27
+ const ratio = config.sampler.TraceIdRatioBasedSampler.Ratio || 1;
28
+ return new TraceIdRatioBasedSampler(ratio);
29
+ } else {
30
+ return new AlwaysOnSampler();
31
+ }
32
+ }
33
+
23
34
  TracerProvider.prototype.register = function(config) {
24
35
  const provider = new BasicTracerProvider({
25
36
  sampler: new ParentBasedSampler({
26
- root: new AlwaysOnSampler()
37
+ root: getSamplerFromConfig(config.openTelemetry)
27
38
  }),
28
39
  resource: new Resource({
29
40
  [SemanticResourceAttributes.SERVICE_NAME]: config.tierName,
30
- "service.name": config.tierName,
31
- "service.namespance": config.applicationName
41
+ [SemanticResourceAttributes.SERVICE_NAMESPACE]: config.applicationName
32
42
  }),
33
43
  });
34
44
 
35
45
  // default collector configuration, can be overridden from agent config
36
- const defaultOptions = {
46
+ const collectorOptions = {
47
+ url: this.url,
37
48
  attributes: {'service.name': config.tierName,
38
49
  'service.namespace': config.applicationName}
39
50
  };
40
51
 
41
- if (config.openTelemetry && config.openTelemetry.collector) {
42
- Object.assign(defaultOptions, config.openTelemetry.collector);
43
- this.url = defaultOptions.url;
44
- try {
45
- var urlObj = url.parse(this.url);
46
- this.host = urlObj.hostname;
47
- this.port = urlObj.port;
48
- } catch (e) {
49
- this.logger.error('Collector url must be in <host>:<port> format');
50
- return false;
52
+ // batch export config, empty by default
53
+ const batchProcessorConfig = {};
54
+
55
+ if (config.openTelemetry) {
56
+ if (config.openTelemetry.collector) {
57
+ Object.assign(collectorOptions, config.openTelemetry.collector);
58
+ this.url = collectorOptions.url;
59
+ this.logger.debug('Exporter using config ' + JSON.stringify(collectorOptions));
60
+ try {
61
+ var urlObj = url.parse(this.url);
62
+ this.host = urlObj.hostname;
63
+ this.port = urlObj.port;
64
+ } catch (e) {
65
+ this.logger.error('Collector url must be in <host>:<port> format');
66
+ return false;
67
+ }
51
68
  }
69
+ if(config.openTelemetry.exporter) {
70
+ if(config.openTelemetry.exporter.maxExportBatchSize) {
71
+ batchProcessorConfig.maxExportBatchSize = config.openTelemetry.exporter.maxExportBatchSize;
72
+ }
73
+ if(config.openTelemetry.exporter.maxQueueSize) {
74
+ batchProcessorConfig.maxQueueSize = config.openTelemetry.exporter.maxQueueSize;
75
+ }
76
+ if(config.openTelemetry.exporter.exportTimeoutMillis) {
77
+ batchProcessorConfig.exportTimeoutMillis = config.openTelemetry.exporter.exportTimeoutMillis;
78
+ }
79
+ if(config.openTelemetry.exporter.maxExportBatchSize) {
80
+ batchProcessorConfig.maxExportBatchSize = config.openTelemetry.exporter.maxExportBatchSize;
81
+ }
82
+ }
83
+ this.logger.debug('Batch Processor config ' + JSON.stringify(batchProcessorConfig));
52
84
  }
53
85
 
54
- const exporter = new CollectorTraceExporter(defaultOptions);
55
- provider.addSpanProcessor(new BatchSpanProcessor(exporter));
56
- if (config.openTelemetry && config.openTelemetry.debug) {
57
- provider.addSpanProcessor(new BatchSpanProcessor(new ConsoleSpanExporter()));
58
- }
86
+ const exporter = new OTLPTraceExporter(collectorOptions);
87
+ provider.addSpanProcessor(new BatchSpanProcessor(exporter, batchProcessorConfig));
88
+ const appdExporter = new AppdynamicsSpanExporter(this.logger, batchProcessorConfig);
89
+ provider.addSpanProcessor(new BatchSpanProcessor(appdExporter, batchProcessorConfig));
59
90
 
60
91
  const majorVersion = parseInt(process.versions.node.split('.')[0]);
61
92
  const minorVersion = parseInt(process.versions.node.split('.')[1]);
@@ -67,4 +98,4 @@ TracerProvider.prototype.register = function(config) {
67
98
 
68
99
  TracerProvider.prototype.getTracer = function(name) {
69
100
  return opentelemetry_api.trace.getTracer(name);
70
- };
101
+ };
@@ -163,6 +163,10 @@ LibagentConnector.prototype.startBusinessTransaction = function (entryPointType,
163
163
  LibagentConnector.prototype.stopBusinessTransaction = function (transaction) {
164
164
  var self = this;
165
165
 
166
+ if(!self.agent.profiler.isValidThreadId(transaction.threadId)) {
167
+ return;
168
+ }
169
+
166
170
  if (transaction.error) {
167
171
  var name = self.protobufModel.extractErrorName(transaction.error);
168
172
  if (!name) {
@@ -203,6 +207,9 @@ LibagentConnector.prototype.stopBusinessTransaction = function (transaction) {
203
207
 
204
208
  LibagentConnector.prototype.startExitCall = function (transaction, exitCall) {
205
209
  var self = this;
210
+ if(!self.agent.profiler.isValidThreadId(exitCall.threadId)) {
211
+ return null;
212
+ }
206
213
 
207
214
  var propertiesArray = [];
208
215
  for (var propName in exitCall.properties) {
@@ -248,7 +255,10 @@ LibagentConnector.prototype.startExitCall = function (transaction, exitCall) {
248
255
 
249
256
  LibagentConnector.prototype.disableResolutionForExitCall = function (exitCall) {
250
257
  var self = this;
251
-
258
+
259
+ if(!self.agent.profiler.isValidThreadId(exitCall.threadId)) {
260
+ return;
261
+ }
252
262
  if (exitCall.exitCallGuid !== undefined) {
253
263
  self.libagent.disableResolutionForExitCall(exitCall.exitCallGuid);
254
264
  }
@@ -256,6 +266,9 @@ LibagentConnector.prototype.disableResolutionForExitCall = function (exitCall) {
256
266
 
257
267
  LibagentConnector.prototype.getCorrelationHeader = function (exitCall) {
258
268
  var self = this;
269
+ if(!self.agent.profiler.isValidThreadId(exitCall.threadId)) {
270
+ return;
271
+ }
259
272
 
260
273
  if (exitCall.exitCallGuid !== undefined) {
261
274
  return self.libagent.getCorrelationHeader(exitCall.exitCallGuid);
@@ -264,6 +277,9 @@ LibagentConnector.prototype.getCorrelationHeader = function (exitCall) {
264
277
 
265
278
  LibagentConnector.prototype.stopExitCall = function (exitCall, error) {
266
279
  var self = this;
280
+ if(!self.agent.profiler.isValidThreadId(exitCall.threadId)) {
281
+ return;
282
+ }
267
283
 
268
284
  if (exitCall.exitCallGuid == undefined) {
269
285
  return;
@@ -310,12 +326,20 @@ LibagentConnector.prototype.sendInstanceTrackerInfo = function (instanceCounts)
310
326
 
311
327
  LibagentConnector.prototype.isSnapshotRequired = function (transaction) {
312
328
  var self = this;
329
+ if(!self.agent.profiler.isValidThreadId(transaction.threadId)) {
330
+ return false;
331
+ }
332
+
313
333
  return self.libagent.isSnapshotRequired(transaction.btGuid);
314
334
  };
315
335
 
316
336
  LibagentConnector.prototype.sendTransactionSnapshot = function (transaction, transactionSnapshot) {
317
337
  var self = this;
318
338
 
339
+ if(!self.agent.profiler.isValidThreadId(transaction.threadId)) {
340
+ return;
341
+ }
342
+
319
343
  // fixup exit calls: set all required fields so that the protobuf message remains valid
320
344
  if (transactionSnapshot.snapshot.exitCalls) {
321
345
  transactionSnapshot.snapshot.exitCalls.forEach(function (item) {
@@ -418,6 +442,9 @@ LibagentConnector.prototype.getBusinessTransactionId = function (txnGuid) {
418
442
  LibagentConnector.prototype.setHttpParamsInTransactionSnapshot = function (transaction) {
419
443
  var self = this;
420
444
  // url, methis and statusCode should be present for all http requests
445
+ if(!self.agent.profiler.isValidThreadId(transaction.threadId)) {
446
+ return false;
447
+ }
421
448
  if (transaction.url && transaction.method && transaction.statusCode) {
422
449
  return self.libagent.setHttpParamsInTransactionSnapshot(
423
450
  transaction.btGuid, transaction.url, transaction.method, transaction.statusCode);
@@ -426,11 +453,17 @@ LibagentConnector.prototype.setHttpParamsInTransactionSnapshot = function (trans
426
453
 
427
454
  LibagentConnector.prototype.addHttpDataToTransactionSnapshot = function (transaction, request) {
428
455
  var self = this;
456
+ if(!self.agent.profiler.isValidThreadId(transaction.threadId)) {
457
+ return false;
458
+ }
429
459
  return self.libagent.addHttpDataToTransactionSnapshot(transaction.btGuid, request);
430
460
  };
431
461
 
432
462
  LibagentConnector.prototype.setSnapshotRequired = function (transaction) {
433
463
  var self = this;
464
+ if(!self.agent.profiler.isValidThreadId(transaction)) {
465
+ return;
466
+ }
434
467
  self.libagent.setSnapshotRequired(transaction.btGuid);
435
468
  };
436
469
 
@@ -535,7 +568,7 @@ LibagentConnector.prototype.initializeTimers = function () {
535
568
 
536
569
  LibagentConnector.prototype.getEumCookieFields = function (transaction, shortForm) {
537
570
  var self = this;
538
- if (!transaction.ignore)
571
+ if (self.agent.profiler.isValidThreadId(transaction.threadId) && !transaction.ignore)
539
572
  return self.libagent.getEumCookieFields(transaction.btGuid, shortForm);
540
573
  else
541
574
  return {};
@@ -545,6 +578,11 @@ LibagentConnector.prototype.setupEum = function (agent) {
545
578
  var libAgentConnector = this;
546
579
  agent.eum.eumCookie.prototype.setFieldValues = function () {
547
580
  var self = this;
581
+
582
+ if(!agent.profiler.isValidThreadId(self.transaction.threadId)) {
583
+ return false;
584
+ }
585
+
548
586
  var shortForm = self.keyForm == 'short';
549
587
  var fields = libAgentConnector.getEumCookieFields(self.transaction, shortForm);
550
588
  if (fields) {
@@ -70,6 +70,7 @@ HttpExitProbe.prototype.attach = function (obj, moduleName) {
70
70
  var isRepeatForHttps = false;
71
71
 
72
72
  var [input, options] = args;
73
+
73
74
  if (typeof(input) != 'string' && !(input instanceof url.URL)) {
74
75
  options = input;
75
76
  input = null;
@@ -78,6 +79,13 @@ HttpExitProbe.prototype.attach = function (obj, moduleName) {
78
79
  args[1] = options;
79
80
  }
80
81
 
82
+ if(typeof(options) == 'function') {
83
+ args[2] = options;
84
+ options = {};
85
+ args[1] = options;
86
+ }
87
+
88
+
81
89
  if (moduleName === 'https') {
82
90
  options.__appdIsHttps = true;
83
91
  }
@@ -204,11 +212,12 @@ HttpExitProbe.prototype.attach = function (obj, moduleName) {
204
212
  },
205
213
  function (obj, args, ret, locals) {
206
214
  var [input, options] = args;
215
+
207
216
  if (typeof(input) != 'string' && !(input instanceof url.URL)) {
208
217
  options = input;
209
218
  input = null;
210
219
  }
211
-
220
+
212
221
  options = options || {};
213
222
  if (!options.appdIgnore && (moduleName != 'http' || (moduleName === 'http' && !options.__appdIsHttps))) {
214
223
  if (locals.parentSpan) {
@@ -261,7 +270,6 @@ HttpExitProbe.prototype.attach = function (obj, moduleName) {
261
270
  httpParser = socket.parser;
262
271
  var kOnHeadersComplete = HTTPParser.kOnHeadersComplete | 0;
263
272
  httpParserMethod = kOnHeadersComplete ? kOnHeadersComplete : 'onHeadersComplete';
264
-
265
273
  var socketCloseHandler = function () {
266
274
  socket.removeListener('close', socketCloseHandler);
267
275
  if (socket.__appdynamicsCleanup) {
@@ -50,26 +50,28 @@ function getAbsoluteUrl(requestUrl, headers) {
50
50
 
51
51
  function getIncomingRequestAttributes(request) {
52
52
  const attributes = {};
53
- const headers = request.headers;
54
- const userAgent = headers ? headers['user-agent'] : null;
55
- const method = request.method || 'GET';
56
- const httpVersion = request.httpVersion;
57
- const requestUrl = request.url ? url.parse(request.url) : null;
58
- const host = requestUrl && requestUrl.host ? requestUrl.host : headers && headers.host ? headers.host : 'localhost';
59
- attributes[SemanticAttributes.HTTP_URL] = getAbsoluteUrl(requestUrl, headers);
60
- attributes[SemanticAttributes.HTTP_HOST] = host;
61
- attributes[SemanticAttributes.HTTP_METHOD] = method;
62
-
63
- if(requestUrl) {
64
- attributes[SemanticAttributes.HTTP_TARGET] = requestUrl.pathname || '/';
65
- }
66
-
67
- if(userAgent) {
68
- attributes[SemanticAttributes.HTTP_USER_AGENT] = userAgent;
69
- }
70
-
71
- if(httpVersion) {
72
- attributes[SemanticAttributes.HTTP_FLAVOR] = httpVersion;
53
+ if (request) {
54
+ const headers = request.headers;
55
+ const userAgent = headers && headers['user-agent'];
56
+ const method = request.method || 'GET';
57
+ const httpVersion = request.httpVersion;
58
+ const requestUrl = request.url && url.parse(request.url);
59
+ const host = requestUrl && requestUrl.host ? requestUrl.host : headers && headers.host ? headers.host : 'localhost';
60
+ attributes[SemanticAttributes.HTTP_URL] = getAbsoluteUrl(requestUrl, headers);
61
+ attributes[SemanticAttributes.HTTP_HOST] = host;
62
+ attributes[SemanticAttributes.HTTP_METHOD] = method;
63
+
64
+ if(requestUrl) {
65
+ attributes[SemanticAttributes.HTTP_TARGET] = requestUrl.pathname || '/';
66
+ }
67
+
68
+ if(userAgent) {
69
+ attributes[SemanticAttributes.HTTP_USER_AGENT] = userAgent;
70
+ }
71
+
72
+ if(httpVersion) {
73
+ attributes[SemanticAttributes.HTTP_FLAVOR] = httpVersion;
74
+ }
73
75
  }
74
76
  return attributes;
75
77
  }
@@ -83,31 +85,33 @@ function getIncomingRequestAttributesOnResponse(response) {
83
85
  }
84
86
 
85
87
  function getOutgoingRequestAttributes(parsedRequest, headers) {
86
- const method = parsedRequest.method || 'GET';
87
- const userAgent = headers ? headers['user-agent'] : null;
88
-
89
88
  const attributes = {};
90
- attributes[SemanticAttributes.HTTP_URL] = getAbsoluteUrl(parsedRequest, headers);
91
- attributes[SemanticAttributes.HTTP_METHOD] = method;
92
- attributes[SemanticAttributes.HTTP_TARGET] = parsedRequest.path || '/';
93
-
89
+ const userAgent = headers && headers['user-agent'] || undefined;
94
90
  if(userAgent) {
95
91
  attributes[SemanticAttributes.HTTP_USER_AGENT] = userAgent;
96
92
  }
93
+
94
+ attributes[SemanticAttributes.HTTP_METHOD] = parsedRequest && parsedRequest.method || 'GET';
95
+ attributes[SemanticAttributes.HTTP_TARGET] = parsedRequest && parsedRequest.path || '/';
96
+
97
+ attributes[SemanticAttributes.HTTP_URL] = getAbsoluteUrl(parsedRequest, headers);
98
+
97
99
  return attributes;
98
100
  }
99
101
 
100
102
  function getOutgoingRequestAttributesOnResponse(response, hostname) {
101
103
  const attributes = {};
102
- const statusCode = response.statusCode;
103
- const httpVersion = response.httpVersion;
104
- const socket = response.socket;
105
- const remotePort = socket ? socket.remotePort : 0;
106
- attributes[SemanticAttributes.HTTP_HOST] = `${hostname}:${remotePort}`;
107
- attributes[SemanticAttributes.HTTP_STATUS_CODE] = statusCode;
108
-
109
- if(httpVersion) {
110
- attributes[SemanticAttributes.HTTP_FLAVOR] = httpVersion;
104
+ if (response) {
105
+ const statusCode = response.statusCode;
106
+ const httpVersion = response.httpVersion;
107
+ const socket = response.socket;
108
+ const remotePort = socket && socket.remotePort || 0;
109
+ attributes[SemanticAttributes.HTTP_HOST] = `${hostname}:${remotePort}`;
110
+ attributes[SemanticAttributes.HTTP_STATUS_CODE] = statusCode;
111
+
112
+ if(httpVersion) {
113
+ attributes[SemanticAttributes.HTTP_FLAVOR] = httpVersion;
114
+ }
111
115
  }
112
116
  return attributes;
113
117
  }
@@ -99,6 +99,8 @@ MongodbProbe.prototype.attach = function (obj) {
99
99
  var cid = event.connectionId;
100
100
  if (typeof (cid) === 'string')
101
101
  serverPool = [cid];
102
+ else if (typeof (cid) === 'number')
103
+ serverPool = [event.address];
102
104
  else
103
105
  serverPool = [cid.host + ':' + cid.port];
104
106
  }
@@ -146,6 +148,7 @@ MongodbProbe.prototype.attach = function (obj) {
146
148
  });
147
149
 
148
150
  supportedCommands.forEach(function (command) {
151
+ proxy.before(obj.Collection.prototype, command, withAPMBeforeHandler);
149
152
  proxy.before(obj.Server.prototype, command, withAPMBeforeHandler);
150
153
  proxy.before(obj.ReplSet.prototype, command, withAPMBeforeHandler);
151
154
  });
@@ -179,6 +179,11 @@ Profiler.prototype.endTransaction = function (time, transaction) {
179
179
  self._endTransaction(time, transaction);
180
180
  };
181
181
 
182
+ Profiler.prototype.isValidThreadId = function(threadId) {
183
+ var self = this;
184
+ return (threadId && self.transactions[threadId]);
185
+ };
186
+
182
187
  Profiler.prototype._endTransaction = function (time, transaction) {
183
188
  var self = this;
184
189
 
@@ -228,7 +233,8 @@ Profiler.prototype.__getNextSequenceInfo = function (transaction) {
228
233
  };
229
234
 
230
235
  Profiler.prototype.createExitCall = function (time, exitCallInfo) {
231
- return this.agent.backendConnector.createExitCall(time, exitCallInfo);
236
+ var self = this;
237
+ return self.agent.backendConnector.createExitCall(time, exitCallInfo);
232
238
  };
233
239
 
234
240
  Profiler.prototype.addExitCall = function (time, exitCall, error) {
@@ -0,0 +1,56 @@
1
+ var path = require('path');
2
+
3
+ const MAX_ITEMS_PER_GROUP = 1024;
4
+
5
+ exports.LibraryMetadata = LibraryMetadata;
6
+ function LibraryMetadata(agent) {
7
+ var self = this;
8
+ self.agent = agent;
9
+ }
10
+
11
+ LibraryMetadata.prototype.init = function() {
12
+ };
13
+
14
+ LibraryMetadata.prototype.getMetadata = function() {
15
+ var self = this;
16
+ if(!self.metadataCache) {
17
+ self.metadataCache = self._getMetadata();
18
+ }
19
+ return self.metadataCache;
20
+ };
21
+
22
+ LibraryMetadata.prototype._getJson = function()
23
+ {
24
+ var rootPath = require('path').resolve('./');
25
+ return require(path.join(rootPath, './package-lock.json'));
26
+ };
27
+
28
+ LibraryMetadata.prototype._getMetadata = function() {
29
+ var self = this;
30
+ try {
31
+ var pkjson = self._getJson();
32
+ var metadataCache = [];
33
+ var cacheGroup = [];
34
+ var itemCount = 0;
35
+ for (var pkg in pkjson.dependencies) {
36
+ if(itemCount == MAX_ITEMS_PER_GROUP) {
37
+ metadataCache.push(cacheGroup);
38
+ cacheGroup = [];
39
+ itemCount = 0;
40
+ }
41
+ cacheGroup.push({
42
+ name: pkg,
43
+ version: pkjson.dependencies[pkg].version
44
+ });
45
+ itemCount = itemCount + 1;
46
+ }
47
+ } catch(error) {
48
+ self.agent.logger.warn('Error reading package metadata ' + error);
49
+ }
50
+
51
+ if(cacheGroup.length) {
52
+ metadataCache.push(cacheGroup);
53
+ }
54
+ return metadataCache;
55
+ };
56
+
@@ -0,0 +1,311 @@
1
+ const http = require('http');
2
+ const https = require('https');
3
+ const url = require('url');
4
+ const HttpsProxyAgent = require('https-proxy-agent');
5
+
6
+ const LibraryMetadata = require('./library_metadata').LibraryMetadata;
7
+ const MessageSender = require('../libagent/message-sender').MessageSender;
8
+
9
+ const AUTH_PATH = "/auth/v1/oauth/token";
10
+ const EVENT_PATH = "/argento-agent/v1/report";
11
+ const REGISTER_PATH = "/argento-agent/v1/management";
12
+
13
+ const MIN_TIMER = 60 * 1000;
14
+ const DAY_TIMER = 24 * 60 * 60 * 1000;
15
+ const RETRY_THRESHOLD = 2;
16
+ const TIMEOUT = 1000;
17
+
18
+ exports.SecureApp = SecureApp;
19
+ function SecureApp() {
20
+ }
21
+
22
+ SecureApp.prototype.init = function(agent) {
23
+ var self = this;
24
+ self.agent = agent;
25
+ self.libraryMetadata = new LibraryMetadata(agent);
26
+ if(!process.env.SKIP_AUTH_FOR_TESTS) {
27
+ self.authTimerId = new MessageSender(self.agent, 10 * 1000, MIN_TIMER * 9, function () {
28
+ self.authToken();
29
+ });
30
+ } else {
31
+ self.agent.logger.debug('Secure App skipping auth as SKIP_AUTH_FOR_TESTS is ' + process.env.SKIP_AUTH_FOR_TESTS);
32
+ self.regTimerId = new MessageSender(self.agent, 10 * 1000, MIN_TIMER, function () {
33
+ self.register();
34
+ });
35
+ }
36
+ self.httpModule = (self.agent.opts.controllerSslEnabled) ? https : http;
37
+ self.libraryMetadata.init();
38
+ self.agent.logger.debug('Secure App module initialized');
39
+ };
40
+
41
+ SecureApp.prototype._sendRequestWithRetry = function(options, data, cb)
42
+ {
43
+ var self = this;
44
+ options.currentRetryAttempt = 0;
45
+ self._sendRequest(options, data, cb);
46
+ };
47
+
48
+ SecureApp.prototype._retryRequest = function(options, data, cb)
49
+ {
50
+ var self = this;
51
+ if (options.currentRetryAttempt === RETRY_THRESHOLD) {
52
+ return cb(null, new Error('Retry Threshold Reached'));
53
+ }
54
+ options.currentRetryAttempt++;
55
+ self.agent.logger.trace('SecureApp._retryRequest: Retry attempt ' + options.currentRetryAttempt + ' ' + options.path);
56
+ setTimeout(() => {
57
+ self._sendRequest(options, data, cb);
58
+ }, TIMEOUT);
59
+ };
60
+
61
+ SecureApp.prototype._sendRequest = function(options, data, cb) {
62
+ var self = this;
63
+ self.agent.logger.trace('SecureApp._sendRequest: ' + options.path + " Data: " + ((options.headers['Content-Type'] != 'application/json') ?
64
+ data.length.toString() : JSON.stringify(data).length.toString()));
65
+
66
+ var request = self.httpModule.request(options, function(response) {
67
+ const chunks = [];
68
+ response.on('data', data_chunk => chunks.push(data_chunk));
69
+ response.on('end', () => {
70
+ var statusCode = response.statusCode | 0;
71
+ // don't retry on http 413 (payload too large)
72
+ if(statusCode == 413) {
73
+ self.agent.logger.warn('Secure App Http Request failed ' + ' statusCode ' + statusCode);
74
+ return cb(null, new Error('Http Payload too large'));
75
+ }
76
+
77
+ if (statusCode != 200 || response.is_error) {
78
+ self.agent.logger.info('Secure App Http Request failed ' + ' statusCode ' + statusCode + ' is_error ' + response.is_error);
79
+ return self._retryRequest(options, data, cb);
80
+ }
81
+ let body = Buffer.concat(chunks);
82
+ cb(body, null);
83
+ });
84
+ });
85
+
86
+ request.on('error', function(error) {
87
+ self.agent.logger.info('Secure app http request error : ' + error);
88
+ return self._retryRequest(options, data, cb);
89
+ });
90
+
91
+ request.end(data);
92
+ };
93
+
94
+ SecureApp.prototype._requestHeaders = function(requestOptions) {
95
+ var self = this;
96
+ requestOptions.headers['Authorization'] = `Bearer ${self.accessToken}`;
97
+ requestOptions.headers['User-Agent'] = "NodeJs";
98
+ requestOptions.headers['appdynamics-agent-applicationName'] = self.agent.opts.applicationName;
99
+ requestOptions.headers['appdynamics-agent-tierName'] = self.agent.opts.tierName;
100
+ requestOptions.headers['appdynamics-agent-nodeName'] = self.agent.opts.nodeName;
101
+ requestOptions.headers['appdynamics-agent-accountName'] = 'singularity-agent@' + self.agent.opts.accountName;
102
+ if (self.uuid) {
103
+ requestOptions.headers['appdynamics-agent-nodeUUID'] = self.uuid;
104
+ }
105
+ };
106
+
107
+ SecureApp.prototype._requestOptions = function(path, length, type) {
108
+ var self = this;
109
+ var requestOptions = {
110
+ 'method': 'POST',
111
+ };
112
+
113
+ requestOptions.headers = {
114
+ 'Content-Length': length,
115
+ 'Content-Type': type
116
+ };
117
+
118
+ if (self.agent.opts.certificateFile) {
119
+ requestOptions['ca'] = self.agent.opts.certificateFile;
120
+ }
121
+
122
+ if(process.env.CONTROLLER_HOST_TEST) {
123
+ self.agent.logger.debug('Secure App using controller hostname CONTROLLER_HOST_TEST: ' + process.env.CONTROLLER_HOST_TEST);
124
+ requestOptions['hostname'] = process.env.CONTROLLER_HOST_TEST;
125
+ } else {
126
+ requestOptions['hostname'] = self.agent.opts.controllerHostName;
127
+ }
128
+ if(process.env.CONTROLLER_PORT_TEST) {
129
+ self.agent.logger.debug('Secure App using controller port CONTROLLER_PORT_TEST: ' + process.env.CONTROLLER_PORT_TEST);
130
+ requestOptions['port'] = process.env.CONTROLLER_PORT_TEST;
131
+ } else {
132
+ requestOptions['port'] = self.agent.opts.controllerPort;
133
+ }
134
+ requestOptions['path'] = path;
135
+
136
+ var proxy = {
137
+ hostName: self.agent.opts.proxyHost,
138
+ port: self.agent.opts.proxyPort,
139
+ userName: self.agent.opts.proxyUser,
140
+ password: ""
141
+ };
142
+
143
+ if (self.agent.opts.proxyPasswordFile) {
144
+ var fs = require('fs');
145
+ proxy.password = (fs.readFileSync(self.agent.opts.proxyPasswordFile, 'utf-8')).trim();
146
+ }
147
+
148
+ if (proxy.hostName) {
149
+ self.agent.logger.debug('Secure App using proxy');
150
+ var ro = requestOptions;
151
+ var proxyAuth = proxy.userName && proxy.password && Buffer.from(`${proxy.userName}:${proxy.password}`).toString('base64');
152
+
153
+ if (self.agent.opts.controllerSslEnabled) {
154
+ var proxyUrl = `http://${proxy.hostName}:${proxy.port}`;
155
+ var proxyOpts = url.parse(proxyUrl);
156
+ if (proxyAuth) {
157
+ proxyOpts.headers = {
158
+ 'Proxy-Authentication': `Basic ${proxyAuth}`
159
+ };
160
+ }
161
+ var agent = new HttpsProxyAgent(proxyOpts);
162
+ ro['agent'] = agent;
163
+ } else {
164
+ ro['path'] = `http://${ro.hostname}:${ro.port}${ro.path}`;
165
+ ro['headers']['Host'] = `${ro.hostname}:${ro.port}`;
166
+ ro['hostname'] = proxy.hostName;
167
+ ro['port'] = proxy.port;
168
+ if (proxyAuth) {
169
+ ro['headers']['Proxy-Authorization'] = `Basic ${proxyAuth}`;
170
+ }
171
+ }
172
+ }
173
+ return requestOptions;
174
+ };
175
+
176
+ SecureApp.prototype._initializeTimers = function() {
177
+ var self = this;
178
+ if (self.timersInitialized) {
179
+ return;
180
+ }
181
+ self.timersInitialized = true;
182
+
183
+ self.reportEventTimerId = new MessageSender(self.agent, 10 * 1000, DAY_TIMER, function () {
184
+ self.reportEvents();
185
+ });
186
+ };
187
+
188
+ SecureApp.prototype.authenticate = function(grantType) {
189
+ var self = this;
190
+ var postData = "password=" + self.agent.opts.accountAccessKey +
191
+ "&username=" + 'singularity-agent@' + self.agent.opts.accountName +
192
+ "&grant_type=" + grantType;
193
+
194
+ if(grantType == 'refresh_token') {
195
+ postData += '&refresh_token=' + self.refreshToken;
196
+ }
197
+
198
+ var requestOptions = self._requestOptions(AUTH_PATH, postData.length.toString(),
199
+ "application/x-www-form-urlencoded; charset=utf-8");
200
+
201
+ self._sendRequestWithRetry(requestOptions, postData, function(body, error) {
202
+ if (error != null) {
203
+ self.agent.logger.info('Secure App authentication failed');
204
+ self.accessToken = null;
205
+ self.refreshToken = null;
206
+ return;
207
+ }
208
+
209
+ try {
210
+ body = JSON.parse(body);
211
+ if (!self.accessToken) {
212
+ self.regTimerId = new MessageSender(self.agent, 10 * 1000, MIN_TIMER, function () {
213
+ self.register();
214
+ });
215
+ }
216
+
217
+ self.accessToken = body.access_token;
218
+ self.refreshToken = body.refresh_token;
219
+
220
+ self.agent.logger.debug('Secure App module authenticated');
221
+ } catch(err) {
222
+ self.accessToken = null;
223
+ self.refreshToken = null;
224
+ self.agent.logger.warn('Failed to parse Secure app authentication response ' + err);
225
+ }
226
+ });
227
+ };
228
+
229
+ SecureApp.prototype.authToken = function() {
230
+ var self = this;
231
+ if(!self.accessToken) {
232
+ self.authenticate("password");
233
+ } else {
234
+ self.authenticate("refresh_token");
235
+ }
236
+ };
237
+
238
+ SecureApp.prototype.register = function() {
239
+ var self = this;
240
+ var postData = JSON.stringify({
241
+ "message_type": 0,
242
+ "app": self.agent.opts.applicationName,
243
+ "tier": self.agent.opts.tierName,
244
+ "node": self.agent.opts.nodeName,
245
+ "access_key": self.agent.opts.accountAccessKey,
246
+ "account_name": self.agent.opts.accountName,
247
+ "file_name": "",
248
+ "epoch_msec": 0,
249
+ "force": false,
250
+ "version": 1,
251
+ "is_started": true,
252
+ "is_enabled": true,
253
+ "agent_type": "NodeJs",
254
+ "agent_build_version": ""
255
+ });
256
+
257
+ var requestOptions = self._requestOptions(REGISTER_PATH, postData.length.toString(), "application/json");
258
+ self._requestHeaders(requestOptions);
259
+
260
+ self._sendRequestWithRetry(requestOptions, postData, function(body, error) {
261
+ if (error != null) {
262
+ self.agent.logger.info('Secure App module registeration failed');
263
+ return;
264
+ }
265
+
266
+ try {
267
+ body = JSON.parse(body);
268
+ self.uuid = body.node_uuid;
269
+ self._initializeTimers();
270
+ self.agent.logger.debug('Secure App module registered');
271
+ } catch(err) {
272
+ self.agent.logger.warn('Failed to parse Secure app registration response ' + err);
273
+ }
274
+ });
275
+ };
276
+
277
+ SecureApp.prototype.sendVulnerabilityEvent = function() {
278
+ var self = this;
279
+ if(!self.vulnerabilityEvent) {
280
+ return;
281
+ }
282
+
283
+ self.vulnerabilityEvent.forEach((data, index) => {
284
+ var isDone = index == self.vulnerabilityEvent.length - 1 ? true : false;
285
+ var postData = JSON.stringify({
286
+ "id": 0,
287
+ "is_done": isDone,
288
+ "fragment_index": index,
289
+ "max_fragments": self.vulnerabilityEvent.length,
290
+ "nodejs_report_component_vulnerability_list": data
291
+ });
292
+ var requestOptions = self._requestOptions(EVENT_PATH, postData.length.toString(), "application/json");
293
+ self._requestHeaders(requestOptions);
294
+
295
+ self._sendRequestWithRetry(requestOptions, postData, function(body, error) {
296
+ if (error != null) {
297
+ self.agent.logger.info('Secure App module vulnerability event failed');
298
+ return;
299
+ }
300
+ self.agent.logger.info('Secure App module vulnerability event sent fragment');
301
+ });
302
+ });
303
+ };
304
+
305
+ SecureApp.prototype.reportEvents = function() {
306
+ var self = this;
307
+ if(!self.vulnerabilityEvent) {
308
+ self.vulnerabilityEvent = self.libraryMetadata.getMetadata();
309
+ }
310
+ self.sendVulnerabilityEvent();
311
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "appdynamics",
3
- "version": "22.2.0",
3
+ "version": "22.7.0",
4
4
  "description": "Performance Profiler and Monitor",
5
5
  "author": "AppDynamics, Inc.",
6
6
  "homepage": "https://www.appdynamics.com",
@@ -34,18 +34,20 @@
34
34
  "n": "^6.7.0"
35
35
  },
36
36
  "dependencies": {
37
- "@opentelemetry/api": "^1.0.3",
38
- "@opentelemetry/exporter-collector": "^0.25.0",
39
- "@opentelemetry/node": "^0.24.0",
40
- "@opentelemetry/semantic-conventions": "^1.0.1",
41
- "@opentelemetry/tracing": "^0.24.0",
37
+ "@opentelemetry/api": "~1.0.3",
38
+ "@opentelemetry/context-async-hooks": "~1.0.1",
39
+ "@opentelemetry/core": "~1.0.1",
40
+ "@opentelemetry/exporter-trace-otlp-proto": "~0.27.0",
41
+ "@opentelemetry/resources": "~1.0.1",
42
+ "@opentelemetry/sdk-trace-base": "~1.0.1",
43
+ "@opentelemetry/semantic-conventions": "~1.0.1",
42
44
  "cls-hooked": "4.2.2",
43
- "n": "^6.7.0",
44
- "shelljs": "^0.8.5",
45
+ "https-proxy-agent": "^5.0.0",
45
46
  "uuid": "^8.3.2",
46
- "appdynamics-libagent-napi": "https://cdn.appdynamics.com/packages/nodejs/22.2.0.0/appdynamics-libagent-napi-node.tgz",
47
- "appdynamics-native": "https://cdn.appdynamics.com/packages/nodejs/22.2.0.0/appdynamics-native-node.tgz",
48
- "appdynamics-protobuf": "https://cdn.appdynamics.com/packages/nodejs/22.2.0.0/appdynamics-protobuf-node.tgz"
47
+ "y18n": "^5.0.8",
48
+ "appdynamics-libagent-napi": "https://cdn.appdynamics.com/packages/nodejs/22.7.0.0/appdynamics-libagent-napi-node.tgz",
49
+ "appdynamics-native": "https://cdn.appdynamics.com/packages/nodejs/22.7.0.0/appdynamics-native-node.tgz",
50
+ "appdynamics-protobuf": "https://cdn.appdynamics.com/packages/nodejs/22.7.0.0/appdynamics-protobuf-node.tgz"
49
51
  },
50
52
  "engines": {
51
53
  "node": ">=12 <=v16.*"
package/packageBck.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "appdynamics",
3
- "version": "22.2.0",
3
+ "version": "22.7.0",
4
4
  "description": "Performance Profiler and Monitor",
5
5
  "author": "AppDynamics, Inc.",
6
6
  "homepage": "https://www.appdynamics.com",
@@ -34,18 +34,20 @@
34
34
  "n": "^6.7.0"
35
35
  },
36
36
  "dependencies": {
37
- "@opentelemetry/api": "^1.0.3",
38
- "@opentelemetry/exporter-collector": "^0.25.0",
39
- "@opentelemetry/node": "^0.24.0",
40
- "@opentelemetry/semantic-conventions": "^1.0.1",
41
- "@opentelemetry/tracing": "^0.24.0",
37
+ "@opentelemetry/api": "~1.0.3",
38
+ "@opentelemetry/context-async-hooks": "~1.0.1",
39
+ "@opentelemetry/core": "~1.0.1",
40
+ "@opentelemetry/exporter-trace-otlp-proto": "~0.27.0",
41
+ "@opentelemetry/resources": "~1.0.1",
42
+ "@opentelemetry/sdk-trace-base": "~1.0.1",
43
+ "@opentelemetry/semantic-conventions": "~1.0.1",
42
44
  "cls-hooked": "4.2.2",
43
- "n": "^6.7.0",
44
- "shelljs": "^0.8.5",
45
+ "https-proxy-agent": "^5.0.0",
45
46
  "uuid": "^8.3.2",
46
- "appdynamics-libagent-napi": "https://cdn.appdynamics.com/packages/nodejs/22.2.0.0/appdynamics-libagent-napi-node.tgz",
47
- "appdynamics-native": "https://cdn.appdynamics.com/packages/nodejs/22.2.0.0/appdynamics-native-node.tgz",
48
- "appdynamics-protobuf": "https://cdn.appdynamics.com/packages/nodejs/22.2.0.0/appdynamics-protobuf-node.tgz"
47
+ "y18n": "^5.0.8",
48
+ "appdynamics-libagent-napi": "https://cdn.appdynamics.com/packages/nodejs/22.7.0.0/appdynamics-libagent-napi-node.tgz",
49
+ "appdynamics-native": "https://cdn.appdynamics.com/packages/nodejs/22.7.0.0/appdynamics-native-node.tgz",
50
+ "appdynamics-protobuf": "https://cdn.appdynamics.com/packages/nodejs/22.7.0.0/appdynamics-protobuf-node.tgz"
49
51
  },
50
52
  "engines": {
51
53
  "node": ">=12 <=v16.*"