From e84dc4b7615214ad64412a5f3947215ac604772d Mon Sep 17 00:00:00 2001 From: Manjunath Davanam Date: Mon, 21 Oct 2024 11:02:25 +0530 Subject: [PATCH 01/12] #OBS-I250: Generation of AUDIT events in the openTelemetry format --- api-service/src/otel/OTelService.ts | 163 ++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 api-service/src/otel/OTelService.ts diff --git a/api-service/src/otel/OTelService.ts b/api-service/src/otel/OTelService.ts new file mode 100644 index 00000000..087381e6 --- /dev/null +++ b/api-service/src/otel/OTelService.ts @@ -0,0 +1,163 @@ +import { Counter, diag, DiagConsoleLogger, DiagLogLevel, Meter, metrics } from '@opentelemetry/api'; +import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; +import { MeterProvider, PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics'; +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; +import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http'; +import { Resource } from '@opentelemetry/resources'; +import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base'; +import { LoggerProvider, BatchLogRecordProcessor } from '@opentelemetry/sdk-logs'; +import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http'; +import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; +import logger from '../logger'; +import * as logsAPI from '@opentelemetry/api-logs'; + + +export class OTelService { + private static meterProvider: MeterProvider; + private static loggerProvider: LoggerProvider; + private static tracerProvider: NodeTracerProvider; + + public static init() { + const collectorEndpoint = process.env.OTEL_COLLECTOR_ENDPOINT || 'http://localhost:4318'; + this.tracerProvider = this.createTracerProvider(collectorEndpoint); // Store the tracer provider + this.meterProvider = this.createMeterProvider(collectorEndpoint); // Store the meter provider + this.loggerProvider = this.createLoggerProvider(collectorEndpoint); // Store the logger provider + + // Register the global tracer, meter, and logger providers + this.tracerProvider.register(); + this.setGlobalMeterProvider(this.meterProvider); + + logger.info("OpenTelemetry Service Initialized"); + + // Add shutdown hook + process.on('SIGTERM', async () => { + await this.tracerProvider.shutdown(); + await this.meterProvider.shutdown(); + await this.loggerProvider.shutdown(); + }); + } + + private static createTracerProvider(endpoint: string) { + const traceExporter = new OTLPTraceExporter({ + url: `${endpoint}/v1/traces`, + }); + + const tracerProvider = new NodeTracerProvider({ + resource: this.createServiceResource('obsrv-api-service'), + }); + + tracerProvider.addSpanProcessor(new BatchSpanProcessor(traceExporter)); + + return tracerProvider; + } + + private static createMeterProvider(endpoint: string) { + const metricExporter = new OTLPMetricExporter({ + url: `${endpoint}/v1/metrics`, + }); + + const meterProvider = new MeterProvider({ + resource: this.createServiceResource('obsrv-api-service'), + }); + + meterProvider.addMetricReader( + new PeriodicExportingMetricReader({ + exporter: metricExporter, + exportIntervalMillis: 10000, + }) + ); + + return meterProvider; + } + + private static createLoggerProvider(endpoint: string) { + const logExporter = new OTLPLogExporter({ + url: `${endpoint}/v1/logs`, + }); + + const loggerProvider = new LoggerProvider({ + resource: this.createServiceResource('obsrv-api-service'), + }); + + loggerProvider.addLogRecordProcessor( + new BatchLogRecordProcessor(logExporter) + ); + + return loggerProvider; + } + + // Helper method to create a Resource with service name + private static createServiceResource(serviceName: string) { + return new Resource({ + [SemanticResourceAttributes.SERVICE_NAME]: serviceName, + }); + } + + private static setGlobalMeterProvider(meterProvider: MeterProvider) { + diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.INFO); + diag.info('Registering MeterProvider globally.'); + metrics.setGlobalMeterProvider(meterProvider); + } + + // Method to create a counter metric + public static createCounterMetric(name: string): Counter { + const meter = this.getMeterProvider(); // Use the updated getMeterProvider method + const counter = meter.createCounter(name, { + description: 'Counts the number of API calls', + }); + return counter; + } + + public static getMeterProvider(): Meter { + return this.meterProvider.getMeter('obsrv-api-service'); + } + + public static getLoggerProvider(): LoggerProvider { + return this.loggerProvider; + } + + public static getTracerProvider(): NodeTracerProvider { + return this.tracerProvider; + } + + // Method to record the counter metric + public static recordCounter(counter: Counter, value: number) { + counter.add(value, { + // Optional attributes can be added here + service: 'obsrv-api-service', + }); + } + + // Method to log messages + public static log() { + const loggerInstance = this.loggerProvider.getLogger('obsrv-api-service'); // Retrieve a logger instance + + loggerInstance.emit({ + severityNumber: logsAPI.SeverityNumber.INFO, + severityText: 'INFO', + body: 'test', + attributes: { 'log.type': 'LogRecord' }, + }); + } + + public static emitAuditLog(auditLog: Record) { + const loggerInstance = this.loggerProvider.getLogger('obsrv-api-service'); + + // Construct the log record + const logRecord = { + severityNumber: logsAPI.SeverityNumber.INFO, // or ERROR depending on the context + severityText: 'INFO', + body: JSON.stringify(auditLog), // Convert the log object to a string + attributes: { + 'log.type': 'AuditLog', + ...auditLog, // Include the whole log object as attributes if necessary + }, + }; + + // Emit the log record to OpenTelemetry + loggerInstance.emit(logRecord); + + // Log the same message to Winston (optional) + logger.info("Audit log emitted", { auditLog }); + } +} From 2900c631a8a2fb251ab0d2e17e1bc49c163bd81d Mon Sep 17 00:00:00 2001 From: Manjunath Davanam Date: Tue, 5 Nov 2024 14:57:43 +0530 Subject: [PATCH 02/12] #000: Open Telemetry Integration: Generation of system events as open telemetry format --- api-service/package.json | 10 +++ api-service/src/app.ts | 80 +++++++++++++++++++- api-service/src/configs/ConnectionsConfig.ts | 4 +- api-service/src/services/telemetry.ts | 2 +- 4 files changed, 92 insertions(+), 4 deletions(-) diff --git a/api-service/package.json b/api-service/package.json index 2b858f69..761549f8 100644 --- a/api-service/package.json +++ b/api-service/package.json @@ -22,6 +22,16 @@ "@azure/storage-blob": "^12.17.0", "@google-cloud/storage": "^7.9.0", "@jsonhero/schema-infer": "^0.1.5", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/exporter-logs-otlp-http": "^0.53.0", + "@opentelemetry/exporter-metrics-otlp-http": "^0.53.0", + "@opentelemetry/exporter-trace-otlp-http": "^0.53.0", + "@opentelemetry/resources": "^1.26.0", + "@opentelemetry/sdk-logs": "^0.53.0", + "@opentelemetry/sdk-metrics": "^1.26.0", + "@opentelemetry/sdk-node": "^0.53.0", + "@opentelemetry/sdk-trace-node": "^1.26.0", + "@opentelemetry/semantic-conventions": "^1.27.0", "@project-sunbird/logger": "^0.0.9", "ajv": "^8.11.2", "ajv-formats": "^2.1.1", diff --git a/api-service/src/app.ts b/api-service/src/app.ts index 6615ad74..e61ea038 100644 --- a/api-service/src/app.ts +++ b/api-service/src/app.ts @@ -9,9 +9,87 @@ import { ResponseHandler } from "./helpers/ResponseHandler"; import { config } from "./configs/Config"; import { alertsRouter } from "./routes/AlertsRouter"; import { interceptAuditEvents } from "./services/telemetry"; +import { OTelService } from "./otel/OTelService"; +import { LogRecord } from "@opentelemetry/sdk-logs"; + + const app: Application = express(); - +OTelService.init() +OTelService.log() + +const auditLog = { + "eid": "AUDIT", + "ets": 1729158293107, + "ver": "1.0.0", + "mid": "759b9471-b3bd-4818-89f5-cbdf5cdfc421", + "actor": { + "id": "SYSTEM", + "type": "User" + }, + "context": { + "env": "local", + "sid": "37229d4c-38a1-4aac-94cf-5fe34230fb1a", + "pdata": { + "id": "local.api.service", + "ver": "1.0" + } + }, + "object": {}, + "edata": { + "action": "dataset:create", + "props": [ + { + "property": "id", + "ov": null, + "nv": "api.data.in" + }, + { + "property": "ver", + "ov": null, + "nv": "v2" + }, + { + "property": "ts", + "ov": null, + "nv": "1711966306164" + }, + { + "property": "params", + "ov": null, + "nv": { + "msgid": "e180ecac-8f41-4f21-9a21-0b3a1a368917" + } + }, + { + "property": "data", + "ov": null, + "nv": { + "eid": "INTERACT", + "date": "2022-01-01", + "ver": "3.0", + "syncts": 1668591949682, + "ets": 1668591949682 + } + } + ], + "transition": { + "timeUnit": "ms", + "duration": 437, + "toState": "completed", + "fromState": "inprogress" + } + } +}; + +// Emit the audit log +OTelService.emitAuditLog(auditLog); +//const loggerInstance = OTelService.getLoggerProvider(); + + + + + app.use(bodyParser.json({ limit: config.body_parser_limit})); app.use(express.text()); app.use(express.json()); diff --git a/api-service/src/configs/ConnectionsConfig.ts b/api-service/src/configs/ConnectionsConfig.ts index b21512c9..a12d0a56 100644 --- a/api-service/src/configs/ConnectionsConfig.ts +++ b/api-service/src/configs/ConnectionsConfig.ts @@ -5,8 +5,8 @@ export const connectionConfig = { postgres: { host: env.postgres_host || "localhost", port: env.postgres_port || 5432, - database: env.postgres_database || "obsrv", - username: env.postgres_username || "postgres", + database: env.postgres_database || "postgres", + username: env.postgres_username || "manjunathdavanam", password: env.postgres_password || "postgres", }, kafka: { diff --git a/api-service/src/services/telemetry.ts b/api-service/src/services/telemetry.ts index 8408dfb3..6f3a1670 100644 --- a/api-service/src/services/telemetry.ts +++ b/api-service/src/services/telemetry.ts @@ -50,7 +50,7 @@ const getDefaultEdata = ({ action }: any) => ({ }) const sendTelemetryEvents = async (event: Record) => { - send({ messages: [{ value: JSON.stringify(event) }] }, telemetryTopic).catch(console.log); + send(event, telemetryTopic).catch(console.log); } const transformProps = (body: Record) => { From 4a34787fe024e6f596fbfeb16d16e1389dfad5d2 Mon Sep 17 00:00:00 2001 From: Manjunath Date: Tue, 26 Nov 2024 14:41:03 +0530 Subject: [PATCH 03/12] #I250: Obsrv API Service: Generation of open telemetry log for the audit events --- api-service/package.json | 9 +-- api-service/src/app.ts | 86 +++------------------------ api-service/src/otel/OTelService.ts | 57 ++++++++---------- api-service/src/services/telemetry.ts | 2 + 4 files changed, 38 insertions(+), 116 deletions(-) diff --git a/api-service/package.json b/api-service/package.json index b8f4548f..bf0a08fd 100644 --- a/api-service/package.json +++ b/api-service/package.json @@ -25,13 +25,14 @@ "@opentelemetry/api": "^1.9.0", "@opentelemetry/exporter-logs-otlp-http": "^0.53.0", "@opentelemetry/exporter-metrics-otlp-http": "^0.53.0", - "@opentelemetry/exporter-trace-otlp-http": "^0.53.0", - "@opentelemetry/resources": "^1.26.0", + "@opentelemetry/exporter-trace-otlp-http": "^0.55.0", + "@opentelemetry/resources": "^1.28.0", "@opentelemetry/sdk-logs": "^0.53.0", "@opentelemetry/sdk-metrics": "^1.26.0", "@opentelemetry/sdk-node": "^0.53.0", - "@opentelemetry/sdk-trace-node": "^1.26.0", - "@opentelemetry/semantic-conventions": "^1.27.0", + "@opentelemetry/sdk-trace-base": "^1.28.0", + "@opentelemetry/sdk-trace-node": "^1.28.0", + "@opentelemetry/semantic-conventions": "^1.28.0", "@project-sunbird/logger": "^0.0.9", "ajv": "^8.11.2", "ajv-formats": "^2.1.1", diff --git a/api-service/src/app.ts b/api-service/src/app.ts index e61ea038..9e834d83 100644 --- a/api-service/src/app.ts +++ b/api-service/src/app.ts @@ -1,92 +1,20 @@ import express, { Application } from "express"; -import {router as v2Router} from "./routes/Router" -import { metricRouter } from "./routes/MetricRouter" -import { druidProxyRouter } from "./routes/DruidProxyRouter" +import { druidProxyRouter } from "./routes/DruidProxyRouter"; +import { metricRouter } from "./routes/MetricRouter"; +import { router as v2Router } from "./routes/Router"; import bodyParser from "body-parser"; -import { errorHandler, obsrvErrorHandler } from "./middlewares/errors"; -import { ResponseHandler } from "./helpers/ResponseHandler"; import { config } from "./configs/Config"; +import { ResponseHandler } from "./helpers/ResponseHandler"; +import { errorHandler, obsrvErrorHandler } from "./middlewares/errors"; +import { OTelService } from "./otel/OTelService"; import { alertsRouter } from "./routes/AlertsRouter"; import { interceptAuditEvents } from "./services/telemetry"; -import { OTelService } from "./otel/OTelService"; -import { LogRecord } from "@opentelemetry/sdk-logs"; const app: Application = express(); -OTelService.init() -OTelService.log() - -const auditLog = { - "eid": "AUDIT", - "ets": 1729158293107, - "ver": "1.0.0", - "mid": "759b9471-b3bd-4818-89f5-cbdf5cdfc421", - "actor": { - "id": "SYSTEM", - "type": "User" - }, - "context": { - "env": "local", - "sid": "37229d4c-38a1-4aac-94cf-5fe34230fb1a", - "pdata": { - "id": "local.api.service", - "ver": "1.0" - } - }, - "object": {}, - "edata": { - "action": "dataset:create", - "props": [ - { - "property": "id", - "ov": null, - "nv": "api.data.in" - }, - { - "property": "ver", - "ov": null, - "nv": "v2" - }, - { - "property": "ts", - "ov": null, - "nv": "1711966306164" - }, - { - "property": "params", - "ov": null, - "nv": { - "msgid": "e180ecac-8f41-4f21-9a21-0b3a1a368917" - } - }, - { - "property": "data", - "ov": null, - "nv": { - "eid": "INTERACT", - "date": "2022-01-01", - "ver": "3.0", - "syncts": 1668591949682, - "ets": 1668591949682 - } - } - ], - "transition": { - "timeUnit": "ms", - "duration": 437, - "toState": "completed", - "fromState": "inprogress" - } - } -}; - -// Emit the audit log -OTelService.emitAuditLog(auditLog); -//const loggerInstance = OTelService.getLoggerProvider(); - - +OTelService.init() // Initialisation of Open telemetry Service. diff --git a/api-service/src/otel/OTelService.ts b/api-service/src/otel/OTelService.ts index 087381e6..77c960a1 100644 --- a/api-service/src/otel/OTelService.ts +++ b/api-service/src/otel/OTelService.ts @@ -1,15 +1,15 @@ import { Counter, diag, DiagConsoleLogger, DiagLogLevel, Meter, metrics } from '@opentelemetry/api'; -import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; -import { MeterProvider, PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics'; -import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; +import * as logsAPI from '@opentelemetry/api-logs'; +import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http'; import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http'; +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; import { Resource } from '@opentelemetry/resources'; +import { BatchLogRecordProcessor, LoggerProvider } from '@opentelemetry/sdk-logs'; +import { MeterProvider, PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics'; import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base'; -import { LoggerProvider, BatchLogRecordProcessor } from '@opentelemetry/sdk-logs'; -import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http'; +import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; import logger from '../logger'; -import * as logsAPI from '@opentelemetry/api-logs'; export class OTelService { @@ -19,9 +19,9 @@ export class OTelService { public static init() { const collectorEndpoint = process.env.OTEL_COLLECTOR_ENDPOINT || 'http://localhost:4318'; - this.tracerProvider = this.createTracerProvider(collectorEndpoint); // Store the tracer provider - this.meterProvider = this.createMeterProvider(collectorEndpoint); // Store the meter provider - this.loggerProvider = this.createLoggerProvider(collectorEndpoint); // Store the logger provider + this.tracerProvider = this.createTracerProvider(collectorEndpoint); + this.meterProvider = this.createMeterProvider(collectorEndpoint); + this.loggerProvider = this.createLoggerProvider(collectorEndpoint); // Register the global tracer, meter, and logger providers this.tracerProvider.register(); @@ -123,41 +123,32 @@ export class OTelService { // Method to record the counter metric public static recordCounter(counter: Counter, value: number) { counter.add(value, { - // Optional attributes can be added here service: 'obsrv-api-service', }); } - // Method to log messages - public static log() { - const loggerInstance = this.loggerProvider.getLogger('obsrv-api-service'); // Retrieve a logger instance - loggerInstance.emit({ - severityNumber: logsAPI.SeverityNumber.INFO, - severityText: 'INFO', - body: 'test', - attributes: { 'log.type': 'LogRecord' }, - }); - } - - public static emitAuditLog(auditLog: Record) { + public static generateOTelLog(auditLog: Record, severity: 'INFO' | 'WARN' | 'ERROR', logType?: string) { const loggerInstance = this.loggerProvider.getLogger('obsrv-api-service'); - // Construct the log record + const severityMapping: Record = { + INFO: logsAPI.SeverityNumber.INFO, + WARN: logsAPI.SeverityNumber.WARN, + ERROR: logsAPI.SeverityNumber.ERROR, + }; + + const severityNumber = severityMapping[severity] || logsAPI.SeverityNumber.INFO; + const logRecord = { - severityNumber: logsAPI.SeverityNumber.INFO, // or ERROR depending on the context - severityText: 'INFO', - body: JSON.stringify(auditLog), // Convert the log object to a string + severityNumber, + severityText: severity, + body: JSON.stringify(auditLog), attributes: { - 'log.type': 'AuditLog', - ...auditLog, // Include the whole log object as attributes if necessary + 'log.type': logType || 'console', + ...auditLog, }, }; - - // Emit the log record to OpenTelemetry loggerInstance.emit(logRecord); - - // Log the same message to Winston (optional) - logger.info("Audit log emitted", { auditLog }); } + } diff --git a/api-service/src/services/telemetry.ts b/api-service/src/services/telemetry.ts index 6f3a1670..380f35f1 100644 --- a/api-service/src/services/telemetry.ts +++ b/api-service/src/services/telemetry.ts @@ -3,6 +3,7 @@ import { v4 } from "uuid"; import _ from "lodash"; import { config as appConfig } from "../configs/Config"; import {send} from "../connections/kafkaConnection" +import { OTelService } from "../otel/OTelService"; const {env, version} = _.pick(appConfig, ["env","version"]) const telemetryTopic = _.get(appConfig, "telemetry_dataset"); @@ -50,6 +51,7 @@ const getDefaultEdata = ({ action }: any) => ({ }) const sendTelemetryEvents = async (event: Record) => { + OTelService.generateOTelLog(event, 'INFO', 'audit-log'); send(event, telemetryTopic).catch(console.log); } From 5dd06bedfcad9fb7ec10a5be871a3114d60729fc Mon Sep 17 00:00:00 2001 From: Manjunath Date: Tue, 26 Nov 2024 14:41:57 +0530 Subject: [PATCH 04/12] #I250: Removed the white spaces --- api-service/src/app.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/api-service/src/app.ts b/api-service/src/app.ts index 9e834d83..91b401bb 100644 --- a/api-service/src/app.ts +++ b/api-service/src/app.ts @@ -16,8 +16,6 @@ import { interceptAuditEvents } from "./services/telemetry"; const app: Application = express(); OTelService.init() // Initialisation of Open telemetry Service. - - app.use(bodyParser.json({ limit: config.body_parser_limit})); app.use(express.text()); app.use(express.json()); From 0aa72c0a10326b82d140ca1e99b5c6b37d2f631b Mon Sep 17 00:00:00 2001 From: Manjunath Date: Tue, 26 Nov 2024 14:43:23 +0530 Subject: [PATCH 05/12] #I250: Removed the hardcoded postgres details --- api-service/src/configs/ConnectionsConfig.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api-service/src/configs/ConnectionsConfig.ts b/api-service/src/configs/ConnectionsConfig.ts index a12d0a56..b21512c9 100644 --- a/api-service/src/configs/ConnectionsConfig.ts +++ b/api-service/src/configs/ConnectionsConfig.ts @@ -5,8 +5,8 @@ export const connectionConfig = { postgres: { host: env.postgres_host || "localhost", port: env.postgres_port || 5432, - database: env.postgres_database || "postgres", - username: env.postgres_username || "manjunathdavanam", + database: env.postgres_database || "obsrv", + username: env.postgres_username || "postgres", password: env.postgres_password || "postgres", }, kafka: { From 61ab1a2be050b94322f403cf7ad41d551bd8b900 Mon Sep 17 00:00:00 2001 From: Manjunath Date: Tue, 26 Nov 2024 14:52:51 +0530 Subject: [PATCH 06/12] #I250: Variablised the open telemetry endpoint information --- api-service/src/configs/Config.ts | 1 + api-service/src/otel/OTelService.ts | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/api-service/src/configs/Config.ts b/api-service/src/configs/Config.ts index b26a6906..9ddb129d 100644 --- a/api-service/src/configs/Config.ts +++ b/api-service/src/configs/Config.ts @@ -115,4 +115,5 @@ export const config = { }, "user_token_public_key": process.env.user_token_public_key || "", "is_RBAC_enabled": process.env.is_rbac_enabled || "false", + "otel_collector_endpoint": process.env.OTEL_COLLECTOR_ENDPOINT || "http://localhost:4318" } diff --git a/api-service/src/otel/OTelService.ts b/api-service/src/otel/OTelService.ts index 77c960a1..87891111 100644 --- a/api-service/src/otel/OTelService.ts +++ b/api-service/src/otel/OTelService.ts @@ -10,7 +10,9 @@ import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base'; import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; import logger from '../logger'; - +import * as _ from "lodash"; +import { config } from "../configs/Config"; +const collectorEndpoint = _.get(config, "otel_collector_endpoint", "http://localhost:4318"); export class OTelService { private static meterProvider: MeterProvider; @@ -18,7 +20,6 @@ export class OTelService { private static tracerProvider: NodeTracerProvider; public static init() { - const collectorEndpoint = process.env.OTEL_COLLECTOR_ENDPOINT || 'http://localhost:4318'; this.tracerProvider = this.createTracerProvider(collectorEndpoint); this.meterProvider = this.createMeterProvider(collectorEndpoint); this.loggerProvider = this.createLoggerProvider(collectorEndpoint); From 452b81fae38e8763de55327047b7c65432884666 Mon Sep 17 00:00:00 2001 From: Manjunath Date: Tue, 26 Nov 2024 18:38:13 +0530 Subject: [PATCH 07/12] #I250: Moved the file from otel to services --- api-service/src/app.ts | 2 +- api-service/src/otel/OTelService.ts | 4 +- api-service/src/services/otel/OTelService.ts | 155 +++++++++++++++++++ api-service/src/services/telemetry.ts | 2 +- 4 files changed, 159 insertions(+), 4 deletions(-) create mode 100644 api-service/src/services/otel/OTelService.ts diff --git a/api-service/src/app.ts b/api-service/src/app.ts index 91b401bb..8982e594 100644 --- a/api-service/src/app.ts +++ b/api-service/src/app.ts @@ -7,7 +7,7 @@ import bodyParser from "body-parser"; import { config } from "./configs/Config"; import { ResponseHandler } from "./helpers/ResponseHandler"; import { errorHandler, obsrvErrorHandler } from "./middlewares/errors"; -import { OTelService } from "./otel/OTelService"; +import { OTelService } from "./services/otel/OTelService"; import { alertsRouter } from "./routes/AlertsRouter"; import { interceptAuditEvents } from "./services/telemetry"; diff --git a/api-service/src/otel/OTelService.ts b/api-service/src/otel/OTelService.ts index 87891111..2e4fed7d 100644 --- a/api-service/src/otel/OTelService.ts +++ b/api-service/src/otel/OTelService.ts @@ -9,9 +9,9 @@ import { MeterProvider, PeriodicExportingMetricReader } from '@opentelemetry/sdk import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base'; import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; -import logger from '../logger'; +import logger from '../../logger'; import * as _ from "lodash"; -import { config } from "../configs/Config"; +import { config } from "../../configs/Config"; const collectorEndpoint = _.get(config, "otel_collector_endpoint", "http://localhost:4318"); export class OTelService { diff --git a/api-service/src/services/otel/OTelService.ts b/api-service/src/services/otel/OTelService.ts new file mode 100644 index 00000000..2e4fed7d --- /dev/null +++ b/api-service/src/services/otel/OTelService.ts @@ -0,0 +1,155 @@ +import { Counter, diag, DiagConsoleLogger, DiagLogLevel, Meter, metrics } from '@opentelemetry/api'; +import * as logsAPI from '@opentelemetry/api-logs'; +import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http'; +import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http'; +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; +import { Resource } from '@opentelemetry/resources'; +import { BatchLogRecordProcessor, LoggerProvider } from '@opentelemetry/sdk-logs'; +import { MeterProvider, PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics'; +import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base'; +import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; +import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; +import logger from '../../logger'; +import * as _ from "lodash"; +import { config } from "../../configs/Config"; +const collectorEndpoint = _.get(config, "otel_collector_endpoint", "http://localhost:4318"); + +export class OTelService { + private static meterProvider: MeterProvider; + private static loggerProvider: LoggerProvider; + private static tracerProvider: NodeTracerProvider; + + public static init() { + this.tracerProvider = this.createTracerProvider(collectorEndpoint); + this.meterProvider = this.createMeterProvider(collectorEndpoint); + this.loggerProvider = this.createLoggerProvider(collectorEndpoint); + + // Register the global tracer, meter, and logger providers + this.tracerProvider.register(); + this.setGlobalMeterProvider(this.meterProvider); + + logger.info("OpenTelemetry Service Initialized"); + + // Add shutdown hook + process.on('SIGTERM', async () => { + await this.tracerProvider.shutdown(); + await this.meterProvider.shutdown(); + await this.loggerProvider.shutdown(); + }); + } + + private static createTracerProvider(endpoint: string) { + const traceExporter = new OTLPTraceExporter({ + url: `${endpoint}/v1/traces`, + }); + + const tracerProvider = new NodeTracerProvider({ + resource: this.createServiceResource('obsrv-api-service'), + }); + + tracerProvider.addSpanProcessor(new BatchSpanProcessor(traceExporter)); + + return tracerProvider; + } + + private static createMeterProvider(endpoint: string) { + const metricExporter = new OTLPMetricExporter({ + url: `${endpoint}/v1/metrics`, + }); + + const meterProvider = new MeterProvider({ + resource: this.createServiceResource('obsrv-api-service'), + }); + + meterProvider.addMetricReader( + new PeriodicExportingMetricReader({ + exporter: metricExporter, + exportIntervalMillis: 10000, + }) + ); + + return meterProvider; + } + + private static createLoggerProvider(endpoint: string) { + const logExporter = new OTLPLogExporter({ + url: `${endpoint}/v1/logs`, + }); + + const loggerProvider = new LoggerProvider({ + resource: this.createServiceResource('obsrv-api-service'), + }); + + loggerProvider.addLogRecordProcessor( + new BatchLogRecordProcessor(logExporter) + ); + + return loggerProvider; + } + + // Helper method to create a Resource with service name + private static createServiceResource(serviceName: string) { + return new Resource({ + [SemanticResourceAttributes.SERVICE_NAME]: serviceName, + }); + } + + private static setGlobalMeterProvider(meterProvider: MeterProvider) { + diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.INFO); + diag.info('Registering MeterProvider globally.'); + metrics.setGlobalMeterProvider(meterProvider); + } + + // Method to create a counter metric + public static createCounterMetric(name: string): Counter { + const meter = this.getMeterProvider(); // Use the updated getMeterProvider method + const counter = meter.createCounter(name, { + description: 'Counts the number of API calls', + }); + return counter; + } + + public static getMeterProvider(): Meter { + return this.meterProvider.getMeter('obsrv-api-service'); + } + + public static getLoggerProvider(): LoggerProvider { + return this.loggerProvider; + } + + public static getTracerProvider(): NodeTracerProvider { + return this.tracerProvider; + } + + // Method to record the counter metric + public static recordCounter(counter: Counter, value: number) { + counter.add(value, { + service: 'obsrv-api-service', + }); + } + + + public static generateOTelLog(auditLog: Record, severity: 'INFO' | 'WARN' | 'ERROR', logType?: string) { + const loggerInstance = this.loggerProvider.getLogger('obsrv-api-service'); + + const severityMapping: Record = { + INFO: logsAPI.SeverityNumber.INFO, + WARN: logsAPI.SeverityNumber.WARN, + ERROR: logsAPI.SeverityNumber.ERROR, + }; + + const severityNumber = severityMapping[severity] || logsAPI.SeverityNumber.INFO; + + const logRecord = { + severityNumber, + severityText: severity, + body: JSON.stringify(auditLog), + attributes: { + 'log.type': logType || 'console', + ...auditLog, + }, + }; + loggerInstance.emit(logRecord); + } + +} diff --git a/api-service/src/services/telemetry.ts b/api-service/src/services/telemetry.ts index 380f35f1..7aa4db86 100644 --- a/api-service/src/services/telemetry.ts +++ b/api-service/src/services/telemetry.ts @@ -3,7 +3,7 @@ import { v4 } from "uuid"; import _ from "lodash"; import { config as appConfig } from "../configs/Config"; import {send} from "../connections/kafkaConnection" -import { OTelService } from "../otel/OTelService"; +import { OTelService } from "./otel/OTelService"; const {env, version} = _.pick(appConfig, ["env","version"]) const telemetryTopic = _.get(appConfig, "telemetry_dataset"); From 88f7fba1488003496123385bf89630a074dd1bbd Mon Sep 17 00:00:00 2001 From: Manjunath Date: Tue, 26 Nov 2024 18:39:18 +0530 Subject: [PATCH 08/12] #I250: Moved the file from otel to services --- api-service/src/otel/OTelService.ts | 155 ---------------------------- 1 file changed, 155 deletions(-) delete mode 100644 api-service/src/otel/OTelService.ts diff --git a/api-service/src/otel/OTelService.ts b/api-service/src/otel/OTelService.ts deleted file mode 100644 index 2e4fed7d..00000000 --- a/api-service/src/otel/OTelService.ts +++ /dev/null @@ -1,155 +0,0 @@ -import { Counter, diag, DiagConsoleLogger, DiagLogLevel, Meter, metrics } from '@opentelemetry/api'; -import * as logsAPI from '@opentelemetry/api-logs'; -import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http'; -import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http'; -import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; -import { Resource } from '@opentelemetry/resources'; -import { BatchLogRecordProcessor, LoggerProvider } from '@opentelemetry/sdk-logs'; -import { MeterProvider, PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics'; -import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base'; -import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; -import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; -import logger from '../../logger'; -import * as _ from "lodash"; -import { config } from "../../configs/Config"; -const collectorEndpoint = _.get(config, "otel_collector_endpoint", "http://localhost:4318"); - -export class OTelService { - private static meterProvider: MeterProvider; - private static loggerProvider: LoggerProvider; - private static tracerProvider: NodeTracerProvider; - - public static init() { - this.tracerProvider = this.createTracerProvider(collectorEndpoint); - this.meterProvider = this.createMeterProvider(collectorEndpoint); - this.loggerProvider = this.createLoggerProvider(collectorEndpoint); - - // Register the global tracer, meter, and logger providers - this.tracerProvider.register(); - this.setGlobalMeterProvider(this.meterProvider); - - logger.info("OpenTelemetry Service Initialized"); - - // Add shutdown hook - process.on('SIGTERM', async () => { - await this.tracerProvider.shutdown(); - await this.meterProvider.shutdown(); - await this.loggerProvider.shutdown(); - }); - } - - private static createTracerProvider(endpoint: string) { - const traceExporter = new OTLPTraceExporter({ - url: `${endpoint}/v1/traces`, - }); - - const tracerProvider = new NodeTracerProvider({ - resource: this.createServiceResource('obsrv-api-service'), - }); - - tracerProvider.addSpanProcessor(new BatchSpanProcessor(traceExporter)); - - return tracerProvider; - } - - private static createMeterProvider(endpoint: string) { - const metricExporter = new OTLPMetricExporter({ - url: `${endpoint}/v1/metrics`, - }); - - const meterProvider = new MeterProvider({ - resource: this.createServiceResource('obsrv-api-service'), - }); - - meterProvider.addMetricReader( - new PeriodicExportingMetricReader({ - exporter: metricExporter, - exportIntervalMillis: 10000, - }) - ); - - return meterProvider; - } - - private static createLoggerProvider(endpoint: string) { - const logExporter = new OTLPLogExporter({ - url: `${endpoint}/v1/logs`, - }); - - const loggerProvider = new LoggerProvider({ - resource: this.createServiceResource('obsrv-api-service'), - }); - - loggerProvider.addLogRecordProcessor( - new BatchLogRecordProcessor(logExporter) - ); - - return loggerProvider; - } - - // Helper method to create a Resource with service name - private static createServiceResource(serviceName: string) { - return new Resource({ - [SemanticResourceAttributes.SERVICE_NAME]: serviceName, - }); - } - - private static setGlobalMeterProvider(meterProvider: MeterProvider) { - diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.INFO); - diag.info('Registering MeterProvider globally.'); - metrics.setGlobalMeterProvider(meterProvider); - } - - // Method to create a counter metric - public static createCounterMetric(name: string): Counter { - const meter = this.getMeterProvider(); // Use the updated getMeterProvider method - const counter = meter.createCounter(name, { - description: 'Counts the number of API calls', - }); - return counter; - } - - public static getMeterProvider(): Meter { - return this.meterProvider.getMeter('obsrv-api-service'); - } - - public static getLoggerProvider(): LoggerProvider { - return this.loggerProvider; - } - - public static getTracerProvider(): NodeTracerProvider { - return this.tracerProvider; - } - - // Method to record the counter metric - public static recordCounter(counter: Counter, value: number) { - counter.add(value, { - service: 'obsrv-api-service', - }); - } - - - public static generateOTelLog(auditLog: Record, severity: 'INFO' | 'WARN' | 'ERROR', logType?: string) { - const loggerInstance = this.loggerProvider.getLogger('obsrv-api-service'); - - const severityMapping: Record = { - INFO: logsAPI.SeverityNumber.INFO, - WARN: logsAPI.SeverityNumber.WARN, - ERROR: logsAPI.SeverityNumber.ERROR, - }; - - const severityNumber = severityMapping[severity] || logsAPI.SeverityNumber.INFO; - - const logRecord = { - severityNumber, - severityText: severity, - body: JSON.stringify(auditLog), - attributes: { - 'log.type': logType || 'console', - ...auditLog, - }, - }; - loggerInstance.emit(logRecord); - } - -} From 33a1c69d669d5e8ac94b066d2ec17b55509cd36f Mon Sep 17 00:00:00 2001 From: Manjunath Date: Tue, 26 Nov 2024 18:45:33 +0530 Subject: [PATCH 09/12] #I250: Moved the file from otel to services --- api-service/src/app.ts | 2 +- api-service/src/configs/Config.ts | 7 ++++++- api-service/src/services/otel/OTelService.ts | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/api-service/src/app.ts b/api-service/src/app.ts index 8982e594..bfcc2f9a 100644 --- a/api-service/src/app.ts +++ b/api-service/src/app.ts @@ -14,7 +14,7 @@ import { interceptAuditEvents } from "./services/telemetry"; const app: Application = express(); -OTelService.init() // Initialisation of Open telemetry Service. +(config.otel && config.otel.enable) && OTelService.init() // Initialisation of Open telemetry Service. app.use(bodyParser.json({ limit: config.body_parser_limit})); app.use(express.text()); diff --git a/api-service/src/configs/Config.ts b/api-service/src/configs/Config.ts index 9ddb129d..f681e022 100644 --- a/api-service/src/configs/Config.ts +++ b/api-service/src/configs/Config.ts @@ -115,5 +115,10 @@ export const config = { }, "user_token_public_key": process.env.user_token_public_key || "", "is_RBAC_enabled": process.env.is_rbac_enabled || "false", - "otel_collector_endpoint": process.env.OTEL_COLLECTOR_ENDPOINT || "http://localhost:4318" + "otel": { + "enable": process.env.OTEL_ENABLE || true, + "collector_endpoint": process.env.OTEL_COLLECTOR_ENDPOINT || "http://localhost:4318" + } + + } diff --git a/api-service/src/services/otel/OTelService.ts b/api-service/src/services/otel/OTelService.ts index 2e4fed7d..fc8c0a54 100644 --- a/api-service/src/services/otel/OTelService.ts +++ b/api-service/src/services/otel/OTelService.ts @@ -12,7 +12,7 @@ import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions' import logger from '../../logger'; import * as _ from "lodash"; import { config } from "../../configs/Config"; -const collectorEndpoint = _.get(config, "otel_collector_endpoint", "http://localhost:4318"); +const collectorEndpoint = _.get(config, "otel.collector_endpoint", "http://localhost:4318"); export class OTelService { private static meterProvider: MeterProvider; From 559b43914d7bf73ecc404f1993d6c5c8235448ee Mon Sep 17 00:00:00 2001 From: Harish Kumar Gangula Date: Wed, 27 Nov 2024 10:10:53 +0530 Subject: [PATCH 10/12] #I250: comparison fix --- api-service/src/app.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api-service/src/app.ts b/api-service/src/app.ts index bfcc2f9a..6a503114 100644 --- a/api-service/src/app.ts +++ b/api-service/src/app.ts @@ -10,11 +10,12 @@ import { errorHandler, obsrvErrorHandler } from "./middlewares/errors"; import { OTelService } from "./services/otel/OTelService"; import { alertsRouter } from "./routes/AlertsRouter"; import { interceptAuditEvents } from "./services/telemetry"; +import _ from "lodash"; const app: Application = express(); -(config.otel && config.otel.enable) && OTelService.init() // Initialisation of Open telemetry Service. +((config.otel && _.toLower(config.otel.enable) === "true")) && OTelService.init() // Initialisation of Open telemetry Service. app.use(bodyParser.json({ limit: config.body_parser_limit})); app.use(express.text()); From 56f4cb4ac94cee4e3472c53f56712e76bf240511 Mon Sep 17 00:00:00 2001 From: Harish Kumar Gangula Date: Wed, 27 Nov 2024 10:11:09 +0530 Subject: [PATCH 11/12] #I250: comparison fix --- api-service/src/app.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api-service/src/app.ts b/api-service/src/app.ts index 6a503114..e01491cd 100644 --- a/api-service/src/app.ts +++ b/api-service/src/app.ts @@ -15,7 +15,7 @@ import _ from "lodash"; const app: Application = express(); -((config.otel && _.toLower(config.otel.enable) === "true")) && OTelService.init() // Initialisation of Open telemetry Service. +((config.otel && _.toLower(config?.otel?.enable) === "true")) && OTelService.init() // Initialisation of Open telemetry Service. app.use(bodyParser.json({ limit: config.body_parser_limit})); app.use(express.text()); From 640a8459f36ffc94b6c05ca39ecb3d3f7a7fb3a8 Mon Sep 17 00:00:00 2001 From: Harish Kumar Gangula Date: Wed, 27 Nov 2024 10:11:45 +0530 Subject: [PATCH 12/12] #I250: config updated --- api-service/src/configs/Config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api-service/src/configs/Config.ts b/api-service/src/configs/Config.ts index f681e022..61ca7980 100644 --- a/api-service/src/configs/Config.ts +++ b/api-service/src/configs/Config.ts @@ -116,7 +116,7 @@ export const config = { "user_token_public_key": process.env.user_token_public_key || "", "is_RBAC_enabled": process.env.is_rbac_enabled || "false", "otel": { - "enable": process.env.OTEL_ENABLE || true, + "enable": process.env.OTEL_ENABLE || "true", "collector_endpoint": process.env.OTEL_COLLECTOR_ENDPOINT || "http://localhost:4318" }