Added vscode settings

This commit is contained in:
Kristofers Solo
2022-04-28 20:54:44 +03:00
parent 245c3ca779
commit 837a479d82
25004 changed files with 2499800 additions and 0 deletions

View File

@@ -0,0 +1,116 @@
///<reference path="..\typings\globals\node\index.d.ts" />
"use strict";
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var url = require("url");
var ContractsModule = require("../Library/Contracts");
var Util = require("../Library/Util");
var RequestResponseHeaders = require("../Library/RequestResponseHeaders");
var RequestParser = require("./RequestParser");
/**
* Helper class to read data from the requst/response objects and convert them into the telemetry contract
*/
var ClientRequestParser = (function (_super) {
__extends(ClientRequestParser, _super);
function ClientRequestParser(requestOptions, request) {
_super.call(this);
if (request && request.method && requestOptions) {
// The ClientRequest.method property isn't documented, but is always there.
this.method = request.method;
this.url = ClientRequestParser._getUrlFromRequestOptions(requestOptions, request);
this.startTime = +new Date();
}
}
/**
* Called when the ClientRequest emits an error event.
*/
ClientRequestParser.prototype.onError = function (error, properties) {
this._setStatus(undefined, error, properties);
};
/**
* Called when the ClientRequest emits a response event.
*/
ClientRequestParser.prototype.onResponse = function (response, properties) {
this._setStatus(response.statusCode, undefined, properties);
this.targetIKeyHash =
response.headers && response.headers[RequestResponseHeaders.targetInstrumentationKeyHeader];
};
/**
* Gets a dependency data contract object for a completed ClientRequest.
*/
ClientRequestParser.prototype.getDependencyData = function () {
var urlObject = url.parse(this.url);
urlObject.search = undefined;
urlObject.hash = undefined;
var dependencyName = this.method.toUpperCase() + " " + urlObject.pathname;
var remoteDependency = new ContractsModule.Contracts.RemoteDependencyData();
remoteDependency.type = ContractsModule.Contracts.RemoteDependencyDataConstants.TYPE_HTTP;
if (this.targetIKeyHash) {
remoteDependency.type = "ApplicationInsights";
remoteDependency.target = urlObject.hostname + " | " + this.targetIKeyHash;
}
else {
remoteDependency.type = ContractsModule.Contracts.RemoteDependencyDataConstants.TYPE_HTTP;
remoteDependency.target = urlObject.hostname;
}
remoteDependency.name = dependencyName;
remoteDependency.data = this.url;
remoteDependency.duration = Util.msToTimeSpan(this.duration);
remoteDependency.success = this._isSuccess();
remoteDependency.resultCode = this.statusCode ? this.statusCode.toString() : null;
remoteDependency.properties = this.properties || {};
var data = new ContractsModule.Contracts.Data();
data.baseType = "Microsoft.ApplicationInsights.RemoteDependencyData";
data.baseData = remoteDependency;
return data;
};
/**
* Builds a URL from request options, using the same logic as http.request(). This is
* necessary because a ClientRequest object does not expose a url property.
*/
ClientRequestParser._getUrlFromRequestOptions = function (options, request) {
if (typeof options === 'string') {
options = url.parse(options);
}
else {
// Avoid modifying the original options object.
var originalOptions_1 = options;
options = {};
if (originalOptions_1) {
Object.keys(originalOptions_1).forEach(function (key) {
options[key] = originalOptions_1[key];
});
}
}
// Oddly, url.format ignores path and only uses pathname and search,
// so create them from the path, if path was specified
if (options.path) {
var parsedQuery = url.parse(options.path);
options.pathname = parsedQuery.pathname;
options.search = parsedQuery.search;
}
// Simiarly, url.format ignores hostname and port if host is specified,
// even if host doesn't have the port, but http.request does not work
// this way. It will use the port if one is not specified in host,
// effectively treating host as hostname, but will use the port specified
// in host if it exists.
if (options.host && options.port) {
// Force a protocol so it will parse the host as the host, not path.
// It is discarded and not used, so it doesn't matter if it doesn't match
var parsedHost = url.parse("http://" + options.host);
if (!parsedHost.port && options.port) {
options.hostname = options.host;
delete options.host;
}
}
// Mix in default values used by http.request and others
options.protocol = options.protocol || request.agent.protocol;
options.hostname = options.hostname || 'localhost';
return url.format(options);
};
return ClientRequestParser;
}(RequestParser));
module.exports = ClientRequestParser;

View File

