]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/controllers/api/server/logs.ts
Improve viewer counter
[github/Chocobozzz/PeerTube.git] / server / controllers / api / server / logs.ts
CommitLineData
41fb13c3 1import express from 'express'
2c22613c 2import { readdir, readFile } from 'fs-extra'
fd8710b8 3import { join } from 'path'
64553e88 4import { isArray } from '@server/helpers/custom-validators/misc'
15a7eafb 5import { logger, mtimeSortFilesDesc } from '@server/helpers/logger'
fd8710b8 6import { LogLevel } from '../../../../shared/models/server/log-level.type'
4c7e60bc 7import { UserRight } from '../../../../shared/models/users'
6dd9de95 8import { CONFIG } from '../../../initializers/config'
4c7e60bc
C
9import { AUDIT_LOG_FILENAME, LOG_FILENAME, MAX_LOGS_OUTPUT_CHARACTERS } from '../../../initializers/constants'
10import { asyncMiddleware, authenticate, ensureUserHasRight } from '../../../middlewares'
11import { getAuditLogsValidator, getLogsValidator } from '../../../middlewares/validators/logs'
fd8710b8
C
12
13const logsRouter = express.Router()
14
15logsRouter.get('/logs',
16 authenticate,
17 ensureUserHasRight(UserRight.MANAGE_LOGS),
18 getLogsValidator,
19 asyncMiddleware(getLogs)
20)
21
566c125d
C
22logsRouter.get('/audit-logs',
23 authenticate,
24 ensureUserHasRight(UserRight.MANAGE_LOGS),
25 getAuditLogsValidator,
26 asyncMiddleware(getAuditLogs)
27)
28
fd8710b8
C
29// ---------------------------------------------------------------------------
30
31export {
32 logsRouter
33}
34
35// ---------------------------------------------------------------------------
36
566c125d
C
37const auditLogNameFilter = generateLogNameFilter(AUDIT_LOG_FILENAME)
38async function getAuditLogs (req: express.Request, res: express.Response) {
39 const output = await generateOutput({
40 startDateQuery: req.query.startDate,
41 endDateQuery: req.query.endDate,
42 level: 'audit',
43 nameFilter: auditLogNameFilter
44 })
45
46 return res.json(output).end()
47}
48
49const logNameFilter = generateLogNameFilter(LOG_FILENAME)
fd8710b8 50async function getLogs (req: express.Request, res: express.Response) {
566c125d
C
51 const output = await generateOutput({
52 startDateQuery: req.query.startDate,
53 endDateQuery: req.query.endDate,
54 level: req.query.level || 'info',
64553e88 55 tagsOneOf: req.query.tagsOneOf,
566c125d
C
56 nameFilter: logNameFilter
57 })
58
64553e88 59 return res.json(output)
566c125d
C
60}
61
62async function generateOutput (options: {
a1587156
C
63 startDateQuery: string
64 endDateQuery?: string
64553e88 65
a1587156 66 level: LogLevel
566c125d 67 nameFilter: RegExp
64553e88 68 tagsOneOf?: string[]
566c125d
C
69}) {
70 const { startDateQuery, level, nameFilter } = options
71
64553e88
C
72 const tagsOneOf = Array.isArray(options.tagsOneOf) && options.tagsOneOf.length !== 0
73 ? new Set(options.tagsOneOf)
74 : undefined
75
fd8710b8
C
76 const logFiles = await readdir(CONFIG.STORAGE.LOG_DIR)
77 const sortedLogFiles = await mtimeSortFilesDesc(logFiles, CONFIG.STORAGE.LOG_DIR)
78 let currentSize = 0
79
566c125d
C
80 const startDate = new Date(startDateQuery)
81 const endDate = options.endDateQuery ? new Date(options.endDateQuery) : new Date()
fd8710b8 82
2c22613c 83 let output: string[] = []
fd8710b8
C
84
85 for (const meta of sortedLogFiles) {
566c125d
C
86 if (nameFilter.exec(meta.file) === null) continue
87
fd8710b8 88 const path = join(CONFIG.STORAGE.LOG_DIR, meta.file)
3c0d0c66 89 logger.debug('Opening %s to fetch logs.', path)
fd8710b8 90
64553e88 91 const result = await getOutputFromFile({ path, startDate, endDate, level, currentSize, tagsOneOf })
fd8710b8
C
92 if (!result.output) break
93
2c22613c 94 output = result.output.concat(output)
fd8710b8
C
95 currentSize = result.currentSize
96
2c22613c 97 if (currentSize > MAX_LOGS_OUTPUT_CHARACTERS || (result.logTime && result.logTime < startDate.getTime())) break
fd8710b8
C
98 }
99
566c125d 100 return output
fd8710b8
C
101}
102
64553e88
C
103async function getOutputFromFile (options: {
104 path: string
105 startDate: Date
106 endDate: Date
107 level: LogLevel
108 currentSize: number
109 tagsOneOf: Set<string>
110}) {
111 const { path, startDate, endDate, level, tagsOneOf } = options
112
fd8710b8
C
113 const startTime = startDate.getTime()
114 const endTime = endDate.getTime()
64553e88
C
115 let currentSize = options.currentSize
116
2c22613c 117 let logTime: number
fd8710b8
C
118
119 const logsLevel: { [ id in LogLevel ]: number } = {
566c125d 120 audit: -1,
fd8710b8
C
121 debug: 0,
122 info: 1,
123 warn: 2,
124 error: 3
125 }
126
2c22613c
C
127 const content = await readFile(path)
128 const lines = content.toString().split('\n')
129 const output: any[] = []
fd8710b8 130
2c22613c 131 for (let i = lines.length - 1; i >= 0; i--) {
a1587156 132 const line = lines[i]
2c22613c 133 let log: any
fd8710b8 134
2c22613c
C
135 try {
136 log = JSON.parse(line)
137 } catch {
138 // Maybe there a multiple \n at the end of the file
139 continue
140 }
fd8710b8 141
2c22613c 142 logTime = new Date(log.timestamp).getTime()
64553e88
C
143 if (
144 logTime >= startTime &&
145 logTime <= endTime &&
146 logsLevel[log.level] >= logsLevel[level] &&
147 (!tagsOneOf || lineHasTag(log, tagsOneOf))
148 ) {
2c22613c 149 output.push(log)
fd8710b8 150
2c22613c 151 currentSize += line.length
fd8710b8 152
2c22613c
C
153 if (currentSize > MAX_LOGS_OUTPUT_CHARACTERS) break
154 } else if (logTime < startTime) {
155 break
156 }
157 }
fd8710b8 158
2c22613c 159 return { currentSize, output: output.reverse(), logTime }
fd8710b8 160}
566c125d 161
64553e88
C
162function lineHasTag (line: { tags?: string }, tagsOneOf: Set<string>) {
163 if (!isArray(line.tags)) return false
164
165 for (const lineTag of line.tags) {
166 if (tagsOneOf.has(lineTag)) return true
167 }
168
169 return false
170}
171
566c125d 172function generateLogNameFilter (baseName: string) {
3c0d0c66 173 return new RegExp('^' + baseName.replace(/\.log$/, '') + '\\d*.log$')
566c125d 174}