aboutsummaryrefslogtreecommitdiffhomepage
path: root/server
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2021-10-20 14:23:32 +0200
committerChocobozzz <me@florianbigard.com>2021-10-20 14:33:38 +0200
commit64553e8809271df1113e9143426a27f234410a74 (patch)
treebeba7a8d4016b74dd3b85e8081482a3ea6d00eaf /server
parent1243729899082a71b3a3efb759df1478d9ea5c83 (diff)
downloadPeerTube-64553e8809271df1113e9143426a27f234410a74.tar.gz
PeerTube-64553e8809271df1113e9143426a27f234410a74.tar.zst
PeerTube-64553e8809271df1113e9143426a27f234410a74.zip
Add ability to filter logs by tags
Diffstat (limited to 'server')
-rw-r--r--server/controllers/api/server/logs.ts42
-rw-r--r--server/middlewares/validators/logs.ts7
-rw-r--r--server/tests/api/server/logs.ts23
3 files changed, 66 insertions, 6 deletions
diff --git a/server/controllers/api/server/logs.ts b/server/controllers/api/server/logs.ts
index dfd5491aa..8aa4b7190 100644
--- a/server/controllers/api/server/logs.ts
+++ b/server/controllers/api/server/logs.ts
@@ -1,6 +1,7 @@
1import express from 'express' 1import express from 'express'
2import { readdir, readFile } from 'fs-extra' 2import { readdir, readFile } from 'fs-extra'
3import { join } from 'path' 3import { join } from 'path'
4import { isArray } from '@server/helpers/custom-validators/misc'
4import { logger, mtimeSortFilesDesc } from '@server/helpers/logger' 5import { logger, mtimeSortFilesDesc } from '@server/helpers/logger'
5import { LogLevel } from '../../../../shared/models/server/log-level.type' 6import { LogLevel } from '../../../../shared/models/server/log-level.type'
6import { UserRight } from '../../../../shared/models/users' 7import { UserRight } from '../../../../shared/models/users'
@@ -51,20 +52,27 @@ async function getLogs (req: express.Request, res: express.Response) {
51 startDateQuery: req.query.startDate, 52 startDateQuery: req.query.startDate,
52 endDateQuery: req.query.endDate, 53 endDateQuery: req.query.endDate,
53 level: req.query.level || 'info', 54 level: req.query.level || 'info',
55 tagsOneOf: req.query.tagsOneOf,
54 nameFilter: logNameFilter 56 nameFilter: logNameFilter
55 }) 57 })
56 58
57 return res.json(output).end() 59 return res.json(output)
58} 60}
59 61
60async function generateOutput (options: { 62async function generateOutput (options: {
61 startDateQuery: string 63 startDateQuery: string
62 endDateQuery?: string 64 endDateQuery?: string
65
63 level: LogLevel 66 level: LogLevel
64 nameFilter: RegExp 67 nameFilter: RegExp
68 tagsOneOf?: string[]
65}) { 69}) {
66 const { startDateQuery, level, nameFilter } = options 70 const { startDateQuery, level, nameFilter } = options
67 71
72 const tagsOneOf = Array.isArray(options.tagsOneOf) && options.tagsOneOf.length !== 0
73 ? new Set(options.tagsOneOf)
74 : undefined
75
68 const logFiles = await readdir(CONFIG.STORAGE.LOG_DIR) 76 const logFiles = await readdir(CONFIG.STORAGE.LOG_DIR)
69 const sortedLogFiles = await mtimeSortFilesDesc(logFiles, CONFIG.STORAGE.LOG_DIR) 77 const sortedLogFiles = await mtimeSortFilesDesc(logFiles, CONFIG.STORAGE.LOG_DIR)
70 let currentSize = 0 78 let currentSize = 0
@@ -80,7 +88,7 @@ async function generateOutput (options: {
80 const path = join(CONFIG.STORAGE.LOG_DIR, meta.file) 88 const path = join(CONFIG.STORAGE.LOG_DIR, meta.file)
81 logger.debug('Opening %s to fetch logs.', path) 89 logger.debug('Opening %s to fetch logs.', path)
82 90
83 const result = await getOutputFromFile(path, startDate, endDate, level, currentSize) 91 const result = await getOutputFromFile({ path, startDate, endDate, level, currentSize, tagsOneOf })
84 if (!result.output) break 92 if (!result.output) break
85 93
86 output = result.output.concat(output) 94 output = result.output.concat(output)
@@ -92,9 +100,20 @@ async function generateOutput (options: {
92 return output 100 return output
93} 101}
94 102
95async function getOutputFromFile (path: string, startDate: Date, endDate: Date, level: LogLevel, currentSize: number) { 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
96 const startTime = startDate.getTime() 113 const startTime = startDate.getTime()
97 const endTime = endDate.getTime() 114 const endTime = endDate.getTime()
115 let currentSize = options.currentSize
116
98 let logTime: number 117 let logTime: number
99 118
100 const logsLevel: { [ id in LogLevel ]: number } = { 119 const logsLevel: { [ id in LogLevel ]: number } = {
@@ -121,7 +140,12 @@ async function getOutputFromFile (path: string, startDate: Date, endDate: Date,
121 } 140 }
122 141
123 logTime = new Date(log.timestamp).getTime() 142 logTime = new Date(log.timestamp).getTime()
124 if (logTime >= startTime && logTime <= endTime && logsLevel[log.level] >= logsLevel[level]) { 143 if (
144 logTime >= startTime &&
145 logTime <= endTime &&
146 logsLevel[log.level] >= logsLevel[level] &&
147 (!tagsOneOf || lineHasTag(log, tagsOneOf))
148 ) {
125 output.push(log) 149 output.push(log)
126 150
127 currentSize += line.length 151 currentSize += line.length
@@ -135,6 +159,16 @@ async function getOutputFromFile (path: string, startDate: Date, endDate: Date,
135 return { currentSize, output: output.reverse(), logTime } 159 return { currentSize, output: output.reverse(), logTime }
136} 160}
137 161
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
138function generateLogNameFilter (baseName: string) { 172function generateLogNameFilter (baseName: string) {
139 return new RegExp('^' + baseName.replace(/\.log$/, '') + '\\d*.log$') 173 return new RegExp('^' + baseName.replace(/\.log$/, '') + '\\d*.log$')
140} 174}
diff --git a/server/middlewares/validators/logs.ts b/server/middlewares/validators/logs.ts
index 03c1c4df1..901d8ca64 100644
--- a/server/middlewares/validators/logs.ts
+++ b/server/middlewares/validators/logs.ts
@@ -1,7 +1,8 @@
1import express from 'express' 1import express from 'express'
2import { query } from 'express-validator' 2import { query } from 'express-validator'
3import { isStringArray } from '@server/helpers/custom-validators/search'
3import { isValidLogLevel } from '../../helpers/custom-validators/logs' 4import { isValidLogLevel } from '../../helpers/custom-validators/logs'
4import { isDateValid } from '../../helpers/custom-validators/misc' 5import { isDateValid, toArray } from '../../helpers/custom-validators/misc'
5import { logger } from '../../helpers/logger' 6import { logger } from '../../helpers/logger'
6import { areValidationErrors } from './shared' 7import { areValidationErrors } from './shared'
7 8
@@ -11,6 +12,10 @@ const getLogsValidator = [
11 query('level') 12 query('level')
12 .optional() 13 .optional()
13 .custom(isValidLogLevel).withMessage('Should have a valid level'), 14 .custom(isValidLogLevel).withMessage('Should have a valid level'),
15 query('tagsOneOf')
16 .optional()
17 .customSanitizer(toArray)
18 .custom(isStringArray).withMessage('Should have a valid one of tags array'),
14 query('endDate') 19 query('endDate')
15 .optional() 20 .optional()
16 .custom(isDateValid).withMessage('Should have an end date that conforms to ISO 8601'), 21 .custom(isDateValid).withMessage('Should have an end date that conforms to ISO 8601'),
diff --git a/server/tests/api/server/logs.ts b/server/tests/api/server/logs.ts
index bcd94dda3..4fa13886e 100644
--- a/server/tests/api/server/logs.ts
+++ b/server/tests/api/server/logs.ts
@@ -71,7 +71,7 @@ describe('Test logs', function () {
71 expect(logsString.includes('video 5')).to.be.false 71 expect(logsString.includes('video 5')).to.be.false
72 }) 72 })
73 73
74 it('Should get filter by level', async function () { 74 it('Should filter by level', async function () {
75 this.timeout(20000) 75 this.timeout(20000)
76 76
77 const now = new Date() 77 const now = new Date()
@@ -94,6 +94,27 @@ describe('Test logs', function () {
94 } 94 }
95 }) 95 })
96 96
97 it('Should filter by tag', async function () {
98 const now = new Date()
99
100 const { uuid } = await server.videos.upload({ attributes: { name: 'video 6' } })
101 await waitJobs([ server ])
102
103 {
104 const body = await logsCommand.getLogs({ startDate: now, level: 'debug', tagsOneOf: [ 'toto' ] })
105 expect(body).to.have.lengthOf(0)
106 }
107
108 {
109 const body = await logsCommand.getLogs({ startDate: now, level: 'debug', tagsOneOf: [ uuid ] })
110 expect(body).to.not.have.lengthOf(0)
111
112 for (const line of body) {
113 expect(line.tags).to.contain(uuid)
114 }
115 }
116 })
117
97 it('Should log ping requests', async function () { 118 it('Should log ping requests', async function () {
98 this.timeout(10000) 119 this.timeout(10000)
99 120