TraceView: Display event names of a span (#91382)
* Display event name of a span * Clean up * Retrigger the build * Show colon only when there are fields to display * Rollback * Use event name when exporting to OTLP * Allow filtering spans by event name * Show duration as a key/value pair * Update betterer report (we do not translate panels that are planned to be externalized) * Fix tests after changing how duration is rendered * Handle long names * Test handling long names * Make parenthesis gray * Fix a test * Fix linting * Fix tests * Update label
This commit is contained in:
+3
-1
@@ -4096,7 +4096,9 @@ exports[`better eslint`] = {
|
|||||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "4"],
|
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "4"],
|
||||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "5"],
|
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "5"],
|
||||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "6"],
|
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "6"],
|
||||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "7"]
|
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "7"],
|
||||||
|
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "8"],
|
||||||
|
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "9"]
|
||||||
],
|
],
|
||||||
"public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianReferences.tsx:5381": [
|
"public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianReferences.tsx:5381": [
|
||||||
[0, 0, 0, "Styles should be written using objects.", "0"],
|
[0, 0, 0, "Styles should be written using objects.", "0"],
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export type TraceLog = {
|
|||||||
// Millisecond epoch time
|
// Millisecond epoch time
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
fields: TraceKeyValuePair[];
|
fields: TraceKeyValuePair[];
|
||||||
|
name?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TraceSpanReference = {
|
export type TraceSpanReference = {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ type TraceLog struct {
|
|||||||
// Millisecond epoch time
|
// Millisecond epoch time
|
||||||
Timestamp float64 `json:"timestamp"`
|
Timestamp float64 `json:"timestamp"`
|
||||||
Fields []*KeyValue `json:"fields"`
|
Fields []*KeyValue `json:"fields"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type TraceReference struct {
|
type TraceReference struct {
|
||||||
@@ -260,12 +261,6 @@ func spanEventsToLogs(events ptrace.SpanEventSlice) []*TraceLog {
|
|||||||
for i := 0; i < events.Len(); i++ {
|
for i := 0; i < events.Len(); i++ {
|
||||||
event := events.At(i)
|
event := events.At(i)
|
||||||
fields := make([]*KeyValue, 0, event.Attributes().Len()+1)
|
fields := make([]*KeyValue, 0, event.Attributes().Len()+1)
|
||||||
if event.Name() != "" {
|
|
||||||
fields = append(fields, &KeyValue{
|
|
||||||
Key: TagMessage,
|
|
||||||
Value: event.Name(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
event.Attributes().Range(func(key string, attr pcommon.Value) bool {
|
event.Attributes().Range(func(key string, attr pcommon.Value) bool {
|
||||||
fields = append(fields, &KeyValue{Key: key, Value: getAttributeVal(attr)})
|
fields = append(fields, &KeyValue{Key: key, Value: getAttributeVal(attr)})
|
||||||
return true
|
return true
|
||||||
@@ -273,6 +268,7 @@ func spanEventsToLogs(events ptrace.SpanEventSlice) []*TraceLog {
|
|||||||
logs = append(logs, &TraceLog{
|
logs = append(logs, &TraceLog{
|
||||||
Timestamp: float64(event.Timestamp()) / 1_000_000,
|
Timestamp: float64(event.Timestamp()) / 1_000_000,
|
||||||
Fields: fields,
|
Fields: fields,
|
||||||
|
Name: event.Name(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"go.opentelemetry.io/collector/pdata/ptrace"
|
"go.opentelemetry.io/collector/pdata/ptrace"
|
||||||
|
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -57,7 +58,30 @@ func TestTraceToFrame(t *testing.T) {
|
|||||||
require.Equal(t, json.RawMessage("[{\"value\":\"loki-all\",\"key\":\"service.name\"},{\"value\":\"Jaeger-Go-2.25.0\",\"key\":\"opencensus.exporterversion\"},{\"value\":\"4d019a031941\",\"key\":\"host.hostname\"},{\"value\":\"172.18.0.6\",\"key\":\"ip\"},{\"value\":\"4b19ace06df8e4de\",\"key\":\"client-uuid\"}]"), span["serviceTags"])
|
require.Equal(t, json.RawMessage("[{\"value\":\"loki-all\",\"key\":\"service.name\"},{\"value\":\"Jaeger-Go-2.25.0\",\"key\":\"opencensus.exporterversion\"},{\"value\":\"4d019a031941\",\"key\":\"host.hostname\"},{\"value\":\"172.18.0.6\",\"key\":\"ip\"},{\"value\":\"4b19ace06df8e4de\",\"key\":\"client-uuid\"}]"), span["serviceTags"])
|
||||||
require.Equal(t, 1616072924072.852, span["startTime"])
|
require.Equal(t, 1616072924072.852, span["startTime"])
|
||||||
require.Equal(t, 0.094, span["duration"])
|
require.Equal(t, 0.094, span["duration"])
|
||||||
require.Equal(t, "[{\"timestamp\":1616072924072.856,\"fields\":[{\"value\":\"test event\",\"key\":\"message\"},{\"value\":1,\"key\":\"chunks requested\"}]},{\"timestamp\":1616072924072.9448,\"fields\":[{\"value\":1,\"key\":\"chunks fetched\"}]}]", string(span["logs"].(json.RawMessage)))
|
expectedLogs := `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"timestamp": 1616072924072.856,
|
||||||
|
"name": "test event",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"value": 1,
|
||||||
|
"key": "chunks requested"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1616072924072.9448,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"value": 1,
|
||||||
|
"key": "chunks fetched"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
`
|
||||||
|
assert.JSONEq(t, expectedLogs, string(span["logs"].(json.RawMessage)))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("should transform correct traceID", func(t *testing.T) {
|
t.Run("should transform correct traceID", func(t *testing.T) {
|
||||||
|
|||||||
+14
-6
@@ -67,7 +67,6 @@ export const getStyles = (theme: GrafanaTheme2) => {
|
|||||||
summaryItem: css`
|
summaryItem: css`
|
||||||
label: summaryItem;
|
label: summaryItem;
|
||||||
display: inline;
|
display: inline;
|
||||||
margin-left: 0.7em;
|
|
||||||
padding-right: 0.5rem;
|
padding-right: 0.5rem;
|
||||||
border-right: 1px solid ${autoColor(theme, '#ddd')};
|
border-right: 1px solid ${autoColor(theme, '#ddd')};
|
||||||
&:last-child {
|
&:last-child {
|
||||||
@@ -90,10 +89,11 @@ export const getStyles = (theme: GrafanaTheme2) => {
|
|||||||
export type AccordianKeyValuesProps = {
|
export type AccordianKeyValuesProps = {
|
||||||
className?: string | TNil;
|
className?: string | TNil;
|
||||||
data: TraceKeyValuePair[];
|
data: TraceKeyValuePair[];
|
||||||
|
logName?: string;
|
||||||
highContrast?: boolean;
|
highContrast?: boolean;
|
||||||
interactive?: boolean;
|
interactive?: boolean;
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
label: string;
|
label: string | React.ReactNode;
|
||||||
linksGetter?: ((pairs: TraceKeyValuePair[], index: number) => TraceLink[]) | TNil;
|
linksGetter?: ((pairs: TraceKeyValuePair[], index: number) => TraceLink[]) | TNil;
|
||||||
onToggle?: null | (() => void);
|
onToggle?: null | (() => void);
|
||||||
};
|
};
|
||||||
@@ -127,6 +127,7 @@ export function KeyValuesSummary({ data = null }: KeyValuesSummaryProps) {
|
|||||||
export default function AccordianKeyValues({
|
export default function AccordianKeyValues({
|
||||||
className = null,
|
className = null,
|
||||||
data,
|
data,
|
||||||
|
logName,
|
||||||
highContrast = false,
|
highContrast = false,
|
||||||
interactive = true,
|
interactive = true,
|
||||||
isOpen,
|
isOpen,
|
||||||
@@ -134,11 +135,12 @@ export default function AccordianKeyValues({
|
|||||||
linksGetter,
|
linksGetter,
|
||||||
onToggle = null,
|
onToggle = null,
|
||||||
}: AccordianKeyValuesProps) {
|
}: AccordianKeyValuesProps) {
|
||||||
const isEmpty = !Array.isArray(data) || !data.length;
|
const isEmpty = (!Array.isArray(data) || !data.length) && !logName;
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
const iconCls = cx(alignIcon, { [styles.emptyIcon]: isEmpty });
|
const iconCls = cx(alignIcon, { [styles.emptyIcon]: isEmpty });
|
||||||
let arrow: React.ReactNode | null = null;
|
let arrow: React.ReactNode | null = null;
|
||||||
let headerProps: {} | null = null;
|
let headerProps: {} | null = null;
|
||||||
|
const tableFields = logName ? [{ key: 'event name', value: logName }, ...data] : data;
|
||||||
if (interactive) {
|
if (interactive) {
|
||||||
arrow = isOpen ? (
|
arrow = isOpen ? (
|
||||||
<Icon name={'angle-down'} className={iconCls} />
|
<Icon name={'angle-down'} className={iconCls} />
|
||||||
@@ -152,6 +154,8 @@ export default function AccordianKeyValues({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const showDataSummaryFields = data.length > 0 && !isOpen;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cx(className, styles.container)}>
|
<div className={cx(className, styles.container)}>
|
||||||
<div
|
<div
|
||||||
@@ -165,11 +169,15 @@ export default function AccordianKeyValues({
|
|||||||
{arrow}
|
{arrow}
|
||||||
<strong data-test={markers.LABEL}>
|
<strong data-test={markers.LABEL}>
|
||||||
{label}
|
{label}
|
||||||
{isOpen || ':'}
|
{showDataSummaryFields && ':'}
|
||||||
</strong>
|
</strong>
|
||||||
{!isOpen && <KeyValuesSummary data={data} />}
|
{showDataSummaryFields && (
|
||||||
|
<span className={css({ marginLeft: '0.7em' })}>
|
||||||
|
<KeyValuesSummary data={data} />
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{isOpen && <KeyValuesTable data={data} linksGetter={linksGetter} />}
|
{isOpen && <KeyValuesTable data={tableFields} linksGetter={linksGetter} />}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+44
@@ -30,6 +30,7 @@ const logs = [
|
|||||||
{ key: 'message', value: 'oh the next log message' },
|
{ key: 'message', value: 'oh the next log message' },
|
||||||
{ key: 'more', value: 'stuff' },
|
{ key: 'more', value: 'stuff' },
|
||||||
],
|
],
|
||||||
|
name: 'foo event name',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -72,4 +73,47 @@ describe('AccordianLogs tests', () => {
|
|||||||
expect(screen.getByText(/^something$/)).toBeInTheDocument();
|
expect(screen.getByText(/^something$/)).toBeInTheDocument();
|
||||||
expect(screen.getByText(/^else$/)).toBeInTheDocument();
|
expect(screen.getByText(/^else$/)).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('shows log entries and long event name when expanded', () => {
|
||||||
|
const longNameLog = {
|
||||||
|
timestamp: 20,
|
||||||
|
name: 'This is a very very very very very very very long name',
|
||||||
|
fields: [{ key: 'foo', value: 'test' }],
|
||||||
|
};
|
||||||
|
|
||||||
|
setup({
|
||||||
|
isOpen: true,
|
||||||
|
logs: [longNameLog],
|
||||||
|
openedItems: new Set([longNameLog]),
|
||||||
|
} as AccordianLogsProps);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
screen.getByRole('switch', {
|
||||||
|
name: '15μs (This is a very very ...)',
|
||||||
|
})
|
||||||
|
).toBeInTheDocument();
|
||||||
|
|
||||||
|
expect(screen.getByRole('table')).toBeInTheDocument();
|
||||||
|
expect(screen.queryAllByRole('cell')).toHaveLength(6);
|
||||||
|
expect(screen.getByText(/^event name$/)).toBeInTheDocument();
|
||||||
|
expect(screen.getByText(/This is a very very very very very very very long name/)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders event name and duration when events list is closed', () => {
|
||||||
|
setup({ isOpen: true, openedItems: new Set() } as AccordianLogsProps);
|
||||||
|
expect(
|
||||||
|
screen.getByRole('switch', {
|
||||||
|
name: '15μs (foo event name) : message = oh the next log message more = stuff',
|
||||||
|
})
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.getByRole('switch', { name: '5μs: message = oh the log message something = else' })
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders event name and duration when events list is open', () => {
|
||||||
|
setup({ isOpen: true, openedItems: new Set(logs) } as AccordianLogsProps);
|
||||||
|
expect(screen.getByRole('switch', { name: '15μs (foo event name)' })).toBeInTheDocument();
|
||||||
|
expect(screen.getByRole('switch', { name: '5μs' })).toBeInTheDocument();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+31
-15
@@ -59,6 +59,9 @@ const getStyles = (theme: GrafanaTheme2) => {
|
|||||||
AccordianKeyValuesItem: css({
|
AccordianKeyValuesItem: css({
|
||||||
marginBottom: theme.spacing(0.5),
|
marginBottom: theme.spacing(0.5),
|
||||||
}),
|
}),
|
||||||
|
parenthesis: css({
|
||||||
|
color: `${autoColor(theme, '#777')}`,
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -108,22 +111,35 @@ export default function AccordianLogs({
|
|||||||
</HeaderComponent>
|
</HeaderComponent>
|
||||||
{isOpen && (
|
{isOpen && (
|
||||||
<div className={styles.AccordianLogsContent}>
|
<div className={styles.AccordianLogsContent}>
|
||||||
{_sortBy(logs, 'timestamp').map((log, i) => (
|
{_sortBy(logs, 'timestamp').map((log, i) => {
|
||||||
<AccordianKeyValues
|
const formattedDuration = formatDuration(log.timestamp - timestamp);
|
||||||
// `i` is necessary in the key because timestamps can repeat
|
const truncateLogNameInSummary = log.name && log.name.length > 20;
|
||||||
key={`${log.timestamp}-${i}`}
|
const formattedLogName = log.name && truncateLogNameInSummary ? log.name.slice(0, 20) + '...' : log.name;
|
||||||
className={i < logs.length - 1 ? styles.AccordianKeyValuesItem : null}
|
const label = formattedLogName ? (
|
||||||
data={log.fields || []}
|
<span>
|
||||||
highContrast
|
{formattedDuration} <span>({formattedLogName})</span>
|
||||||
interactive={interactive}
|
</span>
|
||||||
isOpen={openedItems ? openedItems.has(log) : false}
|
) : (
|
||||||
label={`${formatDuration(log.timestamp - timestamp)}`}
|
formattedDuration
|
||||||
linksGetter={linksGetter}
|
);
|
||||||
onToggle={interactive && onItemToggle ? () => onItemToggle(log) : null}
|
return (
|
||||||
/>
|
<AccordianKeyValues
|
||||||
))}
|
// `i` is necessary in the key because timestamps can repeat
|
||||||
|
key={`${log.timestamp}-${i}`}
|
||||||
|
className={i < logs.length - 1 ? styles.AccordianKeyValuesItem : null}
|
||||||
|
data={log.fields || []}
|
||||||
|
logName={truncateLogNameInSummary ? log.name : undefined}
|
||||||
|
highContrast
|
||||||
|
interactive={interactive}
|
||||||
|
isOpen={openedItems ? openedItems.has(log) : false}
|
||||||
|
label={label}
|
||||||
|
linksGetter={linksGetter}
|
||||||
|
onToggle={interactive && onItemToggle ? () => onItemToggle(log) : null}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
<small className={styles.AccordianLogsFooter}>
|
<small className={styles.AccordianLogsFooter}>
|
||||||
Log timestamps are relative to the start time of the full trace.
|
Event timestamps are relative to the start time of the full trace.
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
+2
-1
@@ -48,7 +48,7 @@ export const getStyles = (theme: GrafanaTheme2) => {
|
|||||||
row: css`
|
row: css`
|
||||||
label: row;
|
label: row;
|
||||||
& > td {
|
& > td {
|
||||||
padding: 0rem 0.5rem;
|
padding: 0.5rem 0.5rem;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
}
|
}
|
||||||
&:nth-child(2n) > td {
|
&:nth-child(2n) > td {
|
||||||
@@ -63,6 +63,7 @@ export const getStyles = (theme: GrafanaTheme2) => {
|
|||||||
color: ${autoColor(theme, '#888')};
|
color: ${autoColor(theme, '#888')};
|
||||||
white-space: pre;
|
white-space: pre;
|
||||||
width: 125px;
|
width: 125px;
|
||||||
|
vertical-align: top;
|
||||||
`,
|
`,
|
||||||
copyColumn: css`
|
copyColumn: css`
|
||||||
label: copyColumn;
|
label: copyColumn;
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ export type TraceLink = {
|
|||||||
export type TraceLog = {
|
export type TraceLog = {
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
fields: TraceKeyValuePair[];
|
fields: TraceKeyValuePair[];
|
||||||
|
name?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TraceProcess = {
|
export type TraceProcess = {
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ describe('filterSpans', () => {
|
|||||||
],
|
],
|
||||||
logs: [
|
logs: [
|
||||||
{
|
{
|
||||||
|
name: 'logName0',
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
key: 'logFieldKey0',
|
key: 'logFieldKey0',
|
||||||
@@ -316,6 +317,10 @@ describe('filterSpans', () => {
|
|||||||
).toEqual(new Set([spanID0]));
|
).toEqual(new Set([spanID0]));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('it should return logs have a name which matches the filter', () => {
|
||||||
|
expect(filterSpans({ ...defaultFilters, query: 'logName0' }, spans)).toEqual(new Set([spanID0]));
|
||||||
|
});
|
||||||
|
|
||||||
it('should return no spans when logs is null', () => {
|
it('should return no spans when logs is null', () => {
|
||||||
const nullSpan = { ...span0, logs: null };
|
const nullSpan = { ...span0, logs: null };
|
||||||
expect(
|
expect(
|
||||||
|
|||||||
@@ -90,7 +90,8 @@ export function getQueryMatches(query: string, spans: TraceSpan[] | TNil) {
|
|||||||
(span.instrumentationLibraryName && isTextInQuery(queryParts, span.instrumentationLibraryName)) ||
|
(span.instrumentationLibraryName && isTextInQuery(queryParts, span.instrumentationLibraryName)) ||
|
||||||
(span.instrumentationLibraryVersion && isTextInQuery(queryParts, span.instrumentationLibraryVersion)) ||
|
(span.instrumentationLibraryVersion && isTextInQuery(queryParts, span.instrumentationLibraryVersion)) ||
|
||||||
(span.traceState && isTextInQuery(queryParts, span.traceState)) ||
|
(span.traceState && isTextInQuery(queryParts, span.traceState)) ||
|
||||||
(span.logs !== null && span.logs.some((log) => isTextInKeyValues(log.fields))) ||
|
(span.logs !== null &&
|
||||||
|
span.logs.some((log) => (log.name && isTextInQuery(queryParts, log.name)) || isTextInKeyValues(log.fields))) ||
|
||||||
isTextInKeyValues(span.process.tags) ||
|
isTextInKeyValues(span.process.tags) ||
|
||||||
queryParts.some((queryPart) => queryPart === span.spanID);
|
queryParts.some((queryPart) => queryPart === span.spanID);
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ export type TraceLink = {
|
|||||||
export type TraceLog = {
|
export type TraceLog = {
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
fields: TraceKeyValuePair[];
|
fields: TraceKeyValuePair[];
|
||||||
|
name?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TraceProcess = {
|
export type TraceProcess = {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ export type TraceLink = {
|
|||||||
export type TraceLog = {
|
export type TraceLog = {
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
fields: TraceKeyValuePair[];
|
fields: TraceKeyValuePair[];
|
||||||
|
name?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TraceProcess = {
|
export type TraceProcess = {
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ function getLogs(span: collectorTypes.opentelemetryProto.trace.v1.Span) {
|
|||||||
fields.push({ key: attribute.key, value: getAttributeValue(attribute.value) });
|
fields.push({ key: attribute.key, value: getAttributeValue(attribute.value) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logs.push({ fields, timestamp: event.timeUnixNano / 1000000 });
|
logs.push({ fields, timestamp: event.timeUnixNano / 1000000, name: event.name });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -364,7 +364,7 @@ function getOTLPEvents(logs: TraceLog[]): collectorTypes.opentelemetryProto.trac
|
|||||||
timeUnixNano: log.timestamp * 1000000,
|
timeUnixNano: log.timestamp * 1000000,
|
||||||
attributes: [],
|
attributes: [],
|
||||||
droppedAttributesCount: 0,
|
droppedAttributesCount: 0,
|
||||||
name: '',
|
name: log.name || '',
|
||||||
};
|
};
|
||||||
for (const field of log.fields) {
|
for (const field of log.fields) {
|
||||||
event.attributes!.push({
|
event.attributes!.push({
|
||||||
|
|||||||
@@ -1920,7 +1920,7 @@ export const otlpDataFrameFromResponse = new MutableDataFrame({
|
|||||||
name: 'logs',
|
name: 'logs',
|
||||||
type: FieldType.other,
|
type: FieldType.other,
|
||||||
config: {},
|
config: {},
|
||||||
values: [[]],
|
values: [[{ name: 'DNSDone', fields: [{ key: 'addr', value: '172.18.0.6' }] }]],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'references',
|
name: 'references',
|
||||||
@@ -2138,7 +2138,20 @@ export const otlpDataFrameToResponse = new MutableDataFrame({
|
|||||||
name: 'logs',
|
name: 'logs',
|
||||||
type: FieldType.other,
|
type: FieldType.other,
|
||||||
config: {},
|
config: {},
|
||||||
values: [[]],
|
values: [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
key: 'addr',
|
||||||
|
value: '172.18.0.6',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
timestamp: 1627471657255.809,
|
||||||
|
name: 'DNSDone',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
state: {
|
state: {
|
||||||
displayName: 'logs',
|
displayName: 'logs',
|
||||||
},
|
},
|
||||||
@@ -2240,6 +2253,14 @@ export const otlpResponse = {
|
|||||||
{ key: 'http.url', value: { stringValue: '/' } },
|
{ key: 'http.url', value: { stringValue: '/' } },
|
||||||
{ key: 'component', value: { stringValue: 'net/http' } },
|
{ key: 'component', value: { stringValue: 'net/http' } },
|
||||||
],
|
],
|
||||||
|
events: [
|
||||||
|
{
|
||||||
|
name: 'DNSDone',
|
||||||
|
attributes: [{ key: 'addr', value: { stringValue: '172.18.0.6' } }],
|
||||||
|
droppedAttributesCount: 0,
|
||||||
|
timeUnixNano: 1627471657255809000,
|
||||||
|
},
|
||||||
|
],
|
||||||
links: [
|
links: [
|
||||||
{
|
{
|
||||||
spanId: 'spanId',
|
spanId: 'spanId',
|
||||||
|
|||||||
Reference in New Issue
Block a user