log_rotation
Massimo Melina committed
Mar 8, 2022 at 14:50 UTC
9c330c0c2a145cfbb3b71a8fae90957d3017ad47
5 files changed
+56
-13
.gitignore
+1
-1
@@ -3,7 +3,7 @@ node_modules
3
dist
4
#produced by running
5
counters.yaml
6
-*.log
6
+*.log*
7
config.yaml
8
accounts.yaml
9
shared/lib
README.md
+7
-4
@@ -76,9 +76,9 @@ This will give you auto-restarting of the server on back-end changes.
76
Set an env `DEV=1` to let the code know we are in a dev environment.
77
78
If you want to work on the frontend and admin too, you should *first*
79
-1. set an env `FRONTEND_PROXY=3000`
79
+1. set an env `FRONTEND_PROXY=3005`
80
2. `npm run start-frontend`
81
-1. set an env `ADMIN_PROXY=3001`
81
+1. set an env `ADMIN_PROXY=3006`
82
2. `npm run start-admin`
83
84
Having this env-s will make the server get all related stuff from the other dev servers.
@@ -193,6 +193,7 @@ Supported entries are:
193
- `admin_port` the port where to reach admin interface. Default is 63636.
194
- `admin_network` the network address where to reach admin interface. Default is 127.0.0.1 .
195
- `log` path of the log file. Default is `access.log`.
196
+- `log_rotation` frequency of log rotation. Accepted values are `daily`, `weekly`, `monthly`, or empty string to disable. Default is `weekly`.
197
- `error_log` path of the log file for errors. Default is `error.log`.
198
- `errors_in_main_log` if you want to use a single file for both kind of entries. Default is false.
199
- `accounts` path of the accounts file. Default is `accounts.yaml`.
@@ -202,7 +203,7 @@ Supported entries are:
203
You can use the special value `auto` to attempt automatic detection.
204
- `max_kbps` throttle output speed. Default is Infinity.
205
- `max_kbps_per_ip` throttle output speed on a per-ip basis. Default is Infinity.
205
-- `zip-calculate-size-for-seconds` how long should we wait before the zip archive starts streaming, trying to understand its finale size. Default is 1.
206
+- `zip_calculate_size_for_seconds` how long should we wait before the zip archive starts streaming, trying to understand its finale size. Default is 1.
207
- `open_browser_at_start` should HFS open browser on localhost on start? Default is true.
208
- `https_port` listen on a specific port. Default is 443.
209
- `cert` use this file for https certificate. Minimum to start https is to give a cert and a private_key. Default is none.
@@ -265,7 +266,9 @@ gather multiple accounts and refer to them collectively as `group1`, so you can
266
Other options you can define as properties of an account:
267
268
- `ignore_limits` to ignore speed limits. Default is `false`.
268
-- `redirect` provide a URL if you want the user to be redirected upon login. Default is none.
269
+- `redirect` provide a URL if you want the user to be redirected upon login. Default is none.
270
+- `admin` set `true` if you want to give access to the Admin interface when it's configured to require login.
271
+- `belongs` an array of usernames of other accounts from which to inherit their permissions.
272
273
## License
274
admin/src/ConfigPage.ts
+3
@@ -70,6 +70,9 @@ export default function ConfigPage() {
70
{ k: 'max_kbps_per_ip', comp: NumberField, label: 'Max KB/s per-ip' },
71
{ k: 'log', comp: StringField, label: 'Main log file' },
72
{ k: 'error_log', comp: StringField, label: 'Error log file' },
73
+ { k: 'log_rotation', comp: SelectField, options: [{ value:'', label:"disabled" }, 'daily', 'weekly', 'monthly' ],
74
+ helperText: "To avoid an endlessly-growing single log file, you can opt for rotation"
75
+ },
76
{ k: 'accounts', comp: StringField, label: 'Accounts file' },
77
{ k: 'open_browser_at_start', comp: BoolField },
78
{ k: 'zip_calculate_size_for_seconds', comp: NumberField, label: 'Calculate ZIP size for seconds',
server/src/log.ts
+45
-7
@@ -2,18 +2,32 @@
2
3
import Koa from 'koa'
4
import { Writable } from 'stream'
5
-import { getConfig, subscribeConfig } from './config'
5
+import { defineConfig, getConfig, subscribeConfig } from './config'
6
import { createWriteStream } from 'fs'
7
import * as util from 'util'
8
+import { rename, stat } from 'fs/promises'
9
10
class Logger {
11
stream?: Writable
12
+ last?: Date
13
+ path: string = ''
14
12
- setPath(path: string) {
15
+ async setPath(path: string) {
16
+ this.path = path
17
this.stream?.end()
18
+ this.last = undefined
19
if (!path)
20
return this.stream = undefined
16
- this.stream = createWriteStream(path, { flags: 'a' })
21
+ try {
22
+ const stats = await stat(path)
23
+ this.last = stats.mtime || stats.ctime
24
+ }
25
+ catch {}
26
+ this.reopen()
27
+ }
28
+
29
+ reopen() {
30
+ this.stream = createWriteStream(this.path, { flags: 'a' })
31
}
32
}
33
@@ -30,16 +44,36 @@ subscribeConfig({ k: 'error_log', defaultValue: 'error.log' }, path => {
44
errorLogger.setPath(path)
45
})
46
47
+function getMidnight(date: Date=new Date) {
48
+ date.setHours(0,0,0,0)
49
+ return date
50
+}
51
+
52
+defineConfig('log_rotation', { defaultValue: 'weekly' })
53
+
54
export function log(): Koa.Middleware {
55
return async (ctx, next) => { // wrapping in a function will make it use current 'mw' value
56
await next()
57
const isError = ctx.status >= 400
37
- let st = isError && !getConfig('errors_in_main_log') && errorLogger.stream || accessLogger.stream
38
- if (!st) return
58
+ const logger = isError && !getConfig('errors_in_main_log') && errorLogger || accessLogger
59
+ const freq = getConfig('log_rotation')
60
+ const { stream, last, path } = logger
61
+ if (!stream) return
62
+ const now = new Date()
63
+ const a = now.toString().split(' ')
64
+ if (freq && last) {
65
+ const method = freq[0] === 'w' ? 'getDay' : freq[0] === 'm' ? 'getDate' : 'getTime'
66
+ if (getMidnight(now)[method]() !== getMidnight(last)[method]()) {
67
+ stream.end()
68
+ const postfix = last.getFullYear() + '-' + doubleDigit(last.getMonth() + 1) + '-' + doubleDigit(last.getDate())
69
+ await rename(path, path + '-' + postfix)
70
+ logger.reopen()
71
+ }
72
+ }
73
+ logger.last = now
74
const format = '%s - - [%s] "%s %s HTTP/%s" %d %s\n';
40
- const a = new Date().toString().split(' ')
75
const date = a[2]+'/'+a[1]+'/'+a[3]+':'+a[4]+' '+a[5].slice(3)
42
- st.write(util.format( format,
76
+ logger.stream!.write(util.format( format,
77
ctx.ip,
78
date,
79
ctx.method,
@@ -50,3 +84,7 @@ export function log(): Koa.Middleware {
84
))
85
}
86
}
87
+
88
+function doubleDigit(n: number) {
89
+ return n > 9 ? n : '0'+n
90
+}
todo.md
-1
@@ -11,7 +11,6 @@
11
- allowed referer
12
- admin/plugins
13
- download-counter: expose results on admin
14
-- log rotation
14
- log filter option
15
- log filter plugin
16
- publish to npm (so people can "npm install hfs")