refactor: Update Telemetry class to add gauge and histogram metrics

This code change updates the Telemetry class in the CommonServer/Utils directory to include methods for creating gauge and histogram metrics. The getGauge and getHistogram methods are added, allowing for the creation of observable gauges and histograms with specified names and descriptions. This change enhances the telemetry capabilities of the application by providing more options for metric tracking and analysis.
This commit is contained in:
Simon Larsen 2024-06-10 12:39:49 +01:00
parent 47bf8f9c89
commit 77f1262ff5
No known key found for this signature in database
GPG Key ID: 96C5DCA24769DBCA
5 changed files with 151 additions and 30 deletions

View File

@ -45,6 +45,16 @@ export default class StatusAPI {
description: 'Live check counter',
});
public static statusGuage = Telemetry.getGauge({
name: 'status.guage',
description: 'Status guage',
});
public static stausHistogram = Telemetry.getHistogram({
name: 'status.histogram',
description: 'Status histogram',
});
public static init(options: StatusAPIOptions): ExpressRouter {
const router: ExpressRouter = Express.getRouter();
@ -59,6 +69,14 @@ export default class StatusAPI {
router.get('/status', (req: ExpressRequest, res: ExpressResponse) => {
this.statusCheckSuccessCounter.add(1);
this.statusGuage.addCallback((observableResult)=>{
console.log(observableResult);
observableResult.observe(1.354);
});
this.stausHistogram.record(1.354);
logger.info('Status check: ok');
Response.sendJsonObjectResponse(req, res, {

View File

@ -2,7 +2,9 @@ import OpenTelemetryAPI, { Meter } from '@opentelemetry/api';
import { Logger, logs } from '@opentelemetry/api-logs';
import {
Counter,
Histogram,
MetricOptions,
ObservableGauge,
} from '@opentelemetry/api/build/src/metrics/Metric';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';
@ -232,6 +234,52 @@ export default class Telemetry {
return counter;
}
// guage
public static getGauge(data: {
name: string;
description: string;
unit?: string;
}): ObservableGauge {
const { name, description } = data;
const metricOptions: MetricOptions = {
description: description,
};
if (data.unit) {
metricOptions.unit = data.unit;
}
const guage: ObservableGauge<opentelemetry.api.Attributes> =
this.getMeter().createObservableGauge(name, metricOptions);
return guage;
}
// histogram
public static getHistogram(data: {
name: string;
description: string;
unit?: string;
}): Histogram {
const { name, description } = data;
const metricOptions: MetricOptions = {
description: description,
};
if (data.unit) {
metricOptions.unit = data.unit;
}
const histogram: Histogram<opentelemetry.api.Attributes> =
this.getMeter().createHistogram(name, metricOptions);
return histogram;
}
}
Telemetry.init({

View File

@ -19,7 +19,7 @@ import Express, {
import logger from 'CommonServer/Utils/Logger';
import Response from 'CommonServer/Utils/Response';
import Log, { LogSeverity } from 'Model/AnalyticsModels/Log';
import Metric, { MetricPointType } from 'Model/AnalyticsModels/Metric';
import Metric, { AggregationTemporality, MetricPointType } from 'Model/AnalyticsModels/Metric';
import Span, { SpanKind, SpanStatus } from 'Model/AnalyticsModels/Span';
import protobuf from 'protobufjs';
@ -134,18 +134,18 @@ router.post(
if (
resourceSpan['resource'] &&
(resourceSpan['resource'] as JSONObject)[
'attributes'
'attributes'
] &&
(
(resourceSpan['resource'] as JSONObject)[
'attributes'
'attributes'
] as JSONArray
).length > 0
) {
attributesObject['resource'] =
OTelIngestService.getAttributes(
(resourceSpan['resource'] as JSONObject)[
'attributes'
'attributes'
] as JSONArray
);
}
@ -193,7 +193,7 @@ router.post(
span['status'] &&
(span['status'] as JSONObject)?.['code'] &&
typeof (span['status'] as JSONObject)?.['code'] ===
'number'
'number'
) {
spanStatusCode = (span['status'] as JSONObject)?.[
'code'
@ -204,7 +204,7 @@ router.post(
span['status'] &&
(span['status'] as JSONObject)?.['code'] &&
typeof (span['status'] as JSONObject)?.['code'] ===
'string'
'string'
) {
if (
(span['status'] as JSONObject)?.['code'] ===
@ -388,14 +388,14 @@ router.post(
if (
resourceMetric['resource'] &&
(resourceMetric['resource'] as JSONObject)[
'attributes'
'attributes'
] &&
((resourceMetric['resource'] as JSONObject)[
'attributes'
] as JSONArray) instanceof Array &&
(
(resourceMetric['resource'] as JSONObject)[
'attributes'
'attributes'
] as JSONArray
).length > 0
) {
@ -403,7 +403,7 @@ router.post(
...attributesObject,
resource: OTelIngestService.getAttributes(
(resourceMetric['resource'] as JSONObject)[
'attributes'
'attributes'
] as JSONArray
),
};
@ -428,7 +428,7 @@ router.post(
(metric['sum'] as JSONObject)['dataPoints'] &&
(
(metric['sum'] as JSONObject)[
'dataPoints'
'dataPoints'
] as JSONArray
).length > 0
) {
@ -436,10 +436,12 @@ router.post(
metric['sum'] as JSONObject
)['dataPoints'] as JSONArray) {
const sumMetric: Metric =
OTelIngestService.getMetricFromDatapoint(
dbMetric,
datapoint
);
OTelIngestService.getMetricFromDatapoint({
dbMetric: dbMetric,
datapoint: datapoint,
aggregationTemporality: (metric['sum'] as JSONObject)['aggregationTemporality'] as AggregationTemporality,
isMonotonic: (metric['sum'] as JSONObject)['isMonotonic'] as boolean | undefined
});
sumMetric.metricPointType = MetricPointType.Sum;
@ -450,7 +452,7 @@ router.post(
(metric['gauge'] as JSONObject)['dataPoints'] &&
(
(metric['gauge'] as JSONObject)[
'dataPoints'
'dataPoints'
] as JSONArray
).length > 0
) {
@ -458,10 +460,12 @@ router.post(
metric['gauge'] as JSONObject
)['dataPoints'] as JSONArray) {
const guageMetric: Metric =
OTelIngestService.getMetricFromDatapoint(
dbMetric,
datapoint
);
OTelIngestService.getMetricFromDatapoint({
dbMetric: dbMetric,
datapoint: datapoint,
aggregationTemporality: (metric['gauge'] as JSONObject)['aggregationTemporality'] as AggregationTemporality,
isMonotonic: (metric['gauge'] as JSONObject)['isMonotonic'] as boolean | undefined
});
guageMetric.metricPointType =
MetricPointType.Gauge;
@ -473,7 +477,7 @@ router.post(
(metric['histogram'] as JSONObject)['dataPoints'] &&
(
(metric['histogram'] as JSONObject)[
'dataPoints'
'dataPoints'
] as JSONArray
).length > 0
) {
@ -481,10 +485,12 @@ router.post(
metric['histogram'] as JSONObject
)['dataPoints'] as JSONArray) {
const histogramMetric: Metric =
OTelIngestService.getMetricFromDatapoint(
dbMetric,
datapoint
);
OTelIngestService.getMetricFromDatapoint({
dbMetric: dbMetric,
datapoint: datapoint,
aggregationTemporality: (metric['histogram'] as JSONObject)['aggregationTemporality'] as AggregationTemporality,
isMonotonic: (metric['histogram'] as JSONObject)['isMonotonic'] as boolean | undefined
});
histogramMetric.metricPointType =
MetricPointType.Histogram;
@ -576,11 +582,11 @@ router.post(
if (
resourceLog['resource'] &&
(resourceLog['resource'] as JSONObject)[
'attributes'
'attributes'
] &&
(
(resourceLog['resource'] as JSONObject)[
'attributes'
'attributes'
] as JSONArray
).length > 0
) {
@ -588,7 +594,7 @@ router.post(
...attributesObject,
resource: OTelIngestService.getAttributes(
(resourceLog['resource'] as JSONObject)[
'attributes'
'attributes'
] as JSONArray
),
};

View File

@ -1,7 +1,7 @@
import OneUptimeDate from 'Common/Types/Date';
import { JSONArray, JSONObject, JSONValue } from 'Common/Types/JSON';
import JSONFunctions from 'Common/Types/JSONFunctions';
import Metric from 'Model/AnalyticsModels/Metric';
import Metric, { AggregationTemporality } from 'Model/AnalyticsModels/Metric';
export default class OTelIngestService {
public static getAttributes(items: JSONArray): JSONObject {
@ -28,10 +28,16 @@ export default class OTelIngestService {
return JSONFunctions.flattenObject(finalObj);
}
public static getMetricFromDatapoint(
public static getMetricFromDatapoint(data: {
dbMetric: Metric,
datapoint: JSONObject
datapoint: JSONObject,
aggregationTemporality: AggregationTemporality,
isMonotonic: boolean | undefined
}
): Metric {
const { dbMetric, datapoint, aggregationTemporality, isMonotonic } = data;
const newDbMetric: Metric = Metric.fromJSON(
dbMetric.toJSON(),
Metric
@ -85,6 +91,17 @@ export default class OTelIngestService {
);
}
// aggregationTemporality
if (aggregationTemporality) {
newDbMetric.aggregationTemporality = aggregationTemporality;
}
if(isMonotonic !== undefined) {
newDbMetric.isMonotonic = isMonotonic;
}
return newDbMetric;
}
}

View File

@ -336,6 +336,30 @@ export default class Metric extends AnalyticsBaseModel {
},
}),
new AnalyticsTableColumn({
key: 'isMonotonic',
title: 'Is Monotonic',
description: 'Is Monotonic',
required: false,
type: TableColumnType.Boolean,
accessControl: {
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadTelemetryServiceLog,
],
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.CreateTelemetryServiceLog,
],
update: [],
},
}),
new AnalyticsTableColumn({
key: 'count',
title: 'Count',
@ -559,6 +583,14 @@ export default class Metric extends AnalyticsBaseModel {
this.setColumnValue('unit', v);
}
public get isMonotonic(): boolean | undefined {
return this.getColumnValue('isMonotonic') as boolean | undefined;
}
public set isMonotonic(v: boolean | undefined) {
this.setColumnValue('isMonotonic', v);
}
public set serviceId(v: ObjectID | undefined) {
this.setColumnValue('serviceId', v);
}