123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139 |
- import {Fragment, useMemo, useState} from 'react';
- import styled from '@emotion/styled';
- import debounce from 'lodash/debounce';
- import CompactSelect from 'sentry/components/forms/compactSelect';
- import {Panel} from 'sentry/components/panels';
- import {useReplayContext} from 'sentry/components/replays/replayContext';
- import {relativeTimeInMs, showPlayerTime} from 'sentry/components/replays/utils';
- import SearchBar from 'sentry/components/searchBar';
- import {t} from 'sentry/locale';
- import space from 'sentry/styles/space';
- import type {BreadcrumbLevelType, BreadcrumbTypeDefault} from 'sentry/types/breadcrumbs';
- import EmptyMessage from 'sentry/views/settings/components/emptyMessage';
- import ConsoleMessage from './consoleMessage';
- import {filterBreadcrumbs} from './utils';
- interface Props {
- breadcrumbs: BreadcrumbTypeDefault[];
- startTimestamp: number;
- }
- const getDistinctLogLevels = (breadcrumbs: BreadcrumbTypeDefault[]) =>
- Array.from(new Set<string>(breadcrumbs.map(breadcrumb => breadcrumb.level)));
- function Console({breadcrumbs, startTimestamp = 0}: Props) {
- const {currentHoverTime, currentTime} = useReplayContext();
- const [searchTerm, setSearchTerm] = useState('');
- const [logLevel, setLogLevel] = useState<BreadcrumbLevelType[]>([]);
- const handleSearch = debounce(query => setSearchTerm(query), 150);
- const filteredBreadcrumbs = useMemo(
- () => filterBreadcrumbs(breadcrumbs, searchTerm, logLevel),
- [logLevel, searchTerm, breadcrumbs]
- );
- const activeConsoleBounds = useMemo(() => {
- if (filteredBreadcrumbs.length <= 0 || currentHoverTime === undefined) {
- return [-1, -1];
- }
- let indexUpperBound = 0;
- const finalBreadCrumbIndex = filteredBreadcrumbs.length - 1;
- const finalBreadcrumbTimestamp =
- filteredBreadcrumbs[finalBreadCrumbIndex].timestamp || '';
- if (currentHoverTime >= relativeTimeInMs(finalBreadcrumbTimestamp, startTimestamp)) {
- indexUpperBound = finalBreadCrumbIndex;
- } else {
- indexUpperBound =
- filteredBreadcrumbs.findIndex(
- breadcrumb =>
- relativeTimeInMs(breadcrumb.timestamp || '', startTimestamp) >=
- (currentHoverTime || 0)
- ) - 1;
- }
- const activeMessageBoundary = showPlayerTime(
- filteredBreadcrumbs[indexUpperBound]?.timestamp || '',
- startTimestamp
- );
- const indexLowerBound = filteredBreadcrumbs.findIndex(
- breadcrumb =>
- showPlayerTime(breadcrumb.timestamp || '', startTimestamp) ===
- activeMessageBoundary
- );
- return [indexLowerBound, indexUpperBound];
- }, [currentHoverTime, filteredBreadcrumbs, startTimestamp]);
- return (
- <Fragment>
- <ConsoleFilters>
- <CompactSelect
- triggerProps={{
- prefix: t('Log Level'),
- }}
- multiple
- options={getDistinctLogLevels(breadcrumbs).map(breadcrumbLogLevel => ({
- value: breadcrumbLogLevel,
- label: breadcrumbLogLevel,
- }))}
- onChange={selections =>
- setLogLevel(selections.map(selection => selection.value))
- }
- />
- <SearchBar onChange={handleSearch} placeholder={t('Search console logs...')} />
- </ConsoleFilters>
- {filteredBreadcrumbs.length > 0 ? (
- <ConsoleTable>
- {filteredBreadcrumbs.map((breadcrumb, i) => {
- return (
- <ConsoleMessage
- isActive={i >= activeConsoleBounds[0] && i <= activeConsoleBounds[1]}
- startTimestamp={startTimestamp}
- key={i}
- isLast={i === breadcrumbs.length - 1}
- breadcrumb={breadcrumb}
- hasOccurred={
- currentTime >=
- relativeTimeInMs(breadcrumb?.timestamp || '', startTimestamp)
- }
- />
- );
- })}
- </ConsoleTable>
- ) : (
- <StyledEmptyMessage title={t('No results found.')} />
- )}
- </Fragment>
- );
- }
- const ConsoleFilters = styled('div')`
- display: grid;
- gap: ${space(1)};
- grid-template-columns: max-content 1fr;
- margin-bottom: ${space(1)};
- @media (max-width: ${p => p.theme.breakpoints.small}) {
- margin-top: ${space(1)};
- }
- `;
- const StyledEmptyMessage = styled(EmptyMessage)`
- align-items: center;
- `;
- const ConsoleTable = styled(Panel)`
- display: grid;
- grid-template-columns: max-content auto max-content;
- width: 100%;
- font-family: ${p => p.theme.text.familyMono};
- font-size: 0.8em;
- `;
- export default Console;
|