Skip to content

Commit

Permalink
Merge pull request #316 from Microsoft/develop
Browse files Browse the repository at this point in the history
Merge develop to master for 0.22 release
  • Loading branch information
OsvaldoRosado authored Sep 8, 2017
2 parents 4325e12 + dd34598 commit 7a6357b
Show file tree
Hide file tree
Showing 74 changed files with 3,158 additions and 2,066 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@ obj/

# Ignore Node files
node_modules/**
Tests/SampleApp/node_modules/**
Tests/SampleConsoleApp/node_modules/**

# Ignore Visual Studio files
*.suo
*.sln
*.dat
*.njsproj
.vscode/

# Ignore webstorm files
.idea
Expand Down
20 changes: 14 additions & 6 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "0.1.0",
"command": "tsc",
"command": "npm",
"isShellCommand": true,
"args": ["-w", "-p", "."],
"showOutput": "silent",
"isWatching": true,
"problemMatcher": "$tsc-watch"
"suppressTaskName": true,
"tasks": [
{
"taskName": "test",
"args": ["run", "test"],
"isTestCommand": true
},
{
"taskName": "build",
"args": ["run", "build"],
"isTestCommand": false
}
]
}
7 changes: 4 additions & 3 deletions AutoCollection/Console.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Client = require("../Library/Client");
import TelemetryClient = require("../Library/TelemetryClient");
import Logging = require("../Library/Logging");

import {enable as enableConsole} from "./diagnostic-channel/console.sub";
Expand All @@ -12,10 +12,10 @@ class AutoCollectConsole {
public static INSTANCE: AutoCollectConsole;
private static _methodNames = ["debug", "info", "log", "warn", "error"];

private _client: Client;
private _client: TelemetryClient;
private _isInitialized: boolean;

constructor(client: Client) {
constructor(client: TelemetryClient) {
if(!!AutoCollectConsole.INSTANCE) {
throw new Error("Console logging adapter tracking should be configured from the applicationInsights object");
}
Expand All @@ -35,6 +35,7 @@ class AutoCollectConsole {

public dispose() {
AutoCollectConsole.INSTANCE = null;
this.enable(false);
}
}

Expand Down
141 changes: 14 additions & 127 deletions AutoCollection/Exceptions.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
import http = require("http");

import Contracts = require("../Declarations/Contracts");
import Client = require("../Library/Client");
import TelemetryClient = require("../Library/TelemetryClient");
import Sender = require("../Library/Sender");
import Queue = require("../Library/Channel");
import Util = require("../Library/Util");

class AutoCollectExceptions {

public static INSTANCE: AutoCollectExceptions = null;
public static get UNCAUGHT_EXCEPTION_HANDLER_NAME(): string { return "uncaughtException"; }
public static get UNHANDLED_REJECTION_HANDLER_NAME(): string { return "unhandledRejection"; }

private _exceptionListenerHandle: (reThrow: boolean, error: Error) => void;
private _rejectionListenerHandle: (reThrow: boolean, error: Error) => void;
private _client: Client;
private _client: TelemetryClient;
private _isInitialized: boolean;

constructor(client: Client) {
if(!!AutoCollectExceptions.INSTANCE) {
constructor(client: TelemetryClient) {
if (!!AutoCollectExceptions.INSTANCE) {
throw new Error("Exception tracking should be configured from the applicationInsights object");
}

Expand All @@ -29,29 +31,28 @@ class AutoCollectExceptions {
}

public enable(isEnabled: boolean) {
if(isEnabled) {
if (isEnabled) {
this._isInitialized = true;
var self = this;
if (!this._exceptionListenerHandle) {
var handle = (reThrow: boolean, error: Error) => {
var data = AutoCollectExceptions.getExceptionData(error, false);
var envelope = this._client.getEnvelope(data);
this._client.channel.handleCrash(envelope);
this._client.trackException({ exception: error });
this._client.flush({ isAppCrashing: true });
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);
process.on(AutoCollectExceptions.UNCAUGHT_EXCEPTION_HANDLER_NAME, this._exceptionListenerHandle);
process.on(AutoCollectExceptions.UNHANDLED_REJECTION_HANDLER_NAME, this._rejectionListenerHandle);
}

} else {
if (this._exceptionListenerHandle) {
process.removeListener("uncaughtException", this._exceptionListenerHandle);
process.removeListener("unhandledRejection", this._rejectionListenerHandle);
process.removeListener(AutoCollectExceptions.UNCAUGHT_EXCEPTION_HANDLER_NAME, this._exceptionListenerHandle);
process.removeListener(AutoCollectExceptions.UNHANDLED_REJECTION_HANDLER_NAME, this._rejectionListenerHandle);
this._exceptionListenerHandle = undefined;
this._rejectionListenerHandle = undefined;
delete this._exceptionListenerHandle;
Expand All @@ -60,127 +61,13 @@ class AutoCollectExceptions {
}
}

/**
* 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.
*/
public static getExceptionData(error: Error, isHandled: boolean, properties?:{ [key: string]: string; }, measurements?:{ [key: string]: number; }): Contracts.Data<Contracts.ExceptionData> {
var exception = new Contracts.ExceptionData();
exception.properties = properties;
exception.severityLevel = Contracts.SeverityLevel.Error;
exception.measurements = measurements;
exception.exceptions = [];

var stack = error["stack"];
var exceptionDetails = new 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 Contracts.Data<Contracts.ExceptionData>();
data.baseType = Contracts.DataTypes.EXCEPTION;
data.baseData = exception;
return data;
}

private static parseStack(stack: any): _StackFrame[] {
var parsedStack: _StackFrame[] = 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;
}

public dispose() {
AutoCollectExceptions.INSTANCE = null;
this.enable(false);
this._isInitialized = false;
}
}

class _StackFrame {

// regex to match stack frames from ie/chrome/ff
// methodName=$2, fileName=$4, lineNo=$5, column=$6
public static regex = /^([\s]+at)?(.*?)(\@|\s\(|\s)([^\(\@\n]+):([0-9]+):([0-9]+)(\)?)$/;
public static baseSize = 58; //'{"method":"","level":,"assembly":"","fileName":"","line":}'.length
public sizeInBytes = 0;
public level: number;
public method: string;
public assembly: string;
public fileName: string;
public line: number;

constructor(frame: string, level: number) {
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;
}
}

export = AutoCollectExceptions;
Loading

0 comments on commit 7a6357b

Please sign in to comment.