// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance // with the License. A copy of the License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "LICENSE.txt" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES // OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions and // limitations under the License. import {LogEvent} from '../../types/logs' import React from 'react' import {useSearchParams} from 'react-router-dom' // Model import {ListClusterLogStreams, GetClusterLogEvents} from '../../model' import {clearState, getState, setState, useState} from '../../store' import {useCollection} from '@cloudscape-design/collection-hooks' // UI Elements import Loading from '../../components/Loading' import { Button, CollectionPreferences, ExpandableSection, Pagination, Table, TextFilter, } from '@cloudscape-design/components' // Components import EmptyState from '../../components/EmptyState' import {useTranslation} from 'react-i18next' function LogEventsTable() { const selected = getState(['app', 'clusters', 'selected']) const selectedLogStreamName = useState([ 'app', 'clusters', 'selectedLogStreamName', ]) const events = useState([ 'clusters', 'index', selected, 'logEventIndex', selectedLogStreamName, ]) const columns = useState(['app', 'clusters', 'logs', 'columns']) || [ 'message', ] const pageSize = useState(['app', 'clusters', 'logs', 'pageSize']) || 100 const pending = useState(['app', 'clusters', 'logs', 'pending']) let [searchParams, setSearchParams] = useSearchParams() const refresh = () => { setState(['app', 'clusters', 'logs', 'pending'], true) const clusterName = getState(['app', 'clusters', 'selected']) const logStreamName = getState(['app', 'clusters', 'selectedLogStreamName']) if (clusterName && logStreamName) { GetClusterLogEvents( clusterName, logStreamName, () => clearState(['app', 'clusters', 'logs', 'pending']), () => clearState(['app', 'clusters', 'logs', 'pending']), ) } } const { items, actions, filteredItemsCount, collectionProps, filterProps, paginationProps, } = useCollection(((events && events.events) as LogEvent[]) || [], { filtering: { empty: , noMatch: ( actions.setFiltering('')}> Clear filter } /> ), }, pagination: {pageSize: pageSize}, sorting: {}, selection: {}, }) React.useEffect(() => { filterProps.onChange({ detail: {filteringText: searchParams.get('filter') || ''}, }) // eslint-disable-next-line react-hooks/exhaustive-deps }, [searchParams]) return (
{selectedLogStreamName}
log.timestamp, sortingField: 'timestamp', }, { id: 'message', header: 'message', cell: log =>
{log.message}
, }, ]} loading={events === null} items={items} loadingText="Loading Logs..." pagination={} filter={ { searchParams.set('filter', e.detail.filteringText) setSearchParams(searchParams) filterProps.onChange(e) }} countText={`Results: ${filteredItemsCount}`} filteringAriaLabel="Filter logs" /> } preferences={ { setState( ['app', 'clusters', 'logs', 'columns'], detail.visibleContent, ) setState( ['app', 'clusters', 'logs', 'pageSize'], detail.pageSize, ) }} title="Preferences" confirmLabel="Confirm" cancelLabel="Cancel" preferences={{ pageSize: pageSize, visibleContent: columns, }} pageSizePreference={{ title: 'Select page size', options: [ {value: 100, label: '100 Logs'}, {value: 250, label: '250 Logs'}, {value: 500, label: '500 Logs'}, ], }} visibleContentPreference={{ title: 'Select visible content', options: [ { label: 'Log columns', options: [ { id: 'timestamp', label: 'Timestamp', }, {id: 'message', label: 'Message', editable: false}, ], }, ], }} /> } /> ) } function StreamList({instanceId}: {instanceId: string}) { const logStreams = useState(['app', 'clusters', 'logs', 'index', instanceId, 'streams']) || [] const ip = useState(['app', 'clusters', 'logs', 'index', instanceId, 'ip']) const fnames = Object.keys(logStreams).sort() const selectedLogStreamName = useState([ 'app', 'clusters', 'selectedLogStreamName', ]) let [searchParams, setSearchParams] = useSearchParams() React.useEffect(() => { if ( searchParams.get('instance') && searchParams.get('instance') === instanceId && searchParams.get('filename') && `${ip}.${instanceId}.${searchParams.get('filename')}` !== selectedLogStreamName ) { const selected = getState(['app', 'clusters', 'selected']) const logStreamName = `${ip}.${instanceId}.${searchParams.get( 'filename', )}` setState(['app', 'clusters', 'selectedLogStreamName'], logStreamName) GetClusterLogEvents(selected, logStreamName) } }, [searchParams, instanceId, ip, selectedLogStreamName]) return (
{ if (detail.expanded) { setSearchParams({instance: instanceId}) } }} expanded={searchParams.get('instance') === instanceId} > {fnames.map(fname => (
setSearchParams({filename: fname, instance: instanceId}) } style={{ marginLeft: '10px', cursor: 'pointer', fontWeight: selectedLogStreamName === logStreams[fname].logStreamName ? 'bold' : 'normal', }} > {fname}
))}
) } function LogStreamList() { const logStreamIndex = useState(['app', 'clusters', 'logs', 'index']) const selected = useState(['app', 'clusters', 'selected']) const clusterPath = ['clusters', 'index', selected] const headNode = useState([...clusterPath, 'headNode']) const instanceId = (headNode && headNode.instanceId) || '' return (
HeadNode
{instanceId && }
Compute
{logStreamIndex && Object.keys(logStreamIndex) .filter(k => k !== instanceId) .sort() .map(instanceId => ( ))}
) } export default function ClusterLogs() { const {t} = useTranslation() const selected = getState(['app', 'clusters', 'selected']) const logStreamIndexPath = ['app', 'clusters', 'logs', 'index'] const streams = useState(['clusters', 'index', selected, 'logstreams']) const selectedLogStreamName = useState([ 'app', 'clusters', 'selectedLogStreamName', ]) const logEvents = useState([ 'clusters', 'index', selected, 'logEventIndex', selectedLogStreamName, ]) for (let stream of (streams && streams['logStreams']) || []) { let [ip, id, fname] = stream.logStreamName.split('.') if (!getState([...logStreamIndexPath, id, 'streams', fname])) { setState([...logStreamIndexPath, id, 'streams', fname], stream) setState([...logStreamIndexPath, id, 'ip'], ip) } } React.useEffect(() => { const selected = getState(['app', 'clusters', 'selected']) ListClusterLogStreams(selected) }, []) return (
{streams ? (
{selectedLogStreamName && (logEvents ? : )} {!selectedLogStreamName && t('cluster.logs.label')}
) : ( )}
) }