Skip to content

Commit

Permalink
feat: Panel filters are functional now (#397)
Browse files Browse the repository at this point in the history
Fixes #364 
<img width="1582" alt="Screenshot 2023-09-18 at 11 35 00 AM"
src="https://github.com/TBD54566975/ftl/assets/51647/c7880e3c-c98d-4670-bd85-9c38420b812a">
<img width="1582" alt="Screenshot 2023-09-18 at 11 35 16 AM"
src="https://github.com/TBD54566975/ftl/assets/51647/d069bb7d-c96f-437b-97c6-e2e5cf597053">
<img width="1582" alt="Screenshot 2023-09-18 at 11 37 58 AM"
src="https://github.com/TBD54566975/ftl/assets/51647/7eeade18-5259-4b90-8847-d71a77d9f85f">
  • Loading branch information
wesbillman authored Sep 18, 2023
1 parent 4aaa617 commit 8d0b912
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 35 deletions.
1 change: 1 addition & 0 deletions backend/controller/console.go
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ func eventsQueryProtoToDAL(pb *pbconsole.EventsQuery) ([]dal.EventFilter, error)
return nil, connect.NewError(connect.CodeInvalidArgument, errors.Errorf("unknown event type %v", eventType))
}
}
query = append(query, dal.FilterTypes(eventTypes...))