@@ -0,0 +1,100 @@
///<reference path="..\typings\globals\node\index.d.ts" />
"use strict";
var http = require("http");
var https = require("https");
var Logging = require("../Library/Logging");
var Util = require("../Library/Util");
var RequestResponseHeaders = require("../Library/RequestResponseHeaders");
var ClientRequestParser = require("./ClientRequestParser");
var AutoCollectClientRequests = (function () {
function AutoCollectClientRequests(client) {
if (!!AutoCollectClientRequests.INSTANCE) {
throw new Error("Client request tracking should be configured from the applicationInsights object");
}
AutoCollectClientRequests.INSTANCE = this;
this._client = client;
}
AutoCollectClientRequests.prototype.enable = function (isEnabled) {
this._isEnabled = isEnabled;
if (this._isEnabled && !this._isInitialized) {
this._initialize();
}
};
AutoCollectClientRequests.prototype.isInitialized = function () {
return this._isInitialized;
};
AutoCollectClientRequests.prototype._initialize = function () {
var _this = this;
this._isInitialized = true;
var originalRequest = http.request;
http.request = function (options) {
var requestArgs = [];
for (var _i = 1; _i < arguments.length; _i++) {
requestArgs[_i - 1] = arguments[_i];
}
var request = originalRequest.call.apply(originalRequest, [http, options].concat(requestArgs));
if (request && options && !options[AutoCollectClientRequests.disableCollectionRequestOption]) {
AutoCollectClientRequests.trackRequest(_this._client, options, request);
}
return request;
};
// On node >= v0.11.12, https.request just calls http.request (with additional options).
// But on older versions, https.request needs to be patched also.
// The regex matches versions < 0.11.12 (avoiding a semver package dependency).
if (/^0\.([0-9]\.)|(10\.)|(11\.([0-9]|10|11)$)/.test(process.versions.node)) {
var originalHttpsRequest_1 = https.request;
https.request = function (options) {
var requestArgs = [];
for (var _i = 1; _i < arguments.length; _i++) {
requestArgs[_i - 1] = arguments[_i];
}
var request = originalHttpsRequest_1.call.apply(originalHttpsRequest_1, [https, options].concat(requestArgs));
if (request && options && !options[AutoCollectClientRequests.disableCollectionRequestOption]) {
AutoCollectClientRequests.trackRequest(_this._client, options, request);
}
return request;
};
}
};
/**
* Tracks an outgoing request. Because it may set headers this method must be called before
* writing content to or ending the request.
*/
AutoCollectClientRequests.trackRequest = function (client, requestOptions, request, properties) {
if (!requestOptions || !request || !client) {
Logging.info("AutoCollectClientRequests.trackRequest was called with invalid parameters: ", !requestOptions, !request, !client);
return;
}
var requestParser = new ClientRequestParser(requestOptions, request);
// Add the source ikey hash to the request headers, if a value was not already provided.
// The getHeader/setHeader methods aren't available on very old Node versions, and
// are not included in the v0.10 type declarations currently used. So check if the
// methods exist before invoking them.
if (client.config && client.config.instrumentationKeyHash &&
Util.canIncludeCorrelationHeader(client, requestParser.getUrl()) &&
request['getHeader'] && request['setHeader'] &&
!request['getHeader'](RequestResponseHeaders.sourceInstrumentationKeyHeader)) {
request['setHeader'](RequestResponseHeaders.sourceInstrumentationKeyHeader, client.config.instrumentationKeyHash);
}
// Collect dependency telemetry about the request when it finishes.
if (request.on) {
request.on('response', function (response) {
requestParser.onResponse(response, properties);
var context = { "http.RequestOptions": requestOptions, "http.ClientRequest": request, "http.ClientResponse": response };
client.track(requestParser.getDependencyData(), null, context);
});
request.on('error', function (e) {
requestParser.onError(e, properties);
var context = { "http.RequestOptions": requestOptions, "http.ClientRequest": request, "Error": e };
client.track(requestParser.getDependencyData(), null, context);
});
}
};
AutoCollectClientRequests.prototype.dispose = function () {
AutoCollectClientRequests.INSTANCE = null;
this._isInitialized = false;
};
AutoCollectClientRequests.disableCollectionRequestOption = 'disableAppInsightsAutoCollection';
return AutoCollectClientRequests;
}());
module.exports = AutoCollectClientRequests;

View File

@@ -0,0 +1,22 @@
"use strict";
var AutoCollectConsole = (function () {
function AutoCollectConsole(client) {
if (!!AutoCollectConsole.INSTANCE) {
throw new Error("Console logging adapter tracking should be configured from the applicationInsights object");
}
this._client = client;
AutoCollectConsole.INSTANCE = this;
}
AutoCollectConsole.prototype.enable = function (isEnabled) {
// todo: investigate feasibility/utility of this; does it make sense to have a logging adapter in node?
};
AutoCollectConsole.prototype.isInitialized = function () {
return this._isInitialized;
};
AutoCollectConsole.prototype.dispose = function () {
AutoCollectConsole.INSTANCE = null;
};
AutoCollectConsole._methodNames = ["debug", "info", "log", "warn", "error"];
return AutoCollectConsole;
}());
module.exports = AutoCollectConsole;

View File

