new logger
This commit is contained in:
parent
275ae6460d
commit
76e85ad9ec
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,3 +2,4 @@
|
||||
/keys.json
|
||||
*.zone
|
||||
/dist
|
||||
/logs
|
||||
|
@ -15,6 +15,8 @@ This application is intended to be run behind a proxy. Requires node v14+ for `f
|
||||
- `PORT` - server port
|
||||
- `ZONEFILES` - path to zone files
|
||||
- `CACHE_TTL` - internal zone cache time-to-live
|
||||
- `LOG_DIR` - Logs directory
|
||||
- `LOG_FILES` - Log to files (boolean)
|
||||
- `RNDC_SERVER` - RNDC host
|
||||
- `RNDC_PORT` - RNDC port
|
||||
- `RNDC_KEYFILE` - location of RNDC's key file
|
||||
|
29
src/index.ts
29
src/index.ts
@ -2,6 +2,7 @@ import express, { ErrorRequestHandler, NextFunction, Request, RequestHandler, Re
|
||||
import cors from 'cors';
|
||||
import 'express-async-errors';
|
||||
import path from 'path';
|
||||
import fs from 'fs/promises';
|
||||
import { DNSCache } from './dns/cache';
|
||||
import { DNSRecordType } from './dns/records';
|
||||
import { ReloadExecutor } from './dns/rndc';
|
||||
@ -10,6 +11,7 @@ import { createZoneFile } from './dns/writer';
|
||||
import { fromRequest } from './ip/from-request';
|
||||
import { Keys } from './keys';
|
||||
import { CachedZone } from './models/interfaces';
|
||||
import { logger } from './log/Logger';
|
||||
|
||||
const port = parseInt(process.env.PORT || '9129', 10);
|
||||
const cacheTTL = parseInt(process.env.CACHE_TTL || '2629746', 10);
|
||||
@ -223,6 +225,8 @@ api.patch('/zone/records/:domain', domainAuthorization, async (req, res) => {
|
||||
res.status(400).json({ success: false, message: 'Updating record(s) failed.', changed, errors });
|
||||
} else if (changed.length) {
|
||||
res.json({ success: true, message: 'Record(s) changed successfully.', changed, errors });
|
||||
logger.info('zone %s changed records from %s', domain, req.ip);
|
||||
logger.debug(changed);
|
||||
} else {
|
||||
res.json({ success: true, message: 'Nothing was changed.', changed, errors });
|
||||
}
|
||||
@ -275,6 +279,8 @@ api.delete('/zone/records/:domain', domainAuthorization, async (req, res) => {
|
||||
res.status(400).json({ success: false, message: 'Deleting record(s) failed.', deleted, errors });
|
||||
} else if (deleted.length) {
|
||||
res.json({ success: true, message: 'Record(s) deleted successfully.', deleted, errors });
|
||||
logger.info('zone %s deleted records from %s', domain, req.ip);
|
||||
logger.debug(deleted);
|
||||
} else {
|
||||
res.json({ success: true, message: 'Nothing was deleted.', deleted, errors });
|
||||
}
|
||||
@ -380,6 +386,8 @@ api.post('/zone/records/:domain', domainAuthorization, async (req, res) => {
|
||||
res.status(400).json({ success: false, message: 'Creating record(s) failed.', created, errors });
|
||||
} else if (created.length) {
|
||||
res.status(201).json({ success: true, message: 'Record(s) created successfully.', created, errors });
|
||||
logger.info('zone %s created records from %s', domain, req.ip);
|
||||
logger.debug(created);
|
||||
} else {
|
||||
res.json({ success: true, message: 'Nothing was created.', created, errors });
|
||||
}
|
||||
@ -423,8 +431,10 @@ api.post('/zone/:domain', domainAuthorization, async (req, res) => {
|
||||
|
||||
if (req.body.ttl) {
|
||||
res.json({ success: true, message: 'TTL changed successfully.', ttl: cached.zone.ttl });
|
||||
logger.info('zone %s set ttl: %d from %s', domain, cached.zone.ttl, req.ip);
|
||||
} else {
|
||||
res.json({ success: true, message: 'Zone reloaded successfully.' });
|
||||
logger.info('zone %s reload from %s', domain, req.ip);
|
||||
}
|
||||
});
|
||||
|
||||
@ -505,6 +515,7 @@ api.post('/set-ip/:domain', domainAuthorization, async (req, res) => {
|
||||
message: 'Waiting for next request..',
|
||||
actions
|
||||
});
|
||||
logger.info('set-ip (partial) from %s: %s', req.ip, actions.join('\n'));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -515,6 +526,7 @@ api.post('/set-ip/:domain', domainAuthorization, async (req, res) => {
|
||||
message: 'Successfully updated zone file.',
|
||||
actions
|
||||
});
|
||||
logger.info('set-ip from %s: %s', req.ip, actions.join('\n'));
|
||||
});
|
||||
|
||||
const errorHandler: ErrorRequestHandler = (err: any, req: Request, res: Response, next: NextFunction) => {
|
||||
@ -527,5 +539,18 @@ const errorHandler: ErrorRequestHandler = (err: any, req: Request, res: Response
|
||||
api.use(errorHandler);
|
||||
app.use('/api/v1', api);
|
||||
|
||||
keys.load().catch((e) => console.error(e.stack));
|
||||
app.listen(port, () => console.log(`listening on ${port}`));
|
||||
async function load() {
|
||||
await keys.load();
|
||||
|
||||
if (logger.logToFile) {
|
||||
try {
|
||||
await fs.stat(logger.logDir);
|
||||
} catch {
|
||||
await fs.mkdir(logger.logDir);
|
||||
}
|
||||
}
|
||||
|
||||
app.listen(port, () => logger.info(`listening on ${port}`));
|
||||
}
|
||||
|
||||
load().catch((e) => console.error(e.stack));
|
||||
|
91
src/log/Logger.ts
Normal file
91
src/log/Logger.ts
Normal file
@ -0,0 +1,91 @@
|
||||
import { createWriteStream, WriteStream } from 'fs';
|
||||
import util from 'util';
|
||||
import path from 'path';
|
||||
|
||||
const p = (x: number) => x.toString().padStart(2, '0')
|
||||
|
||||
export enum LogLevel {
|
||||
Info = "INFO",
|
||||
Warn = "WARN",
|
||||
Error = "ERROR",
|
||||
Debug = "DEBUG"
|
||||
}
|
||||
|
||||
export class Logger {
|
||||
private fileName = '';
|
||||
private day = 0;
|
||||
private stream?: WriteStream;
|
||||
|
||||
constructor(public logDir: string, public logToFile = true) {
|
||||
this.day = new Date().getDate();
|
||||
}
|
||||
|
||||
static formatLogDate(date: Date): string {
|
||||
return `${date.getFullYear()}-${p(date.getMonth() + 1)}-${p(date.getDate())}`;
|
||||
}
|
||||
|
||||
static formatLogTime(date: Date): string {
|
||||
return `${p(date.getHours())}:${p(date.getMinutes())}:${p(date.getSeconds())}`;
|
||||
}
|
||||
|
||||
static formatLogDateTime(date: Date): string {
|
||||
return Logger.formatLogDate(date) + ' ' + Logger.formatLogTime(date);
|
||||
}
|
||||
|
||||
static fromEnvironment(): Logger {
|
||||
const logsPath = path.resolve(process.env.LOG_DIR || 'logs');
|
||||
const enableFileLog = process.env.LOG_FILES === "true" || true;
|
||||
return new Logger(logsPath, enableFileLog);
|
||||
}
|
||||
|
||||
public log(level: LogLevel, message: any, ...fmt: any[]): void {
|
||||
const input = util.format(message, ...fmt);
|
||||
const composed = `[${level.toString().padStart(5)}] [${Logger.formatLogDateTime(new Date())}] ${input}`;
|
||||
|
||||
if (level == LogLevel.Error) {
|
||||
process.stderr.write(`${composed}\r\n`);
|
||||
} else {
|
||||
process.stdout.write(`${composed}\r\n`);
|
||||
}
|
||||
|
||||
if (this.logToFile) {
|
||||
this.append(composed);
|
||||
}
|
||||
}
|
||||
|
||||
public info(message: any, ...fmt: any[]): void {
|
||||
this.log(LogLevel.Info, message, ...fmt);
|
||||
}
|
||||
|
||||
public error(message: any, ...fmt: any[]): void {
|
||||
this.log(LogLevel.Error, message, ...fmt);
|
||||
}
|
||||
|
||||
public warn(message: any, ...fmt: any[]): void {
|
||||
this.log(LogLevel.Warn, message, ...fmt);
|
||||
}
|
||||
|
||||
public debug(message: any, ...fmt: any[]): void {
|
||||
this.log(LogLevel.Debug, message, ...fmt);
|
||||
}
|
||||
|
||||
private updateOutputFile(): void {
|
||||
const date = new Date();
|
||||
if (this.day !== date.getDate() || !this.stream) {
|
||||
if (this.stream) {
|
||||
this.stream.close();
|
||||
}
|
||||
|
||||
this.day = date.getDate();
|
||||
this.fileName = `icydns-${Logger.formatLogDate(date)}.log`;
|
||||
this.stream = createWriteStream(path.join(this.logDir, this.fileName), { flags: 'a' })
|
||||
}
|
||||
}
|
||||
|
||||
private append(str: string): void {
|
||||
this.updateOutputFile();
|
||||
this.stream?.write(`${str}\n`);
|
||||
}
|
||||
}
|
||||
|
||||
export const logger = Logger.fromEnvironment();
|
Reference in New Issue
Block a user