@datalayer/core 1.0.1 → 1.0.3
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/README.md +1 -1
- package/lib/api/constants.d.ts +3 -0
- package/lib/api/constants.js +3 -0
- package/lib/api/index.d.ts +1 -0
- package/lib/api/index.js +1 -0
- package/lib/api/otel/index.d.ts +12 -0
- package/lib/api/otel/index.js +16 -0
- package/lib/api/otel/logs.d.ts +19 -0
- package/lib/api/otel/logs.js +43 -0
- package/lib/api/otel/metrics.d.ts +31 -0
- package/lib/api/otel/metrics.js +65 -0
- package/lib/api/otel/query.d.ts +16 -0
- package/lib/api/otel/query.js +37 -0
- package/lib/api/otel/services.d.ts +39 -0
- package/lib/api/otel/services.js +81 -0
- package/lib/api/otel/traces.d.ts +24 -0
- package/lib/api/otel/traces.js +53 -0
- package/lib/api/otel/types.d.ts +112 -0
- package/lib/api/otel/types.js +5 -0
- package/lib/api/spacer/index.d.ts +1 -2
- package/lib/api/spacer/index.js +1 -2
- package/lib/components/avatars/BoringAvatar.d.ts +3 -1
- package/lib/components/avatars/BoringAvatar.js +15 -14
- package/lib/components/avatars/BoringAvatar.stories.d.ts +2 -1
- package/lib/components/storage/ContentsBrowser.d.ts +6 -0
- package/lib/components/storage/ContentsBrowser.js +7 -8
- package/lib/config/Configuration.d.ts +4 -0
- package/lib/hooks/index.d.ts +2 -0
- package/lib/hooks/index.js +2 -0
- package/lib/hooks/useCache.d.ts +16 -40
- package/lib/hooks/useCache.js +28 -233
- package/lib/hooks/useProjectStore.d.ts +58 -0
- package/lib/hooks/useProjectStore.js +64 -0
- package/lib/hooks/useProjects.d.ts +590 -0
- package/lib/hooks/useProjects.js +166 -0
- package/lib/index.d.ts +2 -1
- package/lib/index.js +4 -2
- package/lib/models/Page.d.ts +2 -0
- package/lib/otel/OtelLive.d.ts +12 -0
- package/lib/otel/OtelLive.js +354 -0
- package/lib/otel/OtelLogsList.d.ts +11 -0
- package/lib/otel/OtelLogsList.js +137 -0
- package/lib/otel/OtelMetricsChart.d.ts +22 -0
- package/lib/otel/OtelMetricsChart.js +300 -0
- package/lib/otel/OtelMetricsList.d.ts +15 -0
- package/lib/otel/OtelMetricsList.js +213 -0
- package/lib/otel/OtelSearchBar.d.ts +11 -0
- package/lib/otel/OtelSearchBar.js +22 -0
- package/lib/otel/OtelSpanDetail.d.ts +11 -0
- package/lib/otel/OtelSpanDetail.js +172 -0
- package/lib/otel/OtelSpanTree.d.ts +11 -0
- package/lib/otel/OtelSpanTree.js +176 -0
- package/lib/otel/OtelSqlView.d.ts +16 -0
- package/lib/otel/OtelSqlView.js +239 -0
- package/lib/otel/OtelSystemView.d.ts +15 -0
- package/lib/otel/OtelSystemView.js +75 -0
- package/lib/otel/OtelTimeline.d.ts +11 -0
- package/lib/otel/OtelTimeline.js +101 -0
- package/lib/otel/OtelTimelineRangeSlider.d.ts +16 -0
- package/lib/otel/OtelTimelineRangeSlider.js +338 -0
- package/lib/otel/OtelTracesList.d.ts +13 -0
- package/lib/otel/OtelTracesList.js +199 -0
- package/lib/otel/hooks.d.ts +172 -0
- package/lib/otel/hooks.js +490 -0
- package/lib/otel/index.d.ts +25 -0
- package/lib/otel/index.js +19 -0
- package/lib/otel/types.d.ts +190 -0
- package/lib/otel/types.js +5 -0
- package/lib/otel/utils.d.ts +33 -0
- package/lib/otel/utils.js +181 -0
- package/lib/state/storage/IAMStorage.d.ts +2 -1
- package/lib/state/substates/CoreState.js +1 -0
- package/lib/utils/Jwt.d.ts +42 -0
- package/lib/utils/Jwt.js +44 -0
- package/lib/utils/index.d.ts +1 -0
- package/lib/utils/index.js +1 -0
- package/lib/views/iam/SignInSimple.d.ts +38 -0
- package/lib/views/iam/SignInSimple.js +80 -0
- package/lib/views/iam/index.d.ts +2 -0
- package/lib/views/iam/index.js +5 -0
- package/lib/views/iam-tokens/IAMTokenEdit.js +53 -4
- package/lib/views/iam-tokens/IAMTokens.js +65 -33
- package/lib/views/iam-tokens/Tokens.js +64 -32
- package/lib/views/index.d.ts +2 -1
- package/lib/views/index.js +2 -1
- package/lib/views/profile/UserBadge.d.ts +18 -0
- package/lib/views/profile/UserBadge.js +101 -0
- package/lib/views/profile/index.d.ts +2 -0
- package/lib/views/profile/index.js +5 -0
- package/lib/views/secrets/Secrets.js +1 -1
- package/package.json +27 -3
- package/lib/api/spacer/agentSpaces.d.ts +0 -193
- package/lib/api/spacer/agentSpaces.js +0 -127
- package/lib/theme/DatalayerTheme.d.ts +0 -52
- package/lib/theme/DatalayerTheme.js +0 -228
- package/lib/theme/DatalayerThemeProvider.d.ts +0 -29
- package/lib/theme/DatalayerThemeProvider.js +0 -54
- package/lib/theme/Palette.d.ts +0 -4
- package/lib/theme/Palette.js +0 -10
- package/lib/theme/index.d.ts +0 -4
- package/lib/theme/index.js +0 -8
- package/lib/theme/useSystemColorMode.d.ts +0 -9
- package/lib/theme/useSystemColorMode.js +0 -26
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (c) 2023-2025 Datalayer, Inc.
|
|
4
|
+
* Distributed under the terms of the Modified BSD License.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* OtelLogsList – Tabular log-records view with severity colour coding,
|
|
8
|
+
* expandable body/attributes, and trace correlation links.
|
|
9
|
+
*
|
|
10
|
+
* Uses Primer React components for consistent theming.
|
|
11
|
+
*
|
|
12
|
+
* @module otel/OtelLogsList
|
|
13
|
+
*/
|
|
14
|
+
import React, { useState } from 'react';
|
|
15
|
+
import { Box, Text, Label, Spinner } from '@primer/react';
|
|
16
|
+
import { Blankslate } from '@primer/react/experimental';
|
|
17
|
+
import { LogIcon } from '@primer/octicons-react';
|
|
18
|
+
import { formatTime, severityVariant } from './utils';
|
|
19
|
+
// ── helpers ─────────────────────────────────────────────────────────
|
|
20
|
+
/** Severity badge using Primer Label, centered in its grid cell. */
|
|
21
|
+
const Severity = ({ text }) => (_jsx(Box, { sx: { display: 'flex', alignItems: 'center', justifyContent: 'center' }, children: _jsx(Label, { size: "small", variant: severityVariant(text), children: text }) }));
|
|
22
|
+
/** Expandable row detail for a single log record. */
|
|
23
|
+
const LogDetail = ({ log }) => (_jsxs(Box, { sx: {
|
|
24
|
+
gridColumn: '1 / -1',
|
|
25
|
+
bg: 'canvas.subtle',
|
|
26
|
+
borderBottom: '1px solid',
|
|
27
|
+
borderColor: 'border.default',
|
|
28
|
+
p: 3,
|
|
29
|
+
}, children: [_jsxs(Box, { sx: { mb: 2 }, children: [_jsx(Text, { sx: {
|
|
30
|
+
fontSize: 0,
|
|
31
|
+
fontWeight: 'bold',
|
|
32
|
+
color: 'fg.muted',
|
|
33
|
+
display: 'block',
|
|
34
|
+
mb: 1,
|
|
35
|
+
}, children: "Body" }), _jsx(Box, { as: "pre", sx: {
|
|
36
|
+
fontSize: 1,
|
|
37
|
+
fontFamily: 'mono',
|
|
38
|
+
whiteSpace: 'pre-wrap',
|
|
39
|
+
wordBreak: 'break-word',
|
|
40
|
+
m: 0,
|
|
41
|
+
bg: 'canvas.default',
|
|
42
|
+
border: '1px solid',
|
|
43
|
+
borderColor: 'border.default',
|
|
44
|
+
borderRadius: 2,
|
|
45
|
+
p: 2,
|
|
46
|
+
}, children: log.body })] }), log.trace_id && (_jsxs(Box, { sx: { display: 'flex', gap: 3, mb: 2 }, children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted', fontWeight: 'bold' }, children: "trace_id" }), _jsx(Text, { sx: { fontFamily: 'mono', fontSize: 0 }, children: log.trace_id }), log.span_id && (_jsxs(_Fragment, { children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted', fontWeight: 'bold' }, children: "span_id" }), _jsx(Text, { sx: { fontFamily: 'mono', fontSize: 0 }, children: log.span_id })] }))] })), log.attributes && Object.keys(log.attributes).length > 0 && (_jsxs(Box, { children: [_jsx(Text, { sx: {
|
|
47
|
+
fontSize: 0,
|
|
48
|
+
fontWeight: 'bold',
|
|
49
|
+
color: 'fg.muted',
|
|
50
|
+
display: 'block',
|
|
51
|
+
mb: 1,
|
|
52
|
+
}, children: "Attributes" }), _jsx(Box, { sx: {
|
|
53
|
+
bg: 'canvas.default',
|
|
54
|
+
border: '1px solid',
|
|
55
|
+
borderColor: 'border.default',
|
|
56
|
+
borderRadius: 2,
|
|
57
|
+
p: 2,
|
|
58
|
+
}, children: Object.entries(log.attributes).map(([k, v]) => (_jsxs(Box, { sx: {
|
|
59
|
+
display: 'flex',
|
|
60
|
+
gap: 2,
|
|
61
|
+
py: 1,
|
|
62
|
+
borderBottom: '1px solid',
|
|
63
|
+
borderColor: 'border.muted',
|
|
64
|
+
}, children: [_jsx(Text, { sx: {
|
|
65
|
+
color: 'accent.fg',
|
|
66
|
+
fontSize: 0,
|
|
67
|
+
fontFamily: 'mono',
|
|
68
|
+
minWidth: 150,
|
|
69
|
+
}, children: k }), _jsx(Text, { sx: {
|
|
70
|
+
fontSize: 0,
|
|
71
|
+
fontFamily: 'mono',
|
|
72
|
+
wordBreak: 'break-word',
|
|
73
|
+
}, children: typeof v === 'string' ? v : JSON.stringify(v) })] }, k))) })] }))] }));
|
|
74
|
+
// ── Main component ──────────────────────────────────────────────────
|
|
75
|
+
export const OtelLogsList = ({ logs, loading, selectedLogIndex, onSelectLog, }) => {
|
|
76
|
+
const [expandedIdx, setExpandedIdx] = useState(null);
|
|
77
|
+
const colTemplate = '140px 80px 160px 1fr';
|
|
78
|
+
if (loading && logs.length === 0) {
|
|
79
|
+
return (_jsx(Box, { sx: { display: 'flex', justifyContent: 'center', p: 5 }, children: _jsx(Spinner, { size: "medium" }) }));
|
|
80
|
+
}
|
|
81
|
+
if (!loading && logs.length === 0) {
|
|
82
|
+
return (_jsxs(Blankslate, { children: [_jsx(Blankslate.Visual, { children: _jsx(LogIcon, { size: 24 }) }), _jsx(Blankslate.Heading, { children: "No log records found" }), _jsx(Blankslate.Description, { children: "Send some log data first." })] }));
|
|
83
|
+
}
|
|
84
|
+
return (_jsxs(Box, { sx: { flex: 1, minHeight: 0, overflow: 'auto' }, children: [_jsx(Box, { sx: {
|
|
85
|
+
display: 'grid',
|
|
86
|
+
gridTemplateColumns: colTemplate,
|
|
87
|
+
bg: 'canvas.subtle',
|
|
88
|
+
borderBottom: '2px solid',
|
|
89
|
+
borderColor: 'border.default',
|
|
90
|
+
px: 3,
|
|
91
|
+
py: 1,
|
|
92
|
+
position: 'sticky',
|
|
93
|
+
top: 0,
|
|
94
|
+
zIndex: 1,
|
|
95
|
+
}, children: ['Time', 'Severity', 'Service', 'Body'].map(h => (_jsx(Text, { sx: {
|
|
96
|
+
fontSize: 0,
|
|
97
|
+
fontWeight: 'bold',
|
|
98
|
+
color: 'fg.muted',
|
|
99
|
+
textTransform: 'uppercase',
|
|
100
|
+
letterSpacing: '0.05em',
|
|
101
|
+
textAlign: h === 'Severity' ? 'center' : undefined,
|
|
102
|
+
}, children: h }, h))) }), logs.map((log, idx) => {
|
|
103
|
+
const selected = idx === selectedLogIndex;
|
|
104
|
+
const expanded = idx === expandedIdx;
|
|
105
|
+
return (_jsxs(React.Fragment, { children: [_jsxs(Box, { sx: {
|
|
106
|
+
display: 'grid',
|
|
107
|
+
gridTemplateColumns: colTemplate,
|
|
108
|
+
px: 3,
|
|
109
|
+
py: '5px',
|
|
110
|
+
borderBottom: '1px solid',
|
|
111
|
+
borderColor: 'border.muted',
|
|
112
|
+
cursor: 'pointer',
|
|
113
|
+
bg: selected
|
|
114
|
+
? 'accent.subtle'
|
|
115
|
+
: expanded
|
|
116
|
+
? 'canvas.subtle'
|
|
117
|
+
: 'canvas.default',
|
|
118
|
+
':hover': {
|
|
119
|
+
bg: selected || expanded ? undefined : 'canvas.subtle',
|
|
120
|
+
},
|
|
121
|
+
}, onClick: () => {
|
|
122
|
+
setExpandedIdx(expanded ? null : idx);
|
|
123
|
+
onSelectLog?.(log, idx);
|
|
124
|
+
}, children: [_jsx(Text, { sx: { fontSize: 1, fontFamily: 'mono', color: 'fg.default' }, children: formatTime(log.timestamp) }), _jsx(Severity, { text: log.severity_text }), _jsx(Text, { sx: {
|
|
125
|
+
fontSize: 1,
|
|
126
|
+
color: 'fg.muted',
|
|
127
|
+
overflow: 'hidden',
|
|
128
|
+
textOverflow: 'ellipsis',
|
|
129
|
+
whiteSpace: 'nowrap',
|
|
130
|
+
}, children: log.service_name }), _jsx(Text, { sx: {
|
|
131
|
+
fontSize: 1,
|
|
132
|
+
overflow: 'hidden',
|
|
133
|
+
textOverflow: 'ellipsis',
|
|
134
|
+
whiteSpace: 'nowrap',
|
|
135
|
+
}, children: log.body })] }), expanded && _jsx(LogDetail, { log: log })] }, idx));
|
|
136
|
+
})] }));
|
|
137
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OtelMetricsChart – Type-aware ECharts visualisation for OTEL metrics.
|
|
3
|
+
*
|
|
4
|
+
* Renders one chart section per `metric_type` (`sum`, `histogram`, `gauge`,
|
|
5
|
+
* and any other type present) with an appropriate chart style:
|
|
6
|
+
*
|
|
7
|
+
* - **sum** (counters): area-line chart with gradient fill
|
|
8
|
+
* - **histogram**: bar chart with grouped series
|
|
9
|
+
* - **gauge**: plain line chart (no area fill)
|
|
10
|
+
* - **other**: falls back to area-line
|
|
11
|
+
*
|
|
12
|
+
* @module otel/OtelMetricsChart
|
|
13
|
+
*/
|
|
14
|
+
import React from 'react';
|
|
15
|
+
import type { OtelMetric } from './types';
|
|
16
|
+
export interface OtelMetricsChartProps {
|
|
17
|
+
metrics: OtelMetric[];
|
|
18
|
+
/** Height per chart panel in px. Default 240. */
|
|
19
|
+
height?: number;
|
|
20
|
+
}
|
|
21
|
+
export declare const OtelMetricsChart: React.FC<OtelMetricsChartProps>;
|
|
22
|
+
export default OtelMetricsChart;
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as echarts from 'echarts';
|
|
3
|
+
import ReactECharts from 'echarts-for-react';
|
|
4
|
+
import { Box, Text, Label } from '@primer/react';
|
|
5
|
+
// ── Gradient palette ────────────────────────────────────────────────
|
|
6
|
+
const SERIES_COLORS = [
|
|
7
|
+
{
|
|
8
|
+
line: '#5470C6',
|
|
9
|
+
gradientStart: 'rgba(84,112,198,0.7)',
|
|
10
|
+
gradientEnd: 'rgba(84,112,198,0.02)',
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
line: '#91CC75',
|
|
14
|
+
gradientStart: 'rgba(145,204,117,0.7)',
|
|
15
|
+
gradientEnd: 'rgba(145,204,117,0.02)',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
line: '#EE6666',
|
|
19
|
+
gradientStart: 'rgba(238,102,102,0.7)',
|
|
20
|
+
gradientEnd: 'rgba(238,102,102,0.02)',
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
line: '#FAC858',
|
|
24
|
+
gradientStart: 'rgba(250,200,88,0.7)',
|
|
25
|
+
gradientEnd: 'rgba(250,200,88,0.02)',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
line: '#73C0DE',
|
|
29
|
+
gradientStart: 'rgba(115,192,222,0.7)',
|
|
30
|
+
gradientEnd: 'rgba(115,192,222,0.02)',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
line: '#FC8452',
|
|
34
|
+
gradientStart: 'rgba(252,132,82,0.7)',
|
|
35
|
+
gradientEnd: 'rgba(252,132,82,0.02)',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
line: '#9A60B4',
|
|
39
|
+
gradientStart: 'rgba(154,96,180,0.7)',
|
|
40
|
+
gradientEnd: 'rgba(154,96,180,0.02)',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
line: '#EA7CCC',
|
|
44
|
+
gradientStart: 'rgba(234,124,204,0.7)',
|
|
45
|
+
gradientEnd: 'rgba(234,124,204,0.02)',
|
|
46
|
+
},
|
|
47
|
+
];
|
|
48
|
+
// ── Helpers ─────────────────────────────────────────────────────────
|
|
49
|
+
/** Group metrics by metric_type, then by metric_name within each type. */
|
|
50
|
+
function groupByType(metrics) {
|
|
51
|
+
const byType = new Map();
|
|
52
|
+
for (const m of metrics) {
|
|
53
|
+
const mtype = m.metric_type || 'other';
|
|
54
|
+
if (!byType.has(mtype))
|
|
55
|
+
byType.set(mtype, new Map());
|
|
56
|
+
const nameMap = byType.get(mtype);
|
|
57
|
+
const key = m.metric_name || '(unnamed)';
|
|
58
|
+
if (!nameMap.has(key))
|
|
59
|
+
nameMap.set(key, []);
|
|
60
|
+
nameMap.get(key).push({
|
|
61
|
+
ts: new Date(m.timestamp).getTime(),
|
|
62
|
+
value: m.value,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
// Sort points chronologically within each name group.
|
|
66
|
+
for (const nameMap of byType.values()) {
|
|
67
|
+
for (const points of nameMap.values()) {
|
|
68
|
+
points.sort((a, b) => a.ts - b.ts);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return byType;
|
|
72
|
+
}
|
|
73
|
+
/** Human-friendly label for a metric type. */
|
|
74
|
+
function typeTitle(mtype) {
|
|
75
|
+
switch (mtype) {
|
|
76
|
+
case 'sum':
|
|
77
|
+
return 'Counters (sum)';
|
|
78
|
+
case 'histogram':
|
|
79
|
+
return 'Histograms';
|
|
80
|
+
case 'gauge':
|
|
81
|
+
return 'Gauges';
|
|
82
|
+
case 'exponentialHistogram':
|
|
83
|
+
return 'Exponential Histograms';
|
|
84
|
+
default:
|
|
85
|
+
return mtype.charAt(0).toUpperCase() + mtype.slice(1);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/** Label variant for the type badge. */
|
|
89
|
+
function typeVariant(mtype) {
|
|
90
|
+
switch (mtype) {
|
|
91
|
+
case 'gauge':
|
|
92
|
+
return 'accent';
|
|
93
|
+
case 'sum':
|
|
94
|
+
return 'attention';
|
|
95
|
+
case 'histogram':
|
|
96
|
+
return 'secondary';
|
|
97
|
+
case 'exponentialHistogram':
|
|
98
|
+
return 'success';
|
|
99
|
+
default:
|
|
100
|
+
return 'primary';
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/** Preferred display order. Types not listed appear at the end. */
|
|
104
|
+
const TYPE_ORDER = {
|
|
105
|
+
sum: 0,
|
|
106
|
+
histogram: 1,
|
|
107
|
+
gauge: 2,
|
|
108
|
+
exponentialHistogram: 3,
|
|
109
|
+
};
|
|
110
|
+
// ── Build ECharts option per type ──────────────────────────────────
|
|
111
|
+
function buildOption(mtype, nameMap) {
|
|
112
|
+
const names = [...nameMap.keys()];
|
|
113
|
+
const baseLegend = {
|
|
114
|
+
data: names,
|
|
115
|
+
top: 6,
|
|
116
|
+
textStyle: { fontSize: 11 },
|
|
117
|
+
};
|
|
118
|
+
const baseGrid = { left: 50, right: 20, top: 40, bottom: 30 };
|
|
119
|
+
const baseTooltip = {
|
|
120
|
+
trigger: 'axis',
|
|
121
|
+
axisPointer: { type: 'cross', label: { backgroundColor: '#6a7985' } },
|
|
122
|
+
};
|
|
123
|
+
const baseXAxis = {
|
|
124
|
+
type: 'time',
|
|
125
|
+
boundaryGap: false,
|
|
126
|
+
axisLine: { lineStyle: { color: '#ccc' } },
|
|
127
|
+
axisLabel: { fontSize: 10 },
|
|
128
|
+
};
|
|
129
|
+
const baseYAxis = {
|
|
130
|
+
type: 'value',
|
|
131
|
+
splitLine: { lineStyle: { type: 'dashed', color: '#e8e8e8' } },
|
|
132
|
+
axisLine: { show: false },
|
|
133
|
+
axisLabel: { fontSize: 10 },
|
|
134
|
+
};
|
|
135
|
+
if (mtype === 'histogram' || mtype === 'exponentialHistogram') {
|
|
136
|
+
// ── Bar chart ──
|
|
137
|
+
const series = names.map((name, idx) => {
|
|
138
|
+
const palette = SERIES_COLORS[idx % SERIES_COLORS.length];
|
|
139
|
+
const points = nameMap.get(name) ?? [];
|
|
140
|
+
return {
|
|
141
|
+
name,
|
|
142
|
+
type: 'bar',
|
|
143
|
+
barMaxWidth: 20,
|
|
144
|
+
itemStyle: {
|
|
145
|
+
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
146
|
+
{ offset: 0, color: palette.gradientStart },
|
|
147
|
+
{ offset: 1, color: palette.line },
|
|
148
|
+
]),
|
|
149
|
+
borderRadius: [2, 2, 0, 0],
|
|
150
|
+
},
|
|
151
|
+
emphasis: { focus: 'series' },
|
|
152
|
+
data: points.map(p => [p.ts, p.value]),
|
|
153
|
+
};
|
|
154
|
+
});
|
|
155
|
+
return {
|
|
156
|
+
tooltip: baseTooltip,
|
|
157
|
+
legend: baseLegend,
|
|
158
|
+
grid: baseGrid,
|
|
159
|
+
xAxis: { ...baseXAxis, boundaryGap: true },
|
|
160
|
+
yAxis: baseYAxis,
|
|
161
|
+
series,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
if (mtype === 'gauge') {
|
|
165
|
+
// ── Simple line chart (no area) ──
|
|
166
|
+
const series = names.map((name, idx) => {
|
|
167
|
+
const palette = SERIES_COLORS[idx % SERIES_COLORS.length];
|
|
168
|
+
const points = nameMap.get(name) ?? [];
|
|
169
|
+
return {
|
|
170
|
+
name,
|
|
171
|
+
type: 'line',
|
|
172
|
+
smooth: true,
|
|
173
|
+
symbol: 'circle',
|
|
174
|
+
symbolSize: 5,
|
|
175
|
+
showSymbol: true,
|
|
176
|
+
lineStyle: { width: 2, color: palette.line },
|
|
177
|
+
itemStyle: { color: palette.line },
|
|
178
|
+
emphasis: { focus: 'series' },
|
|
179
|
+
data: points.map(p => [p.ts, p.value]),
|
|
180
|
+
};
|
|
181
|
+
});
|
|
182
|
+
return {
|
|
183
|
+
tooltip: baseTooltip,
|
|
184
|
+
legend: baseLegend,
|
|
185
|
+
grid: baseGrid,
|
|
186
|
+
xAxis: baseXAxis,
|
|
187
|
+
yAxis: baseYAxis,
|
|
188
|
+
series,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
// ── Default (sum / other): area-line with gradient ──
|
|
192
|
+
const series = names.map((name, idx) => {
|
|
193
|
+
const palette = SERIES_COLORS[idx % SERIES_COLORS.length];
|
|
194
|
+
const points = nameMap.get(name) ?? [];
|
|
195
|
+
return {
|
|
196
|
+
name,
|
|
197
|
+
type: 'line',
|
|
198
|
+
smooth: true,
|
|
199
|
+
symbol: 'circle',
|
|
200
|
+
symbolSize: 5,
|
|
201
|
+
showSymbol: false,
|
|
202
|
+
lineStyle: { width: 2, color: palette.line },
|
|
203
|
+
itemStyle: { color: palette.line },
|
|
204
|
+
areaStyle: {
|
|
205
|
+
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
206
|
+
{ offset: 0, color: palette.gradientStart },
|
|
207
|
+
{ offset: 1, color: palette.gradientEnd },
|
|
208
|
+
]),
|
|
209
|
+
},
|
|
210
|
+
emphasis: { focus: 'series' },
|
|
211
|
+
data: points.map(p => [p.ts, p.value]),
|
|
212
|
+
};
|
|
213
|
+
});
|
|
214
|
+
return {
|
|
215
|
+
tooltip: baseTooltip,
|
|
216
|
+
legend: baseLegend,
|
|
217
|
+
grid: baseGrid,
|
|
218
|
+
xAxis: baseXAxis,
|
|
219
|
+
yAxis: baseYAxis,
|
|
220
|
+
series,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
function computeStats(nameMap) {
|
|
224
|
+
let total = 0;
|
|
225
|
+
let count = 0;
|
|
226
|
+
let startTs = null;
|
|
227
|
+
let endTs = null;
|
|
228
|
+
for (const points of nameMap.values()) {
|
|
229
|
+
for (const p of points) {
|
|
230
|
+
total += p.value;
|
|
231
|
+
count += 1;
|
|
232
|
+
if (startTs === null || p.ts < startTs)
|
|
233
|
+
startTs = p.ts;
|
|
234
|
+
if (endTs === null || p.ts > endTs)
|
|
235
|
+
endTs = p.ts;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return { total, count, startTs, endTs };
|
|
239
|
+
}
|
|
240
|
+
function formatTs(ts) {
|
|
241
|
+
return new Date(ts).toLocaleTimeString(undefined, {
|
|
242
|
+
hour: '2-digit',
|
|
243
|
+
minute: '2-digit',
|
|
244
|
+
second: '2-digit',
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
function formatDuration(ms) {
|
|
248
|
+
if (ms < 1000)
|
|
249
|
+
return `${ms}ms`;
|
|
250
|
+
const s = Math.round(ms / 1000);
|
|
251
|
+
if (s < 60)
|
|
252
|
+
return `${s}s`;
|
|
253
|
+
const m = Math.floor(s / 60);
|
|
254
|
+
const rem = s % 60;
|
|
255
|
+
return rem > 0 ? `${m}m ${rem}s` : `${m}m`;
|
|
256
|
+
}
|
|
257
|
+
// ── Component ───────────────────────────────────────────────────────
|
|
258
|
+
export const OtelMetricsChart = ({ metrics, height = 240, }) => {
|
|
259
|
+
if (!metrics || metrics.length === 0) {
|
|
260
|
+
return (_jsx(ReactECharts, { echarts: echarts, option: {
|
|
261
|
+
title: {
|
|
262
|
+
text: 'No metrics data',
|
|
263
|
+
left: 'center',
|
|
264
|
+
top: 'center',
|
|
265
|
+
textStyle: { color: '#999', fontSize: 14, fontWeight: 'normal' },
|
|
266
|
+
},
|
|
267
|
+
}, style: { width: '100%', height: `${height}px` }, notMerge: true }));
|
|
268
|
+
}
|
|
269
|
+
const byType = groupByType(metrics);
|
|
270
|
+
// Sort types in a stable, user-friendly order.
|
|
271
|
+
const sortedTypes = [...byType.keys()].sort((a, b) => (TYPE_ORDER[a] ?? 99) - (TYPE_ORDER[b] ?? 99));
|
|
272
|
+
return (_jsx(Box, { sx: {
|
|
273
|
+
display: 'grid',
|
|
274
|
+
gridTemplateColumns: '1fr 1fr',
|
|
275
|
+
gap: 2,
|
|
276
|
+
}, children: sortedTypes.map(mtype => {
|
|
277
|
+
const nameMap = byType.get(mtype);
|
|
278
|
+
const option = buildOption(mtype, nameMap);
|
|
279
|
+
const stats = computeStats(nameMap);
|
|
280
|
+
const durationMs = stats.startTs !== null && stats.endTs !== null
|
|
281
|
+
? stats.endTs - stats.startTs
|
|
282
|
+
: null;
|
|
283
|
+
return (_jsxs(Box, { children: [_jsxs(Box, { sx: {
|
|
284
|
+
display: 'flex',
|
|
285
|
+
alignItems: 'center',
|
|
286
|
+
gap: 2,
|
|
287
|
+
px: 1,
|
|
288
|
+
mb: 1,
|
|
289
|
+
}, children: [_jsx(Label, { size: "small", variant: typeVariant(mtype), children: mtype }), _jsx(Text, { sx: { fontSize: 1, fontWeight: 'bold', color: 'fg.muted' }, children: typeTitle(mtype) }), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.subtle' }, children: ["(", [...nameMap.keys()].join(', '), ")"] })] }), _jsxs(Box, { sx: {
|
|
290
|
+
display: 'flex',
|
|
291
|
+
alignItems: 'center',
|
|
292
|
+
gap: 3,
|
|
293
|
+
px: 1,
|
|
294
|
+
mb: 1,
|
|
295
|
+
}, children: [_jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: [_jsx(Text, { as: "span", sx: { fontWeight: 'bold', color: 'fg.default' }, children: "Sum:" }), ' ', stats.total.toLocaleString(undefined, {
|
|
296
|
+
maximumFractionDigits: 4,
|
|
297
|
+
})] }), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: [_jsx(Text, { as: "span", sx: { fontWeight: 'bold', color: 'fg.default' }, children: "Points:" }), ' ', stats.count] }), stats.startTs !== null && stats.endTs !== null && (_jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: [_jsx(Text, { as: "span", sx: { fontWeight: 'bold', color: 'fg.default' }, children: "Interval:" }), ' ', formatTs(stats.startTs), " \u2192 ", formatTs(stats.endTs), durationMs !== null && durationMs > 0 && (_jsxs(Text, { as: "span", sx: { color: 'fg.subtle' }, children: [' ', "(", formatDuration(durationMs), ")"] }))] }))] }), _jsx(ReactECharts, { echarts: echarts, option: option, style: { width: '100%', height: `${height}px` }, notMerge: true })] }, mtype));
|
|
298
|
+
}) }));
|
|
299
|
+
};
|
|
300
|
+
export default OtelMetricsChart;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OtelMetricsList – Tabular view of metric data points grouped by
|
|
3
|
+
* metric name, with expandable rows showing individual data points.
|
|
4
|
+
*
|
|
5
|
+
* Uses Primer React components for consistent theming.
|
|
6
|
+
*
|
|
7
|
+
* @module otel/OtelMetricsList
|
|
8
|
+
*/
|
|
9
|
+
import React from 'react';
|
|
10
|
+
import type { OtelMetric } from './types';
|
|
11
|
+
export interface OtelMetricsListProps {
|
|
12
|
+
metrics: OtelMetric[];
|
|
13
|
+
loading?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare const OtelMetricsList: React.FC<OtelMetricsListProps>;
|