@@ -0,0 +1,191 @@
"use strict";
var Util = require("../Library/Util");
var CorrelationContextManager = (function () {
function CorrelationContextManager() {
}
/**
* Provides the current Context.
* The context is the most recent one entered into for the current
* logical chain of execution, including across asynchronous calls.
*/
CorrelationContextManager.getCurrentContext = function () {
if (!CorrelationContextManager.enabled) {
return null;
}
return Zone.current.get("context");
};
/**
* A helper to generate objects conforming to the CorrelationContext interface
*/
CorrelationContextManager.generateContextObject = function (parentId, operationName, operationId) {
operationId = operationId || Util.newGuid();
parentId = parentId || operationId;
if (this.enabled) {
return {
operation: {
name: operationName,
id: operationId,
parentId: parentId
},
customProperties: {}
};
}
return null;
};
/**
* Runs a function inside a given Context.
* All logical children of the execution path that entered this Context
* will receive this Context object on calls to GetCurrentContext.
*/
CorrelationContextManager.runWithContext = function (context, fn) {
if (CorrelationContextManager.enabled) {
var newZone = Zone.current.fork({
name: "AI-" + ((context && context.operation.parentId) || "Unknown"),
properties: { context: context }
});
newZone.run(fn);
}
else {
fn();
}
};
/**
* Patches a callback to restore the correct Context when getCurrentContext
* is run within it. This is necessary if automatic correlation fails to work
* with user-included libraries.
*
* The supplied callback will be given the same context that was present for
* the call to wrapCallback. */
CorrelationContextManager.wrapCallback = function (fn) {
if (CorrelationContextManager.enabled) {
return Zone.current.wrap(fn, "User-wrapped method");
}
return fn;
};
/**
* Enables the CorrelationContextManager.
*/
CorrelationContextManager.enable = function () {
if (!this.isNodeVersionCompatible()) {
this.enabled = false;
return;
}
// Load in Zone.js
require("zone.js");
// Run patches for Zone.js
if (!this.hasEverEnabled) {
this.hasEverEnabled = true;
this.patchError();
this.patchTimers(["setTimeout", "setInterval"]);
this.patchRedis();
}
this.enabled = true;
};
/**
* Disables the CorrelationContextManager.
*/
CorrelationContextManager.disable = function () {
this.enabled = false;
};
/**
* Reports if the CorrelationContextManager is able to run in this environment
*/
CorrelationContextManager.isNodeVersionCompatible = function () {
// Unit tests warn of errors < 3.3 from timer patching. All versions before 4 were 0.x
var nodeVer = process.versions.node.split(".");
return parseInt(nodeVer[0]) > 3 || (parseInt(nodeVer[0]) > 2 && parseInt(nodeVer[1]) > 2);
};
// Patch methods that manually go async that Zone doesn't catch
CorrelationContextManager.requireForPatch = function (module) {
var req = null;
try {
req = require(module);
}
catch (e) {
return null;
}
return req;
};
// A good example of patching a third party library to respect context.
// send_command is always used in this library to send data out.
// By overwriting the function to capture the callback provided to it,
// and wrapping that callback, we ensure that consumers of this library
// will have context persisted.
CorrelationContextManager.patchRedis = function () {
var redis = this.requireForPatch("redis");
if (redis && redis.RedisClient) {
var orig = redis.RedisClient.prototype.send_command;
redis.RedisClient.prototype.send_command = function () {
var args = Array.prototype.slice.call(arguments);
var lastArg = args[args.length - 1];
if (typeof lastArg === "function") {
args[args.length - 1] = Zone.current.wrap(lastArg, "AI.CCM.patchRedis");
}
else if (lastArg instanceof Array && typeof lastArg[lastArg.length - 1] === "function") {
// The last argument can be an array!
var lastIndexLastArg = lastArg[lastArg.length - 1];
lastArg[lastArg.length - 1] = Zone.current.wrap(lastIndexLastArg, "AI.CCM.patchRedis");
}
return orig.apply(this, args);
};
}
};
// Zone.js breaks concatenation of timer return values.
// This fixes that.
CorrelationContextManager.patchTimers = function (methodNames) {
methodNames.forEach(function (methodName) {
var orig = global[methodName];
global[methodName] = function () {
var ret = orig.apply(this, arguments);
ret.toString = function () {
if (this.data && typeof this.data.handleId !== 'undefined') {
return this.data.handleId.toString();
}
else {
return Object.prototype.toString.call(this);
}
};
return ret;
};
});
};
// Zone.js breaks deepEqual on error objects (by making internal properties enumerable).
// This fixes that by subclassing the error object and making all properties not enumerable
CorrelationContextManager.patchError = function () {
var orig = global.Error;
// New error handler
function AppInsightsAsyncCorrelatedErrorWrapper() {
if (!(this instanceof AppInsightsAsyncCorrelatedErrorWrapper)) {
return AppInsightsAsyncCorrelatedErrorWrapper.apply(Object.create(AppInsightsAsyncCorrelatedErrorWrapper.prototype), arguments);
}
orig.apply(this, arguments);
// getOwnPropertyNames should be a superset of Object.keys...
// This appears to not always be the case
var props = Object.getOwnPropertyNames(this).concat(Object.keys(this));
// Zone.js will automatically create some hidden properties at read time.
// We need to proactively make those not enumerable as well as the currently visible properties
for (var i = 0; i < props.length; i++) {
var propertyName = props[i];
var hiddenPropertyName = Zone['__symbol__'](propertyName);
Object.defineProperty(this, propertyName, { enumerable: false });
Object.defineProperty(this, hiddenPropertyName, { enumerable: false, writable: true });
}
return this;
}
// Inherit from the Zone.js error handler
AppInsightsAsyncCorrelatedErrorWrapper.prototype = orig.prototype;
// We need this loop to copy outer methods like Error.captureStackTrace
var props = Object.getOwnPropertyNames(orig);
for (var i = 0; i < props.length; i++) {
var propertyName = props[i];
if (!AppInsightsAsyncCorrelatedErrorWrapper[propertyName]) {
Object.defineProperty(AppInsightsAsyncCorrelatedErrorWrapper, propertyName, Object.getOwnPropertyDescriptor(orig, propertyName));
}
}
global.Error = AppInsightsAsyncCorrelatedErrorWrapper;
};
CorrelationContextManager.enabled = false;
CorrelationContextManager.hasEverEnabled = false;
return CorrelationContextManager;
}());
exports.CorrelationContextManager = CorrelationContextManager;

View File

