@oneuptime/common 10.0.31 → 10.0.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Models/AnalyticsModels/ExceptionInstance.ts +29 -4
- package/Models/AnalyticsModels/Log.ts +110 -4
- package/Models/AnalyticsModels/Metric.ts +16 -9
- package/Models/AnalyticsModels/MonitorLog.ts +4 -2
- package/Models/AnalyticsModels/Span.ts +79 -6
- package/Models/DatabaseModels/Index.ts +8 -0
- package/Models/DatabaseModels/LogDropFilter.ts +480 -0
- package/Models/DatabaseModels/LogPipeline.ts +412 -0
- package/Models/DatabaseModels/LogPipelineProcessor.ts +430 -0
- package/Models/DatabaseModels/LogScrubRule.ts +516 -0
- package/Server/API/TelemetryAPI.ts +261 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1773402621107-MigrationName.ts +131 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1773414578773-MigrationName.ts +79 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1773500000000-MigrationName.ts +41 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1773676206197-MigrationName.ts +57 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Services/AnalyticsDatabaseService.ts +61 -0
- package/Server/Services/LogAggregationService.ts +238 -1
- package/Server/Services/LogDropFilterService.ts +10 -0
- package/Server/Services/LogPipelineProcessorService.ts +10 -0
- package/Server/Services/LogPipelineService.ts +10 -0
- package/Server/Services/LogScrubRuleService.ts +10 -0
- package/Server/Services/TelemetryAttributeService.ts +4 -6
- package/Server/Utils/AnalyticsDatabase/Statement.ts +15 -1
- package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +126 -11
- package/Tests/Server/Services/LogAggregationService.test.ts +3 -2
- package/Types/AnalyticsDatabase/AnalyticsTableName.ts +9 -0
- package/Types/AnalyticsDatabase/TableColumnType.ts +4 -0
- package/Types/Date.ts +22 -0
- package/Types/Log/LogDropFilterAction.ts +6 -0
- package/Types/Log/LogPipelineProcessorType.ts +44 -0
- package/Types/Log/LogScrubAction.ts +7 -0
- package/Types/Log/LogScrubPatternType.ts +10 -0
- package/Types/Permission.ts +174 -0
- package/UI/Components/LogsViewer/LogsViewer.tsx +152 -4
- package/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.tsx +92 -0
- package/UI/Components/LogsViewer/components/LogDetailsPanel.tsx +332 -117
- package/UI/Components/LogsViewer/components/LogSearchBar.tsx +294 -274
- package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +513 -234
- package/UI/Components/LogsViewer/components/LogsFilterCard.tsx +37 -29
- package/UI/Components/LogsViewer/components/LogsTable.tsx +6 -1
- package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +106 -0
- package/UI/Utils/LogExport.ts +160 -0
- package/build/dist/Models/AnalyticsModels/ExceptionInstance.js +28 -4
- package/build/dist/Models/AnalyticsModels/ExceptionInstance.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/Log.js +97 -4
- package/build/dist/Models/AnalyticsModels/Log.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/Metric.js +16 -9
- package/build/dist/Models/AnalyticsModels/Metric.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/MonitorLog.js +4 -2
- package/build/dist/Models/AnalyticsModels/MonitorLog.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/Span.js +73 -6
- package/build/dist/Models/AnalyticsModels/Span.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +8 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/LogDropFilter.js +508 -0
- package/build/dist/Models/DatabaseModels/LogDropFilter.js.map +1 -0
- package/build/dist/Models/DatabaseModels/LogPipeline.js +438 -0
- package/build/dist/Models/DatabaseModels/LogPipeline.js.map +1 -0
- package/build/dist/Models/DatabaseModels/LogPipelineProcessor.js +452 -0
- package/build/dist/Models/DatabaseModels/LogPipelineProcessor.js.map +1 -0
- package/build/dist/Models/DatabaseModels/LogScrubRule.js +545 -0
- package/build/dist/Models/DatabaseModels/LogScrubRule.js.map +1 -0
- package/build/dist/Server/API/TelemetryAPI.js +155 -0
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1773402621107-MigrationName.js +52 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1773402621107-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1773414578773-MigrationName.js +34 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1773414578773-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1773500000000-MigrationName.js +22 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1773500000000-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1773676206197-MigrationName.js +26 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1773676206197-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js +30 -0
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/LogAggregationService.js +188 -1
- package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
- package/build/dist/Server/Services/LogDropFilterService.js +9 -0
- package/build/dist/Server/Services/LogDropFilterService.js.map +1 -0
- package/build/dist/Server/Services/LogPipelineProcessorService.js +9 -0
- package/build/dist/Server/Services/LogPipelineProcessorService.js.map +1 -0
- package/build/dist/Server/Services/LogPipelineService.js +9 -0
- package/build/dist/Server/Services/LogPipelineService.js.map +1 -0
- package/build/dist/Server/Services/LogScrubRuleService.js +9 -0
- package/build/dist/Server/Services/LogScrubRuleService.js.map +1 -0
- package/build/dist/Server/Services/TelemetryAttributeService.js +4 -6
- package/build/dist/Server/Services/TelemetryAttributeService.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js +13 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +89 -2
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
- package/build/dist/Tests/Server/Services/LogAggregationService.test.js +3 -2
- package/build/dist/Tests/Server/Services/LogAggregationService.test.js.map +1 -1
- package/build/dist/Types/AnalyticsDatabase/AnalyticsTableName.js +10 -0
- package/build/dist/Types/AnalyticsDatabase/AnalyticsTableName.js.map +1 -0
- package/build/dist/Types/AnalyticsDatabase/TableColumnType.js +4 -0
- package/build/dist/Types/AnalyticsDatabase/TableColumnType.js.map +1 -1
- package/build/dist/Types/Date.js +16 -0
- package/build/dist/Types/Date.js.map +1 -1
- package/build/dist/Types/Log/LogDropFilterAction.js +7 -0
- package/build/dist/Types/Log/LogDropFilterAction.js.map +1 -0
- package/build/dist/Types/Log/LogPipelineProcessorType.js +9 -0
- package/build/dist/Types/Log/LogPipelineProcessorType.js.map +1 -0
- package/build/dist/Types/Log/LogScrubAction.js +8 -0
- package/build/dist/Types/Log/LogScrubAction.js.map +1 -0
- package/build/dist/Types/Log/LogScrubPatternType.js +11 -0
- package/build/dist/Types/Log/LogScrubPatternType.js.map +1 -0
- package/build/dist/Types/Permission.js +152 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +124 -11
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js +36 -0
- package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js.map +1 -0
- package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js +114 -4
- package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogSearchBar.js +17 -5
- package/build/dist/UI/Components/LogsViewer/components/LogSearchBar.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +229 -122
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsFilterCard.js +5 -4
- package/build/dist/UI/Components/LogsViewer/components/LogsFilterCard.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsTable.js +4 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsTable.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +28 -0
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
- package/build/dist/UI/Utils/LogExport.js +129 -0
- package/build/dist/UI/Utils/LogExport.js.map +1 -0
- package/package.json +1 -1
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, {
|
|
2
|
+
FunctionComponent,
|
|
3
|
+
ReactElement,
|
|
4
|
+
useCallback,
|
|
5
|
+
useEffect,
|
|
6
|
+
useMemo,
|
|
7
|
+
useState,
|
|
8
|
+
} from "react";
|
|
2
9
|
import Log from "../../../../Models/AnalyticsModels/Log";
|
|
3
10
|
import Service from "../../../../Models/DatabaseModels/Service";
|
|
4
11
|
import Dictionary from "../../../../Types/Dictionary";
|
|
@@ -11,6 +18,15 @@ import Link from "../../Link/Link";
|
|
|
11
18
|
import OneUptimeDate from "../../../../Types/Date";
|
|
12
19
|
import JSONFunctions from "../../../../Types/JSONFunctions";
|
|
13
20
|
import SeverityBadge from "./SeverityBadge";
|
|
21
|
+
import { JSONObject } from "../../../../Types/JSON";
|
|
22
|
+
import API from "../../../Utils/API/API";
|
|
23
|
+
import ModelAPI from "../../../Utils/ModelAPI/ModelAPI";
|
|
24
|
+
import { APP_API_URL } from "../../../Config";
|
|
25
|
+
import HTTPResponse from "../../../../Types/API/HTTPResponse";
|
|
26
|
+
import HTTPErrorResponse from "../../../../Types/API/HTTPErrorResponse";
|
|
27
|
+
import ObjectID from "../../../../Types/ObjectID";
|
|
28
|
+
|
|
29
|
+
type LogDetailTab = "details" | "context";
|
|
14
30
|
|
|
15
31
|
export interface LogDetailsPanelProps {
|
|
16
32
|
log: Log;
|
|
@@ -23,6 +39,7 @@ export interface LogDetailsPanelProps {
|
|
|
23
39
|
| ((spanId: string, log: Log) => Route | URL | undefined)
|
|
24
40
|
| undefined;
|
|
25
41
|
variant?: "floating" | "embedded";
|
|
42
|
+
projectId?: ObjectID | undefined;
|
|
26
43
|
}
|
|
27
44
|
|
|
28
45
|
interface PreparedBody {
|
|
@@ -32,6 +49,14 @@ interface PreparedBody {
|
|
|
32
49
|
raw: string;
|
|
33
50
|
}
|
|
34
51
|
|
|
52
|
+
interface ContextLog {
|
|
53
|
+
id: string;
|
|
54
|
+
time: string;
|
|
55
|
+
severity: string;
|
|
56
|
+
body: string;
|
|
57
|
+
serviceId: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
35
60
|
const prepareBody: (body: string | undefined) => PreparedBody = (
|
|
36
61
|
body: string | undefined,
|
|
37
62
|
): PreparedBody => {
|
|
@@ -45,7 +70,7 @@ const prepareBody: (body: string | undefined) => PreparedBody = (
|
|
|
45
70
|
}
|
|
46
71
|
|
|
47
72
|
try {
|
|
48
|
-
const parsed:
|
|
73
|
+
const parsed: unknown = JSON.parse(body);
|
|
49
74
|
const pretty: string = JSON.stringify(parsed, null, 2);
|
|
50
75
|
const compact: string = JSON.stringify(parsed);
|
|
51
76
|
return {
|
|
@@ -64,9 +89,26 @@ const prepareBody: (body: string | undefined) => PreparedBody = (
|
|
|
64
89
|
}
|
|
65
90
|
};
|
|
66
91
|
|
|
92
|
+
function parseContextRow(row: JSONObject): ContextLog {
|
|
93
|
+
return {
|
|
94
|
+
id: String(row["_id"] || ""),
|
|
95
|
+
time: String(row["time"] || ""),
|
|
96
|
+
severity: String(row["severityText"] || "Unspecified"),
|
|
97
|
+
body: String(row["body"] || ""),
|
|
98
|
+
serviceId: String(row["serviceId"] || ""),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
67
102
|
const LogDetailsPanel: FunctionComponent<LogDetailsPanelProps> = (
|
|
68
103
|
props: LogDetailsPanelProps,
|
|
69
104
|
): ReactElement => {
|
|
105
|
+
const [activeTab, setActiveTab] = useState<LogDetailTab>("details");
|
|
106
|
+
const [contextBefore, setContextBefore] = useState<Array<ContextLog>>([]);
|
|
107
|
+
const [contextAfter, setContextAfter] = useState<Array<ContextLog>>([]);
|
|
108
|
+
const [contextLoading, setContextLoading] = useState<boolean>(false);
|
|
109
|
+
const [contextError, setContextError] = useState<string>("");
|
|
110
|
+
const [contextLoaded, setContextLoaded] = useState<boolean>(false);
|
|
111
|
+
|
|
70
112
|
const variant: "floating" | "embedded" = props.variant || "floating";
|
|
71
113
|
const serviceId: string = props.log.serviceId?.toString() || "";
|
|
72
114
|
const service: Service | undefined = props.serviceMap[serviceId];
|
|
@@ -137,6 +179,71 @@ const LogDetailsPanel: FunctionComponent<LogDetailsPanelProps> = (
|
|
|
137
179
|
return undefined;
|
|
138
180
|
}, [spanId, props, traceId]);
|
|
139
181
|
|
|
182
|
+
const loadContext: () => Promise<void> =
|
|
183
|
+
useCallback(async (): Promise<void> => {
|
|
184
|
+
if (!props.projectId || !serviceId || !props.log.time) {
|
|
185
|
+
setContextError("Missing project or service information for context.");
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
try {
|
|
190
|
+
setContextLoading(true);
|
|
191
|
+
setContextError("");
|
|
192
|
+
|
|
193
|
+
const response: HTTPResponse<JSONObject> | HTTPErrorResponse =
|
|
194
|
+
await API.post({
|
|
195
|
+
url: URL.fromString(APP_API_URL.toString()).addRoute(
|
|
196
|
+
"/telemetry/logs/context",
|
|
197
|
+
),
|
|
198
|
+
data: {
|
|
199
|
+
logId: props.log.getColumnValue("_id")?.toString() || "",
|
|
200
|
+
serviceId: serviceId,
|
|
201
|
+
time: props.log.time
|
|
202
|
+
? OneUptimeDate.toString(props.log.time)
|
|
203
|
+
: "",
|
|
204
|
+
count: 5,
|
|
205
|
+
},
|
|
206
|
+
headers: {
|
|
207
|
+
...ModelAPI.getCommonHeaders(),
|
|
208
|
+
},
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
if (response instanceof HTTPErrorResponse) {
|
|
212
|
+
throw response;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const before: Array<JSONObject> =
|
|
216
|
+
(response.data["before"] as Array<JSONObject>) || [];
|
|
217
|
+
const after: Array<JSONObject> =
|
|
218
|
+
(response.data["after"] as Array<JSONObject>) || [];
|
|
219
|
+
|
|
220
|
+
setContextBefore(before.map(parseContextRow));
|
|
221
|
+
setContextAfter(after.map(parseContextRow));
|
|
222
|
+
setContextLoaded(true);
|
|
223
|
+
} catch (err) {
|
|
224
|
+
setContextError(
|
|
225
|
+
`Failed to load log context. ${API.getFriendlyErrorMessage(err as Error)}`,
|
|
226
|
+
);
|
|
227
|
+
} finally {
|
|
228
|
+
setContextLoading(false);
|
|
229
|
+
}
|
|
230
|
+
}, [props.projectId, serviceId, props.log]);
|
|
231
|
+
|
|
232
|
+
useEffect(() => {
|
|
233
|
+
if (activeTab === "context" && !contextLoaded && !contextLoading) {
|
|
234
|
+
void loadContext();
|
|
235
|
+
}
|
|
236
|
+
}, [activeTab, contextLoaded, contextLoading, loadContext]);
|
|
237
|
+
|
|
238
|
+
// Reset context when log changes
|
|
239
|
+
useEffect(() => {
|
|
240
|
+
setContextLoaded(false);
|
|
241
|
+
setContextBefore([]);
|
|
242
|
+
setContextAfter([]);
|
|
243
|
+
setContextError("");
|
|
244
|
+
setActiveTab("details");
|
|
245
|
+
}, [props.log]);
|
|
246
|
+
|
|
140
247
|
const containerClassName: string =
|
|
141
248
|
variant === "embedded"
|
|
142
249
|
? "rounded-lg border border-gray-200 bg-white p-5 shadow-sm"
|
|
@@ -149,6 +256,47 @@ const LogDetailsPanel: FunctionComponent<LogDetailsPanelProps> = (
|
|
|
149
256
|
const smallBadgeClass: string =
|
|
150
257
|
"inline-flex items-center gap-1 rounded-full border border-gray-200 bg-gray-50 px-2 py-1 text-[11px] font-mono uppercase tracking-wide text-gray-600";
|
|
151
258
|
|
|
259
|
+
const tabClass: (isActive: boolean) => string = (
|
|
260
|
+
isActive: boolean,
|
|
261
|
+
): string => {
|
|
262
|
+
return `px-3 py-1.5 text-xs font-medium rounded-md transition-colors ${
|
|
263
|
+
isActive
|
|
264
|
+
? "bg-indigo-50 text-indigo-700 border border-indigo-200"
|
|
265
|
+
: "text-gray-600 hover:text-gray-800 hover:bg-gray-50 border border-transparent"
|
|
266
|
+
}`;
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
const renderContextLogRow: (
|
|
270
|
+
ctxLog: ContextLog,
|
|
271
|
+
isCurrent: boolean,
|
|
272
|
+
) => ReactElement = (
|
|
273
|
+
ctxLog: ContextLog,
|
|
274
|
+
isCurrent: boolean,
|
|
275
|
+
): ReactElement => {
|
|
276
|
+
const rowClass: string = isCurrent
|
|
277
|
+
? "border-l-2 border-l-indigo-500 bg-indigo-50"
|
|
278
|
+
: "border-l-2 border-l-transparent hover:bg-gray-50";
|
|
279
|
+
|
|
280
|
+
return (
|
|
281
|
+
<div
|
|
282
|
+
key={ctxLog.id || ctxLog.time}
|
|
283
|
+
className={`flex items-start gap-3 px-3 py-2 ${rowClass}`}
|
|
284
|
+
>
|
|
285
|
+
<span className="flex-none whitespace-nowrap font-mono text-[11px] text-gray-400">
|
|
286
|
+
{ctxLog.time
|
|
287
|
+
? OneUptimeDate.getDateAsUserFriendlyFormattedString(
|
|
288
|
+
new Date(ctxLog.time),
|
|
289
|
+
)
|
|
290
|
+
: "-"}
|
|
291
|
+
</span>
|
|
292
|
+
<SeverityBadge severity={ctxLog.severity} />
|
|
293
|
+
<span className="min-w-0 flex-1 truncate font-mono text-xs text-gray-700">
|
|
294
|
+
{ctxLog.body.slice(0, 200) || "-"}
|
|
295
|
+
</span>
|
|
296
|
+
</div>
|
|
297
|
+
);
|
|
298
|
+
};
|
|
299
|
+
|
|
152
300
|
return (
|
|
153
301
|
<div className={containerClassName}>
|
|
154
302
|
<div
|
|
@@ -204,134 +352,201 @@ const LogDetailsPanel: FunctionComponent<LogDetailsPanelProps> = (
|
|
|
204
352
|
)}
|
|
205
353
|
</div>
|
|
206
354
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
{
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
</p>
|
|
229
|
-
)}
|
|
230
|
-
</div>
|
|
231
|
-
</section>
|
|
232
|
-
|
|
233
|
-
{(traceId || spanId) && (
|
|
234
|
-
<section className="grid gap-4 md:grid-cols-2">
|
|
235
|
-
{traceId && (
|
|
236
|
-
<div className={`rounded-lg border ${surfaceCardClass} p-4`}>
|
|
237
|
-
<div className="mb-2 flex items-center justify-between text-[11px] uppercase tracking-wide text-gray-400">
|
|
238
|
-
<span>Trace ID</span>
|
|
239
|
-
<CopyTextButton
|
|
240
|
-
textToBeCopied={traceId}
|
|
241
|
-
size="xs"
|
|
242
|
-
variant="ghost"
|
|
243
|
-
iconOnly={true}
|
|
244
|
-
title="Copy trace id"
|
|
245
|
-
/>
|
|
246
|
-
</div>
|
|
247
|
-
<div className="flex items-center justify-between gap-2">
|
|
248
|
-
{traceRoute ? (
|
|
249
|
-
<Link
|
|
250
|
-
to={traceRoute}
|
|
251
|
-
className="max-w-full truncate font-mono text-xs text-indigo-600 hover:text-indigo-500"
|
|
252
|
-
title={`View trace ${traceId}`}
|
|
253
|
-
>
|
|
254
|
-
{traceId}
|
|
255
|
-
</Link>
|
|
256
|
-
) : (
|
|
257
|
-
<span
|
|
258
|
-
className="max-w-full truncate font-mono text-xs text-gray-700"
|
|
259
|
-
title={traceId}
|
|
260
|
-
>
|
|
261
|
-
{traceId}
|
|
262
|
-
</span>
|
|
263
|
-
)}
|
|
264
|
-
{traceRoute && (
|
|
265
|
-
<Icon
|
|
266
|
-
icon={IconProp.ExternalLink}
|
|
267
|
-
className="h-4 w-4 flex-none text-indigo-400"
|
|
268
|
-
/>
|
|
269
|
-
)}
|
|
270
|
-
</div>
|
|
271
|
-
</div>
|
|
272
|
-
)}
|
|
273
|
-
|
|
274
|
-
{spanId && (
|
|
275
|
-
<div className={`rounded-lg border ${surfaceCardClass} p-4`}>
|
|
276
|
-
<div className="mb-2 flex items-center justify-between text-[11px] uppercase tracking-wide text-gray-400">
|
|
277
|
-
<span>Span ID</span>
|
|
278
|
-
<CopyTextButton
|
|
279
|
-
textToBeCopied={spanId}
|
|
280
|
-
size="xs"
|
|
281
|
-
variant="ghost"
|
|
282
|
-
iconOnly={true}
|
|
283
|
-
title="Copy span id"
|
|
284
|
-
/>
|
|
285
|
-
</div>
|
|
286
|
-
<div className="flex items-center justify-between gap-2">
|
|
287
|
-
{spanRoute ? (
|
|
288
|
-
<Link
|
|
289
|
-
to={spanRoute}
|
|
290
|
-
className="max-w-full truncate font-mono text-xs text-indigo-600 hover:text-indigo-500"
|
|
291
|
-
title={`View span ${spanId}`}
|
|
292
|
-
>
|
|
293
|
-
{spanId}
|
|
294
|
-
</Link>
|
|
295
|
-
) : (
|
|
296
|
-
<span
|
|
297
|
-
className="max-w-full truncate font-mono text-xs text-gray-700"
|
|
298
|
-
title={spanId}
|
|
299
|
-
>
|
|
300
|
-
{spanId}
|
|
301
|
-
</span>
|
|
302
|
-
)}
|
|
303
|
-
{spanRoute && (
|
|
304
|
-
<Icon
|
|
305
|
-
icon={IconProp.ExternalLink}
|
|
306
|
-
className="h-4 w-4 flex-none text-indigo-400"
|
|
307
|
-
/>
|
|
308
|
-
)}
|
|
309
|
-
</div>
|
|
310
|
-
</div>
|
|
311
|
-
)}
|
|
312
|
-
</section>
|
|
355
|
+
{/* Tab bar */}
|
|
356
|
+
<div className="mt-3 flex items-center gap-1">
|
|
357
|
+
<button
|
|
358
|
+
type="button"
|
|
359
|
+
className={tabClass(activeTab === "details")}
|
|
360
|
+
onClick={() => {
|
|
361
|
+
setActiveTab("details");
|
|
362
|
+
}}
|
|
363
|
+
>
|
|
364
|
+
Details
|
|
365
|
+
</button>
|
|
366
|
+
{props.projectId && (
|
|
367
|
+
<button
|
|
368
|
+
type="button"
|
|
369
|
+
className={tabClass(activeTab === "context")}
|
|
370
|
+
onClick={() => {
|
|
371
|
+
setActiveTab("context");
|
|
372
|
+
}}
|
|
373
|
+
>
|
|
374
|
+
Context
|
|
375
|
+
</button>
|
|
313
376
|
)}
|
|
377
|
+
</div>
|
|
314
378
|
|
|
315
|
-
|
|
379
|
+
{/* Tab content */}
|
|
380
|
+
{activeTab === "details" && (
|
|
381
|
+
<div className="mt-4 space-y-5 text-sm text-gray-700">
|
|
316
382
|
<section className="space-y-3">
|
|
317
383
|
<header className="flex items-center justify-between text-[11px] uppercase tracking-wide text-gray-400">
|
|
318
|
-
<span>
|
|
384
|
+
<span>Log Body</span>
|
|
319
385
|
<CopyTextButton
|
|
320
|
-
textToBeCopied={
|
|
386
|
+
textToBeCopied={bodyDetails.raw}
|
|
321
387
|
size="xs"
|
|
322
388
|
variant="ghost"
|
|
323
389
|
iconOnly={false}
|
|
324
|
-
title="Copy
|
|
390
|
+
title="Copy log body"
|
|
325
391
|
/>
|
|
326
392
|
</header>
|
|
393
|
+
|
|
327
394
|
<div className={`rounded-lg border ${surfaceCardClass} p-4`}>
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
395
|
+
{bodyDetails.isJson ? (
|
|
396
|
+
<pre className="max-h-80 overflow-auto whitespace-pre-wrap break-words font-mono text-[13px] leading-6 text-gray-800">
|
|
397
|
+
{bodyDetails.pretty}
|
|
398
|
+
</pre>
|
|
399
|
+
) : (
|
|
400
|
+
<p className="whitespace-pre-wrap break-words font-mono text-[13px] leading-6 text-gray-800">
|
|
401
|
+
{bodyDetails.pretty || "-"}
|
|
402
|
+
</p>
|
|
403
|
+
)}
|
|
331
404
|
</div>
|
|
332
405
|
</section>
|
|
333
|
-
|
|
334
|
-
|
|
406
|
+
|
|
407
|
+
{(traceId || spanId) && (
|
|
408
|
+
<section className="grid gap-4 md:grid-cols-2">
|
|
409
|
+
{traceId && (
|
|
410
|
+
<div className={`rounded-lg border ${surfaceCardClass} p-4`}>
|
|
411
|
+
<div className="mb-2 flex items-center justify-between text-[11px] uppercase tracking-wide text-gray-400">
|
|
412
|
+
<span>Trace ID</span>
|
|
413
|
+
<CopyTextButton
|
|
414
|
+
textToBeCopied={traceId}
|
|
415
|
+
size="xs"
|
|
416
|
+
variant="ghost"
|
|
417
|
+
iconOnly={true}
|
|
418
|
+
title="Copy trace id"
|
|
419
|
+
/>
|
|
420
|
+
</div>
|
|
421
|
+
<div className="flex items-center justify-between gap-2">
|
|
422
|
+
{traceRoute ? (
|
|
423
|
+
<Link
|
|
424
|
+
to={traceRoute}
|
|
425
|
+
className="max-w-full truncate font-mono text-xs text-indigo-600 hover:text-indigo-500"
|
|
426
|
+
title={`View trace ${traceId}`}
|
|
427
|
+
>
|
|
428
|
+
{traceId}
|
|
429
|
+
</Link>
|
|
430
|
+
) : (
|
|
431
|
+
<span
|
|
432
|
+
className="max-w-full truncate font-mono text-xs text-gray-700"
|
|
433
|
+
title={traceId}
|
|
434
|
+
>
|
|
435
|
+
{traceId}
|
|
436
|
+
</span>
|
|
437
|
+
)}
|
|
438
|
+
{traceRoute && (
|
|
439
|
+
<Icon
|
|
440
|
+
icon={IconProp.ExternalLink}
|
|
441
|
+
className="h-4 w-4 flex-none text-indigo-400"
|
|
442
|
+
/>
|
|
443
|
+
)}
|
|
444
|
+
</div>
|
|
445
|
+
</div>
|
|
446
|
+
)}
|
|
447
|
+
|
|
448
|
+
{spanId && (
|
|
449
|
+
<div className={`rounded-lg border ${surfaceCardClass} p-4`}>
|
|
450
|
+
<div className="mb-2 flex items-center justify-between text-[11px] uppercase tracking-wide text-gray-400">
|
|
451
|
+
<span>Span ID</span>
|
|
452
|
+
<CopyTextButton
|
|
453
|
+
textToBeCopied={spanId}
|
|
454
|
+
size="xs"
|
|
455
|
+
variant="ghost"
|
|
456
|
+
iconOnly={true}
|
|
457
|
+
title="Copy span id"
|
|
458
|
+
/>
|
|
459
|
+
</div>
|
|
460
|
+
<div className="flex items-center justify-between gap-2">
|
|
461
|
+
{spanRoute ? (
|
|
462
|
+
<Link
|
|
463
|
+
to={spanRoute}
|
|
464
|
+
className="max-w-full truncate font-mono text-xs text-indigo-600 hover:text-indigo-500"
|
|
465
|
+
title={`View span ${spanId}`}
|
|
466
|
+
>
|
|
467
|
+
{spanId}
|
|
468
|
+
</Link>
|
|
469
|
+
) : (
|
|
470
|
+
<span
|
|
471
|
+
className="max-w-full truncate font-mono text-xs text-gray-700"
|
|
472
|
+
title={spanId}
|
|
473
|
+
>
|
|
474
|
+
{spanId}
|
|
475
|
+
</span>
|
|
476
|
+
)}
|
|
477
|
+
{spanRoute && (
|
|
478
|
+
<Icon
|
|
479
|
+
icon={IconProp.ExternalLink}
|
|
480
|
+
className="h-4 w-4 flex-none text-indigo-400"
|
|
481
|
+
/>
|
|
482
|
+
)}
|
|
483
|
+
</div>
|
|
484
|
+
</div>
|
|
485
|
+
)}
|
|
486
|
+
</section>
|
|
487
|
+
)}
|
|
488
|
+
|
|
489
|
+
{prettyAttributes && (
|
|
490
|
+
<section className="space-y-3">
|
|
491
|
+
<header className="flex items-center justify-between text-[11px] uppercase tracking-wide text-gray-400">
|
|
492
|
+
<span>Attributes</span>
|
|
493
|
+
<CopyTextButton
|
|
494
|
+
textToBeCopied={prettyAttributes}
|
|
495
|
+
size="xs"
|
|
496
|
+
variant="ghost"
|
|
497
|
+
iconOnly={false}
|
|
498
|
+
title="Copy attributes"
|
|
499
|
+
/>
|
|
500
|
+
</header>
|
|
501
|
+
<div className={`rounded-lg border ${surfaceCardClass} p-4`}>
|
|
502
|
+
<pre className="max-h-72 overflow-auto whitespace-pre-wrap break-words font-mono text-[13px] leading-6 text-gray-800">
|
|
503
|
+
{prettyAttributes}
|
|
504
|
+
</pre>
|
|
505
|
+
</div>
|
|
506
|
+
</section>
|
|
507
|
+
)}
|
|
508
|
+
</div>
|
|
509
|
+
)}
|
|
510
|
+
|
|
511
|
+
{activeTab === "context" && (
|
|
512
|
+
<div className="mt-4 text-sm text-gray-700">
|
|
513
|
+
{contextLoading && (
|
|
514
|
+
<div className="flex items-center justify-center py-8 text-xs text-gray-400">
|
|
515
|
+
Loading surrounding logs...
|
|
516
|
+
</div>
|
|
517
|
+
)}
|
|
518
|
+
{contextError && (
|
|
519
|
+
<div className="rounded-md bg-red-50 p-3 text-xs text-red-600">
|
|
520
|
+
{contextError}
|
|
521
|
+
</div>
|
|
522
|
+
)}
|
|
523
|
+
{!contextLoading && !contextError && contextLoaded && (
|
|
524
|
+
<div className="divide-y divide-gray-100 rounded-lg border border-gray-200">
|
|
525
|
+
{contextBefore.length === 0 && contextAfter.length === 0 && (
|
|
526
|
+
<div className="px-3 py-6 text-center text-xs text-gray-400">
|
|
527
|
+
No surrounding logs found for this service.
|
|
528
|
+
</div>
|
|
529
|
+
)}
|
|
530
|
+
{contextBefore.map((ctxLog: ContextLog) => {
|
|
531
|
+
return renderContextLogRow(ctxLog, false);
|
|
532
|
+
})}
|
|
533
|
+
{renderContextLogRow(
|
|
534
|
+
{
|
|
535
|
+
id: props.log.getColumnValue("_id")?.toString() || "current",
|
|
536
|
+
time: props.log.time ? props.log.time.toString() : "",
|
|
537
|
+
severity: props.log.severityText?.toString() || "Unspecified",
|
|
538
|
+
body: props.log.body || "",
|
|
539
|
+
serviceId: serviceId,
|
|
540
|
+
},
|
|
541
|
+
true,
|
|
542
|
+
)}
|
|
543
|
+
{contextAfter.map((ctxLog: ContextLog) => {
|
|
544
|
+
return renderContextLogRow(ctxLog, false);
|
|
545
|
+
})}
|
|
546
|
+
</div>
|
|
547
|
+
)}
|
|
548
|
+
</div>
|
|
549
|
+
)}
|
|
335
550
|
</div>
|
|
336
551
|
);
|
|
337
552
|
};
|