mirror of https://github.com/grafana/grafana
Logs: Add new Controls component to Explore (#103401)
* ControlledLogRows: create component * Fix imports * ControlledLogRows: handle scroll events * Rename storage key prop * LogListControls: externally control syntax highlighting * ControlledLogRows: add support for level filtering * Logs: implement deduplication from controls * Fix imports * Create feature flag * Use feature flag * LogListControls: add download button * LogsMetaRow: extract download function to logs utils * Filter and download logs * Update tests with new props * LogList: pass logs and logs meta to context * Remove prefix from downloaded file * Update unit tests * LogListControl: update unit tests * Fix type assertion * Fix imports * Formatting * i18n * Fix test * LogListControls: adjust scroll to top styles * LogListContext: control legacy options * LogListControls: add showUniqueLabels and prettifyJSON options * LogListControls: test new controls * Extract translations * Hide old controls by feature flag * LogListControls: update prettify json copy * ControlledLogRows: disable preview * Prettier * LogListControls: Fix testpull/103553/head
parent
e9ed7223a6
commit
e2a6f9a849
|
@ -0,0 +1,140 @@ |
||||
import { css } from '@emotion/css'; |
||||
import { useEffect, useMemo, useRef } from 'react'; |
||||
|
||||
import { AbsoluteTimeRange, CoreApp, EventBusSrv, LogsMetaItem, LogsSortOrder, TimeRange } from '@grafana/data'; |
||||
import { config } from '@grafana/runtime'; |
||||
|
||||
import { InfiniteScroll } from './InfiniteScroll'; |
||||
import { LogRows, Props } from './LogRows'; |
||||
import { LogListControlOptions } from './panel/LogList'; |
||||
import { LogListContextProvider, useLogListContext } from './panel/LogListContext'; |
||||
import { LogListControls } from './panel/LogListControls'; |
||||
import { ScrollToLogsEvent } from './panel/virtualization'; |
||||
|
||||
interface ControlledLogRowsProps extends Omit<Props, 'scrollElement'> { |
||||
loading: boolean; |
||||
logsMeta?: LogsMetaItem[]; |
||||
loadMoreLogs?: (range: AbsoluteTimeRange) => void; |
||||
logOptionsStorageKey?: string; |
||||
onLogOptionsChange?: (option: keyof LogListControlOptions, value: string | boolean | string[]) => void; |
||||
range: TimeRange; |
||||
} |
||||
|
||||
type LogRowsComponentProps = Omit< |
||||
ControlledLogRowsProps, |
||||
'app' | 'dedupStrategy' | 'showLabels' | 'showTime' | 'logsSortOrder' | 'prettifyLogMessage' | 'wrapLogMessage' |
||||
>; |
||||
|
||||
export const ControlledLogRows = ({ |
||||
deduplicatedRows, |
||||
dedupStrategy, |
||||
showLabels, |
||||
showTime, |
||||
logsMeta, |
||||
logOptionsStorageKey, |
||||
logsSortOrder, |
||||
prettifyLogMessage, |
||||
onLogOptionsChange, |
||||
wrapLogMessage, |
||||
...rest |
||||
}: ControlledLogRowsProps) => { |
||||
return ( |
||||
<LogListContextProvider |
||||
app={rest.app || CoreApp.Unknown} |
||||
displayedFields={[]} |
||||
dedupStrategy={dedupStrategy} |
||||
logOptionsStorageKey={logOptionsStorageKey} |
||||
logs={deduplicatedRows ?? []} |
||||
logsMeta={logsMeta} |
||||
prettifyJSON={prettifyLogMessage} |
||||
showControls |
||||
showTime={showTime} |
||||
showUniqueLabels={showLabels} |
||||
sortOrder={logsSortOrder || LogsSortOrder.Descending} |
||||
onLogOptionsChange={onLogOptionsChange} |
||||
wrapLogMessage={wrapLogMessage} |
||||
> |
||||
<LogRowsComponent {...rest} deduplicatedRows={deduplicatedRows} /> |
||||
</LogListContextProvider> |
||||
); |
||||
}; |
||||
|
||||
const LogRowsComponent = ({ loading, loadMoreLogs, deduplicatedRows = [], range, ...rest }: LogRowsComponentProps) => { |
||||
const { app, dedupStrategy, filterLevels, prettifyJSON, sortOrder, showTime, showUniqueLabels, wrapLogMessage } = |
||||
useLogListContext(); |
||||
const eventBus = useMemo(() => new EventBusSrv(), []); |
||||
const scrollElementRef = useRef<HTMLDivElement | null>(null); |
||||
|
||||
useEffect(() => { |
||||
const subscription = eventBus.subscribe(ScrollToLogsEvent, (e: ScrollToLogsEvent) => |
||||
handleScrollToEvent(e, scrollElementRef.current) |
||||
); |
||||
return () => subscription.unsubscribe(); |
||||
}, [eventBus]); |
||||
|
||||
const filteredLogs = useMemo( |
||||
() => |
||||
filterLevels.length === 0 |
||||
? deduplicatedRows |
||||
: deduplicatedRows.filter((log) => filterLevels.includes(log.logLevel)), |
||||
[filterLevels, deduplicatedRows] |
||||
); |
||||
|
||||
return ( |
||||
<div className={styles.logRowsContainer}> |
||||
<div |
||||
ref={scrollElementRef} |
||||
className={config.featureToggles.logsInfiniteScrolling ? styles.scrollableLogRows : styles.logRows} |
||||
> |
||||
<InfiniteScroll |
||||
loading={loading} |
||||
loadMoreLogs={loadMoreLogs} |
||||
range={range} |
||||
timeZone={rest.timeZone} |
||||
rows={filteredLogs} |
||||
scrollElement={scrollElementRef.current} |
||||
sortOrder={sortOrder} |
||||
> |
||||
<LogRows |
||||
{...rest} |
||||
app={app} |
||||
dedupStrategy={dedupStrategy} |
||||
deduplicatedRows={filteredLogs} |
||||
logRows={filteredLogs} |
||||
logsSortOrder={sortOrder} |
||||
scrollElement={scrollElementRef.current} |
||||
prettifyLogMessage={Boolean(prettifyJSON)} |
||||
showLabels={Boolean(showUniqueLabels)} |
||||
showTime={showTime} |
||||
wrapLogMessage={wrapLogMessage} |
||||
/> |
||||
</InfiniteScroll> |
||||
</div> |
||||
<LogListControls eventBus={eventBus} /> |
||||
</div> |
||||
); |
||||
}; |
||||
|
||||
function handleScrollToEvent(event: ScrollToLogsEvent, scrollElement: HTMLDivElement | null) { |
||||
if (event.payload.scrollTo === 'top') { |
||||
scrollElement?.scrollTo(0, 0); |
||||
} else if (scrollElement) { |
||||
scrollElement.scrollTo(0, scrollElement.scrollHeight); |
||||
} |
||||
} |
||||
|
||||
const styles = { |
||||
scrollableLogRows: css({ |
||||
overflowY: 'scroll', |
||||
width: '100%', |
||||
maxHeight: '75vh', |
||||
}), |
||||
logRows: css({ |
||||
overflowX: 'scroll', |
||||
overflowY: 'visible', |
||||
width: '100%', |
||||
}), |
||||
logRowsContainer: css({ |
||||
display: 'flex', |
||||
}), |
||||
}; |
Loading…
Reference in new issue