Logs: more analytics (#115330)
* Logs: more analytics * LogLineDetails: collect fields data * analytics: report length count * Prettier * LogLineDetailsHeader: track details mode toggle
This commit is contained in:
@@ -9,7 +9,7 @@ import { LogListModel, NEWLINES_REGEX } from '../panel/processing';
|
||||
export const OTEL_PROBE_FIELD = 'severity_number';
|
||||
const OTEL_LANGUAGE_UNKNOWN = 'unknown';
|
||||
|
||||
function identifyOTelLanguages(logs: LogListModel[] | LogRowModel[]): string[] {
|
||||
export function identifyOTelLanguages(logs: LogListModel[] | LogRowModel[]): string[] {
|
||||
const languagesSet = new Set<string>();
|
||||
logs.forEach((log) => {
|
||||
const lang = identifyOTelLanguage(log);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { camelCase, groupBy } from 'lodash';
|
||||
import { memo, startTransition, useCallback, useMemo, useRef, useState } from 'react';
|
||||
import { memo, startTransition, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
|
||||
import { DataFrameType, GrafanaTheme2, store, TimeRange } from '@grafana/data';
|
||||
import { t, Trans } from '@grafana/i18n';
|
||||
@@ -18,6 +18,7 @@ import { LogLineDetailsLinks } from './LogLineDetailsLinks';
|
||||
import { LogLineDetailsLog } from './LogLineDetailsLog';
|
||||
import { LogLineDetailsTrace } from './LogLineDetailsTrace';
|
||||
import { useLogListContext } from './LogListContext';
|
||||
import { reportInteractionOnce } from './analytics';
|
||||
import { getTempoTraceFromLinks } from './links';
|
||||
import { LogListModel } from './processing';
|
||||
|
||||
@@ -124,6 +125,21 @@ export const LogLineDetailsComponent = memo(
|
||||
[fieldsWithLinks.links, fieldsWithLinks.linksFromVariableMap]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (noInteractions) {
|
||||
return;
|
||||
}
|
||||
reportInteractionOnce('logs_log_line_details_fields_displayed', {
|
||||
links: allLinks.length,
|
||||
trace: trace !== undefined,
|
||||
fields: fieldsWithoutLinks.length,
|
||||
labels: labelsWithLinks.length,
|
||||
labelGroups: labelGroups.join(', '),
|
||||
});
|
||||
// Once
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<LogLineDetailsHeader focusLogLine={focusLogLine} log={log} search={search} onSearch={handleSearch} />
|
||||
|
||||
@@ -102,7 +102,11 @@ export const LogLineDetailsHeader = ({ focusLogLine, log, search, onSearch }: Pr
|
||||
}
|
||||
|
||||
setDetailsMode(newMode);
|
||||
}, [detailsMode, logOptionsStorageKey, setDetailsMode]);
|
||||
|
||||
reportInteractionWrapper('logs_log_line_details_header_toggle_details_mode', {
|
||||
newMode,
|
||||
});
|
||||
}, [detailsMode, logOptionsStorageKey, reportInteractionWrapper, setDetailsMode]);
|
||||
|
||||
const toggleLogLine = useCallback(() => {
|
||||
if (logLineDisplayed) {
|
||||
|
||||
@@ -34,7 +34,7 @@ import { getDefaultDetailsMode, getDetailsWidth } from './LogDetailsContext';
|
||||
import { LogLineTimestampResolution } from './LogLine';
|
||||
import { GetRowContextQueryFn, LogLineMenuCustomItem } from './LogLineMenu';
|
||||
import { LogListOptions, LogListFontSize } from './LogList';
|
||||
import { reportInteractionOnce } from './analytics';
|
||||
import { collectInsights } from './analytics';
|
||||
import { LogListModel } from './processing';
|
||||
|
||||
export interface LogListContextData extends Omit<Props, 'containerElement' | 'logs' | 'logsMeta' | 'showControls'> {
|
||||
@@ -241,7 +241,7 @@ export const LogListContextProvider = ({
|
||||
if (noInteractions) {
|
||||
return;
|
||||
}
|
||||
reportInteractionOnce(`logs_log_list_${app}_logs_displayed`, {
|
||||
collectInsights(logs, app, {
|
||||
dedupStrategy,
|
||||
fontSize,
|
||||
forceEscape: logListState.forceEscape,
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { CoreApp, LogRowModel } from '@grafana/data';
|
||||
import { reportInteraction } from '@grafana/runtime';
|
||||
|
||||
import { identifyOTelLanguages } from '../otel/formats';
|
||||
|
||||
export const reportInteractionOnce = (interactionName: string, properties?: Record<string, unknown>) => {
|
||||
const key = `logs.interactions.${interactionName}`;
|
||||
if (sessionStorage.getItem(key)) {
|
||||
@@ -8,3 +11,56 @@ export const reportInteractionOnce = (interactionName: string, properties?: Reco
|
||||
sessionStorage.setItem(key, '1');
|
||||
reportInteraction(interactionName, properties);
|
||||
};
|
||||
|
||||
export function collectInsights(logs: LogRowModel[], app: CoreApp, properties?: Record<string, unknown>) {
|
||||
if (!logs.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { longest, shortest, average, median } = getLogsStats(logs);
|
||||
|
||||
reportInteractionOnce(`logs_log_list_${app}_logs_displayed`, {
|
||||
...properties,
|
||||
otelLanguage: identifyOTelLanguages(logs).join(', '),
|
||||
ansi: logs.some((logs) => logs.hasAnsi),
|
||||
unescaped: logs.some((logs) => logs.hasUnescapedContent),
|
||||
dsType: logs[0]?.datasourceType ?? '',
|
||||
count: logs.length,
|
||||
longestLog: longest,
|
||||
shortestLog: shortest,
|
||||
averageLog: average,
|
||||
medianLog: median,
|
||||
});
|
||||
}
|
||||
|
||||
function getLogsStats(logs: LogRowModel[]) {
|
||||
let longest = 0,
|
||||
shortest = logs[0].raw.length,
|
||||
median = 0;
|
||||
|
||||
const lengths: number[] = [];
|
||||
let sum = 0;
|
||||
|
||||
for (let i = 0; i < logs.length; i++) {
|
||||
let length = logs[i].raw.length;
|
||||
if (length > longest) {
|
||||
longest = length;
|
||||
} else if (length < shortest) {
|
||||
shortest = length;
|
||||
}
|
||||
sum += length;
|
||||
lengths.push(length);
|
||||
}
|
||||
|
||||
lengths.sort((a, b) => a - b);
|
||||
|
||||
const mid = Math.floor(lengths.length / 2);
|
||||
|
||||
if (lengths.length % 2 === 0) {
|
||||
median = (lengths[mid - 1] + lengths[mid]) / 2;
|
||||
} else {
|
||||
median = lengths[mid];
|
||||
}
|
||||
|
||||
return { longest, shortest, average: Math.round(sum / logs.length), median };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user