mirror of
https://github.com/OneUptime/oneuptime
synced 2024-11-21 14:49:07 +00:00
refactor: Update telemetry exception view layout components
This commit is contained in:
parent
a3856588bb
commit
cc7696f481
@ -297,6 +297,31 @@ export default class ExceptionInstance extends AnalyticsBaseModel {
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "attributes",
|
||||
title: "Attributes",
|
||||
description: "Attributes",
|
||||
required: true,
|
||||
defaultValue: {},
|
||||
type: TableColumnType.JSON,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryException,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryException,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
],
|
||||
sortKeys: ["projectId", "serviceId", "fingerprint", "time"],
|
||||
primaryKeys: ["projectId", "serviceId", "fingerprint"],
|
||||
@ -390,4 +415,12 @@ export default class ExceptionInstance extends AnalyticsBaseModel {
|
||||
public set fingerprint(v: string | undefined) {
|
||||
this.setColumnValue("fingerprint", v);
|
||||
}
|
||||
|
||||
public get attributes(): Record<string, any> {
|
||||
return this.getColumnValue("attributes") as Record<string, any>;
|
||||
}
|
||||
|
||||
public set attributes(v: Record<string, any>) {
|
||||
this.setColumnValue("attributes", v);
|
||||
}
|
||||
}
|
||||
|
@ -16,8 +16,8 @@ export enum SpanKind {
|
||||
}
|
||||
|
||||
export enum SpanEventType {
|
||||
Exception = "Exception",
|
||||
Event = "Event",
|
||||
Exception = "exception",
|
||||
Event = "event",
|
||||
}
|
||||
|
||||
export enum SpanStatus {
|
||||
|
@ -938,4 +938,38 @@ export default class TelemetryException extends DatabaseBaseModel {
|
||||
default: false,
|
||||
})
|
||||
public isArchived?: boolean = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.CreateTelemetryException,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryException,
|
||||
],
|
||||
update: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.EditTelemetryException,
|
||||
],
|
||||
})
|
||||
@TableColumn({
|
||||
title: "Occurances",
|
||||
description: "Number of times this exception has occured",
|
||||
isDefaultValueColumn: true,
|
||||
required: true,
|
||||
type: TableColumnType.Number,
|
||||
})
|
||||
@Column({
|
||||
type: ColumnType.Number,
|
||||
nullable: false,
|
||||
unique: false,
|
||||
default: 1,
|
||||
})
|
||||
public occuranceCount?: number = undefined;
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { SpanStatusCode } from "@opentelemetry/api";
|
||||
|
||||
import LocalCache from "../Infrastructure/LocalCache";
|
||||
import Express, {
|
||||
ExpressRequest,
|
||||
@ -7,12 +7,11 @@ import Express, {
|
||||
} from "../Utils/Express";
|
||||
import logger from "../Utils/Logger";
|
||||
import Response from "../Utils/Response";
|
||||
import Telemetry, { Span } from "../Utils/Telemetry";
|
||||
import Telemetry, { Span, SpanStatusCode } from "../Utils/Telemetry";
|
||||
import Exception from "Common/Types/Exception/Exception";
|
||||
import ServerException from "Common/Types/Exception/ServerException";
|
||||
import BadDataException from "../../Types/Exception/BadDataException";
|
||||
|
||||
|
||||
export interface StatusAPIOptions {
|
||||
readyCheck: () => Promise<void>;
|
||||
liveCheck: () => Promise<void>;
|
||||
@ -95,16 +94,14 @@ export default class StatusAPI {
|
||||
router.get(
|
||||
"/status/live",
|
||||
async (req: ExpressRequest, res: ExpressResponse) => {
|
||||
|
||||
const span: Span = Telemetry.startSpan({
|
||||
name: "status.live",
|
||||
attributes: {
|
||||
"status": "live"
|
||||
}
|
||||
status: "live",
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
|
||||
logger.debug("Live check");
|
||||
await options.readyCheck();
|
||||
logger.info("Live check: ok");
|
||||
@ -115,16 +112,14 @@ export default class StatusAPI {
|
||||
Response.sendJsonObjectResponse(req, res, {
|
||||
status: "ok",
|
||||
});
|
||||
|
||||
} catch (e) {
|
||||
|
||||
// record exception
|
||||
span.recordException(e as Exception);
|
||||
|
||||
// set span status
|
||||
span.setStatus({
|
||||
code: SpanStatusCode.OK,
|
||||
message: "Live check failed"
|
||||
code: SpanStatusCode.ERROR,
|
||||
message: "Live check failed",
|
||||
});
|
||||
|
||||
this.stausLiveFailed.add(1);
|
||||
|
@ -256,5 +256,5 @@ export const AccountsClientUrl: URL = new URL(
|
||||
AccountsRoute,
|
||||
);
|
||||
|
||||
|
||||
export const DisableTelemetry: boolean = process.env["DISABLE_TELEMETRY"] === "true";
|
||||
export const DisableTelemetry: boolean =
|
||||
process.env["DISABLE_TELEMETRY"] === "true";
|
||||
|
@ -25,12 +25,7 @@ export class MigrationName1724613666632 implements MigrationInterface {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "TelemetryException" ADD "isArchived" boolean NOT NULL DEFAULT false`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "rotation" SET DEFAULT '{"_type":"Recurring","value":{"intervalType":"Day","intervalCount":{"_type":"PositiveNumber","value":1}}}'`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "restrictionTimes" SET DEFAULT '{"_type":"RestrictionTimes","value":{"restictionType":"None","dayRestrictionTimes":null,"weeklyRestrictionTimes":[]}}'`,
|
||||
);
|
||||
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "TelemetryException" ADD CONSTRAINT "FK_3def22373f0cb84e16cb355b5e5" FOREIGN KEY ("markedAsArchivedByUserId") REFERENCES "User"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||
);
|
||||
@ -40,12 +35,6 @@ export class MigrationName1724613666632 implements MigrationInterface {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "TelemetryException" DROP CONSTRAINT "FK_3def22373f0cb84e16cb355b5e5"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "restrictionTimes" SET DEFAULT '{"_type": "RestrictionTimes", "value": {"restictionType": "None", "dayRestrictionTimes": null, "weeklyRestrictionTimes": []}}'`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "rotation" SET DEFAULT '{"_type": "Recurring", "value": {"intervalType": "Day", "intervalCount": {"_type": "PositiveNumber", "value": 1}}}'`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "TelemetryException" DROP COLUMN "isArchived"`,
|
||||
);
|
||||
|
@ -0,0 +1,14 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class MigrationName1724659071843 implements MigrationInterface {
|
||||
public name = 'MigrationName1724659071843'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "TelemetryException" ADD "occuranceCount" integer NOT NULL DEFAULT '1'`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "TelemetryException" DROP COLUMN "occuranceCount"`);
|
||||
}
|
||||
|
||||
}
|
@ -43,6 +43,7 @@ import { MigrationName1723828588502 } from "./1723828588502-MigrationName";
|
||||
import { MigrationName1724078044172 } from "./1724078044172-MigrationName";
|
||||
import { MigrationName1724610006927 } from "./1724610006927-MigrationName";
|
||||
import { MigrationName1724613666632 } from "./1724613666632-MigrationName";
|
||||
import { MigrationName1724659071843 } from "./1724659071843-MigrationName";
|
||||
|
||||
export default [
|
||||
InitialMigration,
|
||||
@ -90,4 +91,5 @@ export default [
|
||||
MigrationName1724078044172,
|
||||
MigrationName1724610006927,
|
||||
MigrationName1724613666632,
|
||||
MigrationName1724659071843
|
||||
];
|
||||
|
@ -60,7 +60,6 @@ import API from "Common/Utils/API";
|
||||
import Slug from "Common/Utils/Slug";
|
||||
import { DataSource, Repository, SelectQueryBuilder } from "typeorm";
|
||||
import { FindWhere } from "../../Types/BaseDatabase/Query";
|
||||
import QueryDeepPartialEntity from "../../Types/Database/PartialEntity";
|
||||
|
||||
class DatabaseService<TBaseModel extends BaseModel> extends BaseService {
|
||||
public modelType!: { new (): TBaseModel };
|
||||
@ -427,10 +426,10 @@ class DatabaseService<TBaseModel extends BaseModel> extends BaseService {
|
||||
}
|
||||
|
||||
private async sanitizeCreateOrUpdate(
|
||||
data: TBaseModel | QueryDeepPartialEntity<TBaseModel>,
|
||||
data: TBaseModel | PartialEntity<TBaseModel>,
|
||||
props: DatabaseCommonInteractionProps,
|
||||
isUpdate: boolean = false,
|
||||
): Promise<TBaseModel | QueryDeepPartialEntity<TBaseModel>> {
|
||||
): Promise<TBaseModel | PartialEntity<TBaseModel>> {
|
||||
data = this.checkMaxLengthOfFields(data as TBaseModel);
|
||||
|
||||
const columns: Columns = this.model.getTableColumns();
|
||||
@ -1274,12 +1273,12 @@ class DatabaseService<TBaseModel extends BaseModel> extends BaseService {
|
||||
beforeUpdateBy.props,
|
||||
);
|
||||
|
||||
const data: QueryDeepPartialEntity<TBaseModel> =
|
||||
const data: PartialEntity<TBaseModel> =
|
||||
(await this.sanitizeCreateOrUpdate(
|
||||
beforeUpdateBy.data,
|
||||
updateBy.props,
|
||||
true,
|
||||
)) as QueryDeepPartialEntity<TBaseModel>;
|
||||
)) as PartialEntity<TBaseModel>;
|
||||
|
||||
if (!(updateBy.skip instanceof PositiveNumber)) {
|
||||
updateBy.skip = new PositiveNumber(updateBy.skip);
|
||||
|
@ -8,7 +8,6 @@ import UpdatePermission from "./UpdatePermission";
|
||||
import BaseModel from "Common/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
|
||||
import DatabaseCommonInteractionProps from "Common/Types/BaseDatabase/DatabaseCommonInteractionProps";
|
||||
|
||||
|
||||
export default class ModelPermission {
|
||||
public static async checkDeletePermissionByModel<
|
||||
TBaseModel extends BaseModel,
|
||||
|
@ -3,7 +3,6 @@ import DatabaseCommonInteractionProps from "Common/Types/BaseDatabase/DatabaseCo
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
import QueryDeepPartialEntity from "../../../Types/Database/PartialEntity";
|
||||
|
||||
|
||||
export default interface UpdateBy<TBaseModel extends BaseModel> {
|
||||
id: ObjectID;
|
||||
data: QueryDeepPartialEntity<TBaseModel>;
|
||||
|
@ -29,8 +29,14 @@ import { DisableTelemetry } from "../EnvironmentConfig";
|
||||
// Enable this line to see debug logs
|
||||
// diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.DEBUG);
|
||||
|
||||
export type Span = opentelemetry.api.Span
|
||||
export type SpanStatus = opentelemetry.api.SpanStatus
|
||||
export type Span = opentelemetry.api.Span;
|
||||
export type SpanStatus = opentelemetry.api.SpanStatus;
|
||||
|
||||
export enum SpanStatusCode {
|
||||
UNSET = 0,
|
||||
OK = 1,
|
||||
ERROR = 2
|
||||
}
|
||||
|
||||
export default class Telemetry {
|
||||
public static sdk: opentelemetry.NodeSDK | null = null;
|
||||
@ -104,9 +110,10 @@ export default class Telemetry {
|
||||
});
|
||||
}
|
||||
|
||||
public static init(data: { serviceName: string }): opentelemetry.NodeSDK | null {
|
||||
|
||||
if(DisableTelemetry){
|
||||
public static init(data: {
|
||||
serviceName: string;
|
||||
}): opentelemetry.NodeSDK | null {
|
||||
if (DisableTelemetry) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -290,7 +297,8 @@ export default class Telemetry {
|
||||
}
|
||||
|
||||
public static getTracer(): opentelemetry.api.Tracer {
|
||||
const tracer: opentelemetry.api.Tracer = OpenTelemetryAPI.trace.getTracer("default");
|
||||
const tracer: opentelemetry.api.Tracer =
|
||||
OpenTelemetryAPI.trace.getTracer("default");
|
||||
return tracer;
|
||||
}
|
||||
|
||||
@ -303,5 +311,4 @@ export default class Telemetry {
|
||||
const span: Span = this.getTracer().startSpan(name, attributes);
|
||||
return span;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,11 +1,16 @@
|
||||
|
||||
/**
|
||||
* Make all properties in T optional. Deep version.
|
||||
*/
|
||||
|
||||
|
||||
type QueryDeepPartialEntity<T> = {
|
||||
[P in keyof T]?: (T[P] extends Array<infer U> ? Array<QueryDeepPartialEntity<U>> : T[P] extends ReadonlyArray<infer U> ? ReadonlyArray<QueryDeepPartialEntity<U>> : QueryDeepPartialEntity<T[P]>) | (() => string) | null;
|
||||
[P in keyof T]?:
|
||||
| (T[P] extends Array<infer U>
|
||||
? Array<QueryDeepPartialEntity<U>>
|
||||
: T[P] extends ReadonlyArray<infer U>
|
||||
? ReadonlyArray<QueryDeepPartialEntity<U>>
|
||||
: QueryDeepPartialEntity<T[P]>)
|
||||
| (() => string)
|
||||
| null;
|
||||
};
|
||||
|
||||
export default QueryDeepPartialEntity;
|
||||
|
@ -210,4 +210,4 @@ const getOpenTelemetryExporterOtlpHeaders: GetOpenTelemetryExporterOtlpHeadersFu
|
||||
export const OpenTelemetryExporterOtlpHeaders: Dictionary<string> =
|
||||
getOpenTelemetryExporterOtlpHeaders();
|
||||
|
||||
export const DisableTelemetry: boolean = env("DISABLE_TELEMETRY") === "true";
|
||||
export const DisableTelemetry: boolean = env("DISABLE_TELEMETRY") === "true";
|
||||
|
@ -19,9 +19,7 @@ import URL from "Common/Types/API/URL";
|
||||
|
||||
export default class Telemetry {
|
||||
public static init(data: { serviceName: string }): void {
|
||||
|
||||
|
||||
if(DisableTelemetry){
|
||||
if (DisableTelemetry) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -419,7 +419,8 @@ const TelemetryRoutes: FunctionComponent<ComponentProps> = (
|
||||
|
||||
<PageRoute
|
||||
path={RouteUtil.getLastPathForKey(
|
||||
PageMap.TELEMETRY_SERVICES_VIEW_EXCEPTIONS_UNRESOLVED, 2
|
||||
PageMap.TELEMETRY_SERVICES_VIEW_EXCEPTIONS_UNRESOLVED,
|
||||
2,
|
||||
)}
|
||||
element={
|
||||
<Suspense fallback={Loader}>
|
||||
@ -437,7 +438,8 @@ const TelemetryRoutes: FunctionComponent<ComponentProps> = (
|
||||
|
||||
<PageRoute
|
||||
path={RouteUtil.getLastPathForKey(
|
||||
PageMap.TELEMETRY_SERVICES_VIEW_EXCEPTIONS_RESOLVED,2
|
||||
PageMap.TELEMETRY_SERVICES_VIEW_EXCEPTIONS_RESOLVED,
|
||||
2,
|
||||
)}
|
||||
element={
|
||||
<Suspense fallback={Loader}>
|
||||
@ -455,7 +457,8 @@ const TelemetryRoutes: FunctionComponent<ComponentProps> = (
|
||||
|
||||
<PageRoute
|
||||
path={RouteUtil.getLastPathForKey(
|
||||
PageMap.TELEMETRY_SERVICES_VIEW_EXCEPTIONS_ARCHIVED,2
|
||||
PageMap.TELEMETRY_SERVICES_VIEW_EXCEPTIONS_ARCHIVED,
|
||||
2,
|
||||
)}
|
||||
element={
|
||||
<Suspense fallback={Loader}>
|
||||
|
@ -108,7 +108,7 @@ export function getTelemetryBreadcrumbs(path: string): Array<Link> | undefined {
|
||||
PageMap.TELEMETRY_SERVICES_VIEW_EXCEPTIONS,
|
||||
["Project", "Telemetry", "Services", "View Service", "Exceptions"],
|
||||
),
|
||||
|
||||
|
||||
...BuildBreadcrumbLinksByTitles(
|
||||
PageMap.TELEMETRY_SERVICES_VIEW_EXCEPTIONS_UNRESOLVED,
|
||||
[
|
||||
|
@ -308,12 +308,22 @@ router.post(
|
||||
exception.traceId = dbSpan.traceId;
|
||||
exception.time = eventTime;
|
||||
exception.timeUnixNano = eventTimeUnixNano;
|
||||
exception.message = eventAttributes["message"] as string;
|
||||
exception.stackTrace = eventAttributes[
|
||||
"stacktrace"
|
||||
] as string;
|
||||
exception.exceptionType = eventAttributes["type"] as string;
|
||||
exception.escaped = eventAttributes["escaped"] as boolean;
|
||||
exception.message = (eventAttributes["exception.message"] as string) || "";
|
||||
exception.stackTrace = (eventAttributes["exception.stacktrace"] as string) || "";
|
||||
exception.exceptionType = (eventAttributes["exception.type"] as string) || "";
|
||||
exception.escaped = (eventAttributes["exception.escaped"] as boolean) || false;
|
||||
const exceptionAttributes: JSONObject = {
|
||||
...eventAttributes,
|
||||
};
|
||||
|
||||
for(const keys of Object.keys(exceptionAttributes)) {
|
||||
// delete all keys that start with exception to avoid duplicate keys because we already saved it.
|
||||
if(keys.startsWith("exception.")) {
|
||||
delete exceptionAttributes[keys];
|
||||
}
|
||||
}
|
||||
|
||||
exception.attributes = exceptionAttributes;
|
||||
exception.fingerprint =
|
||||
ExceptionUtil.getFingerprint(exception);
|
||||
|
||||
@ -322,7 +332,7 @@ router.post(
|
||||
|
||||
// save exception status
|
||||
// maybe this can be improved instead of doing a lot of db calls.
|
||||
// await ExceptionUtil.saveOrUpdateTelemetryException(exception);
|
||||
await ExceptionUtil.saveOrUpdateTelemetryException(exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user