mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
feat(telemetry): account for Exceptions in usage billing and add avg exception row size
- Update TelemetryUsageBilling description to include Exceptions. - Add AverageExceptionRowSizeInBytes env/config (env example, docker-compose, Helm values & schema). - Use ExceptionInstanceService in TelemetryUsageBillingService to include exception row counts when estimating bytes for Traces. - Add helper to read average exception row size and adjust billing calculations.
This commit is contained in:
@@ -39,7 +39,7 @@ export const DEFAULT_RETENTION_IN_DAYS: number = 15;
|
||||
pluralName: "Telemetry Usage Billings",
|
||||
icon: IconProp.Billing,
|
||||
tableDescription:
|
||||
"Stores historical usage billing data for your telemetry data like Logs, Metrics, and Traces.",
|
||||
"Stores historical usage billing data for your telemetry data like Logs, Metrics, Traces, and Exceptions.",
|
||||
})
|
||||
@Entity({
|
||||
name: "TelemetryUsageBilling",
|
||||
|
||||
@@ -380,6 +380,9 @@ export const AverageMetricRowSizeInBytes: number = parsePositiveNumberFromEnv(
|
||||
1024,
|
||||
);
|
||||
|
||||
export const AverageExceptionRowSizeInBytes: number =
|
||||
parsePositiveNumberFromEnv("AVERAGE_EXCEPTION_ROW_SIZE_IN_BYTES", 1024);
|
||||
|
||||
export const SlackAppClientId: string | null =
|
||||
process.env["SLACK_APP_CLIENT_ID"] || null;
|
||||
export const SlackAppClientSecret: string | null =
|
||||
|
||||
@@ -15,6 +15,7 @@ import TelemetryServiceService from "./TelemetryServiceService";
|
||||
import SpanService from "./SpanService";
|
||||
import LogService from "./LogService";
|
||||
import MetricService from "./MetricService";
|
||||
import ExceptionInstanceService from "./ExceptionInstanceService";
|
||||
import AnalyticsQueryHelper from "../Types/AnalyticsDatabase/QueryHelper";
|
||||
import DiskSize from "../../Types/DiskSize";
|
||||
import logger from "../Utils/Logger";
|
||||
@@ -24,6 +25,7 @@ import {
|
||||
AverageSpanRowSizeInBytes,
|
||||
AverageLogRowSizeInBytes,
|
||||
AverageMetricRowSizeInBytes,
|
||||
AverageExceptionRowSizeInBytes,
|
||||
IsBillingEnabled,
|
||||
} from "../EnvironmentConfig";
|
||||
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
||||
@@ -76,8 +78,21 @@ export class Service extends DatabaseService<Model> {
|
||||
const averageRowSizeInBytes: number = this.getAverageRowSizeForProduct(
|
||||
data.productType,
|
||||
);
|
||||
const averageExceptionRowSizeInBytes: number =
|
||||
this.getAverageExceptionRowSize();
|
||||
|
||||
if (averageRowSizeInBytes <= 0) {
|
||||
if (
|
||||
data.productType !== ProductType.Traces &&
|
||||
averageRowSizeInBytes <= 0
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
data.productType === ProductType.Traces &&
|
||||
averageRowSizeInBytes <= 0 &&
|
||||
averageExceptionRowSizeInBytes <= 0
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -129,11 +144,11 @@ export class Service extends DatabaseService<Model> {
|
||||
continue;
|
||||
}
|
||||
|
||||
let rowCount: number = 0;
|
||||
let estimatedBytes: number = 0;
|
||||
|
||||
try {
|
||||
if (data.productType === ProductType.Traces) {
|
||||
const count: PositiveNumber = await SpanService.countBy({
|
||||
const spanCount: PositiveNumber = await SpanService.countBy({
|
||||
query: {
|
||||
projectId: data.projectId,
|
||||
serviceId: telemetryService.id,
|
||||
@@ -146,7 +161,30 @@ export class Service extends DatabaseService<Model> {
|
||||
},
|
||||
});
|
||||
|
||||
rowCount = count.toNumber();
|
||||
const exceptionCount: PositiveNumber =
|
||||
await ExceptionInstanceService.countBy({
|
||||
query: {
|
||||
projectId: data.projectId,
|
||||
serviceId: telemetryService.id,
|
||||
time: AnalyticsQueryHelper.inBetween(startOfDay, endOfDay),
|
||||
},
|
||||
skip: 0,
|
||||
limit: LIMIT_INFINITY,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
const totalSpanCount: number = spanCount.toNumber();
|
||||
const totalExceptionCount: number = exceptionCount.toNumber();
|
||||
|
||||
if (totalSpanCount <= 0 && totalExceptionCount <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
estimatedBytes =
|
||||
totalSpanCount * averageRowSizeInBytes +
|
||||
totalExceptionCount * averageExceptionRowSizeInBytes;
|
||||
} else if (data.productType === ProductType.Logs) {
|
||||
const count: PositiveNumber = await LogService.countBy({
|
||||
query: {
|
||||
@@ -161,7 +199,13 @@ export class Service extends DatabaseService<Model> {
|
||||
},
|
||||
});
|
||||
|
||||
rowCount = count.toNumber();
|
||||
const totalRowCount: number = count.toNumber();
|
||||
|
||||
if (totalRowCount <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
estimatedBytes = totalRowCount * averageRowSizeInBytes;
|
||||
} else if (data.productType === ProductType.Metrics) {
|
||||
const count: PositiveNumber = await MetricService.countBy({
|
||||
query: {
|
||||
@@ -176,7 +220,13 @@ export class Service extends DatabaseService<Model> {
|
||||
},
|
||||
});
|
||||
|
||||
rowCount = count.toNumber();
|
||||
const totalRowCount: number = count.toNumber();
|
||||
|
||||
if (totalRowCount <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
estimatedBytes = totalRowCount * averageRowSizeInBytes;
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
@@ -186,11 +236,10 @@ export class Service extends DatabaseService<Model> {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rowCount <= 0) {
|
||||
if (estimatedBytes <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const estimatedBytes: number = rowCount * averageRowSizeInBytes;
|
||||
const estimatedGigabytes: number = DiskSize.byteSizeToGB(estimatedBytes);
|
||||
|
||||
if (!Number.isFinite(estimatedGigabytes) || estimatedGigabytes <= 0) {
|
||||
@@ -344,6 +393,20 @@ export class Service extends DatabaseService<Model> {
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private getAverageExceptionRowSize(): number {
|
||||
const fallbackSize: number = 1024;
|
||||
|
||||
if (!Number.isFinite(AverageExceptionRowSizeInBytes)) {
|
||||
return fallbackSize;
|
||||
}
|
||||
|
||||
if (AverageExceptionRowSizeInBytes <= 0) {
|
||||
return fallbackSize;
|
||||
}
|
||||
|
||||
return AverageExceptionRowSizeInBytes;
|
||||
}
|
||||
}
|
||||
|
||||
export default new Service();
|
||||
|
||||
@@ -493,6 +493,8 @@ Usage:
|
||||
|
||||
- name: AVERAGE_METRIC_ROW_SIZE_IN_BYTES
|
||||
value: {{ $.Values.billing.telemetry.averageMetricRowSizeInBytes | quote }}
|
||||
- name: AVERAGE_EXCEPTION_ROW_SIZE_IN_BYTES
|
||||
value: {{ $.Values.billing.telemetry.averageExceptionRowSizeInBytes | quote }}
|
||||
|
||||
{{- end }}
|
||||
|
||||
|
||||
@@ -611,6 +611,10 @@
|
||||
"averageMetricRowSizeInBytes": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"averageExceptionRowSizeInBytes": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
|
||||
@@ -234,6 +234,7 @@ billing:
|
||||
averageSpanRowSizeInBytes: 1024
|
||||
averageLogRowSizeInBytes: 1024
|
||||
averageMetricRowSizeInBytes: 1024
|
||||
averageExceptionRowSizeInBytes: 1024
|
||||
|
||||
subscriptionPlan:
|
||||
basic:
|
||||
|
||||
@@ -208,6 +208,7 @@ BILLING_PRIVATE_KEY=
|
||||
AVERAGE_SPAN_ROW_SIZE_IN_BYTES=1024
|
||||
AVERAGE_LOG_ROW_SIZE_IN_BYTES=1024
|
||||
AVERAGE_METRIC_ROW_SIZE_IN_BYTES=1024
|
||||
AVERAGE_EXCEPTION_ROW_SIZE_IN_BYTES=1024
|
||||
|
||||
# Use this when you want to disable incident creation.
|
||||
DISABLE_AUTOMATIC_INCIDENT_CREATION=false
|
||||
|
||||
@@ -122,6 +122,7 @@ x-common-server-variables: &common-server-variables
|
||||
AVERAGE_SPAN_ROW_SIZE_IN_BYTES: ${AVERAGE_SPAN_ROW_SIZE_IN_BYTES}
|
||||
AVERAGE_LOG_ROW_SIZE_IN_BYTES: ${AVERAGE_LOG_ROW_SIZE_IN_BYTES}
|
||||
AVERAGE_METRIC_ROW_SIZE_IN_BYTES: ${AVERAGE_METRIC_ROW_SIZE_IN_BYTES}
|
||||
AVERAGE_EXCEPTION_ROW_SIZE_IN_BYTES: ${AVERAGE_EXCEPTION_ROW_SIZE_IN_BYTES}
|
||||
|
||||
WORKFLOW_SCRIPT_TIMEOUT_IN_MS: ${WORKFLOW_SCRIPT_TIMEOUT_IN_MS}
|
||||
WORKFLOW_TIMEOUT_IN_MS: ${WORKFLOW_TIMEOUT_IN_MS}
|
||||
|
||||
Reference in New Issue
Block a user