Showing details in case of CORS exception

This commit is contained in:
Max Shekhovtcov 2015-06-01 16:52:16 -07:00
Родитель 0c34a1db62
Коммит a90e49b604
4 изменённых файлов: 94 добавлений и 17 удалений

Просмотреть файл

@ -34,14 +34,14 @@ class UtilTests extends TestClass {
// mock cookies
((document) => {
var cookies = {};
document.__defineGetter__('cookie', () => {
document.__defineGetter__('cookie',() => {
var output = [];
for (var cookieName in cookies) {
output.push(cookieName + "=" + cookies[cookieName]);
}
return output.join(";");
});
document.__defineSetter__('cookie', (s) => {
document.__defineSetter__('cookie',(s) => {
var indexOfSeparator = s.indexOf("=");
var key = s.substr(0, indexOfSeparator);
var value = s.substring(indexOfSeparator + 1);
@ -93,7 +93,7 @@ class UtilTests extends TestClass {
this.testCase({
name: "UtilTests: new GUID",
test: () => {
sinon.stub(Math, "random", () => 0);
sinon.stub(Math, "random",() => 0);
var expected = "00000000-0000-4000-8000-000000000000";
var actual = Microsoft.ApplicationInsights.Util.newGuid();
Assert.equal(expected, actual, "expected guid was generated");
@ -163,7 +163,7 @@ class UtilTests extends TestClass {
Assert.ok(Microsoft.ApplicationInsights.Util.stringToBoolOrDefault(null) === false);
Assert.ok(Microsoft.ApplicationInsights.Util.stringToBoolOrDefault("") === false);
Assert.ok(Microsoft.ApplicationInsights.Util.stringToBoolOrDefault("asdf") === false);
Assert.ok(Microsoft.ApplicationInsights.Util.stringToBoolOrDefault(0) === false);
Assert.ok(Microsoft.ApplicationInsights.Util.stringToBoolOrDefault(0) === false);
Assert.ok(Microsoft.ApplicationInsights.Util.stringToBoolOrDefault({ asfd: "sdf" }) === false);
Assert.ok(Microsoft.ApplicationInsights.Util.stringToBoolOrDefault(new Object()) === false);
@ -171,6 +171,16 @@ class UtilTests extends TestClass {
Assert.ok(Microsoft.ApplicationInsights.Util.stringToBoolOrDefault("TrUe") === true);
}
});
this.testCase({
name: "UtilTests: isCrossOriginError",
test: () => {
Assert.ok(Microsoft.ApplicationInsights.Util.isCrossOriginError("Script error.", "", 0, 0, null) === true);
Assert.ok(Microsoft.ApplicationInsights.Util.isCrossOriginError("Script error.", "http://microsoft.com", 0, 0, null)
=== false);
}
});
}
}
new UtilTests().registerTests();

Просмотреть файл

@ -32,6 +32,39 @@ module Microsoft.ApplicationInsights.Telemetry {
this.handledAt = handledAt || "unhandled";
this.exceptions = [new _ExceptionDetails(exception)];
}
/**
* Creates a simple exception with 1 stack frame. Useful for manual constracting of exception.
*/
public static CreateSimpleException(message: string, typeName: string, assembly: string, fileName: string,
details: string, line: number, handledAt?: string): Telemetry.Exception {
// We can't override constructors, so throwing a fake error to use existing constructor and override all fields after that.
var exceptionTelemetry;
try {
throw new Error();
} catch (e) {
exceptionTelemetry = new Telemetry.Exception(e);
}
var stack = exceptionTelemetry.exceptions[0].parsedStack[0];
stack.assembly = assembly;
stack.fileName = fileName;
stack.level = 0;
stack.line = line;
stack.method = "unknown";
var exception = exceptionTelemetry.exceptions[0];
exception.hasFullStack = true;
exception.message = message;
exception.parsedStack = null;
exception.stack = details;
exception.typeName = typeName;
exceptionTelemetry.handledAt = handledAt || "unhandled";
return exceptionTelemetry;
}
}
class _ExceptionDetails extends AI.ExceptionDetails implements ISerializable {
@ -45,7 +78,7 @@ module Microsoft.ApplicationInsights.Telemetry {
stack: false,
parsedStack: []
};
constructor(exception: Error) {
super();
this.typeName = Common.DataSanitizer.sanitizeString(exception.name);

Просмотреть файл

@ -135,5 +135,17 @@
return hour + ":" + min + ":" + sec + "." + ms;
}
/**
* Checks if error has no meaningful data inside. Ususally such errors are received by window.onerror when error
* happens in a script from other domain (cross origin, CORS).
*/
public static isCrossOriginError(message: string, url: string, lineNumber: number, columnNumber: number, error: Error): boolean {
return (message == "Script error." || message == "Script error")
&& url == ""
&& lineNumber == 0
&& columnNumber == 0
&& error == null;
}
}
}

Просмотреть файл

@ -65,7 +65,7 @@ module Microsoft.ApplicationInsights {
emitLineDelimitedJson: () => this.config.emitLineDelimitedJson,
maxBatchSizeInBytes: () => this.config.maxBatchSizeInBytes,
maxBatchInterval: () => this.config.maxBatchInterval,
disableTelemetry: () => this.config.disableTelemetry
disableTelemetry: () => this.config.disableTelemetry
}
this.context = new ApplicationInsights.TelemetryContext(configGetters);
@ -311,6 +311,21 @@ module Microsoft.ApplicationInsights {
}
}
/**
* In case of CORS exceptions - construct an exception manually.
* See this for more info: http://stackoverflow.com/questions/5913978/cryptic-script-error-reported-in-javascript-in-chrome-and-firefox
*/
private SendCORSException() {
var exceptionData = Microsoft.ApplicationInsights.Telemetry.Exception.CreateSimpleException(
"Script error.", "Error", "unknown", "unknown",
"There is no details for this exception because it violated browsers same-origin policy. For cross-domain error reporting you can use crossorigtin attribute together with appropriate CORS HTTP headers. For more information please see http://www.w3.org/TR/cors/",
0, null);
var data = new ApplicationInsights.Telemetry.Common.Data<ApplicationInsights.Telemetry.Exception>(Telemetry.Exception.dataType, exceptionData);
var envelope = new Telemetry.Common.Envelope(data, Telemetry.Exception.envelopeType);
this.context.track(envelope);
}
/**
* The custom error handler for Application Insights
* @param {string} message - The error message
@ -321,21 +336,28 @@ module Microsoft.ApplicationInsights {
*/
public _onerror(message: string, url: string, lineNumber: number, columnNumber: number, error: Error) {
try {
if (!Util.isError(error)) {
// ensure that we have an error object (browser may not pass an error i.e safari)
try {
throw new Error(message);
} catch (exception) {
error = exception;
if (!error["stack"]) {
error["stack"] = "@" + url + ":" + lineNumber + ":" + (columnNumber || 0);
if (Util.isCrossOriginError(message, url, lineNumber, columnNumber, error)) {
this.SendCORSException();
} else {
if (!Util.isError(error)) {
// ensure that we have an error object (browser may not pass an error i.e safari)
try {
throw new Error(message);
} catch (exception) {
error = exception;
if (!error["stack"]) {
error["stack"] = "@" + url + ":" + lineNumber + ":" + (columnNumber || 0);
}
}
this.trackException(error);
}
}
this.trackException(error);
} catch (exception) {
_InternalLogging.throwInternalNonUserActionable(LoggingSeverity.CRITICAL, "_onerror threw an exception: " + JSON.stringify(exception) + " while logging error: " + error.name + ", " + error.message);
var errorString =
error ? (error.name + ", " + error.message) : "null";
_InternalLogging.throwInternalNonUserActionable(LoggingSeverity.CRITICAL, "_onerror threw an exception: " + JSON.stringify(exception) + " while logging error: " + errorString);
}
}
}