case *pbconsole.EventsQuery_Filter_LogLevel:
level := log.Level(filter.LogLevel.LogLevel)
Expand Down
4 changes: 1 addition & 3 deletions console/client/src/features/timeline/Timeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ export const Timeline = ({ timeSettings, filters }: Props) => {
const abortController = new AbortController()
abortController.signal
const fetchEvents = async () => {
console.log('fetching events')
let eventFilters = filters
if (timeSettings.newerThan || timeSettings.olderThan) {
eventFilters = [timeFilter(timeSettings.olderThan, timeSettings.newerThan), ...filters]
Expand All @@ -42,14 +41,13 @@ export const Timeline = ({ timeSettings, filters }: Props) => {
}

if (timeSettings.isTailing && !timeSettings.isPaused) {
console.log('streaming events')
setEntries([])
streamEvents({
abortControllerSignal: abortController.signal,
filters,
onEventReceived: (event) => {
if (!timeSettings.isPaused) {
setEntries((prev) => [event, ...prev].slice(0, maxTimelineEntries))
setEntries((prev) => [...prev, event].slice(0, maxTimelineEntries))
}
},
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
import { PhoneIcon, RocketLaunchIcon } from '@heroicons/react/24/outline'
import React from 'react'
import { EventsQuery_Filter, LogLevel } from '../../../protos/xyz/block/ftl/v1/console/console_pb'
import { EventType, EventsQuery_Filter, LogLevel } from '../../../protos/xyz/block/ftl/v1/console/console_pb'
import { modulesContext } from '../../../providers/modules-provider'
import { eventTypesFilter, logLevelFilter, modulesFilter } from '../../../services/console.service'
import { textColor } from '../../../utils'
import { LogLevelBadgeSmall } from '../../logs/LogLevelBadgeSmall'
import { logLevelColor } from '../../logs/log.utils'
import { FilterPanelSection } from './FilterPanelSection'

const EVENT_TYPES: Record<string, string> = {
call: 'Call',
log: 'Log',
deployment: 'Deployment',
interface EventFilter {
label: string
type: EventType
icon: React.ReactNode
}

const EVENT_TYPE_ICON: Record<string, React.ReactNode> = {
call: <PhoneIcon className='w-4 h-4 text-indigo-400 ml-1' />,
log: <LogLevelBadgeSmall logLevel={LogLevel.INFO} />,
deployment: <RocketLaunchIcon className='w-4 h-4 text-indigo-400 ml-1' />,
const EVENT_TYPES: Record<string, EventFilter> = {
call: { label: 'Call', type: EventType.CALL, icon: <PhoneIcon className='w-4 h-4 text-indigo-600 ml-1' /> },
log: { label: 'Log', type: EventType.LOG, icon: <LogLevelBadgeSmall logLevel={LogLevel.INFO} /> },
deployment: {
label: 'Deployment',
type: EventType.DEPLOYMENT,
icon: <RocketLaunchIcon className='w-4 h-4 text-indigo-600 ml-1' />,
},
}

const LOG_LEVELS: Record<number, string> = {
Expand All @@ -39,13 +44,25 @@ export const TimelineFilterPanel = ({ onFiltersChanged }: Props) => {

React.useEffect(() => {
if (selectedModules.length === 0) {
setSelectedModules(modules.modules.map((module) => module.name))
setSelectedModules(modules.modules.map((module) => module.deploymentName))
}
}, [modules])

React.useEffect(() => {
onFiltersChanged([])
}, [selectedEventTypes, setSelectedLogLevel, selectedModules])
const filter: EventsQuery_Filter[] = []
if (selectedEventTypes.length !== Object.keys(EVENT_TYPES).length) {
const selectedTypes = selectedEventTypes.map((key) => EVENT_TYPES[key].type)

filter.push(eventTypesFilter(selectedTypes))
}
if (selectedLogLevel !== LogLevel.TRACE) {
filter.push(logLevelFilter(selectedLogLevel))
}

filter.push(modulesFilter(selectedModules))

onFiltersChanged(filter)
}, [selectedEventTypes, selectedLogLevel, selectedModules])

const handleTypeChanged = (eventType: string, checked: boolean) => {
if (checked) {
Expand All @@ -55,11 +72,11 @@ export const TimelineFilterPanel = ({ onFiltersChanged }: Props) => {
}
}

const handleModuleChanged = (moduleName: string, checked: boolean) => {
const handleModuleChanged = (deploymentName: string, checked: boolean) => {
if (checked) {
setSelectedModules((prev) => [...prev, moduleName])
setSelectedModules((prev) => [...prev, deploymentName])
} else {
setSelectedModules((prev) => prev.filter((filter) => filter !== moduleName))
setSelectedModules((prev) => prev.filter((filter) => filter !== deploymentName))
}
}

Expand Down Expand Up @@ -89,8 +106,8 @@ export const TimelineFilterPanel = ({ onFiltersChanged }: Props) => {
htmlFor={`event-type-${key}`}
className={`flex justify-between items-center ${textColor} cursor-pointer`}
>
{EVENT_TYPES[key]}
<span>{EVENT_TYPE_ICON[key]}</span>
{EVENT_TYPES[key].label}
<span>{EVENT_TYPES[key].icon}</span>
</label>
</div>
</div>
Expand Down Expand Up @@ -120,20 +137,35 @@ export const TimelineFilterPanel = ({ onFiltersChanged }: Props) => {
</FilterPanelSection>

<FilterPanelSection title='Modules'>
<div className='relative flex items-center mb-2'>
<button
onClick={() => setSelectedModules(modules.modules.map((module) => module.deploymentName))}
className='text-indigo-600 cursor-pointer hover:text-indigo-500'
>
Select All
</button>
<span className='px-1 text-indigo-700'>|</span>
<button
onClick={() => setSelectedModules([])}
className='text-indigo-600 cursor-pointer hover:text-indigo-500'
>
Deselect All
</button>
</div>
{modules.modules.map((module) => (
<div key={module.name} className='relative flex items-start'>
<div key={module.deploymentName} className='relative flex items-start'>
<div className='flex h-6 items-center'>
<input
id={`module-${module.name}`}
name={`module-${module.name}`}
id={`module-${module.deploymentName}`}
name={`module-${module.deploymentName}`}
type='checkbox'
checked={selectedModules.includes(module.name)}
onChange={(e) => handleModuleChanged(module.name, e.target.checked)}
checked={selectedModules.includes(module.deploymentName)}
onChange={(e) => handleModuleChanged(module.deploymentName, e.target.checked)}
className='h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-600 cursor-pointer'
/>
</div>
<div className='ml-2 text-sm leading-6 w-full'>
<label htmlFor={`module-${module.name}`} className={`${textColor} flex cursor-pointer`}>
<label htmlFor={`module-${module.deploymentName}`} className={`${textColor} flex cursor-pointer`}>
{module.name}
</label>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,13 @@ export const TimelineTimeControls = ({ onTimeSettingsChange }: Props) => {

const handleRangeChanged = (range: TimeRange) => {
setSelected(range)
if (!newerThan) {
const newerThanDate = new Date(new Date().getTime() - range.value)
setNewerThan(Timestamp.fromDate(newerThanDate))
}

if (range.value === TIME_RANGES['tail'].value) {
setNewerThan(undefined)
setIsPaused(false)
} else {
const newerThanDate = new Date(new Date().getTime() - range.value)
setNewerThan(Timestamp.fromDate(newerThanDate))
}
}

Expand Down
40 changes: 35 additions & 5 deletions console/client/src/services/console.service.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Code, ConnectError } from '@bufbuild/connect'
import { Timestamp } from '@bufbuild/protobuf'
import { createClient } from '../hooks/use-client'
import { ConsoleService } from '../protos/xyz/block/ftl/v1/console/console_connect'
Expand All @@ -6,16 +7,19 @@ import {
Event,
EventType,
EventsQuery_CallFilter,
EventsQuery_DeploymentFilter,
EventsQuery_EventTypeFilter,
EventsQuery_Filter,
EventsQuery_LogLevelFilter,
EventsQuery_Order,
EventsQuery_RequestFilter,
EventsQuery_TimeFilter,
LogLevel,
} from '../protos/xyz/block/ftl/v1/console/console_pb'

const client = createClient(ConsoleService)

const requestKeysFilter = (requestKeys: string[]): EventsQuery_Filter => {
export const requestKeysFilter = (requestKeys: string[]): EventsQuery_Filter => {
const filter = new EventsQuery_Filter()
const requestFilter = new EventsQuery_RequestFilter()
requestFilter.requests = requestKeys
Expand All @@ -26,7 +30,7 @@ const requestKeysFilter = (requestKeys: string[]): EventsQuery_Filter => {
return filter
}

const eventTypesFilter = (eventTypes: EventType[]): EventsQuery_Filter => {
export const eventTypesFilter = (eventTypes: EventType[]): EventsQuery_Filter => {
const filter = new EventsQuery_Filter()
const typesFilter = new EventsQuery_EventTypeFilter()
typesFilter.eventTypes = eventTypes
Expand All @@ -37,7 +41,29 @@ const eventTypesFilter = (eventTypes: EventType[]): EventsQuery_Filter => {
return filter
}

const callFilter = (
export const logLevelFilter = (logLevel: LogLevel): EventsQuery_Filter => {
const filter = new EventsQuery_Filter()
const logFilter = new EventsQuery_LogLevelFilter()
logFilter.logLevel = logLevel
filter.filter = {
case: 'logLevel',
value: logFilter,
}
return filter
}

export const modulesFilter = (modules: string[]): EventsQuery_Filter => {
const filter = new EventsQuery_Filter()
const deploymentsFilter = new EventsQuery_DeploymentFilter()
deploymentsFilter.deployments = modules
filter.filter = {
case: 'deployments',
value: deploymentsFilter,
}
return filter
}

export const callFilter = (
destModule: string,
destVerb: string | undefined = undefined,
sourceModule: string | undefined = undefined,
Expand Down Expand Up @@ -102,14 +128,18 @@ export interface StreamEventsParams {
export const streamEvents = async ({ abortControllerSignal, filters, onEventReceived }: StreamEventsParams) => {
try {
for await (const response of client.streamEvents(
{ updateInterval: { seconds: BigInt(1) }, query: { limit: 1000, filters } },
{ updateInterval: { seconds: BigInt(1) }, query: { limit: 1000, order: EventsQuery_Order.DESC, filters } },
{ signal: abortControllerSignal },
)) {
if (response.event != null) {
onEventReceived(response.event)
}
}
} catch (error) {
console.error('Streaming error:', error)
if (error instanceof ConnectError) {
if (error.code !== Code.Canceled) {
console.error('Connect error:', error.code)
}
}
}
}

0 comments on commit 8d0b912

Please sign in to comment.