diff --git a/AutoCollection/Exceptions.ts b/AutoCollection/Exceptions.ts index b235c958..4a9348f8 100644 --- a/AutoCollection/Exceptions.ts +++ b/AutoCollection/Exceptions.ts @@ -67,14 +67,14 @@ class AutoCollectExceptions { * @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; }) { - + public static getExceptionData(error: Error, isHandled: boolean, properties?:{ [key: string]: string; }, measurements?:{ [key: string]: number; }) { var exception = new ContractsModule.Contracts.ExceptionData(); exception.handledAt = isHandled ? "User" : "Unhandled"; exception.properties = properties; exception.severityLevel = ContractsModule.Contracts.SeverityLevel.Error; - exception.properties = properties; + exception.measurements = measurements; exception.exceptions = []; var stack = error["stack"]; diff --git a/Library/Channel.ts b/Library/Channel.ts index 749f9b6b..04fe0582 100644 --- a/Library/Channel.ts +++ b/Library/Channel.ts @@ -24,8 +24,8 @@ class Channel { /** * Enable or disable offline mode */ - public setOfflineMode(value: boolean) { - this._sender.setOfflineMode(value); + public setOfflineMode(value: boolean, resendInterval?: number) { + this._sender.setOfflineMode(value, resendInterval); } /** diff --git a/Library/Client.ts b/Library/Client.ts index b79ca20c..cdc79ef8 100644 --- a/Library/Client.ts +++ b/Library/Client.ts @@ -87,12 +87,12 @@ class Client { * @param properties map[string, string] - additional data used to filter events and metrics in the portal. Defaults to empty. * @param measurements map[string, number] - metrics associated with this event, displayed in Metrics Explorer on the portal. Defaults to empty. */ - public trackException(exception: Error, properties?: { [key: string]: string; }) { + public trackException(exception: Error, properties?: { [key: string]: string; }, measurements?:{ [key: string]: number; }) { if (!Util.isError(exception)) { exception = new Error(exception); } - var data = ExceptionTracking.getExceptionData(exception, true, properties) + var data = ExceptionTracking.getExceptionData(exception, true, properties, measurements); this.track(data); } diff --git a/Library/Config.ts b/Library/Config.ts index 5a64a2aa..5214a135 100644 --- a/Library/Config.ts +++ b/Library/Config.ts @@ -19,7 +19,7 @@ class Config { constructor(instrumentationKey?: string) { this.instrumentationKey = instrumentationKey || this._getInstrumentationKey(); - this.endpointUrl = "http://dc.services.visualstudio.com/v2/track"; + this.endpointUrl = "https://dc.services.visualstudio.com/v2/track"; this.sessionRenewalMs = 30 * 60 * 1000; this.sessionExpirationMs = 24 * 60 * 60 * 1000; this.maxBatchSize = 250; diff --git a/Library/Contracts.ts b/Library/Contracts.ts index ad1430ef..c726fe94 100644 --- a/Library/Contracts.ts +++ b/Library/Contracts.ts @@ -81,8 +81,8 @@ export module Contracts { this.deviceOEMName = "ai.device.oemName"; this.deviceOS = "ai.device.os"; this.deviceOSVersion = "ai.device.osVersion"; - this.deviceRoleInstance = "ai.device.roleInstance"; - this.deviceRoleName = "ai.device.roleName"; + this.deviceRoleInstance = "ai.cloud.roleInstance"; + this.deviceRoleName = "ai.cloud.role"; this.deviceScreenResolution = "ai.device.screenResolution"; this.deviceType = "ai.device.type"; this.deviceMachineName = "ai.device.machineName"; diff --git a/Library/Sender.ts b/Library/Sender.ts index 062fe0ea..9a57c96f 100644 --- a/Library/Sender.ts +++ b/Library/Sender.ts @@ -20,19 +20,24 @@ class Sender { private _onSuccess: (response: string) => void; private _onError: (error: Error) => void; private _enableOfflineMode: boolean; + protected _resendInterval: number; constructor(getUrl: () => string, onSuccess?: (response: string) => void, onError?: (error: Error) => void) { this._getUrl = getUrl; this._onSuccess = onSuccess; this._onError = onError; this._enableOfflineMode = false; + this._resendInterval = Sender.WAIT_BETWEEN_RESEND; } /** * Enable or disable offline mode */ - public setOfflineMode(value: boolean) { + public setOfflineMode(value: boolean, resendInterval?: number) { this._enableOfflineMode = value; + if (typeof resendInterval === 'number' && resendInterval >= 0) { + this._resendInterval = Math.floor(resendInterval); + } } public send(payload: Buffer, callback?: (string) => void) { @@ -90,7 +95,7 @@ class Sender { if (this._enableOfflineMode) { // try to send any cached events if the user is back online if (res.statusCode === 200) { - setTimeout(() => this._sendFirstFileOnDisk(), Sender.WAIT_BETWEEN_RESEND); + setTimeout(() => this._sendFirstFileOnDisk(), this._resendInterval); // store to disk in case of burst throttling } else if (res.statusCode === 206 || res.statusCode === 429 || diff --git a/Tests/EndToEnd.tests.ts b/Tests/EndToEnd.tests.ts index 076deedc..dcde1051 100644 --- a/Tests/EndToEnd.tests.ts +++ b/Tests/EndToEnd.tests.ts @@ -212,7 +212,7 @@ describe("EndToEnd", () => { beforeEach(() => { AppInsights.client = undefined; - this.request = sinon.stub(http, 'request'); + this.request = sinon.stub(https, 'request'); this.writeFile = sinon.stub(fs, 'writeFile'); this.writeFileSync = sinon.stub(fs, 'writeFileSync'); this.exists = sinon.stub(fs, 'exists').yields(true); @@ -247,7 +247,7 @@ describe("EndToEnd", () => { done(); }); }); - }); + }); it("stores data to disk when enabled", (done) => { var req = new fakeRequest(); @@ -272,10 +272,9 @@ describe("EndToEnd", () => { var req = new fakeRequest(false); var res = new fakeResponse(); res.statusCode = 200; - Sender.WAIT_BETWEEN_RESEND =0; var client = AppInsights.getClient("key"); - client.channel.setOfflineMode(true); + client.channel.setOfflineMode(true, 0); client.trackEvent("test event"); @@ -307,8 +306,5 @@ describe("EndToEnd", () => { assert(this.existsSync.callCount === 1); assert(this.writeFileSync.callCount === 1); }); - - - }); }); diff --git a/Tests/Library/Client.tests.ts b/Tests/Library/Client.tests.ts index 73ecf683..f5cf9a3e 100644 --- a/Tests/Library/Client.tests.ts +++ b/Tests/Library/Client.tests.ts @@ -120,19 +120,41 @@ describe("Library/Client", () => { }); describe("#trackException()", () => { - it("should track Exception with correct data", () => { + it("should track Exception with correct data - Error only", () => { trackStub.reset(); client.trackException(new Error(name)); + + assert.ok(trackStub.calledOnce); + + var args = trackStub.args; + assert.equal(args[0][0].baseData.exceptions[0].message, name); + }); + + it("should track Exception with correct data - Error and properties", () => { + trackStub.reset(); client.trackException(new Error(name), properties); - assert.ok(trackStub.calledTwice); + assert.ok(trackStub.calledOnce); var args = trackStub.args; + assert.equal(args[0][0].baseData.exceptions[0].message, name); - assert.equal(args[1][0].baseData.exceptions[0].message, name); - assert.deepEqual(args[1][0].baseData.properties, properties); + assert.deepEqual(args[0][0].baseData.properties, properties); }); + it("should track Exception with correct data - Error, properties and measurements", () => { + trackStub.reset(); + client.trackException(new Error(name), properties, measurements); + + assert.ok(trackStub.calledOnce); + + var args = trackStub.args; + + assert.equal(args[0][0].baseData.exceptions[0].message, name); + assert.deepEqual(args[0][0].baseData.properties, properties); + assert.deepEqual(args[0][0].baseData.measurements, measurements); + }); + it("should not crash with invalid input", () => { invalidInputHelper("trackException"); }); diff --git a/Tests/Library/Sender.tests.ts b/Tests/Library/Sender.tests.ts new file mode 100644 index 00000000..e1a276a9 --- /dev/null +++ b/Tests/Library/Sender.tests.ts @@ -0,0 +1,43 @@ +/// +/// + +import assert = require("assert"); + +import Sender = require("../../Library/Sender"); + +class SenderMock extends Sender { + public getResendInterval() { + return this._resendInterval; + } +} + +describe("Library/Sender", () => { + var sender:SenderMock; + + beforeEach(() => { + sender = new SenderMock(() => "https://www.microsoft.com"); + }); + + describe("#setOfflineMode(value, resendInterval)", () => { + it("default resend interval is 60 seconds", () => { + sender.setOfflineMode(true); + assert.equal(Sender.WAIT_BETWEEN_RESEND, sender.getResendInterval()); + }); + + it("resend interval can be configured", () => { + sender.setOfflineMode(true, 0); + assert.equal(0, sender.getResendInterval()); + + sender.setOfflineMode(true, 1234); + assert.equal(1234, sender.getResendInterval()); + + sender.setOfflineMode(true, 1234.56); + assert.equal(1234, sender.getResendInterval()); + }); + + it("resend interval can't be negative", () => { + sender.setOfflineMode(true, -1234); + assert.equal(Sender.WAIT_BETWEEN_RESEND, sender.getResendInterval()); + }); + }); +}); diff --git a/applicationinsights.ts b/applicationinsights.ts index c50b9e26..9b5be25d 100644 --- a/applicationinsights.ts +++ b/applicationinsights.ts @@ -138,13 +138,14 @@ class ApplicationInsights { /** * Enable or disable offline mode to cache events when client is offline (disabled by default) - * @param value if true events that occured while client is offline will be cahced on disk + * @param value if true events that occured while client is offline will be cached on disk + * @param resendInterval. The wait interval for resending cached events. * @returns {ApplicationInsights} this class */ - public static setOfflineMode(value: boolean) { + public static setOfflineMode(value: boolean, resendInterval?: number) { ApplicationInsights._isOfflineMode = value; if (ApplicationInsights.client && ApplicationInsights.client.channel){ - ApplicationInsights.client.channel.setOfflineMode(value); + ApplicationInsights.client.channel.setOfflineMode(value, resendInterval); } return ApplicationInsights; diff --git a/package.json b/package.json index dd75410a..e5b2c62e 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "applicationinsights", "license": "MIT", "bugs": "/~https://github.com/Microsoft/ApplicationInsights-node.js/issues", - "version": "0.15.19", + "version": "0.16.0", "description": "Microsoft Application Insights module for Node.JS", "repository": { "type": "git", @@ -49,7 +49,7 @@ "chai": "^2.3.0", "mocha": "^2.2.4", "node-mocks-http": "^1.2.3", - "sinon": "^1.14.1", + "sinon": "^1.17.6", "typescript": "^1.8.9" } }