feat: add Log Stream and Trace List components to dashboard with configuration options

This commit is contained in:
Nawaz Dhandala
2026-03-25 22:54:52 +00:00
parent 467921e899
commit 7fac485049
11 changed files with 786 additions and 0 deletions

View File

@@ -4,12 +4,16 @@ import DashboardChartComponentType from "Common/Types/Dashboard/DashboardCompone
import DashboardValueComponentType from "Common/Types/Dashboard/DashboardComponents/DashboardValueComponent";
import DashboardTableComponentType from "Common/Types/Dashboard/DashboardComponents/DashboardTableComponent";
import DashboardGaugeComponentType from "Common/Types/Dashboard/DashboardComponents/DashboardGaugeComponent";
import DashboardLogStreamComponentType from "Common/Types/Dashboard/DashboardComponents/DashboardLogStreamComponent";
import DashboardTraceListComponentType from "Common/Types/Dashboard/DashboardComponents/DashboardTraceListComponent";
import DashboardBaseComponent from "Common/Types/Dashboard/DashboardComponents/DashboardBaseComponent";
import DashboardChartComponent from "./DashboardChartComponent";
import DashboardValueComponent from "./DashboardValueComponent";
import DashboardTextComponent from "./DashboardTextComponent";
import DashboardTableComponent from "./DashboardTableComponent";
import DashboardGaugeComponent from "./DashboardGaugeComponent";
import DashboardLogStreamComponent from "./DashboardLogStreamComponent";
import DashboardTraceListComponent from "./DashboardTraceListComponent";
import DefaultDashboardSize, {
GetDashboardComponentHeightInDashboardUnits,
GetDashboardComponentWidthInDashboardUnits,
@@ -439,6 +443,22 @@ const DashboardBaseComponentElement: FunctionComponent<ComponentProps> = (
component={component as DashboardGaugeComponentType}
/>
)}
{component.componentType === DashboardComponentType.LogStream && (
<DashboardLogStreamComponent
{...props}
isEditMode={props.isEditMode}
isSelected={props.isSelected}
component={component as DashboardLogStreamComponentType}
/>
)}
{component.componentType === DashboardComponentType.TraceList && (
<DashboardTraceListComponent
{...props}
isEditMode={props.isEditMode}
isSelected={props.isSelected}
component={component as DashboardTraceListComponentType}
/>
)}
{getResizeWidthElement()}
{getResizeHeightElement()}

View File

