summaryrefslogtreecommitdiff
path: root/src/services/logger.ts
blob: eb2b257ddeb005424dce662ec7a71e191173f25d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import * as cluster from 'cluster';
import * as os from 'os';
import * as chalk from 'chalk';
import * as dateformat from 'dateformat';
import { program } from '../argv';
import { getRepository } from 'typeorm';
import { Log } from '../models/entities/log';
import { genId } from '../misc/gen-id';
import config from '../config';

const SyslogPro = require('syslog-pro');

type Domain = {
	name: string;
	color?: string;
};

type Level = 'error' | 'success' | 'warning' | 'debug' | 'info';

export default class Logger {
	private domain: Domain;
	private parentLogger: Logger | null = null;
	private store: boolean;
	private syslogClient: any | null = null;

	constructor(domain: string, color?: string, store = true) {
		this.domain = {
			name: domain,
			color: color,
		};
		this.store = store;

		if (config.syslog) {
			this.syslogClient = new SyslogPro.RFC5424({
				applacationName: 'Misskey',
				timestamp: true,
				encludeStructuredData: true,
				color: true,
				extendedColor: true,
				server: {
					target: config.syslog.host,
					port: config.syslog.port,
				}
			});
		}
	}

	public createSubLogger(domain: string, color?: string, store = true): Logger {
		const logger = new Logger(domain, color, store);
		logger.parentLogger = this;
		return logger;
	}

	private log(level: Level, message: string, data?: Record<string, any> | null, important = false, subDomains: Domain[] = [], store = true): void {
		if (program.quiet) return;
		if (!this.store) store = false;
		if (level === 'debug') store = false;

		if (this.parentLogger) {
			this.parentLogger.log(level, message, data, important, [this.domain].concat(subDomains), store);
			return;
		}

		const time = dateformat(new Date(), 'HH:MM:ss');
		const worker = cluster.isMaster ? '*' : cluster.worker.id;
		const l =
			level === 'error' ? important ? chalk.bgRed.white('ERR ') : chalk.red('ERR ') :
			level === 'warning' ? chalk.yellow('WARN') :
			level === 'success' ? important ? chalk.bgGreen.white('DONE') : chalk.green('DONE') :
			level === 'debug' ? chalk.gray('VERB') :
			level === 'info' ? chalk.blue('INFO') :
			null;
		const domains = [this.domain].concat(subDomains).map(d => d.color ? chalk.keyword(d.color)(d.name) : chalk.white(d.name));
		const m =
			level === 'error' ? chalk.red(message) :
			level === 'warning' ? chalk.yellow(message) :
			level === 'success' ? chalk.green(message) :
			level === 'debug' ? chalk.gray(message) :
			level === 'info' ? message :
			null;

		let log = `${l} ${worker}\t[${domains.join(' ')}]\t${m}`;
		if (program.withLogTime) log = chalk.gray(time) + ' ' + log;

		console.log(important ? chalk.bold(log) : log);

		if (store) {
			if (this.syslogClient) {
				const send =
					level === 'error' ? this.syslogClient.error :
					level === 'warning' ? this.syslogClient.warning :
					level === 'success' ? this.syslogClient.info :
					level === 'debug' ? this.syslogClient.info :
					level === 'info' ? this.syslogClient.info :
					null as never;

				send.bind(this.syslogClient)(message);
			} else {
				const Logs = getRepository(Log);
				Logs.insert({
					id: genId(),
					createdAt: new Date(),
					machine: os.hostname(),
					worker: worker.toString(),
					domain: [this.domain].concat(subDomains).map(d => d.name),
					level: level,
					message: message.substr(0, 1000), // 1024を超えるとログが挿入できずエラーになり無限ループする
					data: data,
				} as Log);
			}
		}
	}

	public error(x: string | Error, data?: Record<string, any> | null, important = false): void { // 実行を継続できない状況で使う
		if (x instanceof Error) {
			data = data || {};
			data.e = x;
			this.log('error', x.toString(), data, important);
		} else {
			this.log('error', x, data, important);
		}
	}

	public warn(message: string, data?: Record<string, any> | null, important = false): void { // 実行を継続できるが改善すべき状況で使う
		this.log('warning', message, data, important);
	}

	public succ(message: string, data?: Record<string, any> | null, important = false): void { // 何かに成功した状況で使う
		this.log('success', message, data, important);
	}

	public debug(message: string, data?: Record<string, any> | null, important = false): void { // デバッグ用に使う(開発者に必要だが利用者に不要な情報)
		if (process.env.NODE_ENV != 'production' || program.verbose) {
			this.log('debug', message, data, important);
		}
	}

	public info(message: string, data?: Record<string, any> | null, important = false): void { // それ以外
		this.log('info', message, data, important);
	}
}