@@ -0,0 +1,150 @@
///<reference path="..\typings\globals\node\index.d.ts" />
"use strict";
var ContractsModule = require("../Library/Contracts");
var Util = require("../Library/Util");
var AutoCollectExceptions = (function () {
function AutoCollectExceptions(client) {
if (!!AutoCollectExceptions.INSTANCE) {
throw new Error("Exception tracking should be configured from the applicationInsights object");
}
AutoCollectExceptions.INSTANCE = this;
this._client = client;
}
AutoCollectExceptions.prototype.isInitialized = function () {
return this._isInitialized;
};
AutoCollectExceptions.prototype.enable = function (isEnabled) {
var _this = this;
if (isEnabled) {
this._isInitialized = true;
var self = this;
if (!this._exceptionListenerHandle) {
var handle = function (reThrow, error) {
var data = AutoCollectExceptions.getExceptionData(error, false);
var envelope = _this._client.getEnvelope(data);
_this._client.channel.handleCrash(envelope);
if (reThrow) {
throw error;
}
};
this._exceptionListenerHandle = handle.bind(this, true);
this._rejectionListenerHandle = handle.bind(this, false);
process.on("uncaughtException", this._exceptionListenerHandle);
process.on("unhandledRejection", this._rejectionListenerHandle);
}
}
else {
if (this._exceptionListenerHandle) {
process.removeListener("uncaughtException", this._exceptionListenerHandle);
process.removeListener("unhandledRejection", this._rejectionListenerHandle);
this._exceptionListenerHandle = undefined;
this._rejectionListenerHandle = undefined;
delete this._exceptionListenerHandle;
delete this._rejectionListenerHandle;
}
}
};
/**
* Track an exception
* @param error the exception to track
* @param handledAt where this exception was handled (leave null for unhandled)
* @param properties additional properties
* @param measurements metrics associated with this event, displayed in Metrics Explorer on the portal. Defaults to empty.
*/
AutoCollectExceptions.getExceptionData = function (error, isHandled, properties, measurements) {
var exception = new ContractsModule.Contracts.ExceptionData();
exception.properties = properties;
exception.severityLevel = ContractsModule.Contracts.SeverityLevel.Error;
exception.measurements = measurements;
exception.exceptions = [];
var stack = error["stack"];
var exceptionDetails = new ContractsModule.Contracts.ExceptionDetails();
exceptionDetails.message = error.message;
exceptionDetails.typeName = error.name;
exceptionDetails.parsedStack = this.parseStack(stack);
exceptionDetails.hasFullStack = Util.isArray(exceptionDetails.parsedStack) && exceptionDetails.parsedStack.length > 0;
exception.exceptions.push(exceptionDetails);
var data = new ContractsModule.Contracts.Data();
data.baseType = "Microsoft.ApplicationInsights.ExceptionData";
data.baseData = exception;
return data;
};
AutoCollectExceptions.parseStack = function (stack) {
var parsedStack = undefined;
if (typeof stack === "string") {
var frames = stack.split("\n");
parsedStack = [];
var level = 0;
var totalSizeInBytes = 0;
for (var i = 0; i <= frames.length; i++) {
var frame = frames[i];
if (_StackFrame.regex.test(frame)) {
var parsedFrame = new _StackFrame(frames[i], level++);
totalSizeInBytes += parsedFrame.sizeInBytes;
parsedStack.push(parsedFrame);
}
}
// DP Constraint - exception parsed stack must be < 32KB
// remove frames from the middle to meet the threshold
var exceptionParsedStackThreshold = 32 * 1024;
if (totalSizeInBytes > exceptionParsedStackThreshold) {
var left = 0;
var right = parsedStack.length - 1;
var size = 0;
var acceptedLeft = left;
var acceptedRight = right;
while (left < right) {
// check size
var lSize = parsedStack[left].sizeInBytes;
var rSize = parsedStack[right].sizeInBytes;
size += lSize + rSize;
if (size > exceptionParsedStackThreshold) {
// remove extra frames from the middle
var howMany = acceptedRight - acceptedLeft + 1;
parsedStack.splice(acceptedLeft, howMany);
break;
}
// update pointers
acceptedLeft = left;
acceptedRight = right;
left++;
right--;
}
}
}
return parsedStack;
};
AutoCollectExceptions.prototype.dispose = function () {
AutoCollectExceptions.INSTANCE = null;
this._isInitialized = false;
};
AutoCollectExceptions.INSTANCE = null;
return AutoCollectExceptions;
}());
var _StackFrame = (function () {
function _StackFrame(frame, level) {
this.sizeInBytes = 0;
this.level = level;
this.method = "<no_method>";
this.assembly = Util.trim(frame);
var matches = frame.match(_StackFrame.regex);
if (matches && matches.length >= 5) {
this.method = Util.trim(matches[2]) || this.method;
this.fileName = Util.trim(matches[4]) || "<no_filename>";
this.line = parseInt(matches[5]) || 0;
}
this.sizeInBytes += this.method.length;
this.sizeInBytes += this.fileName.length;
this.sizeInBytes += this.assembly.length;
// todo: these might need to be removed depending on how the back-end settles on their size calculation
this.sizeInBytes += _StackFrame.baseSize;
this.sizeInBytes += this.level.toString().length;
this.sizeInBytes += this.line.toString().length;
}
// regex to match stack frames from ie/chrome/ff
// methodName=$2, fileName=$4, lineNo=$5, column=$6
_StackFrame.regex = /^([\s]+at)?(.*?)(\@|\s\(|\s)([^\(\@\n]+):([0-9]+):([0-9]+)(\)?)$/;
_StackFrame.baseSize = 58; //'{"method":"","level":,"assembly":"","fileName":"","line":}'.length
return _StackFrame;
}());
module.exports = AutoCollectExceptions;

View File

@@ -0,0 +1,231 @@
///<reference path="..\typings\globals\node\index.d.ts" />
"use strict";
var http = require("http");
var os = require("os");
var ContractsModule = require("../Library/Contracts");
var Logging = require("../Library/Logging");
var PerfCounterType;
(function (PerfCounterType) {
PerfCounterType[PerfCounterType["ProcessorTime"] = 0] = "ProcessorTime";
PerfCounterType[PerfCounterType["AvailableMemory"] = 1] = "AvailableMemory";
PerfCounterType[PerfCounterType["RequestsPerSec"] = 2] = "RequestsPerSec";
PerfCounterType[PerfCounterType["PrivateBytes"] = 3] = "PrivateBytes";
PerfCounterType[PerfCounterType["RequestExecutionTime"] = 4] = "RequestExecutionTime";
PerfCounterType[PerfCounterType["PercentProcessorTime"] = 5] = "PercentProcessorTime";
})(PerfCounterType || (PerfCounterType = {}));
var AutoCollectPerformance = (function () {
function AutoCollectPerformance(client) {
if (!!AutoCollectPerformance.INSTANCE) {
throw new Error("Performance tracking should be configured from the applicationInsights object");
}
AutoCollectPerformance.INSTANCE = this;
this._isInitialized = false;
this._client = client;
}
AutoCollectPerformance.prototype.enable = function (isEnabled) {
var _this = this;
this._isEnabled = isEnabled;
if (this._isEnabled && !this._isInitialized) {
this._initialize();
}
if (isEnabled) {
if (!this._handle) {
this._lastCpus = os.cpus();
this._lastRequests = {
totalRequestCount: AutoCollectPerformance._totalRequestCount,
totalFailedRequestCount: AutoCollectPerformance._totalFailedRequestCount,
time: +new Date
};
this._handle = setInterval(function () { return _this.trackPerformance(); }, 10000);
}
}
else {
if (this._handle) {
clearInterval(this._handle);
this._handle = undefined;
}
}
};
AutoCollectPerformance.prototype.isInitialized = function () {
return this._isInitialized;
};
AutoCollectPerformance.prototype._initialize = function () {
var _this = this;
this._isInitialized = true;
var originalServer = http.createServer;
http.createServer = function (onRequest) {
return originalServer(function (request, response) {
if (_this._isEnabled) {
AutoCollectPerformance.countRequest(request, response);
}
if (typeof onRequest === "function") {
onRequest(request, response);
}
});
};
};
AutoCollectPerformance.countRequest = function (request, response) {
var _this = this;
var start = +new Date;
if (!request || !response) {
Logging.warn("AutoCollectPerformance.countRequest was called with invalid parameters: ", !!request, !!response);
return;
}
// response listeners
if (typeof response.once === "function") {
response.once("finish", function () {
var end = +new Date;
_this._lastRequestExecutionTime = end - start;
AutoCollectPerformance._totalRequestCount++;
if (response.statusCode >= 400) {
AutoCollectPerformance._totalFailedRequestCount++;
}
});
}
};
AutoCollectPerformance.prototype.trackPerformance = function () {
this._trackCpu();
this._trackMemory();
this._trackNetwork();
};
// this is necessary to accommodate some point-in-time UI quirks
AutoCollectPerformance.prototype._trackLegacyPerformance = function (counterType, value) {
var perfmetric = new ContractsModule.Contracts.PerformanceCounterData();
// semantic descriptions of these can be found here: https://support.microsoft.com/en-us/kb/815159/
switch (counterType) {
case PerfCounterType.ProcessorTime:
perfmetric.categoryName = "Process";
perfmetric.counterName = "% Processor Time";
break;
case PerfCounterType.AvailableMemory:
perfmetric.categoryName = "Memory";
perfmetric.counterName = "Available Bytes";
break;
case PerfCounterType.RequestsPerSec:
perfmetric.categoryName = "ASP.NET Applications";
perfmetric.counterName = "Requests/Sec";
break;
case PerfCounterType.PrivateBytes:
perfmetric.categoryName = "Process";
perfmetric.counterName = "Private Bytes";
break;
case PerfCounterType.RequestExecutionTime:
perfmetric.categoryName = "ASP.NET Applications";
perfmetric.counterName = "Request Execution Time";
break;
case PerfCounterType.PercentProcessorTime:
perfmetric.categoryName = "Processor";
perfmetric.counterName = "% Processor Time";
break;
}
perfmetric.count = 1;
perfmetric.kind = ContractsModule.Contracts.DataPointType.Aggregation;
perfmetric.max = value;
perfmetric.min = value;
perfmetric.stdDev = 0;
perfmetric.value = value;
var data = new ContractsModule.Contracts.Data();
data.baseType = "Microsoft.ApplicationInsights.PerformanceCounterData";
data.baseData = perfmetric;
this._client.track(data);
};
AutoCollectPerformance.prototype._trackCpu = function () {
// this reports total ms spent in each category since the OS was booted, to calculate percent it is necessary
// to find the delta since the last measurement
var cpus = os.cpus();
if (cpus && cpus.length && this._lastCpus && cpus.length === this._lastCpus.length) {
var totalUser = 0;
var totalSys = 0;
var totalNice = 0;
var totalIdle = 0;
var totalIrq = 0;
for (var i = 0; !!cpus && i < cpus.length; i++) {
var cpu = cpus[i];
var lastCpu = this._lastCpus[i];
var name = "% cpu(" + i + ") ";
var model = cpu.model;
var speed = cpu.speed;
var times = cpu.times;
var lastTimes = lastCpu.times;
// user cpu time (or) % CPU time spent in user space
var user = (times.user - lastTimes.user) || 0;
totalUser += user;
// system cpu time (or) % CPU time spent in kernel space
var sys = (times.sys - lastTimes.sys) || 0;
totalSys += sys;
// user nice cpu time (or) % CPU time spent on low priority processes
var nice = (times.nice - lastTimes.nice) || 0;
totalNice += nice;
// idle cpu time (or) % CPU time spent idle
var idle = (times.idle - lastTimes.idle) || 0;
totalIdle += idle;
// irq (or) % CPU time spent servicing/handling hardware interrupts
var irq = (times.irq - lastTimes.irq) || 0;
totalIrq += irq;
var total = (user + sys + nice + idle + irq) || 1; // don"t let this be 0 since it is a divisor
this._client.trackMetric(name + "user", user / total);
}
var combinedName = "% total cpu ";
var combinedTotal = (totalUser + totalSys + totalNice + totalIdle + totalIrq) || 1;
this._client.trackMetric(combinedName + "user", totalUser / combinedTotal);
this._client.trackMetric(combinedName + "sys", totalSys / combinedTotal);
this._client.trackMetric(combinedName + "nice", totalNice / combinedTotal);
this._client.trackMetric(combinedName + "idle", totalIdle / combinedTotal);
this._client.trackMetric(combinedName + "irq", totalIrq / combinedTotal);
// todo: remove this legacy counter once the UI updates (~june 2015)
this._trackLegacyPerformance(PerfCounterType.ProcessorTime, totalUser / combinedTotal);
this._trackLegacyPerformance(PerfCounterType.PercentProcessorTime, (combinedTotal - totalIdle) / combinedTotal);
}
this._lastCpus = cpus;
};
AutoCollectPerformance.prototype._trackMemory = function () {
var totalMem = os.totalmem();
var freeMem = os.freemem();
var usedMem = totalMem - freeMem;
var percentUsedMem = usedMem / (totalMem || 1);
var percentAvailableMem = freeMem / (totalMem || 1);
this._client.trackMetric("Memory Used", usedMem);
this._client.trackMetric("Memory Free", freeMem);
this._client.trackMetric("Memory Total", totalMem);
this._client.trackMetric("% Memory Used", percentUsedMem);
this._client.trackMetric("% Memory Free", percentAvailableMem);
// todo: remove this legacy counter once the UI updates (~june 2015)
this._trackLegacyPerformance(PerfCounterType.AvailableMemory, freeMem);
this._trackLegacyPerformance(PerfCounterType.PrivateBytes, usedMem);
};
AutoCollectPerformance.prototype._trackNetwork = function () {
// track total request counters
var lastRequests = this._lastRequests;
var requests = {
totalRequestCount: AutoCollectPerformance._totalRequestCount,
totalFailedRequestCount: AutoCollectPerformance._totalFailedRequestCount,
time: +new Date
};
var intervalRequests = (requests.totalRequestCount - lastRequests.totalRequestCount) || 0;
var intervalFailedRequests = (requests.totalFailedRequestCount - lastRequests.totalFailedRequestCount) || 0;
var elapsedMs = requests.time - lastRequests.time;
var elapsedSeconds = elapsedMs / 1000;
if (elapsedMs > 0) {
var requestsPerSec = intervalRequests / elapsedSeconds;
var failedRequestsPerSec = intervalFailedRequests / elapsedSeconds;
this._client.trackMetric("Total Requests", requests.totalRequestCount);
this._client.trackMetric("Total Failed Requests", requests.totalFailedRequestCount);
this._client.trackMetric("Requests per Second", requestsPerSec);
this._client.trackMetric("Failed Requests per Second", failedRequestsPerSec);
this._client.trackMetric("Last Request Execution Time", AutoCollectPerformance._lastRequestExecutionTime);
// todo: remove this legacy counter once the UI updates (~june 2015)
this._trackLegacyPerformance(PerfCounterType.RequestsPerSec, requestsPerSec);
this._trackLegacyPerformance(PerfCounterType.RequestExecutionTime, AutoCollectPerformance._lastRequestExecutionTime);
}
this._lastRequests = requests;
};
AutoCollectPerformance.prototype.dispose = function () {
AutoCollectPerformance.INSTANCE = null;
this._isInitialized = false;
};
AutoCollectPerformance._totalRequestCount = 0;
AutoCollectPerformance._totalFailedRequestCount = 0;
AutoCollectPerformance._lastRequestExecutionTime = 0;
return AutoCollectPerformance;
}());
module.exports = AutoCollectPerformance;

View File

@@ -0,0 +1,45 @@
"use strict";
/**
* Base class for helpers that read data from HTTP requst/response objects and convert them
* into the telemetry contract objects.
*/
var RequestParser = (function () {
function RequestParser() {
}
/**
* Gets a url parsed out from request options
*/
RequestParser.prototype.getUrl = function () {
return this.url;
};
RequestParser.prototype.RequestParser = function () {
this.startTime = +new Date();
};
RequestParser.prototype._setStatus = function (status, error, properties) {
var endTime = +new Date();
this.duration = endTime - this.startTime;
this.statusCode = status;
if (error) {
if (!properties) {
properties = {};
}
if (typeof error === "string") {
properties["error"] = error;
}
else if (error instanceof Error) {
properties["error"] = error.message;
}
else if (typeof error === "object") {
for (var key in error) {
properties[key] = error[key] && error[key].toString && error[key].toString();
}
}
}
this.properties = properties;
};
RequestParser.prototype._isSuccess = function () {
return (0 < this.statusCode) && (this.statusCode < 400);
};
return RequestParser;
}());
module.exports = RequestParser;

View File

@@ -0,0 +1,145 @@
///<reference path="..\typings\globals\node\index.d.ts" />
"use strict";
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var url = require("url");
var ContractsModule = require("../Library/Contracts");
var Util = require("../Library/Util");
var RequestResponseHeaders = require("../Library/RequestResponseHeaders");
var RequestParser = require("./RequestParser");
/**
* Helper class to read data from the requst/response objects and convert them into the telemetry contract
*/
var ServerRequestParser = (function (_super) {
__extends(ServerRequestParser, _super);
function ServerRequestParser(request, requestId) {
_super.call(this);
if (request) {
this.requestId = requestId || Util.newGuid();
this.method = request.method;
this.url = this._getAbsoluteUrl(request);
this.startTime = +new Date();
this.rawHeaders = request.headers || request.rawHeaders;
this.socketRemoteAddress = request.socket && request.socket.remoteAddress;
this.userAgent = request.headers && request.headers["user-agent"];
this.sourceIKeyHash =
request.headers && request.headers[RequestResponseHeaders.sourceInstrumentationKeyHeader];
this.parentId =
request.headers && request.headers[RequestResponseHeaders.parentIdHeader];
this.operationId =
request.headers && request.headers[RequestResponseHeaders.rootIdHeader];
if (request.connection) {
this.connectionRemoteAddress = request.connection.remoteAddress;
this.legacySocketRemoteAddress = request.connection["socket"] && request.connection["socket"].remoteAddress;
}
}
}
ServerRequestParser.prototype.onError = function (error, properties, ellapsedMilliseconds) {
this._setStatus(undefined, error, properties);
};
ServerRequestParser.prototype.onResponse = function (response, properties, ellapsedMilliseconds) {
this._setStatus(response.statusCode, undefined, properties);
if (ellapsedMilliseconds) {
this.duration = ellapsedMilliseconds;
}
};
ServerRequestParser.prototype.getRequestData = function () {
var requestData = new ContractsModule.Contracts.RequestData();
requestData.id = this.requestId;
requestData.name = this.method + " " + url.parse(this.url).pathname;
requestData.url = this.url;
requestData.source = this.sourceIKeyHash;
requestData.duration = Util.msToTimeSpan(this.duration);
requestData.responseCode = this.statusCode ? this.statusCode.toString() : null;
requestData.success = this._isSuccess();
requestData.properties = this.properties;
var data = new ContractsModule.Contracts.Data();
data.baseType = "Microsoft.ApplicationInsights.RequestData";
data.baseData = requestData;
return data;
};
ServerRequestParser.prototype.getRequestTags = function (tags) {
// create a copy of the context for requests since client info will be used here
var newTags = {};
for (var key in tags) {
newTags[key] = tags[key];
}
// don't override tags if they are already set
newTags[ServerRequestParser.keys.locationIp] = tags[ServerRequestParser.keys.locationIp] || this._getIp();
newTags[ServerRequestParser.keys.sessionId] = tags[ServerRequestParser.keys.sessionId] || this._getId("ai_session");
newTags[ServerRequestParser.keys.userId] = tags[ServerRequestParser.keys.userId] || this._getId("ai_user");
newTags[ServerRequestParser.keys.userAgent] = tags[ServerRequestParser.keys.userAgent] || this.userAgent;
newTags[ServerRequestParser.keys.operationName] = this.getOperationName(tags);
newTags[ServerRequestParser.keys.operationParentId] = this.getOperationParentId(tags);
newTags[ServerRequestParser.keys.operationId] = this.getOperationId(tags);
return newTags;
};
ServerRequestParser.prototype.getOperationId = function (tags) {
return tags[ServerRequestParser.keys.operationId] || this.operationId;
};
ServerRequestParser.prototype.getOperationParentId = function (tags) {
return tags[ServerRequestParser.keys.operationParentId] || this.parentId || this.getOperationId(tags);
};
ServerRequestParser.prototype.getOperationName = function (tags) {
return tags[ServerRequestParser.keys.operationName] || this.method + " " + url.parse(this.url).pathname;
};
ServerRequestParser.prototype.getRequestId = function () {
return this.requestId;
};
ServerRequestParser.prototype._getAbsoluteUrl = function (request) {
if (!request.headers) {
return request.url;
}
var encrypted = request.connection ? request.connection.encrypted : null;
var requestUrl = url.parse(request.url);
var pathName = requestUrl.pathname;
var search = requestUrl.search;
var absoluteUrl = url.format({
protocol: encrypted ? "https" : "http",
host: request.headers.host,
pathname: pathName,
search: search
});
return absoluteUrl;
};
ServerRequestParser.prototype._getIp = function () {
// regex to match ipv4 without port
// Note: including the port would cause the payload to be rejected by the data collector
var ipMatch = /[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/;
var check = function (str) {
var results = ipMatch.exec(str);
if (results) {
return results[0];
}
};
var ip = check(this.rawHeaders["x-forwarded-for"])
|| check(this.rawHeaders["x-client-ip"])
|| check(this.rawHeaders["x-real-ip"])
|| check(this.connectionRemoteAddress)
|| check(this.socketRemoteAddress)
|| check(this.legacySocketRemoteAddress);
// node v12 returns this if the address is "localhost"
if (!ip
&& this.connectionRemoteAddress
&& this.connectionRemoteAddress.substr
&& this.connectionRemoteAddress.substr(0, 2) === "::") {
ip = "127.0.0.1";
}
return ip;
};
ServerRequestParser.prototype._getId = function (name) {
var cookie = (this.rawHeaders && this.rawHeaders["cookie"] &&
typeof this.rawHeaders["cookie"] === 'string' && this.rawHeaders["cookie"]) || "";
var value = ServerRequestParser.parseId(Util.getCookie(name, cookie));
return value;
};
ServerRequestParser.parseId = function (cookieValue) {
return cookieValue.substr(0, cookieValue.indexOf('|'));
};
ServerRequestParser.keys = new ContractsModule.Contracts.ContextTagKeys();
return ServerRequestParser;
}(RequestParser));
module.exports = ServerRequestParser;

View File

@@ -0,0 +1,169 @@
///<reference path="..\typings\globals\node\index.d.ts" />
"use strict";
var http = require("http");
var https = require("https");
var Logging = require("../Library/Logging");
var Util = require("../Library/Util");
var RequestResponseHeaders = require("../Library/RequestResponseHeaders");
var ServerRequestParser = require("./ServerRequestParser");
var CorrelationContextManager_1 = require("./CorrelationContextManager");
var AutoCollectServerRequests = (function () {
function AutoCollectServerRequests(client) {
if (!!AutoCollectServerRequests.INSTANCE) {
throw new Error("Server request tracking should be configured from the applicationInsights object");
}
AutoCollectServerRequests.INSTANCE = this;
this._client = client;
}
AutoCollectServerRequests.prototype.enable = function (isEnabled) {
this._isEnabled = isEnabled;
// Autocorrelation requires automatic monitoring of incoming server requests
// Disabling autocollection but enabling autocorrelation will still enable
// request monitoring but will not produce request events
if ((this._isAutoCorrelating || this._isEnabled) && !this._isInitialized) {
this.useAutoCorrelation(this._isAutoCorrelating);
this._initialize();
}
};
AutoCollectServerRequests.prototype.useAutoCorrelation = function (isEnabled) {
if (isEnabled && !this._isAutoCorrelating) {
CorrelationContextManager_1.CorrelationContextManager.enable();
}
else if (!isEnabled && this._isAutoCorrelating) {
CorrelationContextManager_1.CorrelationContextManager.disable();
}
this._isAutoCorrelating = isEnabled;
};
AutoCollectServerRequests.prototype.isInitialized = function () {
return this._isInitialized;
};
AutoCollectServerRequests.prototype.isAutoCorrelating = function () {
return this._isAutoCorrelating;
};
AutoCollectServerRequests.prototype._generateCorrelationContext = function (requestParser) {
if (!this._isAutoCorrelating) {
return;
}
return CorrelationContextManager_1.CorrelationContextManager.generateContextObject(requestParser.getRequestId(), requestParser.getOperationName(this._client.context.tags), requestParser.getOperationId(this._client.context.tags));
};
AutoCollectServerRequests.prototype._initialize = function () {
var _this = this;
this._isInitialized = true;
var originalHttpServer = http.createServer;
http.createServer = function (onRequest) {
// todo: get a pointer to the server so the IP address can be read from server.address
return originalHttpServer(function (request, response) {
// Set up correlation context
var requestParser = new ServerRequestParser(request);
var correlationContext = _this._generateCorrelationContext(requestParser);
CorrelationContextManager_1.CorrelationContextManager.runWithContext(correlationContext, function () {
if (_this._isEnabled) {
// Auto collect request
AutoCollectServerRequests.trackRequest(_this._client, request, response, null, requestParser);
}
if (typeof onRequest === "function") {
onRequest(request, response);
}
});
});
};
var originalHttpsServer = https.createServer;
https.createServer = function (options, onRequest) {
return originalHttpsServer(options, function (request, response) {
// Set up correlation context
var requestParser = new ServerRequestParser(request);
var correlationContext = _this._generateCorrelationContext(requestParser);
CorrelationContextManager_1.CorrelationContextManager.runWithContext(correlationContext, function () {
if (_this._isEnabled) {
AutoCollectServerRequests.trackRequest(_this._client, request, response, null, requestParser);
}
if (typeof onRequest === "function") {
onRequest(request, response);
}
});
});
};
};
/**
* Tracks a request synchronously (doesn't wait for response 'finish' event)
*/
AutoCollectServerRequests.trackRequestSync = function (client, request, response, ellapsedMilliseconds, properties, error) {
if (!request || !response || !client) {
Logging.info("AutoCollectServerRequests.trackRequestSync was called with invalid parameters: ", !request, !response, !client);
return;
}
AutoCollectServerRequests.addResponseIKeyHeader(client, response);
// store data about the request
var correlationContext = CorrelationContextManager_1.CorrelationContextManager.getCurrentContext();
var requestParser = new ServerRequestParser(request, (correlationContext && correlationContext.operation.parentId) || Util.newGuid());
// Overwrite correlation context with request parser results
if (correlationContext) {
correlationContext.operation.id = requestParser.getOperationId(client.context.tags) || correlationContext.operation.id;
correlationContext.operation.name = requestParser.getOperationName(client.context.tags) || correlationContext.operation.name;
correlationContext.operation.parentId = requestParser.getRequestId() || correlationContext.operation.parentId;
}
AutoCollectServerRequests.endRequest(client, requestParser, request, response, ellapsedMilliseconds, properties, error);
};
/**
* Tracks a request by listening to the response 'finish' event
*/
AutoCollectServerRequests.trackRequest = function (client, request, response, properties, _requestParser) {
if (!request || !response || !client) {
Logging.info("AutoCollectServerRequests.trackRequest was called with invalid parameters: ", !request, !response, !client);
return;
}
// store data about the request
var correlationContext = CorrelationContextManager_1.CorrelationContextManager.getCurrentContext();
var requestParser = _requestParser || new ServerRequestParser(request, correlationContext && correlationContext.operation.parentId || Util.newGuid());
if (Util.canIncludeCorrelationHeader(client, requestParser.getUrl())) {
AutoCollectServerRequests.addResponseIKeyHeader(client, response);
}
// Overwrite correlation context with request parser results (if not an automatic track. we've already precalculated the correlation context in that case)
if (correlationContext && !_requestParser) {
correlationContext.operation.id = requestParser.getOperationId(client.context.tags) || correlationContext.operation.id;
correlationContext.operation.name = requestParser.getOperationName(client.context.tags) || correlationContext.operation.name;
correlationContext.operation.parentId = requestParser.getOperationParentId(client.context.tags) || correlationContext.operation.parentId;
}
// response listeners
if (response.once) {
response.once("finish", function () {
AutoCollectServerRequests.endRequest(client, requestParser, request, response, null, properties, null);
});
}
// track a failed request if an error is emitted
if (request.on) {
request.on("error", function (error) {
AutoCollectServerRequests.endRequest(client, requestParser, request, response, null, properties, error);
});
}
};
/**
* Add the target ikey hash to the response headers, if not already provided.
*/
AutoCollectServerRequests.addResponseIKeyHeader = function (client, response) {
if (client.config && client.config.instrumentationKeyHash &&
response.getHeader && response.setHeader &&
!response.getHeader(RequestResponseHeaders.targetInstrumentationKeyHeader) &&
!response.headersSent) {
response.setHeader(RequestResponseHeaders.targetInstrumentationKeyHeader, client.config.instrumentationKeyHash);
}
};
AutoCollectServerRequests.endRequest = function (client, requestParser, request, response, ellapsedMilliseconds, properties, error) {
if (error) {
requestParser.onError(error, properties, ellapsedMilliseconds);
}
else {
requestParser.onResponse(response, properties, ellapsedMilliseconds);
}
var context = { "http.ServerRequest": request, "http.ServerResponse": response };
var data = requestParser.getRequestData();
var tags = requestParser.getRequestTags(client.context.tags);
client.track(data, tags, context);
};
AutoCollectServerRequests.prototype.dispose = function () {
AutoCollectServerRequests.INSTANCE = null;
this._isInitialized = false;
};
return AutoCollectServerRequests;
}());
module.exports = AutoCollectServerRequests;