@@ -0,0 +1,249 @@
import React, { FunctionComponent, ReactElement, useEffect } from "react";
import DashboardLogStreamComponent from "Common/Types/Dashboard/DashboardComponents/DashboardLogStreamComponent";
import { DashboardBaseComponentProps } from "./DashboardBaseComponent";
import { PromiseVoidFunction } from "Common/Types/FunctionTypes";
import AnalyticsModelAPI, {
ListResult,
} from "Common/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI";
import Log from "Common/Models/AnalyticsModels/Log";
import API from "Common/UI/Utils/API/API";
import Icon from "Common/UI/Components/Icon/Icon";
import IconProp from "Common/Types/Icon/IconProp";
import { RangeStartAndEndDateTimeUtil } from "Common/Types/Time/RangeStartAndEndDateTime";
import InBetween from "Common/Types/BaseDatabase/InBetween";
import SortOrder from "Common/Types/BaseDatabase/SortOrder";
import OneUptimeDate from "Common/Types/Date";
import Query from "Common/Types/BaseDatabase/Query";
export interface ComponentProps extends DashboardBaseComponentProps {
component: DashboardLogStreamComponent;
}
type SeverityColor = {
dot: string;
text: string;
bg: string;
};
const getSeverityColor: (severity: string) => SeverityColor = (
severity: string,
): SeverityColor => {
const lower: string = severity.toLowerCase();
if (lower === "fatal") {
return { dot: "bg-purple-500", text: "text-purple-700", bg: "bg-purple-50" };
}
if (lower === "error") {
return { dot: "bg-red-500", text: "text-red-700", bg: "bg-red-50" };
}
if (lower === "warning") {
return { dot: "bg-yellow-500", text: "text-yellow-700", bg: "bg-yellow-50" };
}
if (lower === "information") {
return { dot: "bg-blue-500", text: "text-blue-700", bg: "bg-blue-50" };
}
if (lower === "debug") {
return { dot: "bg-gray-400", text: "text-gray-600", bg: "bg-gray-50" };
}
if (lower === "trace") {
return { dot: "bg-gray-300", text: "text-gray-500", bg: "bg-gray-50" };
}
return { dot: "bg-gray-300", text: "text-gray-500", bg: "bg-gray-50" };
};
const DashboardLogStreamComponentElement: FunctionComponent<ComponentProps> = (
props: ComponentProps,
): ReactElement => {
const [logs, setLogs] = React.useState<Array<Log>>([]);
const [error, setError] = React.useState<string | null>(null);
const [isLoading, setIsLoading] = React.useState<boolean>(true);
const maxRows: number = props.component.arguments.maxRows || 50;
const fetchLogs: PromiseVoidFunction = async (): Promise<void> => {
setIsLoading(true);
const startAndEndDate: InBetween<Date> =
RangeStartAndEndDateTimeUtil.getStartAndEndDate(
props.dashboardStartAndEndDate,
);
if (!startAndEndDate.startValue || !startAndEndDate.endValue) {
setIsLoading(false);
setError("Please select a valid start and end date.");
return;
}
try {
const query: Query<Log> = {
time: new InBetween<Date>(
startAndEndDate.startValue,
startAndEndDate.endValue,
),
} as Query<Log>;
// Add severity filter if set
if (
props.component.arguments.severityFilter &&
props.component.arguments.severityFilter !== ""
) {
(query as Record<string, unknown>)["severityText"] =
props.component.arguments.severityFilter;
}
// Add body contains filter if set
if (
props.component.arguments.bodyContains &&
props.component.arguments.bodyContains.trim() !== ""
) {
(query as Record<string, unknown>)["body"] =
props.component.arguments.bodyContains.trim();
}
const listResult: ListResult<Log> =
await AnalyticsModelAPI.getList<Log>({
modelType: Log,
query: query,
limit: maxRows,
skip: 0,
select: {
time: true,
severityText: true,
body: true,
serviceId: true,
traceId: true,
spanId: true,
},
sort: {
time: SortOrder.Descending,
},
requestOptions: {},
});
setLogs(listResult.data);
setError("");
} catch (err: unknown) {
setError(API.getFriendlyErrorMessage(err as Error));
}
setIsLoading(false);
};
useEffect(() => {
fetchLogs();
}, [props.dashboardStartAndEndDate, props.refreshTick]);
useEffect(() => {
fetchLogs();
}, [
props.component.arguments.severityFilter,
props.component.arguments.bodyContains,
props.component.arguments.maxRows,
]);
if (isLoading) {
return (
<div className="h-full flex flex-col animate-pulse">
<div className="h-3 w-24 bg-gray-100 rounded mb-3"></div>
<div className="flex-1 space-y-2">
{Array.from({ length: 6 }).map((_: unknown, i: number) => {
return (
<div
key={i}
className="flex gap-2 items-center"
style={{ opacity: 1 - i * 0.12 }}
>
<div className="w-1.5 h-1.5 bg-gray-200 rounded-full"></div>
<div className="h-3 w-16 bg-gray-100 rounded"></div>
<div
className="h-3 bg-gray-50 rounded flex-1"
style={{ maxWidth: `${40 + Math.random() * 50}%` }}
></div>
</div>
);
})}
</div>
</div>
);
}
if (error) {
return (
<div className="flex flex-col items-center justify-center w-full h-full gap-2">
<div className="w-10 h-10 rounded-full bg-gray-50 flex items-center justify-center">
<div className="h-5 w-5 text-gray-300">
<Icon icon={IconProp.List} />
</div>
</div>
<p className="text-xs text-gray-400 text-center max-w-48">{error}</p>
</div>
);
}
return (
<div className="h-full overflow-auto flex flex-col">
{props.component.arguments.title && (
<div className="flex items-center justify-between mb-2 px-1">
<span className="text-xs font-medium text-gray-400 uppercase tracking-wider">
{props.component.arguments.title}
</span>
<span className="text-xs text-gray-300 tabular-nums">
{logs.length} entries
</span>
</div>
)}
<div className="flex-1 overflow-auto rounded-md border border-gray-100">
<div className="divide-y divide-gray-50">
{logs.map((log: Log, index: number) => {
const severity: string =
(log.severityText as string) || "Unspecified";
const colors: SeverityColor = getSeverityColor(severity);
const body: string = (log.body as string) || "";
const time: Date | undefined = log.time
? OneUptimeDate.fromString(log.time as string)
: undefined;
return (
<div
key={index}
className="flex items-start gap-2 px-3 py-1.5 hover:bg-gray-50/50 transition-colors duration-100 group"
>
<div className="flex items-center gap-1.5 shrink-0 mt-0.5">
<div
className={`w-1.5 h-1.5 rounded-full ${colors.dot}`}
></div>
<span
className={`text-xs font-medium ${colors.text} ${colors.bg} px-1 py-0.5 rounded w-12 text-center`}
style={{ fontSize: "10px" }}
>
{severity.substring(0, 4).toUpperCase()}
</span>
</div>
{time && (
<span
className="text-xs text-gray-400 shrink-0 tabular-nums"
style={{ fontSize: "11px" }}
>
{OneUptimeDate.getDateAsLocalFormattedString(time, true)}
</span>
)}
<span
className="text-xs text-gray-600 truncate flex-1 font-mono"
style={{ fontSize: "11px" }}
>
{body}
</span>
</div>
);
})}
{logs.length === 0 && (
<div className="px-4 py-8 text-center text-gray-400 text-sm">
No logs found
</div>
)}
</div>
</div>
</div>
);
};
export default DashboardLogStreamComponentElement;

