ログをデータベースに保存して管理画面で見れるように
This commit is contained in:
		
							parent
							
								
									f3ceb32a7c
								
							
						
					
					
						commit
						977af0a24d
					
				
					 29 changed files with 326 additions and 99 deletions
				
			
		|  | @ -21,6 +21,7 @@ | |||
| 			<li @click="nav('dashboard')" :class="{ active: page == 'dashboard' }"><fa icon="home" fixed-width/>{{ $t('dashboard') }}</li> | ||||
| 			<li @click="nav('instance')" :class="{ active: page == 'instance' }"><fa icon="cog" fixed-width/>{{ $t('instance') }}</li> | ||||
| 			<li @click="nav('queue')" :class="{ active: page == 'queue' }"><fa :icon="faTasks" fixed-width/>{{ $t('queue') }}</li> | ||||
| 			<li @click="nav('logs')" :class="{ active: page == 'logs' }"><fa :icon="faStream" fixed-width/>{{ $t('logs') }}</li> | ||||
| 			<li @click="nav('moderators')" :class="{ active: page == 'moderators' }"><fa :icon="faHeadset" fixed-width/>{{ $t('moderators') }}</li> | ||||
| 			<li @click="nav('users')" :class="{ active: page == 'users' }"><fa icon="users" fixed-width/>{{ $t('users') }}</li> | ||||
| 			<li @click="nav('drive')" :class="{ active: page == 'drive' }"><fa icon="cloud" fixed-width/>{{ $t('@.drive') }}</li> | ||||
|  | @ -42,6 +43,7 @@ | |||
| 			<div v-if="page == 'dashboard'"><x-dashboard/></div> | ||||
| 			<div v-if="page == 'instance'"><x-instance/></div> | ||||
| 			<div v-if="page == 'queue'"><x-queue/></div> | ||||
| 			<div v-if="page == 'logs'"><x-logs/></div> | ||||
| 			<div v-if="page == 'moderators'"><x-moderators/></div> | ||||
| 			<div v-if="page == 'users'"><x-users/></div> | ||||
| 			<div v-if="page == 'emoji'"><x-emoji/></div> | ||||
|  | @ -62,6 +64,7 @@ import { version } from '../../config'; | |||
| import XDashboard from "./dashboard.vue"; | ||||
| import XInstance from "./instance.vue"; | ||||
| import XQueue from "./queue.vue"; | ||||
| import XLogs from "./logs.vue"; | ||||
| import XModerators from "./moderators.vue"; | ||||
| import XEmoji from "./emoji.vue"; | ||||
| import XAnnouncements from "./announcements.vue"; | ||||
|  | @ -71,7 +74,7 @@ import XDrive from "./drive.vue"; | |||
| import XAbuse from "./abuse.vue"; | ||||
| import XFederation from "./federation.vue"; | ||||
| 
 | ||||
| import { faHeadset, faArrowLeft, faGlobe, faExclamationCircle, faTasks } from '@fortawesome/free-solid-svg-icons'; | ||||
| import { faHeadset, faArrowLeft, faGlobe, faExclamationCircle, faTasks, faStream } from '@fortawesome/free-solid-svg-icons'; | ||||
| import { faGrin } from '@fortawesome/free-regular-svg-icons'; | ||||
| 
 | ||||
