Skip to content

Commit

Permalink
[Beta] Consume @azure/monitor-opentelemetry (#1167)
Browse files Browse the repository at this point in the history
* WIP

* WIP

* Remove not needed files

* Add @azure/monitor-opentelemetry dependency

* Apply suggestions from code review

Co-authored-by: Jackson Weber <47067795+JacksonWeber@users.noreply.github.com>

* Lint

* Update funcitonal test initialization

* Update

* Update

---------

Co-authored-by: Jackson Weber <47067795+JacksonWeber@users.noreply.github.com>
  • Loading branch information
hectorhdzg and JacksonWeber committed Jul 10, 2023
1 parent 2700705 commit aa2c706
Show file tree
Hide file tree
Showing 91 changed files with 1,682 additions and 12,260 deletions.
5,401 changes: 158 additions & 5,243 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
"@azure/core-client": "^1.0.0",
"@azure/core-rest-pipeline": "^1.9.2",
"@azure/identity": "^3.1.3",
"@azure/monitor-opentelemetry-exporter": "^1.0.0-beta.14",
"@azure/monitor-opentelemetry": "^1.0.0-beta.0",
"@azure/opentelemetry-instrumentation-azure-sdk": "^1.0.0-beta.1",
"@opentelemetry/api": "^1.4.1",
"@opentelemetry/api-logs": "0.40.0",
Expand Down
87 changes: 61 additions & 26 deletions src/agent/agentLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,26 @@
// Licensed under the MIT license.

import { ManagedIdentityCredential } from "@azure/identity";
import { ApplicationInsightsClient } from "../applicationInsightsClient";
import { ApplicationInsightsConfig } from "../shared";
import { Util } from "../shared/util";
import { TelemetryClient } from "../shim/telemetryClient";
import { Util } from "../shim/util";
import { ConsoleWriter } from "./diagnostics/writers/consoleWriter";
import { DiagnosticLogger } from "./diagnostics/diagnosticLogger";
import { StatusLogger } from "./diagnostics/statusLogger";
import { AgentResourceProviderType, DiagnosticMessageId, IDiagnosticLog, IDiagnosticLogger, NODE_JS_RUNTIME_MAJOR_VERSION } from "./types";
import { ApplicationInsightsOptions } from "../types";


const forceStart = process.env.APPLICATIONINSIGHTS_FORCE_START === "true";
// Azure Connection String
const ENV_connectionString = "APPLICATIONINSIGHTS_CONNECTION_STRING";
const ENV_AZURE_PREFIX = "APPSETTING_"; // Azure adds this prefix to all environment variables
const ENV_IKEY = "APPINSIGHTS_INSTRUMENTATIONKEY"; // This key is provided in the readme
const LEGACY_ENV_IKEY = "APPINSIGHTS_INSTRUMENTATION_KEY";


export class AgentLoader {
protected _canLoad: boolean;
protected _config: ApplicationInsightsConfig;
protected _options: ApplicationInsightsOptions;
protected _instrumentationKey: string;
protected _diagnosticLogger: IDiagnosticLogger;
protected _statusLogger: StatusLogger;
Expand All @@ -30,24 +36,54 @@ export class AgentLoader {
else {
this._canLoad = true;
this._aadCredential = this._getAuthenticationCredential();
// Default config
this._config = new ApplicationInsightsConfig();
this._config.azureMonitorExporterConfig.disableOfflineStorage = false;
this._config.enableAutoCollectExceptions = true;
this._config.enableAutoCollectPerformance = true;
this._config.enableAutoCollectStandardMetrics = true;
this._config.samplingRatio = 1; // Sample all telemetry by default
this._config.instrumentations.azureSdk.enabled = true;
this._config.instrumentations.http.enabled = true;
this._config.instrumentations.mongoDb.enabled = true;
this._config.instrumentations.mySql.enabled = true;
this._config.instrumentations.postgreSql.enabled = true;
this._config.instrumentations.redis4.enabled = true;
this._config.instrumentations.redis.enabled = true;
this._config.logInstrumentations.bunyan.enabled = true;
this._config.logInstrumentations.console.enabled = true;
this._config.logInstrumentations.winston.enabled = true;
this._instrumentationKey = this._getInstrumentationKey(this._config.azureMonitorExporterConfig.connectionString);
// Default options
this._options = {
azureMonitorExporterConfig: {
disableOfflineStorage: false,
},
enableAutoCollectExceptions: true,
enableAutoCollectPerformance: true,
enableAutoCollectStandardMetrics: true,
samplingRatio: 1, // Sample all telemetry by default
instrumentationOptions: {
azureSdk: {
enabled: true
},
http: {
enabled: true
},
mongoDb: {
enabled: true
},
mySql: {
enabled: true
},
postgreSql: {
enabled: true
},
redis4: {
enabled: true
},
redis: {
enabled: true
},
}
};

const connectionString = process.env[ENV_connectionString];
if (connectionString) {
this._instrumentationKey = this._getInstrumentationKey(connectionString);
}
else {
const instrumentationKey =
process.env[ENV_IKEY] ||
process.env[ENV_AZURE_PREFIX + ENV_IKEY] ||
process.env[LEGACY_ENV_IKEY] ||
process.env[ENV_AZURE_PREFIX + LEGACY_ENV_IKEY];
this._instrumentationKey = instrumentationKey || "unknown";

}


//Default diagnostic using console
this._diagnosticLogger = new DiagnosticLogger(this._instrumentationKey, new ConsoleWriter());
Expand Down Expand Up @@ -85,9 +121,8 @@ export class AgentLoader {
// TODO: Set Prefix

// Initialize Distro
this._config.aadTokenCredential = this._aadCredential;
const appInsightsClient = new ApplicationInsightsClient(this._config);

this._options.azureMonitorExporterConfig.aadTokenCredential = this._aadCredential;
const appInsightsClient = new TelemetryClient(this._options);

Check warning on line 125 in src/agent/agentLoader.ts

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, 14.x)

'appInsightsClient' is assigned a value but never used

Check warning on line 125 in src/agent/agentLoader.ts

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, 16.x)

'appInsightsClient' is assigned a value but never used

Check warning on line 125 in src/agent/agentLoader.ts

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, 18.x)

'appInsightsClient' is assigned a value but never used
// Agent successfully initialized
const diagnosticLog: IDiagnosticLog = {
message: "Azure Monitor Application Insights Distro was started succesfully.",
Expand Down Expand Up @@ -124,7 +159,7 @@ export class AgentLoader {
})
return false;
}
if (!this._instrumentationKey) {
if (this._instrumentationKey === "unknown") {
const diagnosticLog: IDiagnosticLog = {
message: "Azure Monitor Application Insights Distro wanted to be started, but no Connection String was provided",
messageId: DiagnosticMessageId.missingIkey
Expand Down
2 changes: 1 addition & 1 deletion src/agent/appServicesLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export class AppServicesLoader extends AgentLoader {
process.env.WEBSITE_INSTANCE_ID;
}
const resource = new Resource(resourceAttributes);
this._config.resource = resource;
this._options.resource = resource;

let statusLogDir = '/var/log/applicationinsights/';
if (this._isWindows) {
Expand Down
6 changes: 3 additions & 3 deletions src/agent/azureFunctionsLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ export class AzureFunctionsLoader extends AgentLoader {
super();
if (this._canLoad) {
// Azure Fn specific configuration
this._config.enableAutoCollectPerformance = false;
this._config.enableAutoCollectStandardMetrics = false;
this._options.enableAutoCollectPerformance = false;
this._options.enableAutoCollectStandardMetrics = false;
const resourceAttributes: Attributes = {};
if (process.env.WEBSITE_SITE_NAME) {
resourceAttributes[SemanticResourceAttributes.SERVICE_NAME] =
Expand All @@ -27,7 +27,7 @@ export class AzureFunctionsLoader extends AgentLoader {
process.env.WEBSITE_INSTANCE_ID;
}
const resource = new Resource(resourceAttributes);
this._config.resource = resource;
this._options.resource = resource;

const writer = new AzureFunctionsWriter(this._instrumentationKey);
this._diagnosticLogger = new DiagnosticLogger(this._instrumentationKey, writer);
Expand Down
5 changes: 3 additions & 2 deletions src/agent/diagnostics/writers/consoleWriter.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import { Util } from "../../../shim/util";
import { IAgentLogger } from "../../types";

export class ConsoleWriter implements IAgentLogger {
log(message?: any, ...optional: any[]) {

Check warning on line 8 in src/agent/diagnostics/writers/consoleWriter.ts

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, 14.x)

'optional' is defined but never used

Check warning on line 8 in src/agent/diagnostics/writers/consoleWriter.ts

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, 16.x)

'optional' is defined but never used

Check warning on line 8 in src/agent/diagnostics/writers/consoleWriter.ts

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, 18.x)

'optional' is defined but never used
console.log(JSON.stringify(message));
console.log(Util.getInstance().stringify(message));
}

error(message?: any, ...optional: any[]) {

Check warning on line 12 in src/agent/diagnostics/writers/consoleWriter.ts

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, 14.x)

'optional' is defined but never used

Check warning on line 12 in src/agent/diagnostics/writers/consoleWriter.ts

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, 16.x)

'optional' is defined but never used

Check warning on line 12 in src/agent/diagnostics/writers/consoleWriter.ts

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, 18.x)

'optional' is defined but never used
console.error(JSON.stringify(message));
console.error(Util.getInstance().stringify(message));
}
}
3 changes: 2 additions & 1 deletion src/agent/diagnostics/writers/fileWriter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import * as path from "path";
import * as fs from "fs";
import { makeStatusDirs, renameCurrentFile } from "./fileHelpers";
import { IAgentLogger } from "../../types";
import { Util } from "../../../shim/util";

export interface FileWriterOptions {
append: boolean; // Overwrite or append on file write (false)
Expand Down Expand Up @@ -49,7 +50,7 @@ export class FileWriter implements IAgentLogger {
public log(message: any) {
if (this._ready) {
const data = typeof message === "object"
? JSON.stringify(message)
? Util.getInstance().stringify(message)
: message.toString();

// Check if existing file needs to be renamed
Expand Down
104 changes: 23 additions & 81 deletions src/applicationInsightsClient.ts
Original file line number Diff line number Diff line change
@@ -1,58 +1,45 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import { ApplicationInsightsConfig } from "./shared/configuration";
import { Logger } from "./shared/logging";
import { LogHandler } from "./logs";
import { MetricHandler } from "./metrics";
import { TraceHandler } from "./traces";
import { AZURE_MONITOR_STATSBEAT_FEATURES, StatsbeatFeature, StatsbeatInstrumentation } from "./types";

import { ApplicationInsightsConfig } from "./applicationInsightsConfig";
import { Logger } from "./shim/logging";
import { AzureMonitorOpenTelemetryClient, AzureMonitorOpenTelemetryOptions } from "@azure/monitor-opentelemetry";

/**
* @deprecated Use TelemetryClient instead
*/
export class ApplicationInsightsClient {
private _config: ApplicationInsightsConfig;
private _traceHandler: TraceHandler;
private _metricHandler: MetricHandler;
private _logHandler: LogHandler;
private _client: AzureMonitorOpenTelemetryClient;

/**
* Constructs a new client of the client
* @param config Configuration
* Constructs a new client
* @param options AzureMonitorOpenTelemetryOptions
*/
constructor(config?: ApplicationInsightsConfig) {
this._config = config || new ApplicationInsightsConfig();
if (!this._config.azureMonitorExporterConfig.connectionString || this._config.azureMonitorExporterConfig.connectionString === "") {
throw new Error(
"Connection String not found, please provide it before starting Application Insights SDK."
);
}
this._setStatsbeatFeatures();
this._metricHandler = new MetricHandler(this._config);
this._traceHandler = new TraceHandler(this._config, this._metricHandler);
this._logHandler = new LogHandler(this._config, this._metricHandler);
constructor(options?: AzureMonitorOpenTelemetryOptions) {
this._client = new AzureMonitorOpenTelemetryClient(options);
}

/**
* @deprecated This should not be used
*/
public start() {
// No Op
}

public getTraceHandler(): TraceHandler {
return this._traceHandler;
public getTraceHandler(): any {
return this._client["_traceHandler"];
}

public getMetricHandler(): MetricHandler {
return this._metricHandler;
public getMetricHandler(): any {
return this._client["_metricHandler"];
}

public getLogHandler(): LogHandler {
return this._logHandler;
public getLogHandler(): any {
return this._client["_logHandler"];
}

/**
* @deprecated This method should not be used
*/
public getConfig(): ApplicationInsightsConfig {
return this._config;
return null;
}

public getLogger(): Logger {
Expand All @@ -64,9 +51,7 @@ export class ApplicationInsightsClient {
*/
public async flush(): Promise<void> {
try {
await this._traceHandler.flush();
await this._metricHandler.flush();
await this._logHandler.flush();
await this._client.flush();
} catch (err) {
Logger.getInstance().error("Failed to flush telemetry", err);
}
Expand All @@ -76,49 +61,6 @@ export class ApplicationInsightsClient {
*Shutdown all handlers
*/
public async shutdown(): Promise<void> {
this._traceHandler.shutdown();
this._metricHandler.shutdown();
this._logHandler.shutdown();
}

private _setStatsbeatFeatures() {
let instrumentationBitMap = 0;
if (this._config.instrumentations?.azureSdk?.enabled) {
instrumentationBitMap |= StatsbeatInstrumentation.AZURE_CORE_TRACING;
}
if (this._config.instrumentations?.mongoDb?.enabled) {
instrumentationBitMap |= StatsbeatInstrumentation.MONGODB;
}
if (this._config.instrumentations?.mySql?.enabled) {
instrumentationBitMap |= StatsbeatInstrumentation.MYSQL;
}
if (this._config.instrumentations?.postgreSql?.enabled) {
instrumentationBitMap |= StatsbeatInstrumentation.POSTGRES;
}
if (this._config.instrumentations?.redis?.enabled) {
instrumentationBitMap |= StatsbeatInstrumentation.REDIS;
}
if (this._config.logInstrumentations?.bunyan?.enabled) {
instrumentationBitMap |= StatsbeatInstrumentation.BUNYAN;
}
if (this._config.logInstrumentations?.winston?.enabled) {
instrumentationBitMap |= StatsbeatInstrumentation.WINSTON;
}
if (this._config.logInstrumentations?.console?.enabled) {
instrumentationBitMap |= StatsbeatInstrumentation.CONSOLE;
}

let featureBitMap = 0;
featureBitMap |= StatsbeatFeature.DISTRO;


try {
process.env[AZURE_MONITOR_STATSBEAT_FEATURES] = JSON.stringify({
instrumentation: instrumentationBitMap,
feature: featureBitMap
});
} catch (error) {
Logger.getInstance().error("Failed call to JSON.stringify.", error);
}
this._client.shutdown();
}
}
Loading

0 comments on commit aa2c706

Please sign in to comment.