View File

@@ -0,0 +1,294 @@
import React, { FunctionComponent, ReactElement, useEffect } from "react";
import DashboardTraceListComponent from "Common/Types/Dashboard/DashboardComponents/DashboardTraceListComponent";
import { DashboardBaseComponentProps } from "./DashboardBaseComponent";
import { PromiseVoidFunction } from "Common/Types/FunctionTypes";
import AnalyticsModelAPI, {
ListResult,
} from "Common/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI";
import Span, { SpanStatus } from "Common/Models/AnalyticsModels/Span";
import API from "Common/UI/Utils/API/API";
import Icon from "Common/UI/Components/Icon/Icon";
import IconProp from "Common/Types/Icon/IconProp";
import { RangeStartAndEndDateTimeUtil } from "Common/Types/Time/RangeStartAndEndDateTime";
import InBetween from "Common/Types/BaseDatabase/InBetween";
import SortOrder from "Common/Types/BaseDatabase/SortOrder";
import OneUptimeDate from "Common/Types/Date";
import Query from "Common/Types/BaseDatabase/Query";
export interface ComponentProps extends DashboardBaseComponentProps {
component: DashboardTraceListComponent;
}
type StatusStyle = {
label: string;
textClass: string;
bgClass: string;
};
const getStatusStyle: (statusCode: number) => StatusStyle = (
statusCode: number,
): StatusStyle => {
if (statusCode === SpanStatus.Error) {
return {
label: "Error",
textClass: "text-red-700",
bgClass: "bg-red-50 border-red-100",
};
}
if (statusCode === SpanStatus.Ok) {
return {
label: "Ok",
textClass: "text-green-700",
bgClass: "bg-green-50 border-green-100",
};
}
return {
label: "Unset",
textClass: "text-gray-500",
bgClass: "bg-gray-50 border-gray-100",
};
};
const formatDuration: (durationNano: number) => string = (
durationNano: number,
): string => {
if (durationNano < 1000) {
return `${durationNano}ns`;
}
const durationMicro: number = durationNano / 1000;
if (durationMicro < 1000) {
return `${Math.round(durationMicro)}µs`;
}
const durationMs: number = durationMicro / 1000;
if (durationMs < 1000) {
return `${Math.round(durationMs * 10) / 10}ms`;
}
const durationS: number = durationMs / 1000;
return `${Math.round(durationS * 100) / 100}s`;
};
const DashboardTraceListComponentElement: FunctionComponent<ComponentProps> = (
props: ComponentProps,
): ReactElement => {
const [spans, setSpans] = React.useState<Array<Span>>([]);
const [error, setError] = React.useState<string | null>(null);
const [isLoading, setIsLoading] = React.useState<boolean>(true);
const maxRows: number = props.component.arguments.maxRows || 50;
const fetchTraces: PromiseVoidFunction = async (): Promise<void> => {
setIsLoading(true);
const startAndEndDate: InBetween<Date> =
RangeStartAndEndDateTimeUtil.getStartAndEndDate(
props.dashboardStartAndEndDate,
);
if (!startAndEndDate.startValue || !startAndEndDate.endValue) {
setIsLoading(false);
setError("Please select a valid start and end date.");
return;
}
try {
const query: Query<Span> = {
startTime: new InBetween<Date>(
startAndEndDate.startValue,
startAndEndDate.endValue,
),
} as Query<Span>;
// Add status filter if set
if (
props.component.arguments.statusFilter &&
props.component.arguments.statusFilter !== ""
) {
(query as Record<string, unknown>)["statusCode"] = parseInt(
props.component.arguments.statusFilter,
);
}
const listResult: ListResult<Span> =
await AnalyticsModelAPI.getList<Span>({
modelType: Span,
query: query,
limit: maxRows,
skip: 0,
select: {
startTime: true,
name: true,
statusCode: true,
durationUnixNano: true,
traceId: true,
spanId: true,
kind: true,
serviceId: true,
},
sort: {
startTime: SortOrder.Descending,
},
requestOptions: {},
});
setSpans(listResult.data);
setError("");
} catch (err: unknown) {
setError(API.getFriendlyErrorMessage(err as Error));
}
setIsLoading(false);
};
useEffect(() => {
fetchTraces();
}, [props.dashboardStartAndEndDate, props.refreshTick]);
useEffect(() => {
fetchTraces();
}, [
props.component.arguments.statusFilter,
props.component.arguments.maxRows,
]);
if (isLoading) {
return (
<div className="h-full flex flex-col animate-pulse">
<div className="h-3 w-24 bg-gray-100 rounded mb-3"></div>
<div className="flex-1 space-y-2">
<div className="flex gap-4">
<div className="h-3 w-32 bg-gray-100 rounded"></div>
<div className="h-3 w-16 bg-gray-100 rounded"></div>
<div className="h-3 w-12 bg-gray-100 rounded ml-auto"></div>
</div>
{Array.from({ length: 5 }).map((_: unknown, i: number) => {
return (
<div
key={i}
className="flex gap-4"
style={{ opacity: 1 - i * 0.15 }}
>
<div className="h-3 w-28 bg-gray-50 rounded"></div>
<div className="h-3 w-14 bg-gray-50 rounded"></div>
<div className="h-3 w-10 bg-gray-50 rounded ml-auto"></div>
</div>
);
})}
</div>
</div>
);
}
if (error) {
return (
<div className="flex flex-col items-center justify-center w-full h-full gap-2">
<div className="w-10 h-10 rounded-full bg-gray-50 flex items-center justify-center">
<div className="h-5 w-5 text-gray-300">
<Icon icon={IconProp.Activity} />
</div>
</div>
<p className="text-xs text-gray-400 text-center max-w-48">{error}</p>
</div>
);
}
return (
<div className="h-full overflow-auto flex flex-col">
{props.component.arguments.title && (
<div className="flex items-center justify-between mb-2 px-1">
<span className="text-xs font-medium text-gray-400 uppercase tracking-wider">
{props.component.arguments.title}
</span>
<span className="text-xs text-gray-300 tabular-nums">
{spans.length} traces
</span>
</div>
)}
<div className="flex-1 overflow-auto rounded-md border border-gray-100">
<table className="w-full text-sm text-left">
<thead className="text-xs text-gray-400 uppercase bg-gray-50/80 sticky top-0 border-b border-gray-100">
<tr>
<th
className="px-3 py-2.5 font-medium tracking-wider"
style={{ width: "35%" }}
>
Span Name
</th>
<th
className="px-3 py-2.5 font-medium tracking-wider"
style={{ width: "20%" }}
>
Duration
</th>
<th
className="px-3 py-2.5 font-medium tracking-wider"
style={{ width: "15%" }}
>
Status
</th>
<th
className="px-3 py-2.5 font-medium tracking-wider"
style={{ width: "30%" }}
>
Time
</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-50">
{spans.map((span: Span, index: number) => {
const statusCode: number =
(span.statusCode as number) || SpanStatus.Unset;
const statusStyle: StatusStyle = getStatusStyle(statusCode);
const durationNano: number =
(span.durationUnixNano as number) || 0;
const startTime: Date | undefined = span.startTime
? OneUptimeDate.fromString(span.startTime as string)
: undefined;
return (
<tr
key={index}
className="hover:bg-gray-50/50 transition-colors duration-100 group"
>
<td className="px-3 py-2 text-xs text-gray-700 font-mono truncate">
{(span.name as string) || "—"}
</td>
<td className="px-3 py-2 text-xs text-gray-600 tabular-nums font-medium">
{formatDuration(durationNano)}
</td>
<td className="px-3 py-2">
<span
className={`inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium border ${statusStyle.textClass} ${statusStyle.bgClass}`}
style={{ fontSize: "10px" }}
>
{statusStyle.label}
</span>
</td>
<td className="px-3 py-2 text-xs text-gray-500 tabular-nums">
{startTime
? OneUptimeDate.getDateAsLocalFormattedString(
startTime,
true,
)
: "—"}
</td>
</tr>
);
})}
{spans.length === 0 && (
<tr>
<td
colSpan={4}
className="px-4 py-8 text-center text-gray-400 text-sm"
>
No traces found
</td>
</tr>
)}
</tbody>
</table>
</div>
</div>
);
};
export default DashboardTraceListComponentElement;

