diff --git a/Common/Types/Decimal.ts b/Common/Types/Decimal.ts new file mode 100644 index 0000000000..4082af902d --- /dev/null +++ b/Common/Types/Decimal.ts @@ -0,0 +1,77 @@ +// This is for Object ID for all the things in our database. +import { FindOperator } from 'typeorm'; +import DatabaseProperty from './Database/DatabaseProperty'; +import { JSONObject, ObjectType } from './JSON'; +import BadDataException from './Exception/BadDataException'; + +export default class Decimal extends DatabaseProperty { + + private _value: number = 0; + public get value(): number { + return this._value; + } + public set value(v: number) { + this._value = v; + } + + public constructor(value: number | Decimal | string) { + super(); + + if (typeof value === 'string') { + value = parseFloat(value); + } + + if (value instanceof Decimal) { + value = value.value; + } + + this.value = value; + } + + public equals(other: Decimal): boolean { + return this.value.toString() === other.value.toString(); + } + + public override toString(): string { + return this.value.toString(); + } + + + protected static override toDatabase( + value: Decimal | FindOperator + ): string | null { + if (value) { + return value.toString(); + } + + return null; + } + + public override toJSON(): JSONObject { + return { + _type: ObjectType.Decimal, + value: (this as Decimal).toString(), + }; + } + + public static override fromJSON(json: JSONObject): Decimal { + if (json['_type'] === ObjectType.Decimal) { + return new Decimal((json['value'] as number) || 0); + } + + throw new BadDataException('Invalid JSON: ' + JSON.stringify(json)); + } + + protected static override fromDatabase(_value: number): Decimal | null { + if (_value) { + return new Decimal(_value); + } + + return null; + } + + + public static fromString(value: string): Decimal { + return new Decimal(value); + } +} diff --git a/Common/Types/JSON.ts b/Common/Types/JSON.ts index cbd4562f14..00bca7d3ba 100644 --- a/Common/Types/JSON.ts +++ b/Common/Types/JSON.ts @@ -28,6 +28,7 @@ import StartAndEndTime from './Time/StartAndEndTime'; export enum ObjectType { ObjectID = 'ObjectID', + Decimal = 'Decimal', Name = 'Name', EqualToOrNull = 'EqualToOrNull', MonitorSteps = 'MonitorSteps', diff --git a/CommonServer/Services/UsageBillingService.ts b/CommonServer/Services/UsageBillingService.ts index d2e8f5ebdb..70e8044ced 100644 --- a/CommonServer/Services/UsageBillingService.ts +++ b/CommonServer/Services/UsageBillingService.ts @@ -9,6 +9,7 @@ import SortOrder from 'Common/Types/BaseDatabase/SortOrder'; import { MeteredPlanUtil } from '../Types/Billing/MeteredPlan/AllMeteredPlans'; import MeteredPlan from 'Common/Types/Billing/MeteredPlan'; import ServerMeteredPlan from '../Types/Billing/MeteredPlan/ServerMeteredPlan'; +import Decimal from 'Common/Types/Decimal'; export class Service extends DatabaseService { public constructor(postgresDatabase?: PostgresDatabase) { @@ -94,10 +95,10 @@ export class Service extends DatabaseService { id: usageBilling.id, data: { usageCount: - (usageBilling.usageCount || 0) + data.usageCount, - totalCostInUSD: - (usageBilling.totalCostInUSD || 0) + - totalCostOfThisOperationInUSD, + new Decimal((usageBilling.usageCount?.value || 0) + data.usageCount), + totalCostInUSD:new Decimal( + (usageBilling.totalCostInUSD?.value || 0) + + totalCostOfThisOperationInUSD), }, props: { isRoot: true, @@ -107,13 +108,13 @@ export class Service extends DatabaseService { const usageBilling: Model = new Model(); usageBilling.projectId = data.projectId; usageBilling.productType = data.productType; - usageBilling.usageCount = data.usageCount; + usageBilling.usageCount = new Decimal(data.usageCount); usageBilling.isReportedToBillingProvider = false; usageBilling.createdAt = OneUptimeDate.getCurrentDate(); usageBilling.day = OneUptimeDate.getDateString( OneUptimeDate.getCurrentDate() ); - usageBilling.totalCostInUSD = totalCostOfThisOperationInUSD; + usageBilling.totalCostInUSD = new Decimal(totalCostOfThisOperationInUSD); usageBilling.usageUnitName = meteredPlan.getUnitName(); // e.g. "GB" await this.create({ diff --git a/Dashboard/src/Pages/Settings/UsageHistory.tsx b/Dashboard/src/Pages/Settings/UsageHistory.tsx index e934313da8..545986fdf0 100644 --- a/Dashboard/src/Pages/Settings/UsageHistory.tsx +++ b/Dashboard/src/Pages/Settings/UsageHistory.tsx @@ -12,6 +12,7 @@ import FieldType from 'CommonUI/src/Components/Types/FieldType'; import DashboardNavigation from '../../Utils/Navigation'; import Currency from 'Common/Types/Currency'; import DiskSize from 'Common/Types/DiskSize'; +import Decimal from 'Common/Types/Decimal'; export interface ComponentProps extends PageComponentProps {} @@ -93,7 +94,7 @@ const Settings: FunctionComponent = ( getElement: (item: JSONObject) => { return (
{`${DiskSize.convertToDecimalPlaces( - item['usageCount'] as number + (item['usageCount'] as Decimal).value as number )} ${item['usageUnitName'] as string}`}
); }, @@ -107,7 +108,7 @@ const Settings: FunctionComponent = ( getElement: (item: JSONObject) => { return (
{`${Currency.convertToDecimalPlaces( - item['totalCostInUSD'] as number + (item['totalCostInUSD'] as Decimal).value as number )} USD`}
); }, diff --git a/Examples/otel-dotnet/Program.cs b/Examples/otel-dotnet/Program.cs index c59e16771d..d7bca6993c 100644 --- a/Examples/otel-dotnet/Program.cs +++ b/Examples/otel-dotnet/Program.cs @@ -7,6 +7,8 @@ using OpenTelemetry.Trace; var builder = WebApplication.CreateBuilder(args); +const string endpoint = "http://localhost:4317"; + Console.WriteLine($"Env var: {Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_HEADERS")?.ToString()}"); @@ -28,6 +30,15 @@ builder.Logging.AddOpenTelemetry(logging => .AddConsoleExporter() .AddOtlpExporter(opt => { + // If endpoint was not specified, the proper one will be selected according to the protocol. + if (!string.IsNullOrEmpty(endpoint)) + { + opt.Endpoint = new Uri(endpoint); + + // Set headers in OTLP exporter + // opt.Headers = "oneuptime-service-token=0a00ebc0-7f39-11ee-ac8c-3fb43926b224"; + } + System.Console.WriteLine($"OTLP Exporter is using {opt.Protocol} protocol and endpoint {opt.Endpoint}"); }); }); @@ -41,6 +52,16 @@ builder.Services.AddOpenTelemetry() .AddConsoleExporter() .AddOtlpExporter(opt => { + // If endpoint was not specified, the proper one will be selected according to the protocol. + if (!string.IsNullOrEmpty(endpoint)) + { + opt.Endpoint = new Uri(endpoint); + // Set headers in OTLP exporter + // opt.Headers = "oneuptime-service-token=0a00ebc0-7f39-11ee-ac8c-3fb43926b224"; + + + } + System.Console.WriteLine($"OTLP Exporter is using {opt.Protocol} protocol and endpoint {opt.Endpoint}"); })); @@ -61,6 +82,14 @@ builder.Services.AddOpenTelemetry() }) .AddOtlpExporter(opt => { + // If endpoint was not specified, the proper one will be selected according to the protocol. + if (!string.IsNullOrEmpty(endpoint)) + { + opt.Endpoint = new Uri(endpoint); + // Set headers in OTLP exporter + // opt.Headers = "oneuptime-service-token=0a00ebc0-7f39-11ee-ac8c-3fb43926b224"; + } + System.Console.WriteLine($"OTLP Exporter is using {opt.Protocol} protocol and endpoint {opt.Endpoint}"); })); diff --git a/Examples/otel-dotnet/bin/Debug/net6.0/otel-dotnet.dll b/Examples/otel-dotnet/bin/Debug/net6.0/otel-dotnet.dll index fe3f1e353c..89c324d830 100644 Binary files a/Examples/otel-dotnet/bin/Debug/net6.0/otel-dotnet.dll and b/Examples/otel-dotnet/bin/Debug/net6.0/otel-dotnet.dll differ diff --git a/Examples/otel-dotnet/bin/Debug/net6.0/otel-dotnet.pdb b/Examples/otel-dotnet/bin/Debug/net6.0/otel-dotnet.pdb index 5e131971b9..da98d400cf 100644 Binary files a/Examples/otel-dotnet/bin/Debug/net6.0/otel-dotnet.pdb and b/Examples/otel-dotnet/bin/Debug/net6.0/otel-dotnet.pdb differ diff --git a/Examples/otel-dotnet/obj/Debug/net6.0/otel-dotnet.dll b/Examples/otel-dotnet/obj/Debug/net6.0/otel-dotnet.dll index fe3f1e353c..89c324d830 100644 Binary files a/Examples/otel-dotnet/obj/Debug/net6.0/otel-dotnet.dll and b/Examples/otel-dotnet/obj/Debug/net6.0/otel-dotnet.dll differ diff --git a/Examples/otel-dotnet/obj/Debug/net6.0/otel-dotnet.pdb b/Examples/otel-dotnet/obj/Debug/net6.0/otel-dotnet.pdb index 5e131971b9..da98d400cf 100644 Binary files a/Examples/otel-dotnet/obj/Debug/net6.0/otel-dotnet.pdb and b/Examples/otel-dotnet/obj/Debug/net6.0/otel-dotnet.pdb differ diff --git a/Examples/otel-dotnet/obj/staticwebassets.pack.sentinel b/Examples/otel-dotnet/obj/staticwebassets.pack.sentinel index 4e71f7b789..b3820604ab 100644 --- a/Examples/otel-dotnet/obj/staticwebassets.pack.sentinel +++ b/Examples/otel-dotnet/obj/staticwebassets.pack.sentinel @@ -40,3 +40,4 @@ 2.0 2.0 2.0 +2.0 diff --git a/HelmChart/Public/oneuptime/templates/_helpers.tpl b/HelmChart/Public/oneuptime/templates/_helpers.tpl index 641c2fd932..dda4fc9c0b 100644 --- a/HelmChart/Public/oneuptime/templates/_helpers.tpl +++ b/HelmChart/Public/oneuptime/templates/_helpers.tpl @@ -68,8 +68,10 @@ value: {{ $.Values.port.dashboard | squote }} - name: ADMIN_DASHBOARD_PORT value: {{ $.Values.port.adminDashboard | squote }} -- name: OTEL_COLLECTOR_PORT - value: {{ $.Values.port.otelCollector | squote }} +- name: OTEL_COLLECTOR_GRPC_PORT + value: {{ $.Values.port.otelCollectorGrpc | squote }} +- name: OTEL_COLLECTOR_HTTP_PORT + value: {{ $.Values.port.otelCollectorHttp | squote }} {{- end }} diff --git a/HelmChart/Public/oneuptime/values.yaml b/HelmChart/Public/oneuptime/values.yaml index 31424b26e8..a3d5f36c1c 100644 --- a/HelmChart/Public/oneuptime/values.yaml +++ b/HelmChart/Public/oneuptime/values.yaml @@ -147,7 +147,8 @@ port: nginx: 80 haraka: 2525 probe: 3500 - otelCollector: 4317 + otelCollectorGrpc: 4317 + otelCollectorHttp: 4318 testServer: diff --git a/Ingestor/API/OTelIngest.ts b/Ingestor/API/OTelIngest.ts index 490be4ea06..6a6ae1a8d8 100644 --- a/Ingestor/API/OTelIngest.ts +++ b/Ingestor/API/OTelIngest.ts @@ -35,9 +35,11 @@ import { ProductType } from 'Model/Models/UsageBilling'; const LogsProto: protobuf.Root = protobuf.loadSync( '/usr/src/app/ProtoFiles/OTel/v1/logs.proto' ); + const TracesProto: protobuf.Root = protobuf.loadSync( '/usr/src/app/ProtoFiles/OTel/v1/traces.proto' ); + const MetricsProto: protobuf.Root = protobuf.loadSync( '/usr/src/app/ProtoFiles/OTel/v1/metrics.proto' ); @@ -63,6 +65,19 @@ router.use( '/otel/*', async (req: ExpressRequest, _res: ExpressResponse, next: NextFunction) => { try { + + // check header. + + const serviceTokenInHeader: string | undefined = req.headers[ + 'x-oneuptime-service-token' + ] as string | undefined; + + if (!serviceTokenInHeader) { + throw new BadRequestException( + 'Missing header: x-oneuptime-service-token' + ); + } + // size of req.body in bytes. const sizeInBytes: number = Buffer.byteLength( JSON.stringify(req.body) @@ -85,17 +100,7 @@ router.use( throw new BadRequestException('Invalid URL: ' + req.baseUrl); } - // check header. - const serviceTokenInHeader: string | undefined = req.headers[ - 'x-oneuptime-service-token' - ] as string | undefined; - - if (!serviceTokenInHeader) { - throw new BadRequestException( - 'Missing header: oneuptime-service-token' - ); - } const cachedServiceId: string | null = await GlobalCache.getString( 'service-token', @@ -276,7 +281,7 @@ router.post( (metric['sum'] as JSONObject)['dataPoints'] && ( (metric['sum'] as JSONObject)[ - 'dataPoints' + 'dataPoints' ] as JSONArray ).length > 0 ) { @@ -326,7 +331,7 @@ router.post( (metric['gauge'] as JSONObject)['dataPoints'] && ( (metric['gauge'] as JSONObject)[ - 'dataPoints' + 'dataPoints' ] as JSONArray ).length > 0 ) { @@ -377,7 +382,7 @@ router.post( (metric['histogram'] as JSONObject)['dataPoints'] && ( (metric['histogram'] as JSONObject)[ - 'dataPoints' + 'dataPoints' ] as JSONArray ).length > 0 ) { diff --git a/Model/Models/UsageBilling.ts b/Model/Models/UsageBilling.ts index 56fcc5a270..4e04d4c133 100644 --- a/Model/Models/UsageBilling.ts +++ b/Model/Models/UsageBilling.ts @@ -16,6 +16,7 @@ import ColumnAccessControl from 'Common/Types/Database/AccessControl/ColumnAcces import TenantColumn from 'Common/Types/Database/TenantColumn'; import TableMetadata from 'Common/Types/Database/TableMetadata'; import IconProp from 'Common/Types/Icon/IconProp'; +import Decimal from 'Common/Types/Decimal'; export enum ProductType { Logs = 'Logs', @@ -170,8 +171,9 @@ export default class UsageBilling extends AccessControlModel { @Column({ nullable: false, type: ColumnType.Decimal, + transformer: Decimal.getDatabaseTransformer() }) - public usageCount?: number = undefined; + public usageCount?: Decimal = undefined; @ColumnAccessControl({ create: [], @@ -216,8 +218,9 @@ export default class UsageBilling extends AccessControlModel { @Column({ nullable: false, type: ColumnType.Decimal, + transformer: Decimal.getDatabaseTransformer() }) - public totalCostInUSD?: number = undefined; + public totalCostInUSD?: Decimal = undefined; @ColumnAccessControl({ create: [], diff --git a/config.example.env b/config.example.env index 03f9770dba..4ac1c61390 100644 --- a/config.example.env +++ b/config.example.env @@ -104,7 +104,8 @@ ACCOUNTS_PORT=3003 STATUS_PAGE_PORT=3105 DASHBOARD_PORT=3009 ADMIN_DASHBOARD_PORT=3158 -OTEL_COLLECTOR_PORT=4317 +OTEL_COLLECTOR_GRPC_PORT=4317 +OTEL_COLLECTOR_HTTP_PORT=4318 # If USE_INTERNAL_SMTP is true then you need to fill these values. diff --git a/docker-compose.base.yml b/docker-compose.base.yml index 5e4448eb34..eaad686083 100644 --- a/docker-compose.base.yml +++ b/docker-compose.base.yml @@ -44,7 +44,8 @@ x-common-variables: &common-variables STATUS_PAGE_PORT: ${STATUS_PAGE_PORT} DASHBOARD_PORT: ${DASHBOARD_PORT} ADMIN_DASHBOARD_PORT: ${ADMIN_DASHBOARD_PORT} - OTEL_COLLECTOR_PORT: ${OTEL_COLLECTOR_PORT} + OTEL_COLLECTOR_GRPC_PORT: ${OTEL_COLLECTOR_GRPC_PORT} + OTEL_COLLECTOR_HTTP_PORT: ${OTEL_COLLECTOR_HTTP_PORT} x-common-ui-variables: &common-ui-variables <<: *common-variables