Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OpenTelemetry Exporter Fix issue loading SDK version #17595

Merged
merged 1 commit into from
Sep 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,13 @@
// Licensed under the MIT license.

import * as os from "os";
import * as fs from "fs";
import * as path from "path";
import { diag } from "@opentelemetry/api";
import { SDK_INFO } from "@opentelemetry/core";
import { SemanticResourceAttributes } from "@opentelemetry/semantic-conventions";

import { KnownContextTagKeys } from "../../../generated";
import * as ai from "../../../utils/constants/applicationinsights";
import { Tags } from "../../../types";

type PackageJson = { version: string };

let instance: Context | null = null;

/**
Expand All @@ -21,137 +18,40 @@ let instance: Context | null = null;
export class Context {
public tags: Tags;

public static appVersion: { [path: string]: string } = {};

public static sdkVersion: string | null = null;

public static opentelemetryVersion: string | null = null;

public static nodeVersion: string = "";

/**
* Add extra ../ to access on environments not using ts-node
*/
private static readonly JS_NODE_PREFIX = "../";

/**
* Path to azure-opentelemetry-exporter
*/
private static readonly ROOT_PATH = "../../../../";

constructor(
/**
* Path to this module's `package.json` relative to
* `Context.ROOT_PATH`
*/
private _exporterPrefix = "./",
/**
* Path to end user application folder which contains `package.json`
* relative to `Context.ROOT_PATH`
*/
private _appPrefix = "../../../"
) {
constructor() {
this.tags = {};

this._loadApplicationContext();
this._loadDeviceContext();
this._loadInternalContext();
}

private _loadApplicationContext(): void {
if (Object.keys(Context.appVersion).length === 0) {
// note: this should return the host package.json
let packageJson: PackageJson | null = null;
const packageJsonPath = path.resolve(
__dirname,
Context.JS_NODE_PREFIX,
this._appPrefix,
Context.ROOT_PATH,
"./package.json"
);
const packageJsonPathTsNode = path.resolve(
__dirname,
this._appPrefix,
Context.ROOT_PATH,
"./package.json"
);

Context.appVersion[packageJsonPath] = "unknown";

try {
packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8")) as PackageJson;
} catch (_) {
try {
packageJson = JSON.parse(fs.readFileSync(packageJsonPathTsNode, "utf8")) as PackageJson;
} catch (exception) {
diag.warn("Failed to load Application version", exception);
}
}

if (packageJson && typeof packageJson.version === "string") {
Context.appVersion[packageJsonPath] = packageJson.version;
}

this.tags[KnownContextTagKeys.AiApplicationVer] = Context.appVersion[packageJsonPath];
}
}

private _loadDeviceContext(): void {
this.tags[KnownContextTagKeys.AiDeviceId] = "";
this.tags[KnownContextTagKeys.AiDeviceOsVersion] = os && `${os.type()} ${os.release()}`;
}

private _loadInternalContext(): void {
if (!Context.sdkVersion) {
let packageJson: { version: string } | null = null;
const { node } = process.versions;
[Context.nodeVersion] = node.split(".");

// note: this should return the sdk package.json
const packageJsonPath = path.resolve(
__dirname,
Context.JS_NODE_PREFIX,
this._exporterPrefix,
Context.ROOT_PATH,
"./package.json"
);
const packageJsonPathTsNode = path.resolve(
__dirname,
this._exporterPrefix,
Context.ROOT_PATH,
"./package.json"
);

Context.sdkVersion = "unknown";
try {
packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8")) as PackageJson;
} catch (_) {
try {
packageJson = JSON.parse(fs.readFileSync(packageJsonPathTsNode, "utf8")) as PackageJson;
} catch (exception) {
diag.warn("Failed to load Exporter version", exception);
throw exception;
}
}

if (packageJson && typeof packageJson.version === "string") {
Context.sdkVersion = packageJson.version;
}
}

const { node } = process.versions;
[Context.nodeVersion] = node.split(".");
Context.opentelemetryVersion = SDK_INFO[SemanticResourceAttributes.TELEMETRY_SDK_VERSION];
Context.sdkVersion = ai.packageVersion;
this.tags[
KnownContextTagKeys.AiInternalSdkVersion
] = `node${Context.nodeVersion}:otel${SDK_INFO.VERSION}:ext${Context.sdkVersion}`;
] = `node${Context.nodeVersion}:otel${Context.opentelemetryVersion}:ext${Context.sdkVersion}`;
}
}

/**
* Singleton Context instance.
* @internal
*/
export function getInstance(exporterPrefix?: string, appPrefix?: string): Context {
export function getInstance(): Context {
if (!instance) {
instance = new Context(exporterPrefix, appPrefix);
instance = new Context();
}
return instance;
}
Original file line number Diff line number Diff line change
Expand Up @@ -323,10 +323,13 @@ export function readableSpanToEnvelope(span: ReadableSpan, ikey: string): Envelo
}

// Azure SDK
if (span.attributes[AzNamespace] === MicrosoftEventHub) {
parseEventHubSpan(span, baseData);
} else if (span.attributes[AzNamespace] && span.kind === SpanKind.INTERNAL) {
baseData.type = `${DependencyTypes.InProc} | ${span.attributes[AzNamespace]}`;
if (span.attributes[AzNamespace]) {
if (span.kind === SpanKind.INTERNAL) {
baseData.type = `${DependencyTypes.InProc} | ${span.attributes[AzNamespace]}`;
}
if (span.attributes[AzNamespace] === MicrosoftEventHub) {
parseEventHubSpan(span, baseData);
}
}

return {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import fs from "fs";
import path from "path";
import { Span, BasicTracerProvider, TracerConfig } from "@opentelemetry/tracing";
import { SpanKind, SpanStatusCode, ROOT_CONTEXT } from "@opentelemetry/api";
import * as assert from "assert";
Expand All @@ -12,14 +14,13 @@ import {
} from "@opentelemetry/semantic-conventions";

import { Tags, Properties, Measurements } from "../../src/types";
import * as ai from "../../src/utils/constants/applicationinsights";
import { Context, getInstance } from "../../src/platform";
import { msToTimeSpan } from "../../src/utils/breezeUtils";
import { readableSpanToEnvelope } from "../../src/utils/spanUtils";
import { RemoteDependencyData, RequestData, KnownContextTagKeys } from "../../src/generated";
import { TelemetryItem as Envelope } from "../../src/generated";

const context = getInstance(undefined, "./");
const context = getInstance();

const tracerProviderConfig: TracerConfig = {
resource: new Resource({
Expand All @@ -30,6 +31,8 @@ const tracerProviderConfig: TracerConfig = {
};

const tracer = new BasicTracerProvider(tracerProviderConfig).getTracer("default");
const packageJsonPath = path.resolve(__dirname, "../../", "./package.json");
let packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));

function assertEnvelope(
envelope: Envelope,
Expand All @@ -41,10 +44,7 @@ function assertEnvelope(
expectedBaseData: Partial<RequestData | RemoteDependencyData>,
expectedTime?: Date
): void {
assert.strictEqual(Context.sdkVersion, ai.packageVersion);
assert.strictEqual(Object.keys(Context.appVersion).length, 1);
assert.notDeepStrictEqual(Context.appVersion, "unknown");

assert.strictEqual(Context.sdkVersion, packageJson.version);
assert.ok(envelope);
assert.strictEqual(envelope.name, name);
assert.deepStrictEqual(envelope.data?.baseType, baseType);
Expand Down