@oneuptime/common 10.0.20 → 10.0.22

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.
Files changed (95) hide show
  1. package/Server/API/TelemetryAPI.ts +208 -0
  2. package/Server/API/UserCallAPI.ts +29 -0
  3. package/Server/API/UserEmailAPI.ts +29 -0
  4. package/Server/API/UserSmsAPI.ts +29 -0
  5. package/Server/API/UserWhatsAppAPI.ts +29 -0
  6. package/Server/Services/LogAggregationService.ts +251 -0
  7. package/Server/Types/AnalyticsDatabase/ModelPermission.ts +2 -2
  8. package/Server/Types/Database/Permissions/TenantPermission.ts +2 -2
  9. package/Server/Utils/VM/VMRunner.ts +10 -0
  10. package/Types/Log/LogQueryParser.ts +252 -0
  11. package/Types/Log/LogQueryToFilter.ts +131 -0
  12. package/UI/Components/CopyTextButton/CopyTextButton.tsx +3 -3
  13. package/UI/Components/LogsViewer/LogsViewer.tsx +166 -93
  14. package/UI/Components/LogsViewer/components/ActiveFilterChips.tsx +58 -0
  15. package/UI/Components/LogsViewer/components/FacetSection.tsx +119 -0
  16. package/UI/Components/LogsViewer/components/FacetValueRow.tsx +102 -0
  17. package/UI/Components/LogsViewer/components/HistogramTooltip.tsx +122 -0
  18. package/UI/Components/LogsViewer/components/LiveLogsToggle.tsx +4 -4
  19. package/UI/Components/LogsViewer/components/LogDetailsPanel.tsx +22 -26
  20. package/UI/Components/LogsViewer/components/LogSearchBar.tsx +360 -0
  21. package/UI/Components/LogsViewer/components/LogSearchHelp.tsx +128 -0
  22. package/UI/Components/LogsViewer/components/LogSearchSuggestions.tsx +64 -0
  23. package/UI/Components/LogsViewer/components/LogTimeRangePicker.tsx +199 -0
  24. package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +172 -0
  25. package/UI/Components/LogsViewer/components/LogsFilterCard.tsx +27 -57
  26. package/UI/Components/LogsViewer/components/LogsHistogram.tsx +268 -0
  27. package/UI/Components/LogsViewer/components/LogsPagination.tsx +12 -10
  28. package/UI/Components/LogsViewer/components/LogsTable.tsx +33 -32
  29. package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +16 -18
  30. package/UI/Components/LogsViewer/components/severityColors.ts +31 -0
  31. package/UI/Components/LogsViewer/components/severityTheme.ts +25 -25
  32. package/UI/Components/LogsViewer/types.ts +20 -0
  33. package/build/dist/Server/API/TelemetryAPI.js +136 -0
  34. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  35. package/build/dist/Server/API/UserCallAPI.js +17 -0
  36. package/build/dist/Server/API/UserCallAPI.js.map +1 -1
  37. package/build/dist/Server/API/UserEmailAPI.js +17 -0
  38. package/build/dist/Server/API/UserEmailAPI.js.map +1 -1
  39. package/build/dist/Server/API/UserSmsAPI.js +17 -0
  40. package/build/dist/Server/API/UserSmsAPI.js.map +1 -1
  41. package/build/dist/Server/API/UserWhatsAppAPI.js +17 -0
  42. package/build/dist/Server/API/UserWhatsAppAPI.js.map +1 -1
  43. package/build/dist/Server/Services/LogAggregationService.js +163 -0
  44. package/build/dist/Server/Services/LogAggregationService.js.map +1 -0
  45. package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js +2 -2
  46. package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js.map +1 -1
  47. package/build/dist/Server/Types/Database/Permissions/TenantPermission.js +2 -2
  48. package/build/dist/Server/Types/Database/Permissions/TenantPermission.js.map +1 -1
  49. package/build/dist/Server/Utils/VM/VMRunner.js +10 -0
  50. package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
  51. package/build/dist/Types/Log/LogQueryParser.js +200 -0
  52. package/build/dist/Types/Log/LogQueryParser.js.map +1 -0
  53. package/build/dist/Types/Log/LogQueryToFilter.js +96 -0
  54. package/build/dist/Types/Log/LogQueryToFilter.js.map +1 -0
  55. package/build/dist/UI/Components/CopyTextButton/CopyTextButton.js +3 -3
  56. package/build/dist/UI/Components/CopyTextButton/CopyTextButton.js.map +1 -1
  57. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +64 -42
  58. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  59. package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js +24 -0
  60. package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js.map +1 -0
  61. package/build/dist/UI/Components/LogsViewer/components/FacetSection.js +46 -0
  62. package/build/dist/UI/Components/LogsViewer/components/FacetSection.js.map +1 -0
  63. package/build/dist/UI/Components/LogsViewer/components/FacetValueRow.js +35 -0
  64. package/build/dist/UI/Components/LogsViewer/components/FacetValueRow.js.map +1 -0
  65. package/build/dist/UI/Components/LogsViewer/components/HistogramTooltip.js +64 -0
  66. package/build/dist/UI/Components/LogsViewer/components/HistogramTooltip.js.map +1 -0
  67. package/build/dist/UI/Components/LogsViewer/components/LiveLogsToggle.js +4 -4
  68. package/build/dist/UI/Components/LogsViewer/components/LiveLogsToggle.js.map +1 -1
  69. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js +19 -21
  70. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js.map +1 -1
  71. package/build/dist/UI/Components/LogsViewer/components/LogSearchBar.js +230 -0
  72. package/build/dist/UI/Components/LogsViewer/components/LogSearchBar.js.map +1 -0
  73. package/build/dist/UI/Components/LogsViewer/components/LogSearchHelp.js +84 -0
  74. package/build/dist/UI/Components/LogsViewer/components/LogSearchHelp.js.map +1 -0
  75. package/build/dist/UI/Components/LogsViewer/components/LogSearchSuggestions.js +27 -0
  76. package/build/dist/UI/Components/LogsViewer/components/LogSearchSuggestions.js.map +1 -0
  77. package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js +100 -0
  78. package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js.map +1 -0
  79. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +104 -0
  80. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -0
  81. package/build/dist/UI/Components/LogsViewer/components/LogsFilterCard.js +14 -35
  82. package/build/dist/UI/Components/LogsViewer/components/LogsFilterCard.js.map +1 -1
  83. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +127 -0
  84. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -0
  85. package/build/dist/UI/Components/LogsViewer/components/LogsPagination.js +9 -9
  86. package/build/dist/UI/Components/LogsViewer/components/LogsPagination.js.map +1 -1
  87. package/build/dist/UI/Components/LogsViewer/components/LogsTable.js +31 -30
  88. package/build/dist/UI/Components/LogsViewer/components/LogsTable.js.map +1 -1
  89. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +7 -8
  90. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
  91. package/build/dist/UI/Components/LogsViewer/components/severityColors.js +22 -0
  92. package/build/dist/UI/Components/LogsViewer/components/severityColors.js.map +1 -0
  93. package/build/dist/UI/Components/LogsViewer/components/severityTheme.js +25 -25
  94. package/build/dist/UI/Components/LogsViewer/components/severityTheme.js.map +1 -1
  95. package/package.json +1 -1
