mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
优化logger,修复serial与once同时使用导致的调用问题
This commit is contained in:
1 parent
6ba93841ea
commit
3e721fdd55
15 files changed
+260
-123
No files matched your search
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable no-console */
|
||||||
/* eslint-disable import/no-extraneous-dependencies */
|
/* eslint-disable import/no-extraneous-dependencies */
|
||||||
/* eslint-disable consistent-return */
|
/* eslint-disable consistent-return */
|
||||||
/* eslint-disable no-await-in-loop */
|
/* eslint-disable no-await-in-loop */
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable no-console */
|
||||||
/* eslint-disable import/no-extraneous-dependencies */
|
/* eslint-disable import/no-extraneous-dependencies */
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable no-console */
|
||||||
/* eslint-disable import/no-extraneous-dependencies */
|
/* eslint-disable import/no-extraneous-dependencies */
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import globby from 'globby';
|
import globby from 'globby';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hydrooj",
|
"name": "hydrooj",
|
||||||
"version": "2.13.5",
|
"version": "2.13.6",
|
||||||
"bin": "bin/hydrooj.js",
|
"bin": "bin/hydrooj.js",
|
||||||
"main": "dist/loader.js",
|
"main": "dist/loader.js",
|
||||||
"typings": "dist/loader.d.ts",
|
"typings": "dist/loader.d.ts",
|
||||||
@@ -31,6 +31,7 @@
|
|||||||
"serialize-javascript": "^4.0.0",
|
"serialize-javascript": "^4.0.0",
|
||||||
"sockjs": "^0.3.20",
|
"sockjs": "^0.3.20",
|
||||||
"superagent": "^6.1.0",
|
"superagent": "^6.1.0",
|
||||||
|
"supports-color": "^7.2.0",
|
||||||
"systeminformation": "^4.27.3",
|
"systeminformation": "^4.27.3",
|
||||||
"yargs": "^15.4.1"
|
"yargs": "^15.4.1"
|
||||||
},
|
},
|
||||||
@@ -49,6 +50,7 @@
|
|||||||
"@types/serialize-javascript": "^4.0.0",
|
"@types/serialize-javascript": "^4.0.0",
|
||||||
"@types/sockjs": "^0.3.32",
|
"@types/sockjs": "^0.3.32",
|
||||||
"@types/superagent": "^4.1.8",
|
"@types/superagent": "^4.1.8",
|
||||||
|
"@types/supports-color": "^5.3.0",
|
||||||
"@types/yargs": "^15.0.5"
|
"@types/yargs": "^15.0.5"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -3,7 +3,9 @@
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import yaml from 'js-yaml';
|
import yaml from 'js-yaml';
|
||||||
import { argv } from 'yargs';
|
import { Logger } from '../logger';
|
||||||
|
|
||||||
|
const logger = new Logger('common', true);
|
||||||
|
|
||||||
export const builtinLib = [
|
export const builtinLib = [
|
||||||
'jwt', 'download', 'i18n', 'mail', 'useragent',
|
'jwt', 'download', 'i18n', 'mail', 'useragent',
|
||||||
@@ -46,14 +48,12 @@ export async function handler(pending: string[], fail: string[]) {
|
|||||||
const p = path.resolve(i, 'handler.js');
|
const p = path.resolve(i, 'handler.js');
|
||||||
if (fs.existsSync(p) && !fail.includes(i)) {
|
if (fs.existsSync(p) && !fail.includes(i)) {
|
||||||
try {
|
try {
|
||||||
console.log(`Handler init: ${i}`);
|
logger.info('Handler init: %s', i);
|
||||||
console.time(`Handler init: ${i}`);
|
|
||||||
eval('require')(p);
|
eval('require')(p);
|
||||||
console.timeEnd(`Handler init: ${i}`);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
fail.push(i);
|
fail.push(i);
|
||||||
console.error(`Handler Load Fail: ${i}`);
|
logger.error('Handler Load Fail: %s', i);
|
||||||
if (argv.debug) console.error(e);
|
logger.error('%o', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -72,11 +72,11 @@ export async function locale(pending: string[], fail: string[]) {
|
|||||||
locales[file.split('.')[0]] = yaml.safeLoad(content);
|
locales[file.split('.')[0]] = yaml.safeLoad(content);
|
||||||
}
|
}
|
||||||
global.Hydro.lib.i18n(locales);
|
global.Hydro.lib.i18n(locales);
|
||||||
console.log(`Locale init: ${i}`);
|
logger.info('Locale init: %s', i);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
fail.push(i);
|
fail.push(i);
|
||||||
console.error(`Locale Load Fail: ${i}`);
|
logger.error('Locale Load Fail: %s', i);
|
||||||
if (argv.debug) console.error(e);
|
logger.error('%o', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -105,8 +105,8 @@ export async function setting(pending: string[], fail: string[], modelSetting: t
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`Config Load Fail: ${i}`);
|
logger.error('Config Load Fail: %s', i);
|
||||||
if (argv.debug) console.error(e);
|
logger.error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -124,11 +124,11 @@ export async function template(pending: string[], fail: string[]) {
|
|||||||
path.resolve(p, file),
|
path.resolve(p, file),
|
||||||
).toString();
|
).toString();
|
||||||
}
|
}
|
||||||
console.log(`Template init: ${i}`);
|
logger.info('Template init: %s', i);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
fail.push(i);
|
fail.push(i);
|
||||||
console.error(`Template Load Fail: ${i}`);
|
logger.error('Template Load Fail: %s', i);
|
||||||
if (argv.debug) console.error(e);
|
logger.error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -152,14 +152,12 @@ export async function model(pending: string[], fail: string[]) {
|
|||||||
const p = path.resolve(i, 'model.js');
|
const p = path.resolve(i, 'model.js');
|
||||||
if (fs.existsSync(p) && !fail.includes(i)) {
|
if (fs.existsSync(p) && !fail.includes(i)) {
|
||||||
try {
|
try {
|
||||||
console.log(`Model init: ${i}`);
|
logger.info('Model init: %s', i);
|
||||||
console.time(`Model init: ${i}`);
|
|
||||||
eval('require')(p);
|
eval('require')(p);
|
||||||
console.timeEnd(`Model init: ${i}`);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
fail.push(i);
|
fail.push(i);
|
||||||
console.error(`Model Load Fail: ${i}`);
|
logger.error('Model Load Fail: %s', i);
|
||||||
if (argv.debug) console.error(e);
|
logger.error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -170,14 +168,12 @@ export async function lib(pending: string[], fail: string[]) {
|
|||||||
const p = path.resolve(i, 'lib.js');
|
const p = path.resolve(i, 'lib.js');
|
||||||
if (fs.existsSync(p) && !fail.includes(i)) {
|
if (fs.existsSync(p) && !fail.includes(i)) {
|
||||||
try {
|
try {
|
||||||
console.log(`Lib init: ${i}`);
|
logger.info('Lib init: %s', i);
|
||||||
console.time(`Lib init: ${i}`);
|
|
||||||
eval('require')(p);
|
eval('require')(p);
|
||||||
console.timeEnd(`Lib init: ${i}`);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
fail.push(i);
|
fail.push(i);
|
||||||
console.error(`Lib Load Fail: ${i}`);
|
logger.error('Lib Load Fail: %s', i);
|
||||||
if (argv.debug) console.error(e);
|
logger.error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -188,14 +184,12 @@ export async function service(pending: string[], fail: string[]) {
|
|||||||
const p = path.resolve(i, 'service.js');
|
const p = path.resolve(i, 'service.js');
|
||||||
if (fs.existsSync(p) && !fail.includes(i)) {
|
if (fs.existsSync(p) && !fail.includes(i)) {
|
||||||
try {
|
try {
|
||||||
console.log(`Service init: ${i}`);
|
logger.info('Service init: %s', i);
|
||||||
console.time(`Service init: ${i}`);
|
|
||||||
eval('require')(p);
|
eval('require')(p);
|
||||||
console.timeEnd(`Service init: ${i}`);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
fail.push(i);
|
fail.push(i);
|
||||||
console.error(`Service Load Fail: ${i}`);
|
logger.error('Service Load Fail: %s', i);
|
||||||
if (argv.debug) console.error(e);
|
logger.error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -206,13 +200,12 @@ export async function script(pending: string[], fail: string[], active: string[]
|
|||||||
const p = path.resolve(i, 'script.js');
|
const p = path.resolve(i, 'script.js');
|
||||||
if (fs.existsSync(p) && !fail.includes(i)) {
|
if (fs.existsSync(p) && !fail.includes(i)) {
|
||||||
try {
|
try {
|
||||||
console.time(`Script init: ${i}`);
|
logger.info('Script init: %s', i);
|
||||||
eval('require')(p);
|
eval('require')(p);
|
||||||
console.timeEnd(`Script init: ${i}`);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
fail.push(i);
|
fail.push(i);
|
||||||
console.error(`Script Load Fail: ${i}`);
|
logger.error('Script Load Fail: %s', i);
|
||||||
if (argv.debug) console.error(e);
|
logger.error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
active.push(i);
|
active.push(i);
|
||||||
|
|||||||
@@ -47,11 +47,7 @@ export async function load(call: Entry) {
|
|||||||
fs.removeSync(lockfile);
|
fs.removeSync(lockfile);
|
||||||
});
|
});
|
||||||
await new Promise((resolve) => {
|
await new Promise((resolve) => {
|
||||||
const h = () => {
|
bus.once('database/connect', resolve);
|
||||||
console.log('Database connected');
|
|
||||||
resolve();
|
|
||||||
};
|
|
||||||
bus.once('database/connect', h);
|
|
||||||
require('../service/db');
|
require('../service/db');
|
||||||
});
|
});
|
||||||
require('../service/monitor');
|
require('../service/monitor');
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ import Body from 'koa-body';
|
|||||||
import cache from 'koa-static-cache';
|
import cache from 'koa-static-cache';
|
||||||
import mongodb from 'mongodb';
|
import mongodb from 'mongodb';
|
||||||
import { argv } from 'yargs';
|
import { argv } from 'yargs';
|
||||||
|
import { Logger } from '../logger';
|
||||||
|
|
||||||
|
const logger = new Logger('setup');
|
||||||
|
|
||||||
const listenPort = argv.port || 8888;
|
const listenPort = argv.port || 8888;
|
||||||
|
|
||||||
@@ -141,5 +144,5 @@ export function load() {
|
|||||||
return get(ctx);
|
return get(ctx);
|
||||||
});
|
});
|
||||||
server.listen(listenPort);
|
server.listen(listenPort);
|
||||||
console.log('Server listening at:', listenPort);
|
logger.success('Server listening at: %d', listenPort);
|
||||||
}
|
}
|
||||||
@@ -20,11 +20,7 @@ export async function load() {
|
|||||||
uistatic(pending, fail),
|
uistatic(pending, fail),
|
||||||
]);
|
]);
|
||||||
await new Promise((resolve) => {
|
await new Promise((resolve) => {
|
||||||
const h = () => {
|
bus.once('database/connect', resolve);
|
||||||
console.log('Database connected');
|
|
||||||
resolve();
|
|
||||||
};
|
|
||||||
bus.once('database/connect', h);
|
|
||||||
require('../service/db');
|
require('../service/db');
|
||||||
});
|
});
|
||||||
for (const i of builtinLib) require(`../lib/${i}`);
|
for (const i of builtinLib) require(`../lib/${i}`);
|
||||||
|
|||||||
@@ -510,6 +510,7 @@ declare global {
|
|||||||
stat: any,
|
stat: any,
|
||||||
ui: UI,
|
ui: UI,
|
||||||
error: typeof import('./error'),
|
error: typeof import('./error'),
|
||||||
|
Logger: typeof import('./logger'),
|
||||||
locales: Dict<Dict<string>>,
|
locales: Dict<Dict<string>>,
|
||||||
},
|
},
|
||||||
addons: string[],
|
addons: string[],
|
||||||
|
|||||||
@@ -40,14 +40,14 @@ if (!global.Hydro) {
|
|||||||
global.addons = [];
|
global.addons = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argv.debug) {
|
// eslint-disable-next-line import/first
|
||||||
console.log(process.argv);
|
import { Logger } from './logger';
|
||||||
console.log(argv);
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line import/first
|
// eslint-disable-next-line import/first
|
||||||
import * as bus from './service/bus';
|
import * as bus from './service/bus';
|
||||||
|
|
||||||
|
const logger = new Logger('loader');
|
||||||
|
logger.debug('%o', argv);
|
||||||
|
|
||||||
async function terminate() {
|
async function terminate() {
|
||||||
try {
|
try {
|
||||||
await bus.parallel('app/exit');
|
await bus.parallel('app/exit');
|
||||||
@@ -113,9 +113,9 @@ async function executeCommand(input: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function reload(count = 1) {
|
async function reload(count = 1) {
|
||||||
console.log('Reloading');
|
logger.info('Reloading');
|
||||||
await stopWorker();
|
await stopWorker();
|
||||||
console.log('Worker stopped');
|
logger.info('Worker stopped');
|
||||||
await startWorker(count);
|
await startWorker(count);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,13 +176,13 @@ export function addon(addonPath: string) {
|
|||||||
if (fs.existsSync(publicPath)) fs.copySync(publicPath, publicTemp);
|
if (fs.existsSync(publicPath)) fs.copySync(publicPath, publicTemp);
|
||||||
global.addons.push(targetPath);
|
global.addons.push(targetPath);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Addon load fail: ', e);
|
logger.error('Addon load fail: ', e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
} else throw new Error(`Addon not found: ${addonPath}`);
|
} else throw new Error(`Addon not found: ${addonPath}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
process.on('unhandledRejection', (e) => console.error(e));
|
process.on('unhandledRejection', logger.error);
|
||||||
process.on('SIGINT', terminate);
|
process.on('SIGINT', terminate);
|
||||||
|
|
||||||
export async function load() {
|
export async function load() {
|
||||||
@@ -191,7 +191,7 @@ export async function load() {
|
|||||||
process.on('message', messageHandler);
|
process.on('message', messageHandler);
|
||||||
cluster.on('message', messageHandler);
|
cluster.on('message', messageHandler);
|
||||||
if (cluster.isMaster || argv.startAsMaster) {
|
if (cluster.isMaster || argv.startAsMaster) {
|
||||||
console.log(`Master ${process.pid} Starting`);
|
logger.info(`Master ${process.pid} Starting`);
|
||||||
process.stdin.setEncoding('utf8');
|
process.stdin.setEncoding('utf8');
|
||||||
process.stdin.on('data', (buf) => {
|
process.stdin.on('data', (buf) => {
|
||||||
const input = buf.toString();
|
const input = buf.toString();
|
||||||
@@ -205,33 +205,33 @@ export async function load() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
const cnt = await entry({ entry: 'master' });
|
const cnt = await entry({ entry: 'master' });
|
||||||
console.log('Master started');
|
logger.info('Master started');
|
||||||
cluster.on('exit', (worker, code, signal) => {
|
cluster.on('exit', (worker, code, signal) => {
|
||||||
console.log(`Worker ${worker.process.pid} ${worker.id} exit: ${code} ${signal}`);
|
logger.info(`Worker ${worker.process.pid} ${worker.id} exit: ${code} ${signal}`);
|
||||||
});
|
});
|
||||||
cluster.on('disconnect', (worker) => {
|
cluster.on('disconnect', (worker) => {
|
||||||
console.log(`Worker ${worker.process.pid} ${worker.id} disconnected`);
|
logger.info(`Worker ${worker.process.pid} ${worker.id} disconnected`);
|
||||||
});
|
});
|
||||||
cluster.on('listening', (worker, address) => {
|
cluster.on('listening', (worker, address) => {
|
||||||
console.log(`Worker ${worker.process.pid} ${worker.id} listening at `, address);
|
logger.info(`Worker ${worker.process.pid} ${worker.id} listening at `, address);
|
||||||
});
|
});
|
||||||
cluster.on('online', (worker) => {
|
cluster.on('online', (worker) => {
|
||||||
console.log(`Worker ${worker.process.pid} ${worker.id} is online`);
|
logger.success(`Worker ${worker.process.pid} ${worker.id} is online`);
|
||||||
});
|
});
|
||||||
await startWorker(cnt);
|
await startWorker(cnt);
|
||||||
} else {
|
} else {
|
||||||
global.addons = JSON.parse(Buffer.from(argv.addons as string, 'base64').toString());
|
global.addons = JSON.parse(Buffer.from(argv.addons as string, 'base64').toString());
|
||||||
console.log(global.addons);
|
logger.info('%o', global.addons);
|
||||||
if (argv.entry) {
|
if (argv.entry) {
|
||||||
console.log(`Worker ${process.pid} Starting as ${argv.entry}`);
|
logger.info(`Worker ${process.pid} Starting as ${argv.entry}`);
|
||||||
await entry({ entry: argv.entry as string });
|
await entry({ entry: argv.entry as string });
|
||||||
console.log(`Worker ${process.pid} Started as ${argv.entry}`);
|
logger.success(`Worker ${process.pid} Started as ${argv.entry}`);
|
||||||
} else {
|
} else {
|
||||||
if (argv.firstWorker) cluster.isFirstWorker = true;
|
if (argv.firstWorker) cluster.isFirstWorker = true;
|
||||||
else cluster.isFirstWorker = false;
|
else cluster.isFirstWorker = false;
|
||||||
console.log(`Worker ${process.pid} Starting`);
|
logger.info(`Worker ${process.pid} Starting`);
|
||||||
await entry({ entry: 'worker' });
|
await entry({ entry: 'worker' });
|
||||||
console.log(`Worker ${process.pid} Started`);
|
logger.success(`Worker ${process.pid} Started`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (global.gc) global.gc();
|
if (global.gc) global.gc();
|
||||||
@@ -245,7 +245,7 @@ export async function loadCli() {
|
|||||||
if (argv.pandora || !module.parent) {
|
if (argv.pandora || !module.parent) {
|
||||||
const func = argv._[0] === 'cli' ? load : loadCli;
|
const func = argv._[0] === 'cli' ? load : loadCli;
|
||||||
func().catch((e) => {
|
func().catch((e) => {
|
||||||
console.error(e);
|
logger.error(e);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
import { inspect, InspectOptions, format } from 'util';
|
||||||
|
import { argv } from 'yargs';
|
||||||
|
import { stderr } from 'supports-color';
|
||||||
|
|
||||||
|
namespace Time {
|
||||||
|
export const second = 1000;
|
||||||
|
export const minute = second * 60;
|
||||||
|
export const hour = minute * 60;
|
||||||
|
export const day = hour * 24;
|
||||||
|
export const week = day * 7;
|
||||||
|
export function formatTimeShort(ms: number) {
|
||||||
|
const abs = Math.abs(ms);
|
||||||
|
if (abs >= day - hour / 2) {
|
||||||
|
return `${Math.round(ms / day)}d`;
|
||||||
|
} if (abs >= hour - minute / 2) {
|
||||||
|
return `${Math.round(ms / hour)}h`;
|
||||||
|
} if (abs >= minute - second / 2) {
|
||||||
|
return `${Math.round(ms / minute)}m`;
|
||||||
|
} if (abs >= second) {
|
||||||
|
return `${Math.round(ms / second)}s`;
|
||||||
|
}
|
||||||
|
return `${ms}ms`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const colors = stderr.level < 2 ? [6, 2, 3, 4, 5, 1] : [
|
||||||
|
20, 21, 26, 27, 32, 33, 38, 39, 40, 41, 42, 43, 44, 45, 56, 57, 62,
|
||||||
|
63, 68, 69, 74, 75, 76, 77, 78, 79, 80, 81, 92, 93, 98, 99, 112, 113,
|
||||||
|
129, 134, 135, 148, 149, 160, 161, 162, 163, 164, 165, 166, 167, 168,
|
||||||
|
169, 170, 171, 172, 173, 178, 179, 184, 185, 196, 197, 198, 199, 200,
|
||||||
|
201, 202, 203, 204, 205, 206, 207, 208, 209, 214, 215, 220, 221,
|
||||||
|
];
|
||||||
|
|
||||||
|
const instances: Record<string, Logger> = {};
|
||||||
|
|
||||||
|
type LogFunction = (format: any, ...param: any[]) => void
|
||||||
|
|
||||||
|
type LogType = 'success' | 'error' | 'info' | 'warn' | 'debug'
|
||||||
|
|
||||||
|
export interface Logger extends Record<LogType, LogFunction> { }
|
||||||
|
|
||||||
|
export class Logger {
|
||||||
|
static readonly SUCCESS = 1
|
||||||
|
|
||||||
|
static readonly ERROR = 1
|
||||||
|
|
||||||
|
static readonly INFO = 2
|
||||||
|
|
||||||
|
static readonly WARN = 2
|
||||||
|
|
||||||
|
static readonly DEBUG = 3
|
||||||
|
|
||||||
|
static baseLevel = argv.debug ? 3 : 2
|
||||||
|
|
||||||
|
static showDiff = false
|
||||||
|
|
||||||
|
static levels: Record<string, number> = {}
|
||||||
|
|
||||||
|
static lastTime = 0
|
||||||
|
|
||||||
|
static options: InspectOptions = {
|
||||||
|
colors: stderr.hasBasic,
|
||||||
|
}
|
||||||
|
|
||||||
|
static formatters: Record<string, (this: Logger, value: any) => string> = {
|
||||||
|
c: Logger.prototype.color,
|
||||||
|
C: (value) => Logger.color(15, value, ';1'),
|
||||||
|
o: (value) => inspect(value, Logger.options).replace(/\s*\n\s*/g, ' '),
|
||||||
|
}
|
||||||
|
|
||||||
|
static color(code: number, value: any, decoration = '') {
|
||||||
|
if (!Logger.options.colors) return `${value}`;
|
||||||
|
return `\u001B[3${code < 8 ? code : `8;5;${code}`}${decoration}m${value}\u001B[0m`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private code: number
|
||||||
|
|
||||||
|
private displayName: string
|
||||||
|
|
||||||
|
public stream: NodeJS.WritableStream = process.stderr
|
||||||
|
|
||||||
|
constructor(public name: string, private showDiff = false) {
|
||||||
|
if (name in instances) return instances[name];
|
||||||
|
let hash = 0;
|
||||||
|
for (let i = 0; i < name.length; i++) {
|
||||||
|
hash = ((hash << 3) - hash) + name.charCodeAt(i);
|
||||||
|
hash |= 0;
|
||||||
|
}
|
||||||
|
instances[name] = this;
|
||||||
|
this.code = colors[Math.abs(hash) % colors.length];
|
||||||
|
this.displayName = name ? this.color(`${name} `, ';1') : '';
|
||||||
|
this.createMethod('success', '[S] ', Logger.SUCCESS);
|
||||||
|
this.createMethod('error', '[E] ', Logger.ERROR);
|
||||||
|
this.createMethod('info', '[I] ', Logger.INFO);
|
||||||
|
this.createMethod('warn', '[W] ', Logger.WARN);
|
||||||
|
this.createMethod('debug', '[D] ', Logger.DEBUG);
|
||||||
|
}
|
||||||
|
|
||||||
|
private color(value: any, decoration = '') {
|
||||||
|
return Logger.color(this.code, value, decoration);
|
||||||
|
}
|
||||||
|
|
||||||
|
private createMethod(name: LogType, prefix: string, minLevel: number) {
|
||||||
|
this[name] = (...args: [any, ...any[]]) => {
|
||||||
|
if (this.level < minLevel) return;
|
||||||
|
this.stream.write(`${prefix + this.displayName + this.format(...args)}\n`);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
get level() {
|
||||||
|
return Logger.levels[this.name] ?? Logger.baseLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
extend = (namespace: string, showDiff = this.showDiff) => new Logger(`${this.name}:${namespace}`, showDiff)
|
||||||
|
|
||||||
|
format: (format: any, ...param: any[]) => string = (...args) => {
|
||||||
|
if (args[0] instanceof Error) args[0] = args[0].stack || args[0].message;
|
||||||
|
else if (typeof args[0] !== 'string') args.unshift('%O');
|
||||||
|
let index = 0;
|
||||||
|
args[0] = (args[0] as string).replace(/%([a-zA-Z%])/g, (match, fmt) => {
|
||||||
|
if (match === '%%') return '%';
|
||||||
|
index += 1;
|
||||||
|
const formatter = Logger.formatters[fmt];
|
||||||
|
if (typeof formatter === 'function') {
|
||||||
|
match = formatter.call(this, args[index]);
|
||||||
|
args.splice(index, 1);
|
||||||
|
index -= 1;
|
||||||
|
}
|
||||||
|
return match;
|
||||||
|
}).split('\n').join('\n ');
|
||||||
|
if (Logger.showDiff || this.showDiff) {
|
||||||
|
const now = Date.now();
|
||||||
|
if (Logger.lastTime) {
|
||||||
|
args.push(this.color(`+${Time.formatTimeShort(now - Logger.lastTime)}`));
|
||||||
|
}
|
||||||
|
Logger.lastTime = now;
|
||||||
|
}
|
||||||
|
return format(...args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
global.Hydro.Logger = { Logger };
|
||||||
@@ -528,8 +528,8 @@ export function count(domainId: string, query: any, type: Type = document.TYPE_C
|
|||||||
return document.count(domainId, type, query);
|
return document.count(domainId, type, query);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getMulti<K extends Type & keyof document.DocumentType>(
|
export function getMulti<K extends Type & keyof document.DocType>(
|
||||||
domainId: string, query: FilterQuery<document.DocumentType[K]> = {}, type?: K,
|
domainId: string, query: FilterQuery<document.DocType[K]> = {}, type?: K,
|
||||||
) {
|
) {
|
||||||
return document.getMulti(domainId, type || document.TYPE_CONTEST, query);
|
return document.getMulti(domainId, type || document.TYPE_CONTEST, query);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export const TYPE_TRAINING = 40;
|
|||||||
export const TYPE_FILE = 50;
|
export const TYPE_FILE = 50;
|
||||||
export const TYPE_HOMEWORK = 60;
|
export const TYPE_HOMEWORK = 60;
|
||||||
|
|
||||||
export interface DocumentType {
|
export interface DocType {
|
||||||
[TYPE_PROBLEM]: Pdoc,
|
[TYPE_PROBLEM]: Pdoc,
|
||||||
[TYPE_PROBLEM_SOLUTION]: any,
|
[TYPE_PROBLEM_SOLUTION]: any,
|
||||||
[TYPE_PROBLEM_LIST]: any,
|
[TYPE_PROBLEM_LIST]: any,
|
||||||
@@ -35,13 +35,13 @@ export interface DocumentType {
|
|||||||
[TYPE_HOMEWORK]: Tdoc,
|
[TYPE_HOMEWORK]: Tdoc,
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function add<T extends DocID, K extends keyof DocumentType>(
|
export async function add<T extends DocID, K extends keyof DocType>(
|
||||||
domainId: string, content: string, owner: number,
|
domainId: string, content: string, owner: number,
|
||||||
docType: K, docId: DocumentType[K]['docId'],
|
docType: K, docId: DocType[K]['docId'],
|
||||||
parentType?: number | null, parentId?: DocID,
|
parentType?: number | null, parentId?: DocID,
|
||||||
args?: any,
|
args?: any,
|
||||||
): Promise<T>
|
): Promise<T>
|
||||||
export async function add<K extends keyof DocumentType>(
|
export async function add<K extends keyof DocType>(
|
||||||
domainId: string, content: string, owner: number,
|
domainId: string, content: string, owner: number,
|
||||||
docType: K, docId: null,
|
docType: K, docId: null,
|
||||||
parentType?: number, parentId?: DocID,
|
parentType?: number, parentId?: DocID,
|
||||||
@@ -73,13 +73,13 @@ export async function add(
|
|||||||
return docId || res.insertedId;
|
return docId || res.insertedId;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function get<K extends keyof DocumentType>(domainId: string, docType: K, docId: DocumentType[K]['docId']) {
|
export function get<K extends keyof DocType>(domainId: string, docType: K, docId: DocType[K]['docId']) {
|
||||||
return coll.findOne({ domainId, docType, docId });
|
return coll.findOne({ domainId, docType, docId });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function set<K extends keyof DocumentType>(
|
export async function set<K extends keyof DocType>(
|
||||||
domainId: string, docType: K, docId: DocumentType[K]['docId'], $set: Partial<DocumentType[K]>,
|
domainId: string, docType: K, docId: DocType[K]['docId'], $set: Partial<DocType[K]>,
|
||||||
): Promise<DocumentType[K]> {
|
): Promise<DocType[K]> {
|
||||||
await bus.parallel('document/set', domainId, docType, docId, $set);
|
await bus.parallel('document/set', domainId, docType, docId, $set);
|
||||||
const res = await coll.findOneAndUpdate(
|
const res = await coll.findOneAndUpdate(
|
||||||
{ domainId, docType, docId },
|
{ domainId, docType, docId },
|
||||||
@@ -89,8 +89,8 @@ export async function set<K extends keyof DocumentType>(
|
|||||||
return res.value;
|
return res.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deleteOne<K extends keyof DocumentType>(
|
export function deleteOne<K extends keyof DocType>(
|
||||||
domainId: string, docType: K, docId: DocumentType[K]['docId'],
|
domainId: string, docType: K, docId: DocType[K]['docId'],
|
||||||
) {
|
) {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
collStatus.deleteMany({ domainId, docType, docId }),
|
collStatus.deleteMany({ domainId, docType, docId }),
|
||||||
@@ -98,27 +98,27 @@ export function deleteOne<K extends keyof DocumentType>(
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deleteMulti<K extends keyof DocumentType>(
|
export function deleteMulti<K extends keyof DocType>(
|
||||||
domainId: string, docType: K, query?: FilterQuery<DocumentType[K]>,
|
domainId: string, docType: K, query?: FilterQuery<DocType[K]>,
|
||||||
) {
|
) {
|
||||||
return coll.deleteMany({ ...query, domainId, docType });
|
return coll.deleteMany({ ...query, domainId, docType });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deleteMultiStatus<K extends keyof DocumentType>(
|
export function deleteMultiStatus<K extends keyof DocType>(
|
||||||
domainId: string, docType: K, query?: FilterQuery<DocumentType[K]>,
|
domainId: string, docType: K, query?: FilterQuery<DocType[K]>,
|
||||||
) {
|
) {
|
||||||
return collStatus.deleteMany({ ...query, domainId, docType });
|
return collStatus.deleteMany({ ...query, domainId, docType });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getMulti<K extends keyof DocumentType>(
|
export function getMulti<K extends keyof DocType>(
|
||||||
domainId: string, docType: K, query?: FilterQuery<DocumentType[K]>,
|
domainId: string, docType: K, query?: FilterQuery<DocType[K]>,
|
||||||
): Cursor<DocumentType[K]> {
|
): Cursor<DocType[K]> {
|
||||||
return coll.find({ ...query, docType, domainId });
|
return coll.find({ ...query, docType, domainId });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function inc<K extends keyof DocumentType>(
|
export async function inc<K extends keyof DocType>(
|
||||||
domainId: string, docType: K, docId: DocumentType[K]['docId'],
|
domainId: string, docType: K, docId: DocType[K]['docId'],
|
||||||
key: NumberKeys<DocumentType[K]>, value: number,
|
key: NumberKeys<DocType[K]>, value: number,
|
||||||
) {
|
) {
|
||||||
const res = await coll.findOneAndUpdate(
|
const res = await coll.findOneAndUpdate(
|
||||||
{ domainId, docType, docId },
|
{ domainId, docType, docId },
|
||||||
@@ -128,11 +128,11 @@ export async function inc<K extends keyof DocumentType>(
|
|||||||
return res.value;
|
return res.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Q = NumberKeys<DocumentType[10]>
|
type Q = NumberKeys<DocType[10]>
|
||||||
|
|
||||||
export async function incAndSet<K extends keyof DocumentType>(
|
export async function incAndSet<K extends keyof DocType>(
|
||||||
domainId: string, docType: K, docId: DocumentType[K]['docId'],
|
domainId: string, docType: K, docId: DocType[K]['docId'],
|
||||||
key: NumberKeys<DocumentType[K]>, value: number, args: Partial<DocumentType[K]>,
|
key: NumberKeys<DocType[K]>, value: number, args: Partial<DocType[K]>,
|
||||||
) {
|
) {
|
||||||
const res = await coll.findOneAndUpdate(
|
const res = await coll.findOneAndUpdate(
|
||||||
{ domainId, docType, docId },
|
{ domainId, docType, docId },
|
||||||
@@ -142,15 +142,15 @@ export async function incAndSet<K extends keyof DocumentType>(
|
|||||||
return res.value;
|
return res.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function count<K extends keyof DocumentType>(
|
export function count<K extends keyof DocType>(
|
||||||
domainId: string, docType: K, query?: FilterQuery<DocumentType[K]>,
|
domainId: string, docType: K, query?: FilterQuery<DocType[K]>,
|
||||||
) {
|
) {
|
||||||
return coll.find({ ...query, docType, domainId }).count();
|
return coll.find({ ...query, docType, domainId }).count();
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function push<K extends keyof DocumentType, T extends keyof DocumentType[K]>(
|
export async function push<K extends keyof DocType, T extends keyof DocType[K]>(
|
||||||
domainId: string, docType: K, docId: DocumentType[K]['docId'],
|
domainId: string, docType: K, docId: DocType[K]['docId'],
|
||||||
key: keyof DocumentType[K], content: string, owner: number, args: DocumentType[K][T][0] = {},
|
key: keyof DocType[K], content: string, owner: number, args: DocType[K][T][0] = {},
|
||||||
) {
|
) {
|
||||||
const _id = new ObjectID();
|
const _id = new ObjectID();
|
||||||
const doc = await coll.findOneAndUpdate(
|
const doc = await coll.findOneAndUpdate(
|
||||||
@@ -167,10 +167,10 @@ export async function push<K extends keyof DocumentType, T extends keyof Documen
|
|||||||
return [doc.value, _id];
|
return [doc.value, _id];
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function pull<K extends keyof DocumentType>(
|
export async function pull<K extends keyof DocType>(
|
||||||
domainId: string, docType: K, docId: DocumentType[K]['docId'],
|
domainId: string, docType: K, docId: DocType[K]['docId'],
|
||||||
setKey: string, contents: string[],
|
setKey: string, contents: string[],
|
||||||
): Promise<DocumentType[K]> {
|
): Promise<DocType[K]> {
|
||||||
const res = await coll.findOneAndUpdate(
|
const res = await coll.findOneAndUpdate(
|
||||||
{ domainId, docType, docId },
|
{ domainId, docType, docId },
|
||||||
{ $pull: { [setKey]: { $in: contents } } },
|
{ $pull: { [setKey]: { $in: contents } } },
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
/* eslint-disable no-await-in-loop */
|
/* eslint-disable no-await-in-loop */
|
||||||
import cluster from 'cluster';
|
import cluster from 'cluster';
|
||||||
import { Db, ObjectID } from 'mongodb';
|
import { Db, UpdateQuery } from 'mongodb';
|
||||||
|
import { Logger } from '../logger';
|
||||||
import { Mdoc, Rdoc, User } from '../interface';
|
import { Mdoc, Rdoc, User } from '../interface';
|
||||||
|
// only typings.
|
||||||
|
// eslint-disable-next-line import/no-cycle
|
||||||
|
import { DocType } from '../model/document';
|
||||||
|
|
||||||
const _hooks: Record<keyof any, Array<(...args: any[]) => any>> = {};
|
const _hooks: Record<keyof any, Array<(...args: any[]) => any>> = {};
|
||||||
const _disposables = [];
|
const logger = new Logger('bus', true);
|
||||||
|
|
||||||
function isBailed(value: any) {
|
function isBailed(value: any) {
|
||||||
return value !== null && value !== false && value !== undefined;
|
return value !== null && value !== false && value !== undefined;
|
||||||
@@ -12,7 +16,7 @@ function isBailed(value: any) {
|
|||||||
|
|
||||||
export type Disposable = () => void
|
export type Disposable = () => void
|
||||||
|
|
||||||
interface EventMap {
|
export interface EventMap {
|
||||||
'app/started': () => void
|
'app/started': () => void
|
||||||
'app/exit': () => Promise<void> | void
|
'app/exit': () => Promise<void> | void
|
||||||
'dispose': () => void
|
'dispose': () => void
|
||||||
@@ -22,15 +26,16 @@ interface EventMap {
|
|||||||
'user/message': (uid: number, mdoc: Mdoc, udoc: User) => void
|
'user/message': (uid: number, mdoc: Mdoc, udoc: User) => void
|
||||||
|
|
||||||
'document/add': (doc: any) => Promise<void> | void
|
'document/add': (doc: any) => Promise<void> | void
|
||||||
'document/set': (domainId: string, docType: number, docId: ObjectID | string | number, $set: any) => Promise<void> | void
|
'document/set': <T extends keyof DocType>
|
||||||
|
(domainId: string, docType: T, docId: DocType[T], $set: UpdateQuery<DocType[T]>['$set']) => Promise<void> | void
|
||||||
|
|
||||||
'record/change': (rdoc: Rdoc, $set?: any, $push?: any) => void
|
'record/change': (rdoc: Rdoc, $set?: UpdateQuery<Rdoc>['$set'], $push?: UpdateQuery<Rdoc>['$push']) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
function getHooks<K extends keyof EventMap>(name: K) {
|
function getHooks<K extends keyof EventMap>(name: K) {
|
||||||
const hooks = _hooks[name] || (_hooks[name] = []);
|
const hooks = _hooks[name] || (_hooks[name] = []);
|
||||||
if (hooks.length >= 128) {
|
if (hooks.length >= 128) {
|
||||||
console.warn(
|
logger.warn(
|
||||||
'max listener count (128) for event "%s" exceeded, which may be caused by a memory leak',
|
'max listener count (128) for event "%s" exceeded, which may be caused by a memory leak',
|
||||||
name,
|
name,
|
||||||
);
|
);
|
||||||
@@ -49,23 +54,17 @@ export function removeListener<K extends keyof EventMap>(name: K, listener: Even
|
|||||||
|
|
||||||
export function addListener<K extends keyof EventMap>(name: K, listener: EventMap[K]) {
|
export function addListener<K extends keyof EventMap>(name: K, listener: EventMap[K]) {
|
||||||
getHooks(name).push(listener);
|
getHooks(name).push(listener);
|
||||||
const dispose = () => removeListener(name, listener);
|
return () => removeListener(name, listener);
|
||||||
_disposables.push(name === 'dispose' ? listener as Disposable : dispose);
|
|
||||||
return dispose;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function prependListener<K extends keyof EventMap>(name: K, listener: EventMap[K]) {
|
export function prependListener<K extends keyof EventMap>(name: K, listener: EventMap[K]) {
|
||||||
getHooks(name).unshift(listener);
|
getHooks(name).unshift(listener);
|
||||||
const dispose = () => removeListener(name, listener);
|
return () => removeListener(name, listener);
|
||||||
_disposables.push(name === 'dispose' ? listener as Disposable : dispose);
|
|
||||||
return dispose;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function once<K extends keyof EventMap>(name: K, listener: EventMap[K]) {
|
export function once<K extends keyof EventMap>(name: K, listener: EventMap[K]) {
|
||||||
// @ts-ignore
|
const dispose = addListener(name, function _listener(...args: any[]) {
|
||||||
const dispose = addListener(name, (...args: Parameters<EventMap[K]>) => {
|
|
||||||
dispose();
|
dispose();
|
||||||
// @ts-ignore
|
|
||||||
return listener.apply(this, args);
|
return listener.apply(this, args);
|
||||||
});
|
});
|
||||||
return dispose;
|
return dispose;
|
||||||
@@ -91,12 +90,12 @@ export function emit<K extends keyof EventMap>(name: K, ...args: Parameters<Even
|
|||||||
return parallel(name, ...args);
|
return parallel(name, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function serial<K extends keyof EventMap>(name: K, ...args: Parameters<EventMap[K]>): Promise<ReturnType<EventMap[K]>> {
|
export async function serial<K extends keyof EventMap>(name: K, ...args: Parameters<EventMap[K]>): Promise<void> {
|
||||||
for (const callback of _hooks[name] || []) {
|
logger.debug('serial: %s', name, args);
|
||||||
const result = await callback.apply(this, args);
|
const hooks = Array.from(_hooks[name]);
|
||||||
if (isBailed(result)) return result;
|
for (const callback of hooks) {
|
||||||
|
await callback.apply(this, args);
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function bail<K extends keyof EventMap>(name: K, ...args: Parameters<EventMap[K]>): ReturnType<EventMap[K]> {
|
export function bail<K extends keyof EventMap>(name: K, ...args: Parameters<EventMap[K]>): ReturnType<EventMap[K]> {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import serialize, { SerializeJSOptions } from 'serialize-javascript';
|
|||||||
import { argv } from 'yargs';
|
import { argv } from 'yargs';
|
||||||
import { lrucache } from '../utils';
|
import { lrucache } from '../utils';
|
||||||
import { User, DomainDoc } from '../interface';
|
import { User, DomainDoc } from '../interface';
|
||||||
|
import { Logger } from '../logger';
|
||||||
import {
|
import {
|
||||||
UserNotFoundError, BlacklistedError, PermissionError,
|
UserNotFoundError, BlacklistedError, PermissionError,
|
||||||
UserFacingError, ValidationError, PrivilegeError,
|
UserFacingError, ValidationError, PrivilegeError,
|
||||||
@@ -33,6 +34,7 @@ import * as blacklist from '../model/blacklist';
|
|||||||
import * as token from '../model/token';
|
import * as token from '../model/token';
|
||||||
import * as opcount from '../model/opcount';
|
import * as opcount from '../model/opcount';
|
||||||
|
|
||||||
|
const logger = new Logger('server');
|
||||||
export const app = new Koa();
|
export const app = new Koa();
|
||||||
export const server = http.createServer(app.callback());
|
export const server = http.createServer(app.callback());
|
||||||
export const router = new Router();
|
export const router = new Router();
|
||||||
@@ -762,7 +764,7 @@ export function Connection(
|
|||||||
RouteConnHandler: any,
|
RouteConnHandler: any,
|
||||||
...permPrivChecker: Array<number | bigint | Function>
|
...permPrivChecker: Array<number | bigint | Function>
|
||||||
) {
|
) {
|
||||||
const log = argv.debug ? console.log : () => { };
|
const log = (v: string, fmt: string, ...args: any[]) => logger.debug(fmt, ...args);
|
||||||
const sock = sockjs.createServer({ prefix, log });
|
const sock = sockjs.createServer({ prefix, log });
|
||||||
const checker = Checker(permPrivChecker);
|
const checker = Checker(permPrivChecker);
|
||||||
sock.on('connection', async (conn) => {
|
sock.on('connection', async (conn) => {
|
||||||
@@ -792,7 +794,7 @@ export function Connection(
|
|||||||
if (h.finish) await h.finish(args);
|
if (h.finish) await h.finish(args);
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
logger.warn(e);
|
||||||
await h.onerror(e);
|
await h.onerror(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -820,7 +822,7 @@ export async function start() {
|
|||||||
});
|
});
|
||||||
app.use(router.routes()).use(router.allowedMethods());
|
app.use(router.routes()).use(router.allowedMethods());
|
||||||
server.listen(argv.port || port);
|
server.listen(argv.port || port);
|
||||||
console.log('Server listening at: %s', argv.port || port);
|
logger.success('Server listening at: %d', argv.port || port);
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.service.server = {
|
global.Hydro.service.server = {
|
||||||
|
|||||||
Reference in new issue
Block a user