| // Detect the user agent | ||||
|  | @ -84,6 +87,7 @@ export default Vue.extend({ | |||
| 		XDashboard, | ||||
| 		XInstance, | ||||
| 		XQueue, | ||||
| 		XLogs, | ||||
| 		XModerators, | ||||
| 		XEmoji, | ||||
| 		XAnnouncements, | ||||
|  | @ -107,7 +111,8 @@ export default Vue.extend({ | |||
| 			faHeadset, | ||||
| 			faGlobe, | ||||
| 			faExclamationCircle, | ||||
| 			faTasks | ||||
| 			faTasks, | ||||
| 			faStream | ||||
| 		}; | ||||
| 	}, | ||||
| 	methods: { | ||||
|  |  | |||
							
								
								
									
										101
									
								
								src/client/app/admin/views/logs.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								src/client/app/admin/views/logs.vue
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,101 @@ | |||
| <template> | ||||
| <div> | ||||
| 	<ui-card> | ||||
| 		<template #title><fa :icon="faStream"/> {{ $t('logs') }}</template> | ||||
| 		<section class="fit-top"> | ||||
| 			<ui-horizon-group inputs> | ||||
| 				<ui-input v-model="domain"> | ||||
| 					<span>{{ $t('domain') }}</span> | ||||
| 				</ui-input> | ||||
| 				<ui-select v-model="level"> | ||||
| 					<template #label>{{ $t('level') }}</template> | ||||
| 					<option value="all">{{ $t('levels.all') }}</option> | ||||
| 					<option value="info">{{ $t('levels.info') }}</option> | ||||
| 					<option value="success">{{ $t('levels.success') }}</option> | ||||
| 					<option value="warning">{{ $t('levels.warning') }}</option> | ||||
| 					<option value="error">{{ $t('levels.error') }}</option> | ||||
| 					<option value="debug">{{ $t('levels.debug') }}</option> | ||||
| 				</ui-select> | ||||
| 			</ui-horizon-group> | ||||
| 
 | ||||
| 			<div class="nqjzuvev"> | ||||
| 				<code v-for="log in logs" :key="log._id" :class="log.level"> | ||||
| 					<mk-time :time="log.createdAt"/> [{{ log.domain.join(' ') }}] {{ log.message }} | ||||
| 				</code> | ||||
| 			</div> | ||||
| 		</section> | ||||
| 	</ui-card> | ||||
| </div> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts"> | ||||
| import Vue from 'vue'; | ||||
| import i18n from '../../i18n'; | ||||
| import { faStream } from '@fortawesome/free-solid-svg-icons'; | ||||
| 
 | ||||
| export default Vue.extend({ | ||||
| 	i18n: i18n('admin/views/logs.vue'), | ||||
| 
 | ||||
| 	data() { | ||||
| 		return { | ||||
| 			logs: [], | ||||
| 			level: 'all', | ||||
| 			domain: '', | ||||
| 			faStream | ||||
| 		}; | ||||
| 	}, | ||||
| 
 | ||||
| 	watch: { | ||||
| 		level() { | ||||
| 			this.logs = []; | ||||
| 			this.fetch(); | ||||
| 		}, | ||||
| 
 | ||||
| 		domain() { | ||||
| 			this.logs = []; | ||||
| 			this.fetch(); | ||||
| 		} | ||||
| 	}, | ||||
| 
 | ||||
| 	mounted() { | ||||
| 		this.fetch(); | ||||
| 	}, | ||||
| 
 | ||||
| 	methods: { | ||||
| 		fetch() { | ||||
| 			this.$root.api('admin/logs', { | ||||
| 				level: this.level === 'all' ? null : this.level, | ||||
| 				domain: this.domain === '' ? null : this.domain, | ||||
| 				limit: 50 | ||||
| 			}).then(logs => { | ||||
| 				this.logs = logs; | ||||
| 			}); | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
| </script> | ||||
| 
 | ||||
| <style lang="stylus" scoped> | ||||
| .nqjzuvev | ||||
| 	white-space nowrap | ||||
| 	overflow auto | ||||
| 	padding 8px | ||||
| 	background #000 | ||||
| 	color #fff | ||||
| 
 | ||||
| 	> code | ||||
| 		display block | ||||
| 
 | ||||
| 		&.error | ||||
| 			color #f00 | ||||
| 
 | ||||
| 		&.warning | ||||
| 			color #ff0 | ||||
| 
 | ||||
| 		&.success | ||||
| 			color #0f0 | ||||
| 
 | ||||
| 		&.debug | ||||
| 			opacity 0.7 | ||||
| 
 | ||||
| </style> | ||||
|  | @ -1,6 +1,6 @@ | |||
| import * as elasticsearch from 'elasticsearch'; | ||||
| import config from '../config'; | ||||
| import Logger from '../misc/logger'; | ||||
| import Logger from '../services/logger'; | ||||
| 
 | ||||
| const esLogger = new Logger('es'); | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										3
									
								
								src/db/logger.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/db/logger.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | |||
| import Logger from '../services/logger'; | ||||
| 
 | ||||
| export const dbLogger = new Logger('db'); | ||||
|  | @ -18,7 +18,6 @@ export default db; | |||
|  * MongoDB native module (officialy) | ||||
|  */ | ||||
| import * as mongodb from 'mongodb'; | ||||
| import Logger from '../misc/logger'; | ||||
| 
 | ||||
| let mdb: mongodb.Db; | ||||
| 
 | ||||
|  | @ -38,5 +37,3 @@ const nativeDbConn = async (): Promise<mongodb.Db> => { | |||
| }; | ||||
| 
 | ||||
| export { nativeDbConn }; | ||||
| 
 | ||||
| export const dbLogger = new Logger('db'); | ||||
|  |  | |||
							
								
								
									
										28
									
								
								src/index.ts
									
										
									
									
									
								
							
							
						
						
									
										28
									
								
								src/index.ts
									
										
									
									
									
								
							|  | @ -13,7 +13,7 @@ import * as portscanner from 'portscanner'; | |||
| import * as isRoot from 'is-root'; | ||||
| import Xev from 'xev'; | ||||
| 
 | ||||
| import Logger from './misc/logger'; | ||||
| import Logger from './services/logger'; | ||||
| import serverStats from './daemons/server-stats'; | ||||
| import notesStats from './daemons/notes-stats'; | ||||
| import loadConfig from './config/load'; | ||||
|  | @ -25,7 +25,7 @@ import { checkMongoDB } from './misc/check-mongodb'; | |||
| import { showMachineInfo } from './misc/show-machine-info'; | ||||
| 
 | ||||
| const logger = new Logger('core', 'cyan'); | ||||
| const bootLogger = logger.createSubLogger('boot', 'magenta'); | ||||
| const bootLogger = logger.createSubLogger('boot', 'magenta', false); | ||||
| const clusterLogger = logger.createSubLogger('cluster', 'orange'); | ||||
| const ev = new Xev(); | ||||
| 
 | ||||
|  | @ -73,7 +73,7 @@ function greet() { | |||
| 	console.log(chalk`${os.hostname()} {gray (PID: ${process.pid.toString()})}`); | ||||
| 
 | ||||
| 	bootLogger.info('Welcome to Misskey!'); | ||||
| 	bootLogger.info(`Misskey v${pkg.version}`, true); | ||||
| 	bootLogger.info(`Misskey v${pkg.version}`, null, true); | ||||
| 	bootLogger.info('Misskey is maintained by @syuilo, @AyaMorisawa, @mei23, and @acid-chicken.'); | ||||
| } | ||||
| 
 | ||||
|  | @ -90,21 +90,21 @@ async function masterMain() { | |||
| 		config = await init(); | ||||
| 
 | ||||
| 		if (config.port == null) { | ||||
| 			bootLogger.error('The port is not configured. Please configure port.', true); | ||||
| 			bootLogger.error('The port is not configured. Please configure port.', null, true); | ||||
| 			process.exit(1); | ||||
| 		} | ||||
| 
 | ||||
| 		if (process.platform === 'linux' && isWellKnownPort(config.port) && !isRoot()) { | ||||
| 			bootLogger.error('You need root privileges to listen on well-known port on Linux', true); | ||||
| 			bootLogger.error('You need root privileges to listen on well-known port on Linux', null, true); | ||||
| 			process.exit(1); | ||||
| 		} | ||||
| 
 | ||||
| 		if (!await isPortAvailable(config.port)) { | ||||
| 			bootLogger.error(`Port ${config.port} is already in use`, true); | ||||
| 			bootLogger.error(`Port ${config.port} is already in use`, null, true); | ||||
| 			process.exit(1); | ||||
| 		} | ||||
| 	} catch (e) { | ||||
| 		bootLogger.error('Fatal error occurred during initialization', true); | ||||
| 		bootLogger.error('Fatal error occurred during initialization', null, true); | ||||
| 		process.exit(1); | ||||
| 	} | ||||
| 
 | ||||
|  | @ -117,7 +117,7 @@ async function masterMain() { | |||
| 	// start queue
 | ||||
| 	require('./queue').default(); | ||||
| 
 | ||||
| 	bootLogger.succ(`Now listening on port ${config.port} on ${config.url}`, true); | ||||
| 	bootLogger.succ(`Now listening on port ${config.port} on ${config.url}`, null, true); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  | @ -140,7 +140,7 @@ async function queueMain() { | |||
| 		// initialize app
 | ||||
| 		await init(); | ||||
| 	} catch (e) { | ||||
| 		bootLogger.error('Fatal error occurred during initialization', true); | ||||
| 		bootLogger.error('Fatal error occurred during initialization', null, true); | ||||
| 		process.exit(1); | ||||
| 	} | ||||
| 
 | ||||
|  | @ -150,7 +150,7 @@ async function queueMain() { | |||
| 	const queue = require('./queue').default(); | ||||
| 
 | ||||
| 	if (queue) { | ||||
| 		bootLogger.succ('Queue started', true); | ||||
| 		bootLogger.succ('Queue started', null, true); | ||||
| 	} else { | ||||
| 		bootLogger.error('Queue not available'); | ||||
| 	} | ||||
|  | @ -175,7 +175,7 @@ function showEnvironment(): void { | |||
| 
 | ||||
| 	if (env !== 'production') { | ||||
| 		logger.warn('The environment is not in production mode.'); | ||||
| 		logger.warn('DO NOT USE FOR PRODUCTION PURPOSE!', true); | ||||
| 		logger.warn('DO NOT USE FOR PRODUCTION PURPOSE!', null, true); | ||||
| 	} | ||||
| 
 | ||||
| 	logger.info(`You ${isRoot() ? '' : 'do not '}have root privileges`); | ||||
|  | @ -192,7 +192,7 @@ async function init(): Promise<Config> { | |||
| 	nodejsLogger.info(`Version ${runningNodejsVersion.join('.')}`); | ||||
| 
 | ||||
| 	if (!satisfyNodejsVersion) { | ||||
| 		nodejsLogger.error(`Node.js version is less than ${requiredNodejsVersion.join('.')}. Please upgrade it.`, true); | ||||
| 		nodejsLogger.error(`Node.js version is less than ${requiredNodejsVersion.join('.')}. Please upgrade it.`, null, true); | ||||
| 		process.exit(1); | ||||
| 	} | ||||
| 
 | ||||
|  | @ -209,7 +209,7 @@ async function init(): Promise<Config> { | |||
| 			process.exit(1); | ||||
| 		} | ||||
| 		if (exception.code === 'ENOENT') { | ||||
| 			configLogger.error('Configuration file not found', true); | ||||
| 			configLogger.error('Configuration file not found', null, true); | ||||
| 			process.exit(1); | ||||
| 		} | ||||
| 		throw exception; | ||||
|  | @ -221,7 +221,7 @@ async function init(): Promise<Config> { | |||
| 	try { | ||||
| 		await checkMongoDB(config, bootLogger); | ||||
| 	} catch (e) { | ||||
| 		bootLogger.error('Cannot connect to database', true); | ||||
| 		bootLogger.error('Cannot connect to database', null, true); | ||||
| 		process.exit(1); | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| import { nativeDbConn } from '../db/mongodb'; | ||||
| import { Config } from '../config/types'; | ||||
| import Logger from './logger'; | ||||
| import Logger from '../services/logger'; | ||||
| import { lessThan } from '../prelude/array'; | ||||
| 
 | ||||
| const requiredMongoDBVersion = [3, 6]; | ||||
|  |  | |||
|  | @ -1,59 +0,0 @@ | |||
| import * as cluster from 'cluster'; | ||||
| import chalk from 'chalk'; | ||||
| import * as dateformat from 'dateformat'; | ||||
| import { program } from '../argv'; | ||||
| 
 | ||||
| export default class Logger { | ||||
| 	private domain: string; | ||||
| 	private color?: string; | ||||
| 	private parentLogger: Logger; | ||||
| 
 | ||||
| 	constructor(domain: string, color?: string) { | ||||
| 		this.domain = domain; | ||||
| 		this.color = color; | ||||
| 	} | ||||
| 
 | ||||
| 	public createSubLogger(domain: string, color?: string): Logger { | ||||
| 		const logger = new Logger(domain, color); | ||||
| 		logger.parentLogger = this; | ||||
| 		return logger; | ||||
| 	} | ||||
| 
 | ||||
| 	private log(level: string, message: string, important = false, subDomains: string[] = []): void { | ||||
| 		if (program.quiet) return; | ||||
| 		if (process.env.NODE_ENV === 'test') return; | ||||
| 		const domain = this.color ? chalk.keyword(this.color)(this.domain) : chalk.white(this.domain); | ||||
| 		const domains = [domain].concat(subDomains); | ||||
| 		if (this.parentLogger) { | ||||
| 			this.parentLogger.log(level, message, important, domains); | ||||
| 		} else { | ||||
| 			const time = dateformat(new Date(), 'HH:MM:ss'); | ||||
| 			const process = cluster.isMaster ? '*' : cluster.worker.id; | ||||
| 			let log = `${level} ${process}\t[${domains.join(' ')}]\t${message}`; | ||||
| 			if (program.withLogTime) log = chalk.gray(time) + ' ' + log; | ||||
| 			console.log(important ? chalk.bold(log) : log); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	public error(message: string | Error, important = false): void { // 実行を継続できない状況で使う
 | ||||
| 		this.log(important ? chalk.bgRed.white('ERR ') : chalk.red('ERR '), chalk.red(message.toString()), important); | ||||
| 	} | ||||
| 
 | ||||
| 	public warn(message: string, important = false): void { // 実行を継続できるが改善すべき状況で使う
 | ||||
| 		this.log(chalk.yellow('WARN'), chalk.yellow(message), important); | ||||
| 	} | ||||
| 
 | ||||
| 	public succ(message: string, important = false): void { // 何かに成功した状況で使う
 | ||||
| 		this.log(important ? chalk.bgGreen.white('DONE') : chalk.green('DONE'), chalk.green(message), important); | ||||
| 	} | ||||
| 
 | ||||
| 	public debug(message: string, important = false): void { // デバッグ用に使う(開発者に必要だが利用者に不要な情報)
 | ||||
| 		if (process.env.NODE_ENV != 'production' || program.verbose) { | ||||
| 			this.log(chalk.gray('VERB'), chalk.gray(message), important); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	public info(message: string, important = false): void { // それ以外
 | ||||
| 		this.log(chalk.blue('INFO'), message, important); | ||||
| 	} | ||||
| } | ||||
|  | @ -1,6 +1,6 @@ | |||
| import * as os from 'os'; | ||||
| import * as sysUtils from 'systeminformation'; | ||||
| import Logger from './logger'; | ||||
| import Logger from '../services/logger'; | ||||
| 
 | ||||
| export async function showMachineInfo(parentLogger: Logger) { | ||||
| 	const logger = parentLogger.createSubLogger('machine'); | ||||
|  |  | |||
|  | @ -2,9 +2,10 @@ import * as mongo from 'mongodb'; | |||
| import * as deepcopy from 'deepcopy'; | ||||
| import { pack as packFolder } from './drive-folder'; | ||||
| import { pack as packUser } from './user'; | ||||
| import monkDb, { nativeDbConn, dbLogger } from '../db/mongodb'; | ||||
| import monkDb, { nativeDbConn } from '../db/mongodb'; | ||||
| import isObjectId from '../misc/is-objectid'; | ||||
| import getDriveFileUrl, { getOriginalUrl } from '../misc/get-drive-file-url'; | ||||
| import { dbLogger } from '../db/logger'; | ||||
| 
 | ||||
| const DriveFile = monkDb.get<IDriveFile>('driveFiles.files'); | ||||
| DriveFile.createIndex('md5'); | ||||
|  |  | |||
|  | @ -1,8 +1,9 @@ | |||
| import * as mongo from 'mongodb'; | ||||
| import * as deepcopy from 'deepcopy'; | ||||
| import db, { dbLogger } from '../db/mongodb'; | ||||
| import db from '../db/mongodb'; | ||||
| import isObjectId from '../misc/is-objectid'; | ||||
| import { pack as packNote } from './note'; | ||||
| import { dbLogger } from '../db/logger'; | ||||
| 
 | ||||
| const Favorite = db.get<IFavorite>('favorites'); | ||||
| Favorite.createIndex('userId'); | ||||
|  |  | |||
							
								
								
									
										17
									
								
								src/models/log.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/models/log.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,17 @@ | |||
| import * as mongo from 'mongodb'; | ||||
| import db from '../db/mongodb'; | ||||
| 
 | ||||
| const Log = db.get<ILog>('logs'); | ||||
| Log.createIndex('createdAt', { expireAfterSeconds: 3600 * 24 * 3 }); | ||||
| export default Log; | ||||
| 
 | ||||
| export interface ILog { | ||||
| 	_id: mongo.ObjectID; | ||||
| 	createdAt: Date; | ||||
| 	machine: string; | ||||
| 	worker: string; | ||||
| 	domain: string[]; | ||||
| 	level: string; | ||||
| 	message: string; | ||||
| 	data: any; | ||||
| } | ||||
|  | @ -1,7 +1,7 @@ | |||
| import * as mongo from 'mongodb'; | ||||
| import * as deepcopy from 'deepcopy'; | ||||
| import rap from '@prezzemolo/rap'; | ||||
| import db, { dbLogger } from '../db/mongodb'; | ||||
| import db from '../db/mongodb'; | ||||
| import isObjectId from '../misc/is-objectid'; | ||||
| import { length } from 'stringz'; | ||||
| import { IUser, pack as packUser } from './user'; | ||||
|  | @ -11,6 +11,7 @@ import Reaction from './note-reaction'; | |||
| import { packMany as packFileMany, IDriveFile } from './drive-file'; | ||||
| import Following from './following'; | ||||
| import Emoji from './emoji'; | ||||
| import { dbLogger } from '../db/logger'; | ||||
| 
 | ||||
| const Note = db.get<INote>('notes'); | ||||
| Note.createIndex('uri', { sparse: true, unique: true }); | ||||
|  |  | |||
|  | @ -1,9 +1,10 @@ | |||
| import * as mongo from 'mongodb'; | ||||
| import * as deepcopy from 'deepcopy'; | ||||
| import db, { dbLogger } from '../db/mongodb'; | ||||
| import db from '../db/mongodb'; | ||||
| import isObjectId from '../misc/is-objectid'; | ||||
| import { IUser, pack as packUser } from './user'; | ||||
| import { pack as packNote } from './note'; | ||||
| import { dbLogger } from '../db/logger'; | ||||
| 
 | ||||
| const Notification = db.get<INotification>('notifications'); | ||||
| Notification.createIndex('notifieeId'); | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| import * as mongo from 'mongodb'; | ||||
| import * as deepcopy from 'deepcopy'; | ||||
| import rap from '@prezzemolo/rap'; | ||||
| import db, { dbLogger } from '../db/mongodb'; | ||||
| import db from '../db/mongodb'; | ||||
| import isObjectId from '../misc/is-objectid'; | ||||
| import { packMany as packNoteMany } from './note'; | ||||
| import Following from './following'; | ||||
|  | @ -12,6 +12,7 @@ import config from '../config'; | |||
| import FollowRequest from './follow-request'; | ||||
| import fetchMeta from '../misc/fetch-meta'; | ||||
| import Emoji from './emoji'; | ||||
| import { dbLogger } from '../db/logger'; | ||||
| 
 | ||||
| const User = db.get<IUser>('users'); | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,3 +1,3 @@ | |||
| import Logger from '../misc/logger'; | ||||
| import Logger from '../services/logger'; | ||||
| 
 | ||||
| export const queueLogger = new Logger('queue', 'orange'); | ||||
|  |  | |||
|  | @ -7,7 +7,7 @@ import { resolvePerson, updatePerson } from '../../../remote/activitypub/models/ | |||
| import { toUnicode } from 'punycode'; | ||||
| import { URL } from 'url'; | ||||
| import { publishApLogStream } from '../../../services/stream'; | ||||
| import Logger from '../../../misc/logger'; | ||||
| import Logger from '../../../services/logger'; | ||||
| import { registerOrFetchInstanceDoc } from '../../../services/register-or-fetch-instance-doc'; | ||||
| import Instance from '../../../models/instance'; | ||||
| import instanceChart from '../../../services/chart/instance'; | ||||
|  |  | |||
|  | @ -1,3 +1,3 @@ | |||
| import Logger from "../misc/logger"; | ||||
| import Logger from "../services/logger"; | ||||
| 
 | ||||
| export const remoteLogger = new Logger('remote', 'cyan'); | ||||
|  |  | |||
							
								
								
									
										51
									
								
								src/server/api/endpoints/admin/logs.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src/server/api/endpoints/admin/logs.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,51 @@ | |||
| import $ from 'cafy'; | ||||
| import define from '../../define'; | ||||
| import Log from '../../../../models/log'; | ||||
| 
 | ||||
| export const meta = { | ||||
| 	tags: ['admin'], | ||||
| 
 | ||||
| 	requireCredential: true, | ||||
| 	requireModerator: true, | ||||
| 
 | ||||
| 	params: { | ||||
| 		limit: { | ||||
| 			validator: $.optional.num.range(1, 100), | ||||
| 			default: 30 | ||||
| 		}, | ||||
| 
 | ||||
| 		level: { | ||||
| 			validator: $.optional.nullable.str, | ||||
| 			default: null as any | ||||
| 		}, | ||||
| 
 | ||||
| 		domain: { | ||||
| 			validator: $.optional.nullable.str, | ||||
| 			default: null as any | ||||
| 		} | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| export default define(meta, async (ps) => { | ||||
| 	const sort = { | ||||
| 		_id: -1 | ||||
| 	}; | ||||
| 	const query = {} as any; | ||||
| 
 | ||||
| 	if (ps.level) query.level = ps.level; | ||||
| 	if (ps.domain) { | ||||
| 		let i = 0; | ||||
| 		for (const d of ps.domain.split(' ')) { | ||||
| 			query[`domain.${i}`] = d; | ||||
| 			i++; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	const logs = await Log | ||||
| 		.find(query, { | ||||
| 			limit: ps.limit, | ||||
| 			sort: sort | ||||
| 		}); | ||||
| 
 | ||||
| 	return logs; | ||||
| }); | ||||
|  | @ -3,7 +3,7 @@ import limiterDB from '../../db/redis'; | |||
| import { IEndpoint } from './endpoints'; | ||||
| import getAcct from '../../misc/acct/render'; | ||||
| import { IUser } from '../../models/user'; | ||||
| import Logger from '../../misc/logger'; | ||||
| import Logger from '../../services/logger'; | ||||
| 
 | ||||
| const logger = new Logger('limiter'); | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,3 +1,3 @@ | |||
| import Logger from "../../misc/logger"; | ||||
| import Logger from "../../services/logger"; | ||||
| 
 | ||||
| export const apiLogger = new Logger('api'); | ||||
|  |  | |||
|  | @ -23,10 +23,10 @@ import networkChart from '../services/chart/network'; | |||
| import apiServer from './api'; | ||||
| import { sum } from '../prelude/array'; | ||||
| import User from '../models/user'; | ||||
| import Logger from '../misc/logger'; | ||||
| import Logger from '../services/logger'; | ||||
| import { program } from '../argv'; | ||||
| 
 | ||||
| export const serverLogger = new Logger('server', 'gray'); | ||||
| export const serverLogger = new Logger('server', 'gray', false); | ||||
| 
 | ||||
| // Init app
 | ||||
| const app = new Koa(); | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ import * as Koa from 'koa'; | |||
| import * as request from 'request-promise-native'; | ||||
| import summaly from 'summaly'; | ||||
| import fetchMeta from '../../misc/fetch-meta'; | ||||
| import Logger from '../../misc/logger'; | ||||
| import Logger from '../../services/logger'; | ||||
| 
 | ||||
| const logger = new Logger('url-preview'); | ||||
| 
 | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ import { renderActivity } from '../../remote/activitypub/renderer'; | |||
| import renderBlock from '../../remote/activitypub/renderer/block'; | ||||
| import renderUndo from '../../remote/activitypub/renderer/undo'; | ||||
| import { deliver } from '../../queue'; | ||||
| import Logger from '../../misc/logger'; | ||||
| import Logger from '../logger'; | ||||
| 
 | ||||
| const logger = new Logger('blocking/delete'); | ||||
| 
 | ||||
|  |  | |||
|  | @ -8,7 +8,7 @@ import autobind from 'autobind-decorator'; | |||
| import * as mongo from 'mongodb'; | ||||
| import db from '../../db/mongodb'; | ||||
| import { ICollection } from 'monk'; | ||||
| import Logger from '../../misc/logger'; | ||||
| import Logger from '../logger'; | ||||
| import { Schema } from '../../misc/schema'; | ||||
| 
 | ||||
| const logger = new Logger('chart'); | ||||
|  |  | |||
|  | @ -1,3 +1,3 @@ | |||
| import Logger from "../../misc/logger"; | ||||
| import Logger from "../logger"; | ||||
| 
 | ||||
| export const driveLogger = new Logger('drive', 'blue'); | ||||
|  |  | |||
|  | @ -13,7 +13,7 @@ import perUserFollowingChart from '../../services/chart/per-user-following'; | |||
| import { registerOrFetchInstanceDoc } from '../register-or-fetch-instance-doc'; | ||||
| import Instance from '../../models/instance'; | ||||
| import instanceChart from '../../services/chart/instance'; | ||||
| import Logger from '../../misc/logger'; | ||||
| import Logger from '../logger'; | ||||
| import FollowRequest from '../../models/follow-request'; | ||||
| import { IdentifiableError } from '../../misc/identifiable-error'; | ||||
| 
 | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ import renderFollow from '../../remote/activitypub/renderer/follow'; | |||
| import renderUndo from '../../remote/activitypub/renderer/undo'; | ||||
| import { deliver } from '../../queue'; | ||||
| import perUserFollowingChart from '../../services/chart/per-user-following'; | ||||
| import Logger from '../../misc/logger'; | ||||
| import Logger from '../logger'; | ||||
| import { registerOrFetchInstanceDoc } from '../register-or-fetch-instance-doc'; | ||||
| import Instance from '../../models/instance'; | ||||
| import instanceChart from '../../services/chart/instance'; | ||||
|  |  | |||
							
								
								
									
										107
									
								
								src/services/logger.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								src/services/logger.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,107 @@ | |||
| import * as cluster from 'cluster'; | ||||
| import * as os from 'os'; | ||||
| import chalk from 'chalk'; | ||||
| import * as dateformat from 'dateformat'; | ||||
| import { program } from '../argv'; | ||||
| import Log from '../models/log'; | ||||
| 
 | ||||
| type Domain = { | ||||
| 	name: string; | ||||
| 	color: string; | ||||
| }; | ||||
| 
 | ||||
| type Level = 'error' | 'success' | 'warning' | 'debug' | 'info'; | ||||
| 
 | ||||
| export default class Logger { | ||||
| 	private domain: Domain; | ||||
| 	private parentLogger: Logger; | ||||
| 	private store: boolean; | ||||
| 
 | ||||
| 	constructor(domain: string, color?: string, store = true) { | ||||
| 		this.domain = { | ||||
| 			name: domain, | ||||
| 			color: color, | ||||
| 		}; | ||||
| 		this.store = store; | ||||
| 	} | ||||
| 
 | ||||
| 	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>, important = false, subDomains: Domain[] = [], store = true): void { | ||||
| 		if (program.quiet) return; | ||||
| 		if (process.env.NODE_ENV === 'test') return; | ||||
| 		if (!this.store) 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) { | ||||
| 			Log.insert({ | ||||
| 				createdAt: new Date(), | ||||
| 				machine: os.hostname(), | ||||
| 				worker: worker, | ||||
| 				domain: [this.domain].concat(subDomains).map(d => d.name), | ||||
| 				level: level, | ||||
| 				message: message, | ||||
| 				data: data, | ||||
| 			}); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	public error(x: string | Error, data?: Record<string, any>, 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>, important = false): void { // 実行を継続できるが改善すべき状況で使う
 | ||||
| 		this.log('warning', message, data, important); | ||||
| 	} | ||||
| 
 | ||||
| 	public succ(message: string, data?: Record<string, any>, important = false): void { // 何かに成功した状況で使う
 | ||||
| 		this.log('success', message, data, important); | ||||
| 	} | ||||
| 
 | ||||
| 	public debug(message: string, data?: Record<string, any>, 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>, important = false): void { // それ以外
 | ||||
| 		this.log('info', message, data, important); | ||||
| 	} | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue