Skip to content

Commit 1892f08

Browse files
authored
[Monitor OpenTelemetry Exporter] Add Performance Counter Name Mapping (Azure#29583)
### Packages impacted by this PR @azure/monitor-opentelemetry-exporter ### Issues associated with this PR ### Describe the problem that is addressed by this PR Performance counter names should be converted from OpenTelemetry valid names to the those appropriate for breeze to avoid OTel warnings. ### Are there test cases added in this PR? _(If not, why?)_ Yes, in the `metricUitl.tests.ts`. ### Command used to generate this PR:**_(Applicable only to SDK release request PRs)_ ### Checklists - [x] Added impacted package name to the issue description - [ ] Does this PR needs any fixes in the SDK Generator?** _(If so, create an Issue in the [Autorest/typescript](https://github.com/Azure/autorest.typescript) repository and link it here)_ - [x] Added a changelog (if necessary)
1 parent ad24dc2 commit 1892f08

File tree

5 files changed

+295
-8
lines changed

5 files changed

+295
-8
lines changed

sdk/monitor/monitor-opentelemetry-exporter/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66

77
- Capture and export measurements when creating log records from the Application Insights 3.X SDK.
88

9+
### Other Changes
10+
11+
- Convert OTel-valid performance counter names to appropriate breeze names.
12+
913
## 1.0.0-beta.22 (2024-04-16)
1014

1115
### Features Added

sdk/monitor/monitor-opentelemetry-exporter/src/types.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,29 @@ export interface PersistentStorage {
4545
shift(): Promise<unknown>;
4646
push(value: unknown[]): Promise<boolean>;
4747
}
48+
49+
/**
50+
* Performance Counter OpenTelemetry compliant names.
51+
* @internal
52+
*/
53+
export enum OTelPerformanceCounterNames {
54+
PRIVATE_BYTES = "Private_Bytes",
55+
AVAILABLE_BYTES = "Available_Bytes",
56+
PROCESSOR_TIME = "Processor_Time",
57+
PROCESS_TIME = "Process_Time",
58+
REQUEST_RATE = "Request_Rate",
59+
REQUEST_DURATION = "Request_Execution_Time",
60+
}
61+
62+
/**
63+
* Breeze Performance Counter names.
64+
* @internal
65+
*/
66+
export enum BreezePerformanceCounterNames {
67+
PRIVATE_BYTES = "\\Process(??APP_WIN32_PROC??)\\Private Bytes",
68+
AVAILABLE_BYTES = "\\Memory\\Available Bytes",
69+
PROCESSOR_TIME = "\\Processor(_Total)\\% Processor Time",
70+
PROCESS_TIME = "\\Process(??APP_WIN32_PROC??)\\% Processor Time",
71+
REQUEST_RATE = "\\ASP.NET Applications(??APP_W3SVC_PROC??)\\Requests/Sec",
72+
REQUEST_DURATION = "\\ASP.NET Applications(??APP_W3SVC_PROC??)\\Request Execution Time",
73+
}

sdk/monitor/monitor-opentelemetry-exporter/src/utils/metricUtils.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,16 @@ import { Attributes } from "@opentelemetry/api";
55
import { DataPointType, Histogram, ResourceMetrics } from "@opentelemetry/sdk-metrics";
66
import { TelemetryItem as Envelope, MetricsData, MetricDataPoint } from "../generated";
77
import { createTagsFromResource } from "./common";
8+
import { BreezePerformanceCounterNames, OTelPerformanceCounterNames } from "../types";
9+
10+
const breezePerformanceCountersMap = new Map<string, string>([
11+
[OTelPerformanceCounterNames.PRIVATE_BYTES, BreezePerformanceCounterNames.PRIVATE_BYTES],
12+
[OTelPerformanceCounterNames.AVAILABLE_BYTES, BreezePerformanceCounterNames.AVAILABLE_BYTES],
13+
[OTelPerformanceCounterNames.PROCESSOR_TIME, BreezePerformanceCounterNames.PROCESSOR_TIME],
14+
[OTelPerformanceCounterNames.PROCESS_TIME, BreezePerformanceCounterNames.PROCESS_TIME],
15+
[OTelPerformanceCounterNames.REQUEST_RATE, BreezePerformanceCounterNames.REQUEST_RATE],
16+
[OTelPerformanceCounterNames.REQUEST_DURATION, BreezePerformanceCounterNames.REQUEST_DURATION],
17+
]);
818

919
function createPropertiesFromMetricAttributes(attributes?: Attributes): {
1020
[propertyName: string]: string;
@@ -48,8 +58,12 @@ export function resourceMetricsToEnvelope(
4858
properties: {},
4959
};
5060
baseData.properties = createPropertiesFromMetricAttributes(dataPoint.attributes);
61+
let perfCounterName;
62+
if (breezePerformanceCountersMap.has(metric.descriptor.name)) {
63+
perfCounterName = breezePerformanceCountersMap.get(metric.descriptor.name);
64+
}
5165
const metricDataPoint: MetricDataPoint = {
52-
name: metric.descriptor.name,
66+
name: perfCounterName ? perfCounterName : metric.descriptor.name,
5367
value: 0,
5468
dataPointType: "Aggregation",
5569
};

sdk/monitor/monitor-opentelemetry-exporter/test/internal/metricUtil.test.ts

Lines changed: 244 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import {
2222
RequestData,
2323
} from "../../src/generated";
2424
import assert from "assert";
25-
import { Tags } from "../../src/types";
25+
import { BreezePerformanceCounterNames, OTelPerformanceCounterNames, Tags } from "../../src/types";
2626
import { Context, getInstance } from "../../src/platform";
2727

2828
const context = getInstance();
@@ -127,4 +127,247 @@ describe("metricUtil.ts", () => {
127127
);
128128
});
129129
});
130+
131+
describe("#performanceMetricsToEnvelope", () => {
132+
it("should create private bytes envelopes with the correct name", async () => {
133+
const expectedTags: Tags = {
134+
"ai.device.osVersion": os && `${os.type()} ${os.release()}`,
135+
"ai.internal.sdkVersion": `${prefix}node${Context.nodeVersion}:otel${Context.opentelemetryVersion}:${version}`,
136+
};
137+
const expectedBaseData = {
138+
name: BreezePerformanceCounterNames.PRIVATE_BYTES,
139+
value: 1,
140+
dataPointType: "Aggregation",
141+
count: 1,
142+
};
143+
const provider = new MeterProvider({
144+
resource: new Resource({
145+
[SemanticResourceAttributes.SERVICE_NAME]: "basic-service",
146+
}),
147+
});
148+
const exporter = new TestExporter({
149+
connectionString: "InstrumentationKey=00000000-0000-0000-0000-000000000000",
150+
});
151+
const metricReaderOptions: PeriodicExportingMetricReaderOptions = {
152+
exporter: exporter,
153+
};
154+
const metricReader = new PeriodicExportingMetricReader(metricReaderOptions);
155+
provider.addMetricReader(metricReader);
156+
const meter = provider.getMeter("example-meter-node");
157+
// Create Counter instrument with the meter
158+
const counter = meter.createCounter(OTelPerformanceCounterNames.PRIVATE_BYTES);
159+
counter.add(1);
160+
provider.forceFlush();
161+
await new Promise((resolve) => setTimeout(resolve, 800));
162+
const envelope = resourceMetricsToEnvelope(testMetrics, "ikey");
163+
assertEnvelope(
164+
envelope[0],
165+
"Microsoft.ApplicationInsights.Metric",
166+
100,
167+
"MetricData",
168+
expectedTags,
169+
expectedBaseData,
170+
);
171+
});
172+
it("should create available bytes envelopes with the correct name", async () => {
173+
const expectedTags: Tags = {
174+
"ai.device.osVersion": os && `${os.type()} ${os.release()}`,
175+
"ai.internal.sdkVersion": `${prefix}node${Context.nodeVersion}:otel${Context.opentelemetryVersion}:${version}`,
176+
};
177+
const expectedBaseData = {
178+
name: BreezePerformanceCounterNames.AVAILABLE_BYTES,
179+
value: 1,
180+
dataPointType: "Aggregation",
181+
count: 1,
182+
};
183+
const provider = new MeterProvider({
184+
resource: new Resource({
185+
[SemanticResourceAttributes.SERVICE_NAME]: "basic-service",
186+
}),
187+
});
188+
const exporter = new TestExporter({
189+
connectionString: "InstrumentationKey=00000000-0000-0000-0000-000000000000",
190+
});
191+
const metricReaderOptions: PeriodicExportingMetricReaderOptions = {
192+
exporter: exporter,
193+
};
194+
const metricReader = new PeriodicExportingMetricReader(metricReaderOptions);
195+
provider.addMetricReader(metricReader);
196+
const meter = provider.getMeter("example-meter-node");
197+
// Create Counter instrument with the meter
198+
const counter = meter.createCounter(OTelPerformanceCounterNames.AVAILABLE_BYTES);
199+
counter.add(1);
200+
provider.forceFlush();
201+
await new Promise((resolve) => setTimeout(resolve, 800));
202+
const envelope = resourceMetricsToEnvelope(testMetrics, "ikey");
203+
assertEnvelope(
204+
envelope[0],
205+
"Microsoft.ApplicationInsights.Metric",
206+
100,
207+
"MetricData",
208+
expectedTags,
209+
expectedBaseData,
210+
);
211+
});
212+
it("should create processor time envelopes with the correct name", async () => {
213+
const expectedTags: Tags = {
214+
"ai.device.osVersion": os && `${os.type()} ${os.release()}`,
215+
"ai.internal.sdkVersion": `${prefix}node${Context.nodeVersion}:otel${Context.opentelemetryVersion}:${version}`,
216+
};
217+
const expectedBaseData = {
218+
name: BreezePerformanceCounterNames.PROCESSOR_TIME,
219+
value: 1,
220+
dataPointType: "Aggregation",
221+
count: 1,
222+
};
223+
const provider = new MeterProvider({
224+
resource: new Resource({
225+
[SemanticResourceAttributes.SERVICE_NAME]: "basic-service",
226+
}),
227+
});
228+
const exporter = new TestExporter({
229+
connectionString: "InstrumentationKey=00000000-0000-0000-0000-000000000000",
230+
});
231+
const metricReaderOptions: PeriodicExportingMetricReaderOptions = {
232+
exporter: exporter,
233+
};
234+
const metricReader = new PeriodicExportingMetricReader(metricReaderOptions);
235+
provider.addMetricReader(metricReader);
236+
const meter = provider.getMeter("example-meter-node");
237+
// Create Counter instrument with the meter
238+
const counter = meter.createCounter(OTelPerformanceCounterNames.PROCESSOR_TIME);
239+
counter.add(1);
240+
provider.forceFlush();
241+
await new Promise((resolve) => setTimeout(resolve, 800));
242+
const envelope = resourceMetricsToEnvelope(testMetrics, "ikey");
243+
assertEnvelope(
244+
envelope[0],
245+
"Microsoft.ApplicationInsights.Metric",
246+
100,
247+
"MetricData",
248+
expectedTags,
249+
expectedBaseData,
250+
);
251+
});
252+
it("should create process time envelopes with the correct name", async () => {
253+
const expectedTags: Tags = {
254+
"ai.device.osVersion": os && `${os.type()} ${os.release()}`,
255+
"ai.internal.sdkVersion": `${prefix}node${Context.nodeVersion}:otel${Context.opentelemetryVersion}:${version}`,
256+
};
257+
const expectedBaseData = {
258+
name: BreezePerformanceCounterNames.PROCESS_TIME,
259+
value: 1,
260+
dataPointType: "Aggregation",
261+
count: 1,
262+
};
263+
const provider = new MeterProvider({
264+
resource: new Resource({
265+
[SemanticResourceAttributes.SERVICE_NAME]: "basic-service",
266+
}),
267+
});
268+
const exporter = new TestExporter({
269+
connectionString: "InstrumentationKey=00000000-0000-0000-0000-000000000000",
270+
});
271+
const metricReaderOptions: PeriodicExportingMetricReaderOptions = {
272+
exporter: exporter,
273+
};
274+
const metricReader = new PeriodicExportingMetricReader(metricReaderOptions);
275+
provider.addMetricReader(metricReader);
276+
const meter = provider.getMeter("example-meter-node");
277+
// Create Counter instrument with the meter
278+
const counter = meter.createCounter(OTelPerformanceCounterNames.PROCESS_TIME);
279+
counter.add(1);
280+
provider.forceFlush();
281+
await new Promise((resolve) => setTimeout(resolve, 800));
282+
const envelope = resourceMetricsToEnvelope(testMetrics, "ikey");
283+
assertEnvelope(
284+
envelope[0],
285+
"Microsoft.ApplicationInsights.Metric",
286+
100,
287+
"MetricData",
288+
expectedTags,
289+
expectedBaseData,
290+
);
291+
});
292+
it("should create request rate envelopes with the correct name", async () => {
293+
const expectedTags: Tags = {
294+
"ai.device.osVersion": os && `${os.type()} ${os.release()}`,
295+
"ai.internal.sdkVersion": `${prefix}node${Context.nodeVersion}:otel${Context.opentelemetryVersion}:${version}`,
296+
};
297+
const expectedBaseData = {
298+
name: BreezePerformanceCounterNames.REQUEST_RATE,
299+
value: 1,
300+
dataPointType: "Aggregation",
301+
count: 1,
302+
};
303+
const provider = new MeterProvider({
304+
resource: new Resource({
305+
[SemanticResourceAttributes.SERVICE_NAME]: "basic-service",
306+
}),
307+
});
308+
const exporter = new TestExporter({
309+
connectionString: "InstrumentationKey=00000000-0000-0000-0000-000000000000",
310+
});
311+
const metricReaderOptions: PeriodicExportingMetricReaderOptions = {
312+
exporter: exporter,
313+
};
314+
const metricReader = new PeriodicExportingMetricReader(metricReaderOptions);
315+
provider.addMetricReader(metricReader);
316+
const meter = provider.getMeter("example-meter-node");
317+
// Create Counter instrument with the meter
318+
const counter = meter.createCounter(OTelPerformanceCounterNames.REQUEST_RATE);
319+
counter.add(1);
320+
provider.forceFlush();
321+
await new Promise((resolve) => setTimeout(resolve, 800));
322+
const envelope = resourceMetricsToEnvelope(testMetrics, "ikey");
323+
assertEnvelope(
324+
envelope[0],
325+
"Microsoft.ApplicationInsights.Metric",
326+
100,
327+
"MetricData",
328+
expectedTags,
329+
expectedBaseData,
330+
);
331+
});
332+
it("should create request duration envelopes with the correct name", async () => {
333+
const expectedTags: Tags = {
334+
"ai.device.osVersion": os && `${os.type()} ${os.release()}`,
335+
"ai.internal.sdkVersion": `${prefix}node${Context.nodeVersion}:otel${Context.opentelemetryVersion}:${version}`,
336+
};
337+
const expectedBaseData = {
338+
name: BreezePerformanceCounterNames.REQUEST_DURATION,
339+
value: 1,
340+
dataPointType: "Aggregation",
341+
count: 1,
342+
};
343+
const provider = new MeterProvider({
344+
resource: new Resource({
345+
[SemanticResourceAttributes.SERVICE_NAME]: "basic-service",
346+
}),
347+
});
348+
const exporter = new TestExporter({
349+
connectionString: "InstrumentationKey=00000000-0000-0000-0000-000000000000",
350+
});
351+
const metricReaderOptions: PeriodicExportingMetricReaderOptions = {
352+
exporter: exporter,
353+
};
354+
const metricReader = new PeriodicExportingMetricReader(metricReaderOptions);
355+
provider.addMetricReader(metricReader);
356+
const meter = provider.getMeter("example-meter-node");
357+
// Create Counter instrument with the meter
358+
const counter = meter.createCounter(OTelPerformanceCounterNames.REQUEST_DURATION);
359+
counter.add(1);
360+
provider.forceFlush();
361+
await new Promise((resolve) => setTimeout(resolve, 800));
362+
const envelope = resourceMetricsToEnvelope(testMetrics, "ikey");
363+
assertEnvelope(
364+
envelope[0],
365+
"Microsoft.ApplicationInsights.Metric",
366+
100,
367+
"MetricData",
368+
expectedTags,
369+
expectedBaseData,
370+
);
371+
});
372+
});
130373
});

sdk/monitor/monitor-opentelemetry/src/metrics/types.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@ export interface MetricDependencyDimensions extends StandardMetricBaseDimensions
2828
}
2929

3030
export enum PerformanceCounterMetricNames {
31-
PRIVATE_BYTES = "\\Process(??APP_WIN32_PROC??)\\Private Bytes",
32-
AVAILABLE_BYTES = "\\Memory\\Available Bytes",
33-
PROCESSOR_TIME = "\\Processor(_Total)\\% Processor Time",
34-
PROCESS_TIME = "\\Process(??APP_WIN32_PROC??)\\% Processor Time",
35-
REQUEST_RATE = "\\ASP.NET Applications(??APP_W3SVC_PROC??)\\Requests/Sec",
36-
REQUEST_DURATION = "\\ASP.NET Applications(??APP_W3SVC_PROC??)\\Request Execution Time",
31+
PRIVATE_BYTES = "Private_Bytes",
32+
AVAILABLE_BYTES = "Available_Bytes",
33+
PROCESSOR_TIME = "Processor_Time",
34+
PROCESS_TIME = "Process_Time",
35+
REQUEST_RATE = "Request_Rate",
36+
REQUEST_DURATION = "Request_Execution_Time",
3737
}
3838

3939
export type MetricDimensionTypeKeys =

0 commit comments

Comments
 (0)