Skip to content

Commit

Permalink
Handle JSON stringify errors (#867)
Browse files Browse the repository at this point in the history
* Handle JSON stringify errors

* Statsbeat test reliability

* Update

* Add error handling to get more accurate error in tests

* Update

* Using promises instead of callbacks
Remove interval check as it makes test unreliable
  • Loading branch information
hectorhdzg authored Nov 8, 2021
1 parent b4fccad commit f4e1bc4
Show file tree
Hide file tree
Showing 6 changed files with 219 additions and 229 deletions.
152 changes: 75 additions & 77 deletions AutoCollection/Statsbeat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,47 +164,45 @@ class Statsbeat {
}

public async trackShortIntervalStatsbeats() {
this._getResourceProvider(async () => {
let networkProperties = {
"os": this._os,
"rp": this._resourceProvider,
"cikey": this._cikey,
"runtimeVersion": this._runtimeVersion,
"language": this._language,
"version": this._sdkVersion,
"attach": this._attach,
}
this._trackRequestDuration(networkProperties);
this._trackRequestsCount(networkProperties);
await this._sendStatsbeats();
});
await this._getResourceProvider();
let networkProperties = {
"os": this._os,
"rp": this._resourceProvider,
"cikey": this._cikey,
"runtimeVersion": this._runtimeVersion,
"language": this._language,
"version": this._sdkVersion,
"attach": this._attach,
}
this._trackRequestDuration(networkProperties);
this._trackRequestsCount(networkProperties);
await this._sendStatsbeats();
}

public async trackLongIntervalStatsbeats() {
this._getResourceProvider(async () => {
let commonProperties = {
"os": this._os,
"rp": this._resourceProvider,
"cikey": this._cikey,
"runtimeVersion": this._runtimeVersion,
"language": this._language,
"version": this._sdkVersion,
"attach": this._attach,
};
let attachProperties = Object.assign({
"rpId": this._resourceIdentifier,
}, commonProperties);
this._statbeatMetrics.push({ name: Constants.StatsbeatCounter.ATTACH, value: 1, properties: attachProperties });
if (this._instrumentation != Constants.StatsbeatInstrumentation.NONE) {// Only send if there are some instrumentations enabled
let instrumentationProperties = Object.assign({ "feature": this._instrumentation, "type": Constants.StatsbeatFeatureType.Instrumentation }, commonProperties);
this._statbeatMetrics.push({ name: Constants.StatsbeatCounter.FEATURE, value: 1, properties: instrumentationProperties });
}
if (this._feature != Constants.StatsbeatFeature.NONE) {// Only send if there are some features enabled
let featureProperties = Object.assign({ "feature": this._feature, "type": Constants.StatsbeatFeatureType.Feature }, commonProperties);
this._statbeatMetrics.push({ name: Constants.StatsbeatCounter.FEATURE, value: 1, properties: featureProperties });
}
await this._sendStatsbeats();
});
await this._getResourceProvider();
let commonProperties = {
"os": this._os,
"rp": this._resourceProvider,
"cikey": this._cikey,
"runtimeVersion": this._runtimeVersion,
"language": this._language,
"version": this._sdkVersion,
"attach": this._attach,
};
let attachProperties = Object.assign({
"rpId": this._resourceIdentifier,
}, commonProperties);
this._statbeatMetrics.push({ name: Constants.StatsbeatCounter.ATTACH, value: 1, properties: attachProperties });
if (this._instrumentation != Constants.StatsbeatInstrumentation.NONE) {// Only send if there are some instrumentations enabled
let instrumentationProperties = Object.assign({ "feature": this._instrumentation, "type": Constants.StatsbeatFeatureType.Instrumentation }, commonProperties);
this._statbeatMetrics.push({ name: Constants.StatsbeatCounter.FEATURE, value: 1, properties: instrumentationProperties });
}
if (this._feature != Constants.StatsbeatFeature.NONE) {// Only send if there are some features enabled
let featureProperties = Object.assign({ "feature": this._feature, "type": Constants.StatsbeatFeatureType.Feature }, commonProperties);
this._statbeatMetrics.push({ name: Constants.StatsbeatCounter.FEATURE, value: 1, properties: featureProperties });
}
await this._sendStatsbeats();
}

private _getNetworkStatsbeatCounter(endpoint: number, host: string): Network.NetworkStatsbeat {
Expand All @@ -227,10 +225,9 @@ class Statsbeat {
var currentCounter = this._networkStatsbeatCollection[i];
currentCounter.time = +new Date;
var intervalRequests = (currentCounter.totalRequestCount - currentCounter.lastRequestCount) || 0;
var elapsedMs = currentCounter.time - currentCounter.lastTime;
var averageRequestExecutionTime = ((currentCounter.intervalRequestExecutionTime - currentCounter.lastIntervalRequestExecutionTime) / intervalRequests) || 0;
currentCounter.lastIntervalRequestExecutionTime = currentCounter.intervalRequestExecutionTime; // reset
if (elapsedMs > 0 && intervalRequests > 0) {
if (intervalRequests > 0) {
// Add extra properties
let properties = Object.assign({ "endpoint": this._networkStatsbeatCollection[i].endpoint, "host": this._networkStatsbeatCollection[i].host }, commonProperties);
this._statbeatMetrics.push({ name: Constants.StatsbeatCounter.REQUEST_DURATION, value: averageRequestExecutionTime, properties: properties });
Expand Down Expand Up @@ -292,45 +289,46 @@ class Statsbeat {
this._runtimeVersion = process.version;
}

private _getResourceProvider(callback: () => void) {
// Check resource provider
let waiting: boolean = false;
this._resourceProvider = Constants.StatsbeatResourceProvider.unknown;
this._resourceIdentifier = Constants.StatsbeatResourceProvider.unknown;

if (process.env.WEBSITE_SITE_NAME) { // Web apps
this._resourceProvider = Constants.StatsbeatResourceProvider.appsvc;
this._resourceIdentifier = process.env.WEBSITE_SITE_NAME;
if (process.env.WEBSITE_HOME_STAMPNAME) {
this._resourceIdentifier += "/" + process.env.WEBSITE_HOME_STAMPNAME;
}
} else if (process.env.FUNCTIONS_WORKER_RUNTIME) { // Function apps
this._resourceProvider = Constants.StatsbeatResourceProvider.functions;
if (process.env.WEBSITE_HOSTNAME) {
this._resourceIdentifier = process.env.WEBSITE_HOSTNAME;
}
} else if (this._config) {
if (this._isVM === undefined || this._isVM == true) {
waiting = true;
Vm.AzureVirtualMachine.getAzureComputeMetadata(this._config, (vmInfo) => {
this._isVM = vmInfo.isVM;
if (this._isVM) {
this._resourceProvider = Constants.StatsbeatResourceProvider.vm;
this._resourceIdentifier = vmInfo.id + "/" + vmInfo.subscriptionId;
// Override OS as VM info have higher precedence
if (vmInfo.osType) {
this._os = vmInfo.osType;
private _getResourceProvider(): Promise<void> {
return new Promise((resolve, reject) => {
// Check resource provider
let waiting: boolean = false;
this._resourceProvider = Constants.StatsbeatResourceProvider.unknown;
this._resourceIdentifier = Constants.StatsbeatResourceProvider.unknown;
if (process.env.WEBSITE_SITE_NAME) { // Web apps
this._resourceProvider = Constants.StatsbeatResourceProvider.appsvc;
this._resourceIdentifier = process.env.WEBSITE_SITE_NAME;
if (process.env.WEBSITE_HOME_STAMPNAME) {
this._resourceIdentifier += "/" + process.env.WEBSITE_HOME_STAMPNAME;
}
} else if (process.env.FUNCTIONS_WORKER_RUNTIME) { // Function apps
this._resourceProvider = Constants.StatsbeatResourceProvider.functions;
if (process.env.WEBSITE_HOSTNAME) {
this._resourceIdentifier = process.env.WEBSITE_HOSTNAME;
}
} else if (this._config) {
if (this._isVM === undefined || this._isVM == true) {
waiting = true;
Vm.AzureVirtualMachine.getAzureComputeMetadata(this._config, (vmInfo) => {
this._isVM = vmInfo.isVM;
if (this._isVM) {
this._resourceProvider = Constants.StatsbeatResourceProvider.vm;
this._resourceIdentifier = vmInfo.id + "/" + vmInfo.subscriptionId;
// Override OS as VM info have higher precedence
if (vmInfo.osType) {
this._os = vmInfo.osType;
}
}
}
callback();
});
} else {
this._resourceProvider = Constants.StatsbeatResourceProvider.unknown;
resolve();
});
} else {
this._resourceProvider = Constants.StatsbeatResourceProvider.unknown;
}
}
}
if (!waiting) {
callback();
}
if (!waiting) {
resolve();
}
});
}
}

Expand Down
3 changes: 2 additions & 1 deletion AutoCollection/diagnostic-channel/SpanParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import * as Contracts from "../../Declarations/Contracts";
import * as Constants from "../../Declarations/Constants";
import { parseEventHubSpan } from "./Azure/EventHub";
import { DependencyTelemetry } from "../../Declarations/Contracts";
import Util = require ("../../Library/Util");

function createPropertiesFromSpan(span: ReadableSpan): { [key: string]: any; } {
const properties: { [key: string]: any; } = {};
Expand All @@ -30,7 +31,7 @@ function createPropertiesFromSpan(span: ReadableSpan): { [key: string]: any; } {
id: link.context.spanId
}));
if (links.length > 0) {
properties["_MS.links"] = JSON.stringify(links);
properties["_MS.links"] = Util.stringify(links);
}
return properties;
}
Expand Down
2 changes: 1 addition & 1 deletion Library/QuickPulseSender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ class QuickPulseSender {
additionalHeaders?: { name: string, value: string }[]
): Promise<void> {

const payload = JSON.stringify(envelope);
const payload = Util.stringify(envelope);
var options = {
[AutoCollectHttpDependencies.disableCollectionRequestOption]: true,
host: (redirectedHostEndpoint && redirectedHostEndpoint.length > 0) ? redirectedHostEndpoint : this._config.quickPulseHost,
Expand Down
8 changes: 8 additions & 0 deletions Library/Util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,14 @@ class Util {
return objectTypeDump + propertyValueDump;
}

public static stringify(payload: any) {
try {
return JSON.stringify(payload);
} catch (error) {
Logging.warn("Failed to serialize payload", error, payload);
}
}

private static addCorrelationIdHeaderFromString(client: TelemetryClient, response: http.ClientRequest | http.ServerResponse, correlationHeader: string) {
const components = correlationHeader.split(",");
const key = `${RequestResponseHeaders.requestContextSourceKey}=`;
Expand Down
Loading

0 comments on commit f4e1bc4

Please sign in to comment.