View File

@@ -21,6 +21,8 @@ import DashboardValueComponentUtil from "Common/Utils/Dashboard/Components/Dashb
import DashboardTextComponentUtil from "Common/Utils/Dashboard/Components/DashboardTextComponent";
import DashboardTableComponentUtil from "Common/Utils/Dashboard/Components/DashboardTableComponent";
import DashboardGaugeComponentUtil from "Common/Utils/Dashboard/Components/DashboardGaugeComponent";
import DashboardLogStreamComponentUtil from "Common/Utils/Dashboard/Components/DashboardLogStreamComponent";
import DashboardTraceListComponentUtil from "Common/Utils/Dashboard/Components/DashboardTraceListComponent";
import BadDataException from "Common/Types/Exception/BadDataException";
import ObjectID from "Common/Types/ObjectID";
import Dashboard from "Common/Models/DatabaseModels/Dashboard";
@@ -378,6 +380,16 @@ const DashboardViewer: FunctionComponent<ComponentProps> = (
newComponent = DashboardGaugeComponentUtil.getDefaultComponent();
}
if (componentType === DashboardComponentType.LogStream) {
newComponent =
DashboardLogStreamComponentUtil.getDefaultComponent();
}
if (componentType === DashboardComponentType.TraceList) {
newComponent =
DashboardTraceListComponentUtil.getDefaultComponent();
}
if (!newComponent) {
throw new BadDataException(
`Unknown component type: ${componentType}`,

View File

@@ -135,6 +135,24 @@ const DashboardToolbar: FunctionComponent<ComponentProps> = (
props.onAddComponentClick(DashboardComponentType.Gauge);
}}
/>
<MoreMenuItem
text={"Log Stream"}
key={"add-log-stream"}
onClick={() => {
props.onAddComponentClick(
DashboardComponentType.LogStream,
);
}}
/>
<MoreMenuItem
text={"Trace List"}
key={"add-trace-list"}
onClick={() => {
props.onAddComponentClick(
DashboardComponentType.TraceList,
);
}}
/>
</MoreMenu>
<div className="w-px h-6 bg-gray-200 mx-1"></div>

View File

@@ -4,6 +4,8 @@ enum DashboardComponentType {
Text = `Text`,
Table = `Table`,
Gauge = `Gauge`,
LogStream = `LogStream`,
TraceList = `TraceList`,
}
export default DashboardComponentType;

View File

@@ -0,0 +1,14 @@
import ObjectID from "../../ObjectID";
import DashboardComponentType from "../DashboardComponentType";
import BaseComponent from "./DashboardBaseComponent";
export default interface DashboardLogStreamComponent extends BaseComponent {
componentType: DashboardComponentType.LogStream;
componentId: ObjectID;
arguments: {
title?: string | undefined;
severityFilter?: string | undefined;
bodyContains?: string | undefined;
maxRows?: number | undefined;
};
}

View File

@@ -0,0 +1,13 @@
import ObjectID from "../../ObjectID";
import DashboardComponentType from "../DashboardComponentType";
import BaseComponent from "./DashboardBaseComponent";
export default interface DashboardTraceListComponent extends BaseComponent {
componentType: DashboardComponentType.TraceList;
componentId: ObjectID;
arguments: {
title?: string | undefined;
statusFilter?: string | undefined;
maxRows?: number | undefined;
};
}

View File

@@ -0,0 +1,81 @@
import DashboardLogStreamComponent from "../../../Types/Dashboard/DashboardComponents/DashboardLogStreamComponent";
import { ObjectType } from "../../../Types/JSON";
import ObjectID from "../../../Types/ObjectID";
import DashboardBaseComponentUtil from "./DashboardBaseComponent";
import {
ComponentArgument,
ComponentInputType,
} from "../../../Types/Dashboard/DashboardComponents/ComponentArgument";
import DashboardComponentType from "../../../Types/Dashboard/DashboardComponentType";
export default class DashboardLogStreamComponentUtil extends DashboardBaseComponentUtil {
public static override getDefaultComponent(): DashboardLogStreamComponent {
return {
_type: ObjectType.DashboardComponent,
componentType: DashboardComponentType.LogStream,
widthInDashboardUnits: 6,
heightInDashboardUnits: 4,
topInDashboardUnits: 0,
leftInDashboardUnits: 0,
componentId: ObjectID.generate(),
minHeightInDashboardUnits: 3,
minWidthInDashboardUnits: 6,
arguments: {
maxRows: 50,
},
};
}
public static override getComponentConfigArguments(): Array<
ComponentArgument<DashboardLogStreamComponent>
> {
const componentArguments: Array<
ComponentArgument<DashboardLogStreamComponent>
> = [];
componentArguments.push({
name: "Title",
description: "The title of the log stream widget",
required: false,
type: ComponentInputType.Text,
id: "title",
});
componentArguments.push({
name: "Severity Filter",
description: "Filter logs by severity level",
required: false,
type: ComponentInputType.Dropdown,
id: "severityFilter",
dropdownOptions: [
{ label: "All", value: "" },
{ label: "Trace", value: "Trace" },
{ label: "Debug", value: "Debug" },
{ label: "Information", value: "Information" },
{ label: "Warning", value: "Warning" },
{ label: "Error", value: "Error" },
{ label: "Fatal", value: "Fatal" },
],
});
componentArguments.push({
name: "Body Contains",
description: "Filter logs where the body contains this text",
required: false,
type: ComponentInputType.Text,
id: "bodyContains",
placeholder: "Search text...",
});
componentArguments.push({
name: "Max Rows",
description: "Maximum number of log entries to display",
required: false,
type: ComponentInputType.Number,
id: "maxRows",
placeholder: "50",
});
return componentArguments;
}
}

View File

@@ -0,0 +1,69 @@
import DashboardTraceListComponent from "../../../Types/Dashboard/DashboardComponents/DashboardTraceListComponent";
import { ObjectType } from "../../../Types/JSON";
import ObjectID from "../../../Types/ObjectID";
import DashboardBaseComponentUtil from "./DashboardBaseComponent";
import {
ComponentArgument,
ComponentInputType,
} from "../../../Types/Dashboard/DashboardComponents/ComponentArgument";
import DashboardComponentType from "../../../Types/Dashboard/DashboardComponentType";
export default class DashboardTraceListComponentUtil extends DashboardBaseComponentUtil {
public static override getDefaultComponent(): DashboardTraceListComponent {
return {
_type: ObjectType.DashboardComponent,
componentType: DashboardComponentType.TraceList,
widthInDashboardUnits: 6,
heightInDashboardUnits: 4,
topInDashboardUnits: 0,
leftInDashboardUnits: 0,
componentId: ObjectID.generate(),
minHeightInDashboardUnits: 3,
minWidthInDashboardUnits: 6,
arguments: {
maxRows: 50,
},
};
}
public static override getComponentConfigArguments(): Array<
ComponentArgument<DashboardTraceListComponent>
> {
const componentArguments: Array<
ComponentArgument<DashboardTraceListComponent>
> = [];
componentArguments.push({
name: "Title",
description: "The title of the trace list widget",
required: false,
type: ComponentInputType.Text,
id: "title",
});
componentArguments.push({
name: "Status Filter",
description: "Filter traces by status",
required: false,
type: ComponentInputType.Dropdown,
id: "statusFilter",
dropdownOptions: [
{ label: "All", value: "" },
{ label: "Ok", value: "1" },
{ label: "Error", value: "2" },
{ label: "Unset", value: "0" },
],
});
componentArguments.push({
name: "Max Rows",
description: "Maximum number of traces to display",
required: false,
type: ComponentInputType.Number,
id: "maxRows",
placeholder: "50",
});
return componentArguments;
}
}

View File

@@ -4,8 +4,10 @@ import DashboardComponentType from "../../../Types/Dashboard/DashboardComponentT
import BadDataException from "../../../Types/Exception/BadDataException";
import DashboardChartComponentUtil from "./DashboardChartComponent";
import DashboardGaugeComponentUtil from "./DashboardGaugeComponent";
import DashboardLogStreamComponentUtil from "./DashboardLogStreamComponent";
import DashboardTableComponentUtil from "./DashboardTableComponent";
import DashboardTextComponentUtil from "./DashboardTextComponent";
import DashboardTraceListComponentUtil from "./DashboardTraceListComponent";
import DashboardValueComponentUtil from "./DashboardValueComponent";
export default class DashboardComponentsUtil {
@@ -42,6 +44,18 @@ export default class DashboardComponentsUtil {
>;
}
if (dashboardComponentType === DashboardComponentType.LogStream) {
return DashboardLogStreamComponentUtil.getComponentConfigArguments() as Array<
ComponentArgument<DashboardBaseComponent>
>;
}
if (dashboardComponentType === DashboardComponentType.TraceList) {
return DashboardTraceListComponentUtil.getComponentConfigArguments() as Array<
ComponentArgument<DashboardBaseComponent>
>;
}
throw new BadDataException(
`Unknown dashboard component type: ${dashboardComponentType}`,
);