Skip to content

Commit

Permalink
Modified the signature of ITelemetryLogger (#813)
Browse files Browse the repository at this point in the history
* First cut of telemetry

* First cut of telemetry

* changes in 1DSCollector

* renamed from collector to logger

* merged to remote

* Added wrapper

* consumed in client

* added types

* changes

* Created common interfaces and wrapper

* reverted back changes

* removed blank line

* removed blank line

* removed blank line

* populate data

* Removed platformType

* Removed platformType

* changes

* changes

* changes

* revert this changes

* revert this changes

* Modifying OneDSLogger API

* Modifying OneDSLogger API

* fixed table name

* changes

* changes

* changes

* changes
  • Loading branch information
BidishaMS authored Jan 19, 2024
1 parent bdb1a27 commit dfa8aa4
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 53 deletions.
5 changes: 3 additions & 2 deletions src/common/OneDSLoggerTelemetry/EventContants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export enum CustomType {

}

export enum EventTableName {
CUSTOM_EVENT = 'CustomEvent',
export enum OneDSCollectorEventName {
VSCODE_EVENT = 'VscodeEvent',
REDACTED = "-REDACTED-"
}
8 changes: 4 additions & 4 deletions src/common/OneDSLoggerTelemetry/ITelemetryLogger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,21 @@ export interface ITelemetryLogger {
* Actual implementation that send telemetry event
* @param eventName - Telemetry event to send over
*/
traceInfo(eventName:string, customDimension?:Record<string, string>, customMeasurement?: Record<string, number>, message?:string) : void
traceInfo(eventName:string, eventInfo?:object, measurement?: object) : void

/**
* Send warning telemetry event
* @param eventName - Event to send
*/
traceWarning(eventName:string, customDimension?:Record<string, string>, customMeasurement?: Record<string, number>, message?:string) : void
traceWarning(eventName:string, eventInfo?:object, measurement?: object) : void
/**
* Send error telemetry event
* @param eventName - Event to send
*/
traceError(eventName: string, error?:Error, customDimension?:Record<string, string>, customMeasurement?: Record<string, number>, exceptionMessage?:string, exceptionSource?:string, exceptionDetails?:string) : void
traceError(eventName: string, errorMessage: string, exception: Error, eventInfo?:object , measurement?: object) : void
/**
* Send featureName and eventName telemetry event
* @param eventName - Event to send
*/
featureUsage(featureName: string, eventName: string, customDimensions?: Record<string, string>) : void
featureUsage(featureName: string, eventName: string, eventInfo?:object) : void
}
156 changes: 117 additions & 39 deletions src/common/OneDSLoggerTelemetry/oneDSLogger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*/

/* eslint-disable @typescript-eslint/no-non-null-assertion*/

import { AppInsightsCore, type IExtendedConfiguration } from "@microsoft/1ds-core-js";
import { PostChannel, type IChannelConfiguration, type IXHROverride } from "@microsoft/1ds-post-js";
import { ITelemetryLogger } from "./ITelemetryLogger";
import { EventType } from "./telemetryConstants";
import { EventType, Severity } from "./telemetryConstants";
import * as vscode from "vscode";
import {getExtensionType, getExtensionVersion} from "../../common/Utils";
import { EXTENSION_ID } from "../../client/constants";
import {EventTableName} from "./EventContants";
import {OneDSCollectorEventName} from "./EventContants";

interface IInstrumentationSettings {
endpointURL: string;
Expand All @@ -22,7 +24,11 @@ export class OneDSLogger implements ITelemetryLogger{
private readonly appInsightsCore :AppInsightsCore;
private readonly postChannel: PostChannel;


private readonly regexPatternsToRedact = [
/key["\\ ']*[:=]+["\\ ']*([a-zA-Z0-9]*)/igm,
/token["\\ ']*[:=]+["\\ ']*([a-zA-Z0-9]*)/igm,
/session["\\ ']*[:=]+["\\ ']*([a-zA-Z0-9]*)/igm
]

private readonly fetchHttpXHROverride: IXHROverride = {
sendPOST: (payload, oncomplete) => {
Expand Down Expand Up @@ -101,9 +107,7 @@ export class OneDSLogger implements ITelemetryLogger{
this.appInsightsCore.initialize(coreConfig, []);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this.appInsightsCore.addTelemetryInitializer((envelope: any) => {
OneDSLogger.populateCommonAttributes(envelope);
});
this.appInsightsCore.addTelemetryInitializer(this.populateCommonAttributes());
}

private static getInstrumentationSettings(geo?:string): IInstrumentationSettings {
Expand Down Expand Up @@ -146,49 +150,50 @@ export class OneDSLogger implements ITelemetryLogger{
}

/// Trace info log
public traceInfo(eventName:string, customDimension?:Record<string, string>, customMeasurement?: Record<string, number>, message?:string) {
public traceInfo(eventName:string, eventInfo?:object, measurement?: object) {
const event = {
name: EventTableName.CUSTOM_EVENT,
name: OneDSCollectorEventName.VSCODE_EVENT,
data: {
eventName: eventName,
eventType: EventType.INFO,
message: message,
customDimension: customDimension,
customMeasurement: customMeasurement
eventType: EventType.TRACE,
severity: Severity.INFO,
eventInfo: JSON.stringify(eventInfo!),
measurement: JSON.stringify(measurement!)
}
};

this.appInsightsCore.track(event);
}

/// Trace warning log
public traceWarning(eventName:string, customDimension?:Record<string, string>, customMeasurement?: Record<string, number>, message?:string) {
public traceWarning(eventName:string, eventInfo?: object, measurement?: object) {
const event = {
name: EventTableName.CUSTOM_EVENT,
name: OneDSCollectorEventName.VSCODE_EVENT,
data: {
eventName: eventName,
eventType: EventType.WARNING,
message: message,
customDimension: customDimension,
customMeasurement: customMeasurement
eventType: EventType.TRACE,
severity: Severity.WARN,
eventInfo: JSON.stringify(eventInfo!),
measurement: JSON.stringify(measurement!)
}
};

this.appInsightsCore.track(event);
}

// Trace error log
public traceError(eventName: string, error?:Error, customDimension?:Record<string, string>, customMeasurement?: Record<string, number>, exceptionMessage?:string, exceptionSource?:string, exceptionDetails?:string) {
public traceError(eventName: string, errorMessage: string, exception: Error, eventInfo?:object, measurement?: object) {
const event = {
name: EventTableName.CUSTOM_EVENT,
name: OneDSCollectorEventName.VSCODE_EVENT,
data: {
eventName: eventName,
eventType: EventType.ERROR,
exceptionMessage: error?.message,
exceptionDetails: exceptionDetails,
exceptionSource: exceptionSource,
customDimension: customDimension,
customMeasurement: customMeasurement
eventType: EventType.TRACE,
severity: Severity.ERROR,
message: errorMessage!,
errorName: exception!,
errorStack: JSON.stringify(exception!),
eventInfo: JSON.stringify(eventInfo!),
measurement: JSON.stringify(measurement!)
}
};
this.appInsightsCore.track(event);
Expand All @@ -201,10 +206,11 @@ export class OneDSLogger implements ITelemetryLogger{
) {

const event = {
name: EventTableName.CUSTOM_EVENT,
name: OneDSCollectorEventName.VSCODE_EVENT,
data: {
eventName: 'Portal_Metrics_Event',
eventType: EventType.INFO,
eventType: EventType.TRACE,
severity: Severity.INFO,
eventInfo: JSON.stringify({
featureName: featureName,
customDimensions: customDimensions,
Expand All @@ -216,18 +222,90 @@ export class OneDSLogger implements ITelemetryLogger{
}

/// Populate attributes that are common to all events
private populateCommonAttributes() {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return (envelope:any) => {
try {
envelope.data = envelope.data || {}; // create data nested object if doesn't exist already'

envelope.data.clientSessionId = vscode.env.sessionId;
envelope.data.vscodeSurface = getExtensionType();
envelope.data.vscodeMachineId = vscode.env.machineId;
envelope.data.vscodeExtensionName = EXTENSION_ID;
envelope.data.vscodeExtensionVersion = getExtensionVersion();
envelope.data.vscodeVersion = vscode.version;
envelope.data.domain = vscode.env.appHost;
// envelope.data.timestamp = envelope.ext.user.locale;
// envelope.data.userLocale = envelope.ext.user.locale;
// envelope.data.userTimeZone = envelope.ext.loc.tz;
// envelope.data.appLanguage = envelope.ext.app.locale;

// At the end of event enrichment, redact the sensitive data for all the applicable fields
// envelope = this.redactSensitiveDataFromEvent(envelope);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
catch (exception:any)
{
// Such exceptions are likely if we are trying to process event attributes which don't exist
// In such cases, only add common attributes and current exception details, and avoid processing the event attributes further
// However, do log the baseData of the event along with its name in the exception event that gets sent out in this scenario
console.warn("Caught exception processing the telemetry event: " + envelope.name);
console.warn(exception.message);

this.traceExceptionInEventProcessing();
return false;
}
}
}

//// Redact Sensitive data for the fields susceptible to contain codes/tokens/keys/secrets etc.
//// This is done post event enrichment is complete to not impact the dependencies (if any) on actual values like Uri etc.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private static populateCommonAttributes(envelope: any) {
envelope.data = envelope.data || {}; // create data nested object if doesn't exist already'

envelope.data.SessionId = vscode.env.sessionId;
envelope.data.surface = getExtensionType();
envelope.data.extensionVersion = getExtensionVersion();
envelope.data.extensionName = EXTENSION_ID;
envelope.data.dataDomain = vscode.env.appHost;
envelope.data.cloudRoleName = vscode.env.appName;
envelope.data.vscodeVersion = vscode.version;
envelope.data.vscodeMachineId = vscode.env.machineId
private redactSensitiveDataFromEvent(envelope:any) {
//Redact sensitive information from suseptible fields
envelope.data.errorStack = this.getAllSensitiveRedactedFromField(envelope.data.errorStack);
return envelope;
}

//// Get redacted value after all sensitive information is redacted
getAllSensitiveRedactedFromField(value:string) {
try {
// Ensure the value is of type string
if (value && typeof value === 'string') {
this.regexPatternsToRedact.forEach((pattern: RegExp) => {
value = this.getRedactedValueViaRegexMatch(value, pattern);
});
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (exception:any) {
console.warn("Caught exception while processing telemetry data for redaction (if any): " + value);
console.warn(exception.message);
}
return value;
}

//// Get redacted value
getRedactedValueViaRegexMatch(value:string, regexPattern:RegExp) {
let matches;

while ((matches = regexPattern.exec(value)) !== null) {
// This is necessary to avoid infinite loops with zero-width matches
if (matches.index === regexPattern.lastIndex) {
regexPattern.lastIndex++;
}

matches.forEach((match, groupIndex) => {
if (groupIndex == 0) { // Redact the entire matched string
value = value.replace(match, OneDSCollectorEventName.REDACTED); //Replace with string REDACTED
}
});
}
return value;
}

/// Trace exceptions in processing event attributes
private traceExceptionInEventProcessing() {
// TODO : Add exception handling
}

public flushAndTeardown(): void {
Expand Down
15 changes: 8 additions & 7 deletions src/common/OneDSLoggerTelemetry/oneDSLoggerWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,29 +30,30 @@ export class oneDSLoggerWrapper {
}

/// Trace info log
public traceInfo(eventName:string, customDimension?:Record<string, string>, customMeasurement?: Record<string, number>, message?:string) {
public traceInfo(eventName:string, eventInfo?: object, measurement?: object) {
try{
oneDSLoggerWrapper.oneDSLoggerIntance.traceInfo(eventName, customDimension, customMeasurement, message);
oneDSLoggerWrapper.oneDSLoggerIntance.traceInfo(eventName, eventInfo, measurement);
}catch (exception) {
console.warn(exception);
}
}

/// Trace warning log
public traceWarning(eventName:string, customDimension?:Record<string, string>, customMeasurement?: Record<string, number>, message?:string) {
public traceWarning(eventName:string, eventInfo?: object, measurement?: object) {
try{
oneDSLoggerWrapper.oneDSLoggerIntance.traceWarning(eventName, customDimension, customMeasurement, message);
oneDSLoggerWrapper.oneDSLoggerIntance.traceWarning(eventName, eventInfo, measurement);
}catch (exception) {
console.warn(exception);
}
}

/// Trace exception log
public traceError(eventName:string, error?:Error, customDimension?:Record<string, string>, customMeasurement?: Record<string, number>, message?:string) {
public traceError(eventName: string, errorMessage: string, exception: Error, eventInfo?:object, measurement?: object) {
try{
oneDSLoggerWrapper.oneDSLoggerIntance.traceError(eventName, error, customDimension, customMeasurement, message);
oneDSLoggerWrapper.oneDSLoggerIntance.traceError(eventName, errorMessage, exception, eventInfo, measurement);
}catch (exception) {
console.warn(exception);
console.warn("Caught exception processing the telemetry event: " + exception);
console.warn(exception);
}
}

Expand Down
11 changes: 10 additions & 1 deletion src/common/OneDSLoggerTelemetry/telemetryConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,16 @@
*/

export enum EventType {
CLICK = 'Click',
CUSTOM = 'Custom',
GENERAL = 'General',
SCENARIO = 'Scenario',
TRACE = 'Trace'
}

export enum Severity {
DEBUG = 'Debug',
INFO = 'Info',
WARNING = 'Warning',
WARN = 'Warning',
ERROR = 'Error'
}

0 comments on commit dfa8aa4

Please sign in to comment.