Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(settings): basic log api and ui #337

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions overseerr-api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1419,6 +1419,41 @@ paths:
nextExecutionTime:
type: string
example: '2020-09-02T05:02:23.000Z'
/settings/logs:
get:
summary: Returns logs
description: Returns list of all log items and details
tags:
- settings
parameters:
- in: query
name: rows
schema:
type: number
example: 1000
default: 1000
responses:
'200':
description: Server log returned
content:
application/json:
schema:
type: array
items:
type: object
properties:
label:
type: string
example: SERVER
level:
type: string
example: info
message:
type: string
example: Server ready on port 3000
timestamp:
type: string
example: 2020-12-15T16:20:00.069Z
/settings/notifications/email:
get:
summary: Return current email notification settings
Expand Down
13 changes: 9 additions & 4 deletions server/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,18 @@ const hformat = winston.format.printf(
const logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'debug',
format: winston.format.combine(
winston.format.colorize(),
winston.format.splat(),
winston.format.timestamp(),
hformat
winston.format.json()
),
transports: [
new winston.transports.Console(),
new winston.transports.Console({
format: winston.format.combine(
winston.format.colorize(),
winston.format.splat(),
winston.format.timestamp(),
hformat
),
}),
new winston.transports.File({
filename: path.join(__dirname, '../config/logs/overseerr.log'),
}),
Expand Down
28 changes: 21 additions & 7 deletions server/routes/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ settingsRoutes.post('/main', (req, res) => {
return res.status(200).json(settings.main);
});

settingsRoutes.get('/plex', (_req, res) => {
settingsRoutes.get('/plex', (req, res) => {
const settings = getSettings();

res.status(200).json(settings.plex);
Expand Down Expand Up @@ -134,7 +134,7 @@ settingsRoutes.get('/plex/sync', (req, res) => {
return res.status(200).json(jobPlexFullSync.status());
});

settingsRoutes.get('/radarr', (_req, res) => {
settingsRoutes.get('/radarr', (req, res) => {
const settings = getSettings();

res.status(200).json(settings.radarr);
Expand Down Expand Up @@ -275,7 +275,7 @@ settingsRoutes.delete<{ id: string }>('/radarr/:id', (req, res) => {
return res.status(200).json(removed[0]);
});

settingsRoutes.get('/sonarr', (_req, res) => {
settingsRoutes.get('/sonarr', (req, res) => {
const settings = getSettings();

res.status(200).json(settings.sonarr);
Expand Down Expand Up @@ -386,7 +386,7 @@ settingsRoutes.delete<{ id: string }>('/sonarr/:id', (req, res) => {
return res.status(200).json(removed[0]);
});

settingsRoutes.get('/jobs', (_req, res) => {
settingsRoutes.get('/jobs', (req, res) => {
return res.status(200).json(
scheduledJobs.map((job) => ({
name: job.name,
Expand All @@ -395,10 +395,24 @@ settingsRoutes.get('/jobs', (_req, res) => {
);
});

settingsRoutes.get('/logs', (req, res) => {
const options = {
rows: Number(req.query.rows),
fields: null,
};

return logger.query(options, (err, results) => {
if (err) {
return res.status(500).json(err);
}
return res.status(200).json(results.file);
});
});

settingsRoutes.get(
'/initialize',
isAuthenticated(Permission.ADMIN),
(_req, res) => {
(req, res) => {
const settings = getSettings();

settings.public.initialized = true;
Expand All @@ -408,7 +422,7 @@ settingsRoutes.get(
}
);

settingsRoutes.get('/notifications/discord', (_req, res) => {
settingsRoutes.get('/notifications/discord', (req, res) => {
const settings = getSettings();

res.status(200).json(settings.notifications.agents.discord);
Expand All @@ -423,7 +437,7 @@ settingsRoutes.post('/notifications/discord', (req, res) => {
res.status(200).json(settings.notifications.agents.discord);
});

settingsRoutes.get('/notifications/email', (_req, res) => {
settingsRoutes.get('/notifications/email', (req, res) => {
const settings = getSettings();

res.status(200).json(settings.notifications.agents.email);
Expand Down
68 changes: 63 additions & 5 deletions src/components/Settings/SettingsLogs/index.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,72 @@
import React from 'react';
import useSWR from 'swr';
import Error from '../../../pages/_error';
import LoadingSpinner from '../../Common/LoadingSpinner';
import {
FormattedDate,
FormattedTime,
useIntl,
defineMessages,
} from 'react-intl';

// We will localize this file when the complete version is released.
const messages = defineMessages({
logs: 'Logs',
logsDescription:
'You can access your logs directly in stdout (container logs) or looking in /app/config/logs/overseerr.logs',
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will want to have the same <code> tags in here like we did in the temporary message. If you only put the tags in here by themselves though, they will be just rendered out in the string.

You can see how this is done in the RequestList component. I apply tags in there.

});

const SettingsLogs: React.FC = () => {
const intl = useIntl();
const { data, error } = useSWR('/api/v1/settings/logs');

if (error) {
return <Error statusCode={500} />;
}

if (!data && !error) {
return <LoadingSpinner />;
}

if (!data) {
return <LoadingSpinner />;
}

return (
<>
<div className="leading-loose text-gray-300 text-sm">
Logs page is still being built. For now, you can access your logs
directly in <code>stdout</code> (container logs) or looking in{' '}
<code>/app/config/logs/overseerr.logs</code>
<div>
<h3 className="text-lg leading-6 font-medium text-gray-200">
{intl.formatMessage(messages.logs)}
</h3>
<p className="text-sm leading-5 text-gray-500">
{intl.formatMessage(messages.logsDescription)}
</p>

<div className="mt-4 text-sm">
{data?.map((row, index) => (
<div key={`log-list-${index}`} className="space-x-2 text-gray-300">
<span className="inline">
<FormattedDate
value={row.timestamp}
year="numeric"
month="short"
day="2-digit"
/>
&nbsp;
<FormattedTime
value={row.timestamp}
hour="numeric"
minute="numeric"
second="numeric"
hour12={false}
/>
</span>
<span className="inline">
[{row.level}][{row.label}]:
</span>
<span className="inline">{row.message}</span>
</div>
))}
</div>
Comment on lines +44 to +69
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can maybe re-use the table component here to make this look nice? Check out how i use it in the RequestList component.

</div>
</>
);
Expand Down