@@ -0,0 +1,268 @@
1
+ import React, {
2
+ FunctionComponent,
3
+ ReactElement,
4
+ useMemo,
5
+ useCallback,
6
+ useRef,
7
+ useState,
8
+ } from "react";
9
+ import {
10
+ BarChart,
11
+ Bar,
12
+ XAxis,
13
+ YAxis,
14
+ Tooltip,
15
+ ResponsiveContainer,
16
+ ReferenceArea,
17
+ } from "recharts";
18
+ import { HistogramBucket } from "../types";
19
+ import {
20
+ getSeverityColor,
21
+ getAllSeverityKeys,
22
+ SeverityColor,
23
+ } from "./severityColors";
24
+ import HistogramTooltip from "./HistogramTooltip";
25
+ import ComponentLoader from "../../ComponentLoader/ComponentLoader";
26
+
27
+ export interface LogsHistogramProps {
28
+ buckets: Array<HistogramBucket>;
29
+ isLoading: boolean;
30
+ onTimeRangeSelect?: ((startTime: Date, endTime: Date) => void) | undefined;
31
+ }
32
+
33
+ interface PivotedRow {
34
+ time: string;
35
+ [severity: string]: number | string;
36
+ }
37
+
38
+ function pivotBuckets(buckets: Array<HistogramBucket>): Array<PivotedRow> {
39
+ const map: Map<string, PivotedRow> = new Map();
40
+
41
+ for (const bucket of buckets) {
42
+ let row: PivotedRow | undefined = map.get(bucket.time);
43
+
44
+ if (!row) {
45
+ row = { time: bucket.time };
46
+ map.set(bucket.time, row);
47
+ }
48
+
49
+ row[bucket.severity] =
50
+ ((row[bucket.severity] as number) || 0) + bucket.count;
51
+ }
52
+
53
+ return Array.from(map.values());
54
+ }
55
+
56
+ function formatTickTime(time: string): string {
57
+ const date: Date = new Date(time);
58
+
59
+ if (isNaN(date.getTime())) {
60
+ return time;
61
+ }
62
+
63
+ return date.toLocaleTimeString([], {
64
+ hour: "2-digit",
65
+ minute: "2-digit",
66
+ hour12: false,
67
+ });
68
+ }
69
+
70
+ function formatYAxisTick(value: number): string {
71
+ if (value >= 1000000) {
72
+ return `${(value / 1000000).toFixed(1)}M`;
73
+ }
74
+
75
+ if (value >= 1000) {
76
+ return `${(value / 1000).toFixed(value >= 10000 ? 0 : 1)}K`;
77
+ }
78
+
79
+ return value.toString();
80
+ }
81
+
82
+ const LogsHistogram: FunctionComponent<LogsHistogramProps> = (
83
+ props: LogsHistogramProps,
84
+ ): ReactElement => {
85
+ const [selectionStart, setSelectionStart] = useState<string | null>(null);
86
+ const [selectionEnd, setSelectionEnd] = useState<string | null>(null);
87
+ const isSelecting: React.MutableRefObject<boolean> = useRef(false);
88
+
89
+ const pivotedData: Array<PivotedRow> = useMemo(() => {
90
+ return pivotBuckets(props.buckets);
91
+ }, [props.buckets]);
92
+
93
+ const activeSeverities: Array<string> = useMemo(() => {
94
+ const present: Set<string> = new Set<string>();
95
+
96
+ for (const bucket of props.buckets) {
97
+ present.add(bucket.severity);
98
+ }
99
+
100
+ return getAllSeverityKeys().filter((key: string): boolean => {
101
+ return present.has(key);
102
+ });
103
+ }, [props.buckets]);
104
+
105
+ const handleMouseDown: (e: any) => void = useCallback(
106
+ (e: any): void => {
107
+ if (!props.onTimeRangeSelect || !e?.activeLabel) {
108
+ return;
109
+ }
110
+
111
+ isSelecting.current = true;
112
+ setSelectionStart(e.activeLabel as string);
113
+ setSelectionEnd(null);
114
+ },
115
+ [props.onTimeRangeSelect],
116
+ );
117
+
118
+ const handleMouseMove: (e: any) => void = useCallback((e: any): void => {
119
+ if (!isSelecting.current || !e?.activeLabel) {
120
+ return;
121
+ }
122
+
123
+ setSelectionEnd(e.activeLabel as string);
124
+ }, []);
125
+
126
+ const handleMouseUp: () => void = useCallback((): void => {
127
+ if (
128
+ !isSelecting.current ||
129
+ !selectionStart ||
130
+ !selectionEnd ||
131
+ !props.onTimeRangeSelect
132
+ ) {
133
+ isSelecting.current = false;
134
+ setSelectionStart(null);
135
+ setSelectionEnd(null);
136
+ return;
137
+ }
138
+
139
+ isSelecting.current = false;
140
+
141
+ const start: Date = new Date(selectionStart);
142
+ const end: Date = new Date(selectionEnd);
143
+
144
+ if (isNaN(start.getTime()) || isNaN(end.getTime())) {
145
+ setSelectionStart(null);
146
+ setSelectionEnd(null);
147
+ return;
148
+ }
149
+
150
+ const earlierDate: Date = start < end ? start : end;
151
+ const laterDate: Date = start < end ? end : start;
152
+
153
+ props.onTimeRangeSelect(earlierDate, laterDate);
154
+
155
+ setSelectionStart(null);
156
+ setSelectionEnd(null);
157
+ }, [selectionStart, selectionEnd, props.onTimeRangeSelect]);
158
+
159
+ if (props.isLoading && pivotedData.length === 0) {
160
+ return (
161
+ <div className="flex h-32 items-center justify-center rounded-lg border border-gray-200 bg-white">
162
+ <ComponentLoader />
163
+ </div>
164
+ );
165
+ }
166
+
167
+ if (pivotedData.length === 0) {
168
+ return <></>;
169
+ }
170
+
171
+ return (
172
+ <div className="rounded-lg border border-gray-200 bg-white">
173
+ {/* Header with legend */}
174
+ <div className="flex items-center justify-between border-b border-gray-100 px-4 py-2">
175
+ <div className="flex items-center gap-2">
176
+ <span className="text-xs font-medium text-gray-500">Log Volume</span>
177
+ {props.onTimeRangeSelect && (
178
+ <span className="text-[10px] text-gray-300">Drag to zoom</span>
179
+ )}
180
+ </div>
181
+ <div className="flex items-center gap-3">
182
+ {activeSeverities.map((severity: string) => {
183
+ const color: SeverityColor = getSeverityColor(severity);
184
+ return (
185
+ <div key={severity} className="flex items-center gap-1.5">
186
+ <span
187
+ className="inline-block h-2.5 w-2.5 rounded-sm"
188
+ style={{ backgroundColor: color.fill }}
189
+ />
190
+ <span className="text-[11px] text-gray-500">{color.label}</span>
191
+ </div>
192
+ );
193
+ })}
194
+ </div>
195
+ </div>
196
+
197
+ {/* Chart */}
198
+ <div
199
+ className="px-2 pb-1 pt-2"
200
+ style={{
201
+ height: 120,
202
+ cursor: props.onTimeRangeSelect ? "crosshair" : "default",
203
+ }}
204
+ >
205
+ <ResponsiveContainer width="100%" height="100%">
206
+ <BarChart
207
+ data={pivotedData}
208
+ margin={{ top: 4, right: 8, bottom: 0, left: -4 }}
209
+ onMouseDown={handleMouseDown}
210
+ onMouseMove={handleMouseMove}
211
+ onMouseUp={handleMouseUp}
212
+ barCategoryGap="15%"
213
+ barGap={0}
214
+ >
215
+ <XAxis
216
+ dataKey="time"
217
+ tickFormatter={formatTickTime}
218
+ tick={{ fontSize: 10, fill: "#9ca3af" }}
219
+ axisLine={{ stroke: "#e5e7eb" }}
220
+ tickLine={false}
221
+ minTickGap={40}
222
+ dy={4}
223
+ interval="preserveStartEnd"
224
+ />
225
+ <YAxis
226
+ tick={{ fontSize: 10, fill: "#9ca3af" }}
227
+ axisLine={false}
228
+ tickLine={false}
229
+ width={48}
230
+ allowDecimals={false}
231
+ tickFormatter={formatYAxisTick}
232
+ />
233
+ <Tooltip
234
+ content={<HistogramTooltip />}
235
+ cursor={{ fill: "rgba(99,102,241,0.06)" }}
236
+ />
237
+ {activeSeverities.map((severity: string, index: number) => {
238
+ const isLast: boolean = index === activeSeverities.length - 1;
239
+ return (
240
+ <Bar
241
+ key={severity}
242
+ dataKey={severity}
243
+ stackId="severity"
244
+ fill={getSeverityColor(severity).fill}
245
+ radius={isLast ? [1.5, 1.5, 0, 0] : [0, 0, 0, 0]}
246
+ isAnimationActive={false}
247
+ maxBarSize={24}
248
+ />
249
+ );
250
+ })}
251
+ {selectionStart && selectionEnd && (
252
+ <ReferenceArea
253
+ x1={selectionStart}
254
+ x2={selectionEnd}
255
+ fill="rgba(99,102,241,0.12)"
256
+ stroke="rgba(99,102,241,0.5)"
257
+ strokeWidth={1}
258
+ radius={2}
259
+ />
260
+ )}
261
+ </BarChart>
262
+ </ResponsiveContainer>
263
+ </div>
264
+ </div>
265
+ );
266
+ };
267
+
268
+ export default LogsHistogram;
@@ -37,12 +37,12 @@ const LogsPagination: FunctionComponent<LogsPaginationProps> = (
37
37
  props.isDisabled || props.totalItems === 0 || safeCurrentPage >= totalPages;
38
38
 
39
39
  return (
40
- <div className="flex flex-col gap-3 border-t border-slate-800 bg-slate-950/60 px-4 py-3 text-xs text-slate-400 md:flex-row md:items-center md:justify-between">
40
+ <div className="flex flex-col gap-3 border-t border-gray-200 bg-gray-50/50 px-4 py-2.5 text-xs text-gray-500 md:flex-row md:items-center md:justify-between">
41
41
  <div>
42
42
  {props.totalItems === 0 ? (
43
- <span>No results to display.</span>
43
+ <span className="text-gray-500">No results to display.</span>
44
44
  ) : (
45
- <span>
45
+ <span className="text-gray-500">
46
46
  Showing {firstItemIndex.toLocaleString()}-
47
47
  {lastItemIndex.toLocaleString()} of{" "}
48
48
  {props.totalItems.toLocaleString()}
@@ -51,10 +51,12 @@ const LogsPagination: FunctionComponent<LogsPaginationProps> = (
51
51
  </div>
52
52
 
53
53
  <div className="flex flex-wrap items-center gap-3">
54
- <label className="flex items-center gap-2 text-slate-500">
55
- <span className="uppercase tracking-wide text-[10px]">Rows</span>
54
+ <label className="flex items-center gap-2">
55
+ <span className="text-[10px] uppercase tracking-wide text-gray-400">
56
+ Rows
57
+ </span>
56
58
  <select
57
- className="rounded-md border border-slate-700 bg-slate-900/80 px-2 py-1 text-xs text-slate-200 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 disabled:cursor-not-allowed disabled:opacity-50"
59
+ className="rounded-md border border-gray-200 bg-white px-2 py-1 text-xs text-gray-700 focus:border-indigo-400 focus:outline-none focus:ring-1 focus:ring-indigo-200"
58
60
  value={props.pageSize}
59
61
  onChange={(event: React.ChangeEvent<HTMLSelectElement>) => {
60
62
  const size: number = Number(event.target.value) || props.pageSize;
@@ -72,10 +74,10 @@ const LogsPagination: FunctionComponent<LogsPaginationProps> = (
72
74
  </select>
73
75
  </label>
74
76
 
75
- <div className="inline-flex items-center gap-1 rounded-full border border-slate-800 bg-slate-900/70 p-0.5">
77
+ <div className="inline-flex items-center gap-1 rounded-lg border border-gray-200 bg-white p-0.5">
76
78
  <button
77
79
  type="button"
78
- className="rounded-full px-3 py-1 text-xs font-medium text-slate-300 transition-colors hover:bg-slate-800 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 disabled:cursor-not-allowed disabled:opacity-40"
80
+ className="rounded-md px-3 py-1 text-xs font-medium text-gray-600 transition-colors hover:bg-gray-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-300 disabled:cursor-not-allowed disabled:opacity-40"
79
81
  onClick={() => {
80
82
  if (!disablePrev) {
81
83
  props.onPageChange(Math.max(1, safeCurrentPage - 1));
@@ -85,12 +87,12 @@ const LogsPagination: FunctionComponent<LogsPaginationProps> = (
85
87
  >
86
88
  Previous
87
89
  </button>
88
- <span className="px-3 text-[11px] uppercase tracking-wide text-slate-500">
90
+ <span className="px-3 text-[11px] text-gray-400">
89
91
  Page {safeCurrentPage} / {totalPages}
90
92
  </span>
91
93
  <button
92
94
  type="button"
93
- className="rounded-full px-3 py-1 text-xs font-medium text-slate-300 transition-colors hover:bg-slate-800 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 disabled:cursor-not-allowed disabled:opacity-40"
95
+ className="rounded-md px-3 py-1 text-xs font-medium text-gray-600 transition-colors hover:bg-gray-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-300 disabled:cursor-not-allowed disabled:opacity-40"
94
96
  onClick={() => {
95
97
  if (!disableNext) {
96
98
  props.onPageChange(Math.min(totalPages, safeCurrentPage + 1));
@@ -77,23 +77,23 @@ const LogsTable: FunctionComponent<LogsTableProps> = (
77
77
  ): string => {
78
78
  const base: string = "h-3.5 w-3.5 flex-none transition-colors";
79
79
  if (activeSortField === field) {
80
- return `${base} text-indigo-300`;
80
+ return `${base} text-indigo-500`;
81
81
  }
82
82
 
83
- return `${base} text-slate-600`;
83
+ return `${base} text-gray-300`;
84
84
  };
85
85
 
86
86
  return (
87
87
  <div className="relative">
88
- <div className="overflow-x-auto overflow-y-hidden border-b border-slate-900 bg-slate-950">
89
- <table className="min-w-full divide-y divide-slate-900/80">
90
- <thead className="bg-slate-950">
91
- <tr className="text-left text-[11px] font-semibold uppercase tracking-wider text-slate-200">
92
- <th scope="col" className="px-4 py-3">
88
+ <div className="overflow-x-auto bg-white">
89
+ <table className="min-w-full">
90
+ <thead className="bg-gray-50/80">
91
+ <tr className="text-left text-[11px] font-semibold uppercase tracking-wider text-gray-500">
92
+ <th scope="col" className="px-4 py-2.5">
93
93
  <button
94
94
  type="button"
95
- className={`flex items-center gap-2 text-left font-semibold tracking-wider text-slate-300 transition-colors hover:text-slate-100 focus:outline-none ${
96
- activeSortField === "time" ? "text-slate-100" : ""
95
+ className={`flex items-center gap-2 text-left font-semibold tracking-wider text-gray-500 transition-colors hover:text-gray-700 focus:outline-none ${
96
+ activeSortField === "time" ? "text-gray-700" : ""
97
97
  }`}
98
98
  onClick={() => {
99
99
  props.onSortChange?.("time");
@@ -114,14 +114,14 @@ const LogsTable: FunctionComponent<LogsTableProps> = (
114
114
  />
115
115
  </button>
116
116
  </th>
117
- <th scope="col" className="px-4 py-3">
118
- Service
117
+ <th scope="col" className="px-4 py-2.5">
118
+ <span>Service</span>
119
119
  </th>
120
- <th scope="col" className="px-4 py-3">
120
+ <th scope="col" className="px-4 py-2.5">
121
121
  <button
122
122
  type="button"
123
- className={`flex items-center gap-2 text-left font-semibold tracking-wider text-slate-300 transition-colors hover:text-slate-100 focus:outline-none ${
124
- activeSortField === "severityText" ? "text-slate-100" : ""
123
+ className={`flex items-center gap-2 text-left font-semibold tracking-wider text-gray-500 transition-colors hover:text-gray-700 focus:outline-none ${
124
+ activeSortField === "severityText" ? "text-gray-700" : ""
125
125
  }`}
126
126
  onClick={() => {
127
127
  props.onSortChange?.("severityText");
@@ -142,12 +142,12 @@ const LogsTable: FunctionComponent<LogsTableProps> = (
142
142
  />
143
143
  </button>
144
144
  </th>
145
- <th scope="col" className="px-4 py-3">
145
+ <th scope="col" className="px-4 py-2.5">
146
146
  Message
147
147
  </th>
148
148
  </tr>
149
149
  </thead>
150
- <tbody className="divide-y divide-slate-900/70 bg-slate-950">
150
+ <tbody className="divide-y divide-gray-100">
151
151
  {props.logs.map((log: Log, index: number) => {
152
152
  const rowId: string = resolveLogIdentifier(log, index);
153
153
  const serviceId: string = log.serviceId?.toString() || "";
@@ -173,25 +173,25 @@ const LogsTable: FunctionComponent<LogsTableProps> = (
173
173
  onClick={() => {
174
174
  props.onRowClick(log, rowId);
175
175
  }}
176
- className={`group cursor-pointer align-top transition-colors duration-150 hover:bg-slate-900 ${
176
+ className={`group cursor-pointer align-top transition-colors hover:bg-gray-50/70 border-l-[3px] ${severityTheme.borderClass} ${
177
177
  isSelected
178
- ? "bg-slate-900 ring-1 ring-inset ring-indigo-500/40"
178
+ ? "bg-indigo-50/50 ring-1 ring-inset ring-indigo-200"
179
179
  : ""
180
180
  }`}
181
181
  aria-selected={isSelected}
182
182
  aria-expanded={isSelected}
183
183
  >
184
- <td className="whitespace-nowrap px-4 py-3 text-[13px] font-mono text-slate-200">
184
+ <td className="whitespace-nowrap px-4 py-2 text-[13px] font-mono text-gray-600">
185
185
  {log.time
186
186
  ? OneUptimeDate.getDateAsUserFriendlyFormattedString(
187
187
  log.time,
188
188
  )
189
189
  : "-"}
190
190
  </td>
191
- <td className="px-4 py-3">
192
- <div className="flex items-center gap-3 text-sm text-slate-300">
191
+ <td className="px-4 py-2">
192
+ <div className="flex items-center gap-3 text-sm text-gray-700">
193
193
  <span
194
- className="h-2.5 w-2.5 flex-none rounded-full border border-slate-900/40 shadow-sm"
194
+ className="h-2.5 w-2.5 flex-none rounded-full shadow-sm"
195
195
  style={{ backgroundColor: serviceColor }}
196
196
  aria-hidden="true"
197
197
  />
@@ -200,20 +200,20 @@ const LogsTable: FunctionComponent<LogsTableProps> = (
200
200
  </span>
201
201
  </div>
202
202
  </td>
203
- <td className="px-4 py-3">
203
+ <td className="px-4 py-2">
204
204
  <SeverityBadge severity={log.severityText} />
205
205
  </td>
206
- <td className="px-4 py-3">
206
+ <td className="px-4 py-2">
207
207
  <div className="flex items-start justify-between gap-3">
208
208
  <div className="flex min-w-0 flex-1 flex-col gap-1">
209
209
  <p
210
- className={`whitespace-pre-wrap break-words text-sm text-slate-200 transition-colors duration-150 group-hover:text-slate-50 ${severityTheme.textClass}`}
210
+ className="whitespace-pre-wrap break-words text-sm text-gray-800"
211
211
  title={message}
212
212
  >
213
213
  {message || "-"}
214
214
  </p>
215
215
  {(traceId || spanId) && (
216
- <div className="flex flex-wrap gap-3 text-[11px] tracking-wide text-slate-500">
216
+ <div className="flex flex-wrap gap-3 text-[11px] tracking-wide text-gray-400">
217
217
  {traceId && <span>Trace: {traceId}</span>}
218
218
  {spanId && <span>Span: {spanId}</span>}
219
219
  </div>
@@ -225,13 +225,14 @@ const LogsTable: FunctionComponent<LogsTableProps> = (
225
225
  variant="ghost"
226
226
  iconOnly={true}
227
227
  title="Copy log message"
228
+ className="opacity-0 group-hover:opacity-100 transition-opacity"
228
229
  />
229
230
  </div>
230
231
  </td>
231
232
  </tr>
232
233
 
233
234
  {isSelected && props.renderExpandedContent && (
234
- <tr className="bg-slate-950/70">
235
+ <tr className="bg-white">
235
236
  <td colSpan={4} className="px-6 pb-6 pt-3">
236
237
  {props.renderExpandedContent(log)}
237
238
  </td>
@@ -245,18 +246,18 @@ const LogsTable: FunctionComponent<LogsTableProps> = (
245
246
  </div>
246
247
 
247
248
  {props.isLoading && (
248
- <div className="absolute inset-0 flex items-center justify-center bg-slate-950/70 backdrop-blur-md">
249
+ <div className="absolute inset-0 flex items-center justify-center bg-white/80 backdrop-blur-sm">
249
250
  <ComponentLoader />
250
251
  </div>
251
252
  )}
252
253
 
253
254
  {showEmptyState && (
254
- <div className="flex h-full items-center justify-center px-6 py-12 text-center bg-slate-950">
255
- <div className="w-full max-w-xl rounded-md border border-slate-900/70 bg-slate-950 p-6 text-left shadow-inner">
256
- <p className="font-mono text-sm uppercase text-slate-400">
255
+ <div className="flex h-full items-center justify-center px-6 py-12 text-center bg-white">
256
+ <div className="w-full max-w-xl rounded-md border border-gray-200 bg-white p-6 text-left shadow-sm">
257
+ <p className="font-mono text-sm uppercase text-gray-500">
257
258
  No logs found
258
259
  </p>
259
- <p className="mt-3 font-mono text-xs text-slate-500">
260
+ <p className="mt-3 font-mono text-xs text-gray-400">
260
261
  {props.emptyMessage || "Adjust filters or check again later."}
261
262
  </p>
262
263
  </div>
@@ -1,16 +1,17 @@
1
1
  import React, { FunctionComponent, ReactElement } from "react";
2
- import Button, { ButtonSize, ButtonStyleType } from "../../Button/Button";
3
2
  import LiveLogsToggle from "./LiveLogsToggle";
3
+ import LogTimeRangePicker from "./LogTimeRangePicker";
4
4
  import { LiveLogsOptions } from "../types";
5
+ import RangeStartAndEndDateTime from "../../../../Types/Time/RangeStartAndEndDateTime";
5
6
 
6
7
  export interface LogsViewerToolbarProps {
7
8
  resultCount: number;
8
- showApplyButton?: boolean;
9
- onApplyFilters?: () => void;
10
9
  currentPage?: number;
11
10
  totalPages?: number;
12
11
  className?: string;
13
12
  liveOptions?: LiveLogsOptions;
13
+ timeRange?: RangeStartAndEndDateTime;
14
+ onTimeRangeChange?: (value: RangeStartAndEndDateTime) => void;
14
15
  }
15
16
 
16
17
  const LogsViewerToolbar: FunctionComponent<LogsViewerToolbarProps> = (
@@ -23,31 +24,28 @@ const LogsViewerToolbar: FunctionComponent<LogsViewerToolbarProps> = (
23
24
 
24
25
  return (
25
26
  <div
26
- className={`flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between ${props.className || ""}`}
27
+ className={`flex items-center justify-between gap-3 ${props.className || ""}`}
27
28
  >
28
- <div className="flex flex-wrap items-center gap-3 text-xs text-slate-400">
29
- <span className="font-medium text-slate-300">
29
+ <div className="flex flex-wrap items-center gap-3 text-xs text-gray-500">
30
+ <span className="font-medium text-gray-700">
30
31
  {props.resultCount.toLocaleString()} result
31
32
  {props.resultCount === 1 ? "" : "s"}
32
33
  </span>
33
34
  {hasPaginationSummary && (
34
- <span className="text-slate-500">
35
+ <span className="text-gray-400">
35
36
  Page {currentPage} of {totalPages}
36
37
  </span>
37
38
  )}
38
39
  </div>
39
40
 
40
- <div className="flex flex-wrap items-center gap-2">
41
- {props.liveOptions && <LiveLogsToggle {...props.liveOptions} />}
42
- {props.showApplyButton && props.onApplyFilters && (
43
- <Button
44
- title="Apply Filters"
45
- buttonStyle={ButtonStyleType.NORMAL}
46
- buttonSize={ButtonSize.Small}
47
- onClick={props.onApplyFilters}
48
- />
49
- )}
50
- </div>
41
+ {props.timeRange && props.onTimeRangeChange && (
42
+ <LogTimeRangePicker
43
+ value={props.timeRange}
44
+ onChange={props.onTimeRangeChange}
45
+ />
46
+ )}
47
+
48
+ {props.liveOptions && <LiveLogsToggle {...props.liveOptions} />}
51
49
  </div>
52
50
  );
53
51
  };
@@ -0,0 +1,31 @@
1
+ import LogSeverity from "../../../../Types/Log/LogSeverity";
2
+
3
+ export interface SeverityColor {
4
+ fill: string;
5
+ label: string;
6
+ }
7
+
8
+ const severityColorMap: Record<string, SeverityColor> = {
9
+ [LogSeverity.Fatal]: { fill: "#dc2626", label: "Fatal" },
10
+ [LogSeverity.Error]: { fill: "#f87171", label: "Error" },
11
+ [LogSeverity.Warning]: { fill: "#f59e0b", label: "Warning" },
12
+ [LogSeverity.Information]: { fill: "#60a5fa", label: "Info" },
13
+ [LogSeverity.Debug]: { fill: "#a78bfa", label: "Debug" },
14
+ [LogSeverity.Trace]: { fill: "#9ca3af", label: "Trace" },
15
+ Unspecified: { fill: "#cbd5e1", label: "Unspecified" },
16
+ };
17
+
18
+ const defaultSeverityColor: SeverityColor = {
19
+ fill: "#cbd5e1",
20
+ label: "Unknown",
21
+ };
22
+
23
+ export function getSeverityColor(severity: string): SeverityColor {
24
+ return severityColorMap[severity] || defaultSeverityColor;
25
+ }
26
+
27
+ export function getAllSeverityKeys(): Array<string> {
28
+ return Object.keys(severityColorMap);
29
+ }
30
+
31
+ export default severityColorMap;
@@ -9,48 +9,48 @@ export interface SeverityTheme {
9
9
 
10
10
  const severityThemeMap: Record<string, SeverityTheme> = {
11
11
  [LogSeverity.Fatal]: {
12
- badgeClass: "bg-rose-950/80 text-rose-100 ring-rose-500/40",
12
+ badgeClass: "bg-rose-50 text-rose-700 ring-rose-200",
13
13
  dotClass: "bg-rose-500",
14
- borderClass: "border-rose-500/50",
15
- textClass: "text-rose-100",
14
+ borderClass: "border-rose-200",
15
+ textClass: "text-gray-800",
16
16
  },
17
17
  [LogSeverity.Error]: {
18
- badgeClass: "bg-rose-900/60 text-rose-100 ring-rose-500/30",
19
- dotClass: "bg-rose-400",
20
- borderClass: "border-rose-500/40",
21
- textClass: "text-rose-100",
18
+ badgeClass: "bg-red-50 text-red-700 ring-red-200",
19
+ dotClass: "bg-red-400",
20
+ borderClass: "border-red-200",
21
+ textClass: "text-gray-800",
22
22
  },
23
23
  [LogSeverity.Warning]: {
24
- badgeClass: "bg-amber-900/50 text-amber-100 ring-amber-500/30",
24
+ badgeClass: "bg-amber-50 text-amber-700 ring-amber-200",
25
25
  dotClass: "bg-amber-400",
26
- borderClass: "border-amber-400/40",
27
- textClass: "text-amber-100",
26
+ borderClass: "border-amber-200",
27
+ textClass: "text-gray-800",
28
28
  },
29
29
  [LogSeverity.Information]: {
30
- badgeClass: "bg-sky-900/50 text-sky-100 ring-sky-500/30",
31
- dotClass: "bg-sky-400",
32
- borderClass: "border-sky-400/40",
33
- textClass: "text-sky-100",
30
+ badgeClass: "bg-blue-50 text-blue-700 ring-blue-200",
31
+ dotClass: "bg-blue-400",
32
+ borderClass: "border-blue-200",
33
+ textClass: "text-gray-800",
34
34
  },
35
35
  [LogSeverity.Debug]: {
36
- badgeClass: "bg-purple-900/50 text-purple-100 ring-purple-500/30",
36
+ badgeClass: "bg-purple-50 text-purple-700 ring-purple-200",
37
37
  dotClass: "bg-purple-400",
38
- borderClass: "border-purple-500/30",
39
- textClass: "text-purple-100",
38
+ borderClass: "border-purple-200",
39
+ textClass: "text-gray-800",
40
40
  },
41
41
  [LogSeverity.Trace]: {
42
- badgeClass: "bg-slate-900/60 text-slate-300 ring-slate-500/20",
43
- dotClass: "bg-slate-400",
44
- borderClass: "border-slate-600/40",
45
- textClass: "text-slate-200",
42
+ badgeClass: "bg-gray-50 text-gray-600 ring-gray-200",
43
+ dotClass: "bg-gray-400",
44
+ borderClass: "border-gray-200",
45
+ textClass: "text-gray-800",
46
46
  },
47
47
  };
48
48
 
49
49
  const defaultTheme: SeverityTheme = {
50
- badgeClass: "bg-slate-800/60 text-slate-300 ring-slate-600/20",
51
- dotClass: "bg-slate-500",
52
- borderClass: "border-slate-700",
53
- textClass: "text-slate-200",
50
+ badgeClass: "bg-gray-50 text-gray-600 ring-gray-200",
51
+ dotClass: "bg-gray-400",
52
+ borderClass: "border-gray-200",
53
+ textClass: "text-gray-800",
54
54
  };
55
55
 
56
56
  export const getSeverityTheme: (