mirror of
https://github.com/OneUptime/oneuptime
synced 2024-11-23 07:42:10 +00:00
refactor: Update code and field types for telemetry exceptions
This commit is contained in:
parent
3636b160fb
commit
64725b3973
@ -54,8 +54,8 @@ import TelemetryService from "./TelemetryService";
|
||||
@CrudApiEndpoint(new Route("/telemetry-exception-status"))
|
||||
@TableMetadata({
|
||||
tableName: "TelemetryException",
|
||||
singularName: "TelemetryException",
|
||||
pluralName: "TelemetryExceptionsStatus",
|
||||
singularName: "Exception",
|
||||
pluralName: "Exceptions",
|
||||
icon: IconProp.Error,
|
||||
tableDescription:
|
||||
"List of all Telemetry Exceptions created for the telemetry service for this OneUptime project and it's status.",
|
||||
|
@ -5,6 +5,7 @@ enum CodeType {
|
||||
JSON = "json",
|
||||
Markdown = "markdown",
|
||||
SQL = "sql",
|
||||
Text = "text",
|
||||
// TODO add more mime types.
|
||||
}
|
||||
|
||||
|
@ -283,7 +283,8 @@ const Detail: DetailFunction = <T extends GenericObject>(
|
||||
(field.fieldType === FieldType.HTML ||
|
||||
field.fieldType === FieldType.CSS ||
|
||||
field.fieldType === FieldType.JSON ||
|
||||
field.fieldType === FieldType.JavaScript)
|
||||
field.fieldType === FieldType.JavaScript ||
|
||||
field.fieldType === FieldType.Code)
|
||||
) {
|
||||
let codeType: CodeType = CodeType.HTML;
|
||||
|
||||
@ -315,6 +316,10 @@ const Detail: DetailFunction = <T extends GenericObject>(
|
||||
codeType = CodeType.JavaScript;
|
||||
}
|
||||
|
||||
if (field.fieldType === FieldType.Code) {
|
||||
codeType = CodeType.Text;
|
||||
}
|
||||
|
||||
data = (
|
||||
<CodeEditor
|
||||
type={codeType}
|
||||
|
@ -34,6 +34,7 @@ enum FieldType {
|
||||
Element = "Element",
|
||||
Minutes = "Minutes",
|
||||
ArrayOfText = "ArrayOfText",
|
||||
Code = "Code",
|
||||
}
|
||||
|
||||
export default FieldType;
|
||||
|
127
Dashboard/src/Components/Exceptions/ExceptionDetail.tsx
Normal file
127
Dashboard/src/Components/Exceptions/ExceptionDetail.tsx
Normal file
@ -0,0 +1,127 @@
|
||||
import TelemetryService from "Common/Models/DatabaseModels/TelemetryService";
|
||||
import { JSONObject } from "Common/Types/JSON";
|
||||
import Card from "Common/UI/Components/Card/Card";
|
||||
import Detail from "Common/UI/Components/Detail/Detail";
|
||||
import Field from "Common/UI/Components/Detail/Field";
|
||||
import FieldType from "Common/UI/Components/Types/FieldType";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
import TelemetryServiceElement from "../TelemetryService/TelemetryServiceElement";
|
||||
|
||||
export interface ComponentProps {
|
||||
exceptionType?: string | undefined;
|
||||
message?: string | undefined;
|
||||
stackTrace?: string | undefined;
|
||||
fingerprint?: string | undefined;
|
||||
firstSeenAt?: Date | undefined;
|
||||
lastSeenAt?: Date | undefined;
|
||||
occuranceCount?: number | undefined;
|
||||
attributes?: JSONObject | undefined;
|
||||
telemetryService?: TelemetryService | undefined;
|
||||
}
|
||||
|
||||
const ExceptionDetail: FunctionComponent<ComponentProps> = (
|
||||
props: ComponentProps,
|
||||
): ReactElement => {
|
||||
|
||||
const fields: Array<Field<ComponentProps>> = [];
|
||||
|
||||
if (props.message) {
|
||||
fields.push({
|
||||
key: "message",
|
||||
title: "Message",
|
||||
description: "The message of the exception.",
|
||||
fieldType: FieldType.Text,
|
||||
});
|
||||
}
|
||||
|
||||
if (props.exceptionType) {
|
||||
fields.push({
|
||||
key: "exceptionType",
|
||||
title: "Exception Type",
|
||||
description: "The type of the exception.",
|
||||
fieldType: FieldType.Text,
|
||||
});
|
||||
}
|
||||
|
||||
if (props.stackTrace) {
|
||||
fields.push({
|
||||
key: "stackTrace",
|
||||
title: "Stack Trace",
|
||||
description: "The stack trace of the exception.",
|
||||
fieldType: FieldType.Code,
|
||||
});
|
||||
}
|
||||
|
||||
if (props.firstSeenAt) {
|
||||
fields.push({
|
||||
key: "firstSeenAt",
|
||||
title: "First Seen At",
|
||||
description: "The time the exception was first seen.",
|
||||
fieldType: FieldType.DateTime,
|
||||
});
|
||||
}
|
||||
|
||||
if (props.lastSeenAt) {
|
||||
fields.push({
|
||||
key: "lastSeenAt",
|
||||
title: "Last Seen At",
|
||||
description: "The time the exception was last seen.",
|
||||
fieldType: FieldType.DateTime,
|
||||
});
|
||||
}
|
||||
|
||||
if (props.occuranceCount) {
|
||||
fields.push({
|
||||
key: "occuranceCount",
|
||||
title: "Occurance Count",
|
||||
description: "The number of times this exception has occurred.",
|
||||
fieldType: FieldType.Number,
|
||||
});
|
||||
}
|
||||
|
||||
if (props.fingerprint) {
|
||||
fields.push({
|
||||
key: "fingerprint",
|
||||
title: "Fingerprint",
|
||||
description: "SHA256 hash of this exception.",
|
||||
fieldType: FieldType.Text,
|
||||
});
|
||||
}
|
||||
|
||||
if (props.attributes) {
|
||||
fields.push({
|
||||
key: "attributes",
|
||||
title: "Attributes",
|
||||
description: "Additional attributes of the exception.",
|
||||
fieldType: FieldType.JSON,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if(props.telemetryService) {
|
||||
fields.push({
|
||||
key: "telemetryService",
|
||||
title: "Telemetry Service",
|
||||
description: "The service that this exception was received from.",
|
||||
fieldType: FieldType.Element,
|
||||
getElement: () => {
|
||||
return (
|
||||
<TelemetryServiceElement telemetryService={props.telemetryService!} />
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<div className="-mt-4">
|
||||
<Detail<ComponentProps>
|
||||
item={props}
|
||||
fields={fields}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default ExceptionDetail;
|
100
Dashboard/src/Components/Exceptions/ExceptionExplorer.tsx
Normal file
100
Dashboard/src/Components/Exceptions/ExceptionExplorer.tsx
Normal file
@ -0,0 +1,100 @@
|
||||
import React, { FunctionComponent, ReactElement, useEffect } from "react";
|
||||
import TelemetryException from "Common/Models/DatabaseModels/TelemetryException";
|
||||
import ExceptionDetail from "./ExceptionDetail";
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
import PageLoader from "Common/UI/Components/Loader/PageLoader";
|
||||
import ErrorMessage from "Common/UI/Components/ErrorMessage/ErrorMessage";
|
||||
import { PromiseVoidFunction } from "Common/Types/FunctionTypes";
|
||||
import ModelAPI from "Common/UI/Utils/ModelAPI/ModelAPI";
|
||||
import API from "Common/UI/Utils/API/API";
|
||||
import ModelDelete from "Common/UI/Components/ModelDelete/ModelDelete";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import RouteMap from "../../Utils/RouteMap";
|
||||
import PageMap from "../../Utils/PageMap";
|
||||
import Route from "Common/Types/API/Route";
|
||||
|
||||
export interface ComponentProps {
|
||||
telemetryExceptionId: ObjectID;
|
||||
}
|
||||
|
||||
const ExceptionExplorer: FunctionComponent<ComponentProps> = (
|
||||
props: ComponentProps,
|
||||
): ReactElement => {
|
||||
const [telemetryException, setTelemetryException] = React.useState<
|
||||
TelemetryException | undefined
|
||||
>(undefined);
|
||||
const [isLoading, setIsLoading] = React.useState<boolean>(false);
|
||||
const [error, setError] = React.useState<string | undefined>(undefined);
|
||||
|
||||
const fetchItems: PromiseVoidFunction = async (): Promise<void> => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
|
||||
// get trace with this id and then get all the parentSpanId with this traceid.
|
||||
|
||||
const telemetryException: TelemetryException | null =
|
||||
await ModelAPI.getItem<TelemetryException>({
|
||||
id: props.telemetryExceptionId,
|
||||
modelType: TelemetryException,
|
||||
select: {
|
||||
_id: true,
|
||||
exceptionType: true,
|
||||
message: true,
|
||||
stackTrace: true,
|
||||
fingerprint: true,
|
||||
firstSeenAt: true,
|
||||
lastSeenAt: true,
|
||||
occuranceCount: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!telemetryException) {
|
||||
throw new Error("Exception not found");
|
||||
}
|
||||
|
||||
setTelemetryException(telemetryException);
|
||||
} catch (err) {
|
||||
setError(API.getFriendlyMessage(err));
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchItems().catch((err) => {
|
||||
return setError(API.getFriendlyMessage(err));
|
||||
});
|
||||
}, []);
|
||||
|
||||
if (isLoading) {
|
||||
return <PageLoader isVisible={true} />;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return <ErrorMessage error={error} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-4 mb-10">
|
||||
{/** Resolve / Unresolve Button */}
|
||||
|
||||
<ExceptionDetail {...telemetryException} />
|
||||
|
||||
{/** Assign / Unassign Button */}
|
||||
|
||||
{/** Occurance Table */}
|
||||
|
||||
{/** Archive / Unarchive Button Button */}
|
||||
|
||||
<ModelDelete
|
||||
modelType={TelemetryException}
|
||||
modelId={props.telemetryExceptionId}
|
||||
onDeleteSuccess={() => {
|
||||
Navigation.navigate(RouteMap[PageMap.TELEMETRY] as Route);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ExceptionExplorer;
|
@ -1,15 +1,17 @@
|
||||
import MetricExplorer from "../../../../Components/Metrics/MetricExplorer";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import ExceptionExplorer from "../../../../Components/Exceptions/ExceptionExplorer";
|
||||
import PageComponentProps from "../../../PageComponentProps";
|
||||
import React, { Fragment, FunctionComponent, ReactElement } from "react";
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
|
||||
const TelemetryMetricViewPage: FunctionComponent<
|
||||
PageComponentProps
|
||||
> = (): ReactElement => {
|
||||
const exceptionId: string = Navigation.getLastParamAsString(0);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<div className="mb-10">
|
||||
<MetricExplorer />
|
||||
</div>
|
||||
<ExceptionExplorer telemetryExceptionId={new ObjectID(exceptionId)} />
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
@ -1,22 +1,17 @@
|
||||
import DashboardLogsViewer from "../../../../../../Components/Logs/LogsViewer";
|
||||
import PageComponentProps from "../../../../../PageComponentProps";
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import React, { Fragment, FunctionComponent, ReactElement } from "react";
|
||||
import ExceptionExplorer from "../../../../../../Components/Exceptions/ExceptionExplorer";
|
||||
|
||||
const ServiceDelete: FunctionComponent<
|
||||
PageComponentProps
|
||||
> = (): ReactElement => {
|
||||
const modelId: ObjectID = Navigation.getLastParamAsObjectID(1);
|
||||
const modelId: ObjectID = Navigation.getLastParamAsObjectID();
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<DashboardLogsViewer
|
||||
showFilters={true}
|
||||
telemetryServiceIds={[modelId]}
|
||||
enableRealtime={true}
|
||||
id="logs"
|
||||
/>
|
||||
<ExceptionExplorer telemetryExceptionId={modelId} />
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
@ -165,12 +165,6 @@ const TelemetryServicesViewDocumentation: LazyExoticComponent<
|
||||
return import("../Pages/Telemetry/Services/View/Documentation");
|
||||
});
|
||||
|
||||
const TelemetryExceptionView: LazyExoticComponent<
|
||||
FunctionComponent<ComponentProps>
|
||||
> = lazy(() => {
|
||||
return import("../Pages/Telemetry/Exceptions/View/Index");
|
||||
});
|
||||
|
||||
const TelemetryRoutes: FunctionComponent<ComponentProps> = (
|
||||
props: ComponentProps,
|
||||
): ReactElement => {
|
||||
@ -201,7 +195,6 @@ const TelemetryRoutes: FunctionComponent<ComponentProps> = (
|
||||
}
|
||||
/>
|
||||
|
||||
|
||||
<PageRoute
|
||||
path={TelemetryRoutePath[PageMap.TELEMETRY_TRACES] || ""}
|
||||
element={
|
||||
@ -299,25 +292,6 @@ const TelemetryRoutes: FunctionComponent<ComponentProps> = (
|
||||
/>
|
||||
</PageRoute>
|
||||
|
||||
{/** Exception View */}
|
||||
|
||||
<PageRoute
|
||||
path={TelemetryRoutePath[PageMap.TELEMETRY_EXCEPTIONS_ROOT] || ""}
|
||||
element={<TelemetryExceptionViewLayout {...props} />}
|
||||
>
|
||||
<PageRoute
|
||||
path={TelemetryRoutePath[PageMap.TELEMETRY_EXCEPTIONS_VIEW] || ""}
|
||||
element={
|
||||
<Suspense fallback={Loader}>
|
||||
<TelemetryExceptionView
|
||||
{...props}
|
||||
pageRoute={RouteMap[PageMap.TELEMETRY_EXCEPTIONS_VIEW] as Route}
|
||||
/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
</PageRoute>
|
||||
|
||||
{/* Metric View */}
|
||||
|
||||
<PageRoute
|
||||
@ -349,12 +323,11 @@ const TelemetryRoutes: FunctionComponent<ComponentProps> = (
|
||||
/>
|
||||
</PageRoute>
|
||||
|
||||
|
||||
{/** Exception View */}
|
||||
|
||||
<PageRoute
|
||||
path={TelemetryRoutePath[PageMap.TELEMETRY_EXCEPTIONS_ROOT] || ""}
|
||||
element={<TelemetryTraceLayout {...props} />}
|
||||
element={<TelemetryExceptionViewLayout {...props} />}
|
||||
>
|
||||
<PageRoute
|
||||
index
|
||||
@ -473,7 +446,7 @@ const TelemetryRoutes: FunctionComponent<ComponentProps> = (
|
||||
{...props}
|
||||
pageRoute={
|
||||
RouteMap[
|
||||
PageMap.TELEMETRY_SERVICES_VIEW_EXCEPTIONS_UNRESOLVED
|
||||
PageMap.TELEMETRY_SERVICES_VIEW_EXCEPTIONS_UNRESOLVED
|
||||
] as Route
|
||||
}
|
||||
/>
|
||||
@ -492,7 +465,7 @@ const TelemetryRoutes: FunctionComponent<ComponentProps> = (
|
||||
{...props}
|
||||
pageRoute={
|
||||
RouteMap[
|
||||
PageMap.TELEMETRY_SERVICES_VIEW_EXCEPTIONS_RESOLVED
|
||||
PageMap.TELEMETRY_SERVICES_VIEW_EXCEPTIONS_RESOLVED
|
||||
] as Route
|
||||
}
|
||||
/>
|
||||
@ -510,9 +483,7 @@ const TelemetryRoutes: FunctionComponent<ComponentProps> = (
|
||||
<TelemetryServiceViewException
|
||||
{...props}
|
||||
pageRoute={
|
||||
RouteMap[
|
||||
PageMap.TELEMETRY_SERVICES_VIEW_EXCEPTION
|
||||
] as Route
|
||||
RouteMap[PageMap.TELEMETRY_SERVICES_VIEW_EXCEPTION] as Route
|
||||
}
|
||||
/>
|
||||
</Suspense>
|
||||
@ -530,7 +501,7 @@ const TelemetryRoutes: FunctionComponent<ComponentProps> = (
|
||||
{...props}
|
||||
pageRoute={
|
||||
RouteMap[
|
||||
PageMap.TELEMETRY_SERVICES_VIEW_EXCEPTIONS_ARCHIVED
|
||||
PageMap.TELEMETRY_SERVICES_VIEW_EXCEPTIONS_ARCHIVED
|
||||
] as Route
|
||||
}
|
||||
/>
|
||||
@ -646,7 +617,7 @@ const TelemetryRoutes: FunctionComponent<ComponentProps> = (
|
||||
{...props}
|
||||
pageRoute={
|
||||
RouteMap[
|
||||
PageMap.TELEMETRY_SERVICES_VIEW_DOCUMENTATION
|
||||
PageMap.TELEMETRY_SERVICES_VIEW_DOCUMENTATION
|
||||
] as Route
|
||||
}
|
||||
/>
|
||||
|
@ -75,7 +75,7 @@ export const TelemetryRoutePath: Dictionary<string> = {
|
||||
[PageMap.TELEMETRY_TRACE_VIEW]: `traces/view/${RouteParams.ModelID}`, // modelID is spanId
|
||||
|
||||
[PageMap.TELEMETRY_EXCEPTIONS_ROOT]: `exception`,
|
||||
[PageMap.TELEMETRY_EXCEPTIONS_VIEW]: `exception/view/${RouteParams.ModelID}`,
|
||||
[PageMap.TELEMETRY_EXCEPTIONS_VIEW]: `exception/${RouteParams.ModelID}`,
|
||||
|
||||
[PageMap.TELEMETRY_LOG_ROOT]: `logs`,
|
||||
|
||||
@ -946,7 +946,6 @@ const RouteMap: Dictionary<Route> = {
|
||||
}`,
|
||||
),
|
||||
|
||||
|
||||
[PageMap.TELEMETRY_SERVICES_VIEW_EXCEPTIONS_UNRESOLVED]: new Route(
|
||||
`/dashboard/${RouteParams.ProjectID}/telemetry/${
|
||||
TelemetryRoutePath[PageMap.TELEMETRY_SERVICES_VIEW_EXCEPTIONS_UNRESOLVED]
|
||||
@ -1008,7 +1007,7 @@ const RouteMap: Dictionary<Route> = {
|
||||
}`,
|
||||
),
|
||||
|
||||
// view exceptions.
|
||||
// view exceptions.
|
||||
[PageMap.TELEMETRY_SERVICES_VIEW_EXCEPTIONS]: new Route(
|
||||
`/dashboard/${RouteParams.ProjectID}/telemetry/${
|
||||
TelemetryRoutePath[PageMap.TELEMETRY_SERVICES_VIEW_EXCEPTIONS]
|
||||
|
@ -56,6 +56,7 @@ export default class ExceptionUtil {
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
occuranceCount: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
@ -72,6 +73,7 @@ export default class ExceptionUtil {
|
||||
lastSeenAt: OneUptimeDate.now(),
|
||||
markedAsResolvedByUserId: null,
|
||||
markedAsResolvedAt: null, // unmark as resolved if it was marked as resolved
|
||||
occuranceCount: (existingExceptionStatus.occuranceCount || 0) + 1,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
@ -87,6 +89,7 @@ export default class ExceptionUtil {
|
||||
newExceptionStatus.telemetryServiceId = exception.serviceId;
|
||||
newExceptionStatus.lastSeenAt = OneUptimeDate.now();
|
||||
newExceptionStatus.firstSeenAt = OneUptimeDate.now();
|
||||
newExceptionStatus.occuranceCount = 1;
|
||||
|
||||
if (exception.exceptionType) {
|
||||
newExceptionStatus.exceptionType = exception.exceptionType;
|
||||
|
Loading…
Reference in New Issue
Block a user