import React, { useEffect, useState } from 'react'; import { defineMessages, useIntl } from 'react-intl'; import copy from 'copy-to-clipboard'; import useSWR from 'swr'; import { LogMessage, LogsResultsResponse, } from '../../../../server/interfaces/api/settingsInterfaces'; import Error from '../../../pages/_error'; import Badge from '../../Common/Badge'; import Button from '../../Common/Button'; import LoadingSpinner from '../../Common/LoadingSpinner'; import PageTitle from '../../Common/PageTitle'; import Table from '../../Common/Table'; import globalMessages from '../../../i18n/globalMessages'; import { useRouter } from 'next/router'; import Modal from '../../Common/Modal'; import Transition from '../../Transition'; import { useToasts } from 'react-toast-notifications'; const messages = defineMessages({ logs: 'Logs', logsDescription: 'You can also view these logs directly via stdout, or in {configDir}/logs/overseerr.log.', time: 'Timestamp', level: 'Severity', label: 'Label', message: 'Message', filterDebug: 'Debug', filterInfo: 'Info', filterWarn: 'Warning', filterError: 'Error', noresults: 'No results.', showall: 'Show All Logs', showingresults: 'Showing {from} to {to} of {total} results', resultsperpage: 'Display {pageSize} results per page', next: 'Next', previous: 'Previous', pauseLogs: 'Pause', resumeLogs: 'Resume', viewDetails: 'View Details', copyToClipboard: 'Copy to Clipboard', logDetails: 'Log Details', extraData: 'Extra Data', copiedLogMessage: 'Copied log message to clipboard.', }); type Filter = 'debug' | 'info' | 'warn' | 'error'; const SettingsLogs: React.FC = () => { const router = useRouter(); const intl = useIntl(); const { addToast } = useToasts(); const [currentFilter, setCurrentFilter] = useState('debug'); const [currentPageSize, setCurrentPageSize] = useState(25); const [refreshInterval, setRefreshInterval] = useState(5000); const [activeLog, setActiveLog] = useState(null); const page = router.query.page ? Number(router.query.page) : 1; const pageIndex = page - 1; const toggleLogs = () => { setRefreshInterval(refreshInterval === 5000 ? 0 : 5000); }; const { data, error } = useSWR( `/api/v1/settings/logs?take=${currentPageSize}&skip=${ pageIndex * currentPageSize }&filter=${currentFilter}`, { refreshInterval: refreshInterval, revalidateOnFocus: false, } ); const { data: appData } = useSWR('/api/v1/status/appdata'); useEffect(() => { const displayString = window.localStorage.getItem('logs-display-settings'); if (displayString) { const displaySettings = JSON.parse(displayString); setCurrentFilter(displaySettings.currentFilter); setCurrentPageSize(displaySettings.currentPageSize); } }, []); useEffect(() => { window.localStorage.setItem( 'logs-display-settings', JSON.stringify({ currentFilter, currentPageSize, }) ); }, [currentFilter, currentPageSize]); const copyLogString = (log: LogMessage): void => { copy( `${log.timestamp} [${log.level}]${log.label ? `[${log.label}]` : ''}: ${ log.message }${log.data ? `${JSON.stringify(log.data)}` : ''}` ); addToast(intl.formatMessage(messages.copiedLogMessage), { appearance: 'success', autoDismiss: true, }); }; if (!data && !error) { return ; } if (!data) { return ; } const hasNextPage = data.pageInfo.pages > pageIndex + 1; const hasPrevPage = pageIndex > 0; return ( <> setActiveLog(null)} cancelText={intl.formatMessage(globalMessages.close)} onOk={() => (activeLog ? copyLogString(activeLog) : undefined)} okText={intl.formatMessage(messages.copyToClipboard)} okButtonType="primary" > {activeLog && ( <>
{intl.formatMessage(messages.time)}
{intl.formatDate(activeLog.timestamp, { year: 'numeric', month: 'short', day: '2-digit', hour: 'numeric', minute: 'numeric', second: 'numeric', })}
{intl.formatMessage(messages.level)}
{activeLog.level.toUpperCase()}
{intl.formatMessage(messages.label)}
{activeLog.label}
{intl.formatMessage(messages.message)}
{activeLog.message}
{activeLog.data && (
{intl.formatMessage(messages.extraData)}
{JSON.stringify(activeLog.data, null, ' ')}
)} )}

{intl.formatMessage(messages.logs)}

{intl.formatMessage(messages.logsDescription, { code: function code(msg) { return {msg}; }, configDir: appData ? appData.appDataPath : '/app/config', })}

{intl.formatMessage(messages.time)}{intl.formatMessage(messages.level)}{intl.formatMessage(messages.label)}{intl.formatMessage(messages.message)} {data.results.map((row: LogMessage, index: number) => { return ( {intl.formatDate(row.timestamp, { year: 'numeric', month: 'short', day: '2-digit', hour: 'numeric', minute: 'numeric', second: 'numeric', })} {row.level.toUpperCase()} {row.label}{row.message} ); })} {data.results.length === 0 && (
{intl.formatMessage(messages.noresults)} {currentFilter !== 'debug' && (
)}
)}
); }; export default SettingsLogs;