Merge branch 'develop' into feat-1714
This commit is contained in:
		| @@ -7,7 +7,7 @@ import { LoggerService } from '@nestjs/common'; | |||||||
| import Logger from '@/logger.js'; | import Logger from '@/logger.js'; | ||||||
|  |  | ||||||
| const logger = new Logger('core', 'cyan'); | const logger = new Logger('core', 'cyan'); | ||||||
| const nestLogger = logger.createSubLogger('nest', 'green', false); | const nestLogger = logger.createSubLogger('nest', 'green'); | ||||||
|  |  | ||||||
| export class NestLogger implements LoggerService { | export class NestLogger implements LoggerService { | ||||||
| 	/** | 	/** | ||||||
|   | |||||||
| @@ -25,7 +25,7 @@ Error.stackTraceLimit = Infinity; | |||||||
| EventEmitter.defaultMaxListeners = 128; | EventEmitter.defaultMaxListeners = 128; | ||||||
|  |  | ||||||
| const logger = new Logger('core', 'cyan'); | const logger = new Logger('core', 'cyan'); | ||||||
| const clusterLogger = logger.createSubLogger('cluster', 'orange', false); | const clusterLogger = logger.createSubLogger('cluster', 'orange'); | ||||||
| const ev = new Xev(); | const ev = new Xev(); | ||||||
|  |  | ||||||
| //#region Events | //#region Events | ||||||
|   | |||||||
| @@ -25,7 +25,7 @@ const _dirname = dirname(_filename); | |||||||
| const meta = JSON.parse(fs.readFileSync(`${_dirname}/../../../../built/meta.json`, 'utf-8')); | const meta = JSON.parse(fs.readFileSync(`${_dirname}/../../../../built/meta.json`, 'utf-8')); | ||||||
|  |  | ||||||
| const logger = new Logger('core', 'cyan'); | const logger = new Logger('core', 'cyan'); | ||||||
| const bootLogger = logger.createSubLogger('boot', 'magenta', false); | const bootLogger = logger.createSubLogger('boot', 'magenta'); | ||||||
|  |  | ||||||
| const themeColor = chalk.hex('#86b300'); | const themeColor = chalk.hex('#86b300'); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -4,13 +4,36 @@ | |||||||
|  */ |  */ | ||||||
|  |  | ||||||
| import cluster from 'node:cluster'; | import cluster from 'node:cluster'; | ||||||
|  | import * as Sentry from '@sentry/node'; | ||||||
|  | import { nodeProfilingIntegration } from '@sentry/profiling-node'; | ||||||
| import { envOption } from '@/env.js'; | import { envOption } from '@/env.js'; | ||||||
|  | import { loadConfig } from '@/config.js'; | ||||||
| import { jobQueue, server } from './common.js'; | import { jobQueue, server } from './common.js'; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Init worker process |  * Init worker process | ||||||
|  */ |  */ | ||||||
| export async function workerMain() { | export async function workerMain() { | ||||||
|  | 	const config = loadConfig(); | ||||||
|  |  | ||||||
|  | 	if (config.sentryForBackend) { | ||||||
|  | 		Sentry.init({ | ||||||
|  | 			integrations: [ | ||||||
|  | 				...(config.sentryForBackend.enableNodeProfiling ? [nodeProfilingIntegration()] : []), | ||||||
|  | 			], | ||||||
|  |  | ||||||
|  | 			// Performance Monitoring | ||||||
|  | 			tracesSampleRate: 1.0, //  Capture 100% of the transactions | ||||||
|  |  | ||||||
|  | 			// Set sampling rate for profiling - this is relative to tracesSampleRate | ||||||
|  | 			profilesSampleRate: 1.0, | ||||||
|  |  | ||||||
|  | 			maxBreadcrumbs: 0, | ||||||
|  |  | ||||||
|  | 			...config.sentryForBackend.options, | ||||||
|  | 		}); | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	if (envOption.onlyServer) { | 	if (envOption.onlyServer) { | ||||||
| 		await server(); | 		await server(); | ||||||
| 	} else if (envOption.onlyQueue) { | 	} else if (envOption.onlyQueue) { | ||||||
|   | |||||||
| @@ -15,7 +15,7 @@ export class LoggerService { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	@bindThis | 	@bindThis | ||||||
| 	public getLogger(domain: string, color?: KEYWORD | undefined, store?: boolean) { | 	public getLogger(domain: string, color?: KEYWORD | undefined) { | ||||||
| 		return new Logger(domain, color, store); | 		return new Logger(domain, color); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -14,6 +14,6 @@ export class ChartLoggerService { | |||||||
| 	constructor( | 	constructor( | ||||||
| 		private loggerService: LoggerService, | 		private loggerService: LoggerService, | ||||||
| 	) { | 	) { | ||||||
| 		this.logger = this.loggerService.getLogger('chart', 'white', process.env.NODE_ENV !== 'test'); | 		this.logger = this.loggerService.getLogger('chart', 'white'); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -22,31 +22,27 @@ type Level = 'error' | 'success' | 'warning' | 'debug' | 'info'; | |||||||
| export default class Logger { | export default class Logger { | ||||||
| 	private context: Context; | 	private context: Context; | ||||||
| 	private parentLogger: Logger | null = null; | 	private parentLogger: Logger | null = null; | ||||||
| 	private store: boolean; |  | ||||||
|  |  | ||||||
| 	constructor(context: string, color?: KEYWORD, store = true) { | 	constructor(context: string, color?: KEYWORD) { | ||||||
| 		this.context = { | 		this.context = { | ||||||
| 			name: context, | 			name: context, | ||||||
| 			color: color, | 			color: color, | ||||||
| 		}; | 		}; | ||||||
| 		this.store = store; |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	@bindThis | 	@bindThis | ||||||
| 	public createSubLogger(context: string, color?: KEYWORD, store = true): Logger { | 	public createSubLogger(context: string, color?: KEYWORD): Logger { | ||||||
| 		const logger = new Logger(context, color, store); | 		const logger = new Logger(context, color); | ||||||
| 		logger.parentLogger = this; | 		logger.parentLogger = this; | ||||||
| 		return logger; | 		return logger; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	@bindThis | 	@bindThis | ||||||
| 	private log(level: Level, message: string, data?: Record<string, any> | null, important = false, subContexts: Context[] = [], store = true): void { | 	private log(level: Level, message: string, data?: Record<string, any> | null, important = false, subContexts: Context[] = []): void { | ||||||
| 		if (envOption.quiet) return; | 		if (envOption.quiet) return; | ||||||
| 		if (!this.store) store = false; |  | ||||||
| 		if (level === 'debug') store = false; |  | ||||||
|  |  | ||||||
| 		if (this.parentLogger) { | 		if (this.parentLogger) { | ||||||
| 			this.parentLogger.log(level, message, data, important, [this.context].concat(subContexts), store); | 			this.parentLogger.log(level, message, data, important, [this.context].concat(subContexts)); | ||||||
| 			return; | 			return; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -85,7 +85,7 @@ import { bindThis } from '@/decorators.js'; | |||||||
|  |  | ||||||
| export const dbLogger = new MisskeyLogger('db'); | export const dbLogger = new MisskeyLogger('db'); | ||||||
|  |  | ||||||
| const sqlLogger = dbLogger.createSubLogger('sql', 'gray', false); | const sqlLogger = dbLogger.createSubLogger('sql', 'gray'); | ||||||
|  |  | ||||||
| class MyCustomLogger implements Logger { | class MyCustomLogger implements Logger { | ||||||
| 	@bindThis | 	@bindThis | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ | |||||||
|  |  | ||||||
| import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common'; | import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common'; | ||||||
| import * as Bull from 'bullmq'; | import * as Bull from 'bullmq'; | ||||||
|  | import * as Sentry from '@sentry/node'; | ||||||
| import type { Config } from '@/config.js'; | import type { Config } from '@/config.js'; | ||||||
| import { DI } from '@/di-symbols.js'; | import { DI } from '@/di-symbols.js'; | ||||||
| import type Logger from '@/logger.js'; | import type Logger from '@/logger.js'; | ||||||
| @@ -135,7 +136,8 @@ export class QueueProcessorService implements OnApplicationShutdown { | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		//#region system | 		//#region system | ||||||
| 		this.systemQueueWorker = new Bull.Worker(QUEUE.SYSTEM, (job) => { | 		{ | ||||||
|  | 			const processer = (job: Bull.Job) => { | ||||||
| 				switch (job.name) { | 				switch (job.name) { | ||||||
| 					case 'tickCharts': return this.tickChartsProcessorService.process(); | 					case 'tickCharts': return this.tickChartsProcessorService.process(); | ||||||
| 					case 'resyncCharts': return this.resyncChartsProcessorService.process(); | 					case 'resyncCharts': return this.resyncChartsProcessorService.process(); | ||||||
| @@ -145,6 +147,14 @@ export class QueueProcessorService implements OnApplicationShutdown { | |||||||
| 					case 'clean': return this.cleanProcessorService.process(); | 					case 'clean': return this.cleanProcessorService.process(); | ||||||
| 					default: throw new Error(`unrecognized job type ${job.name} for system`); | 					default: throw new Error(`unrecognized job type ${job.name} for system`); | ||||||
| 				} | 				} | ||||||
|  | 			}; | ||||||
|  |  | ||||||
|  | 			this.systemQueueWorker = new Bull.Worker(QUEUE.SYSTEM, (job) => { | ||||||
|  | 				if (this.config.sentryForBackend) { | ||||||
|  | 					return Sentry.startSpan({ name: 'Queue: System: ' + job.name }, () => processer(job)); | ||||||
|  | 				} else { | ||||||
|  | 					return processer(job); | ||||||
|  | 				} | ||||||
| 			}, { | 			}, { | ||||||
| 				...baseQueueOptions(this.config, QUEUE.SYSTEM), | 				...baseQueueOptions(this.config, QUEUE.SYSTEM), | ||||||
| 				autorun: false, | 				autorun: false, | ||||||
| @@ -155,13 +165,22 @@ export class QueueProcessorService implements OnApplicationShutdown { | |||||||
| 			this.systemQueueWorker | 			this.systemQueueWorker | ||||||
| 				.on('active', (job) => systemLogger.debug(`active id=${job.id}`)) | 				.on('active', (job) => systemLogger.debug(`active id=${job.id}`)) | ||||||
| 				.on('completed', (job, result) => systemLogger.debug(`completed(${result}) id=${job.id}`)) | 				.on('completed', (job, result) => systemLogger.debug(`completed(${result}) id=${job.id}`)) | ||||||
| 			.on('failed', (job, err) => systemLogger.warn(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) })) | 				.on('failed', (job, err: Error) => { | ||||||
|  | 					systemLogger.error(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) }); | ||||||
|  | 					if (config.sentryForBackend) { | ||||||
|  | 						Sentry.captureMessage(`Queue: System: ${job?.name ?? '?'}: ${err.message}`, { | ||||||
|  | 							extra: { job, err }, | ||||||
|  | 						}); | ||||||
|  | 					} | ||||||
|  | 				}) | ||||||
| 				.on('error', (err: Error) => systemLogger.error(`error ${err.stack}`, { e: renderError(err) })) | 				.on('error', (err: Error) => systemLogger.error(`error ${err.stack}`, { e: renderError(err) })) | ||||||
| 				.on('stalled', (jobId) => systemLogger.warn(`stalled id=${jobId}`)); | 				.on('stalled', (jobId) => systemLogger.warn(`stalled id=${jobId}`)); | ||||||
|  | 		} | ||||||
| 		//#endregion | 		//#endregion | ||||||
|  |  | ||||||
| 		//#region db | 		//#region db | ||||||
| 		this.dbQueueWorker = new Bull.Worker(QUEUE.DB, (job) => { | 		{ | ||||||
|  | 			const processer = (job: Bull.Job) => { | ||||||
| 				switch (job.name) { | 				switch (job.name) { | ||||||
| 					case 'deleteDriveFiles': return this.deleteDriveFilesProcessorService.process(job); | 					case 'deleteDriveFiles': return this.deleteDriveFilesProcessorService.process(job); | ||||||
| 					case 'exportCustomEmojis': return this.exportCustomEmojisProcessorService.process(job); | 					case 'exportCustomEmojis': return this.exportCustomEmojisProcessorService.process(job); | ||||||
| @@ -184,6 +203,14 @@ export class QueueProcessorService implements OnApplicationShutdown { | |||||||
| 					case 'deleteAccount': return this.deleteAccountProcessorService.process(job); | 					case 'deleteAccount': return this.deleteAccountProcessorService.process(job); | ||||||
| 					default: throw new Error(`unrecognized job type ${job.name} for db`); | 					default: throw new Error(`unrecognized job type ${job.name} for db`); | ||||||
| 				} | 				} | ||||||
|  | 			}; | ||||||
|  |  | ||||||
|  | 			this.dbQueueWorker = new Bull.Worker(QUEUE.DB, (job) => { | ||||||
|  | 				if (this.config.sentryForBackend) { | ||||||
|  | 					return Sentry.startSpan({ name: 'Queue: DB: ' + job.name }, () => processer(job)); | ||||||
|  | 				} else { | ||||||
|  | 					return processer(job); | ||||||
|  | 				} | ||||||
| 			}, { | 			}, { | ||||||
| 				...baseQueueOptions(this.config, QUEUE.DB), | 				...baseQueueOptions(this.config, QUEUE.DB), | ||||||
| 				autorun: false, | 				autorun: false, | ||||||
| @@ -194,13 +221,28 @@ export class QueueProcessorService implements OnApplicationShutdown { | |||||||
| 			this.dbQueueWorker | 			this.dbQueueWorker | ||||||
| 				.on('active', (job) => dbLogger.debug(`active id=${job.id}`)) | 				.on('active', (job) => dbLogger.debug(`active id=${job.id}`)) | ||||||
| 				.on('completed', (job, result) => dbLogger.debug(`completed(${result}) id=${job.id}`)) | 				.on('completed', (job, result) => dbLogger.debug(`completed(${result}) id=${job.id}`)) | ||||||
| 			.on('failed', (job, err) => dbLogger.warn(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) })) | 				.on('failed', (job, err) => { | ||||||
|  | 					dbLogger.error(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) }); | ||||||
|  | 					if (config.sentryForBackend) { | ||||||
|  | 						Sentry.captureMessage(`Queue: DB: ${job?.name ?? '?'}: ${err.message}`, { | ||||||
|  | 							extra: { job, err }, | ||||||
|  | 						}); | ||||||
|  | 					} | ||||||
|  | 				}) | ||||||
| 				.on('error', (err: Error) => dbLogger.error(`error ${err.stack}`, { e: renderError(err) })) | 				.on('error', (err: Error) => dbLogger.error(`error ${err.stack}`, { e: renderError(err) })) | ||||||
| 				.on('stalled', (jobId) => dbLogger.warn(`stalled id=${jobId}`)); | 				.on('stalled', (jobId) => dbLogger.warn(`stalled id=${jobId}`)); | ||||||
|  | 		} | ||||||
| 		//#endregion | 		//#endregion | ||||||
|  |  | ||||||
| 		//#region deliver | 		//#region deliver | ||||||
| 		this.deliverQueueWorker = new Bull.Worker(QUEUE.DELIVER, (job) => this.deliverProcessorService.process(job), { | 		{ | ||||||
|  | 			this.deliverQueueWorker = new Bull.Worker(QUEUE.DELIVER, (job) => { | ||||||
|  | 				if (this.config.sentryForBackend) { | ||||||
|  | 					return Sentry.startSpan({ name: 'Queue: Deliver' }, () => this.deliverProcessorService.process(job)); | ||||||
|  | 				} else { | ||||||
|  | 					return this.deliverProcessorService.process(job); | ||||||
|  | 				} | ||||||
|  | 			}, { | ||||||
| 				...baseQueueOptions(this.config, QUEUE.DELIVER), | 				...baseQueueOptions(this.config, QUEUE.DELIVER), | ||||||
| 				autorun: false, | 				autorun: false, | ||||||
| 				concurrency: this.config.deliverJobConcurrency ?? 128, | 				concurrency: this.config.deliverJobConcurrency ?? 128, | ||||||
| @@ -218,13 +260,28 @@ export class QueueProcessorService implements OnApplicationShutdown { | |||||||
| 			this.deliverQueueWorker | 			this.deliverQueueWorker | ||||||
| 				.on('active', (job) => deliverLogger.debug(`active ${getJobInfo(job, true)} to=${job.data.to}`)) | 				.on('active', (job) => deliverLogger.debug(`active ${getJobInfo(job, true)} to=${job.data.to}`)) | ||||||
| 				.on('completed', (job, result) => deliverLogger.debug(`completed(${result}) ${getJobInfo(job, true)} to=${job.data.to}`)) | 				.on('completed', (job, result) => deliverLogger.debug(`completed(${result}) ${getJobInfo(job, true)} to=${job.data.to}`)) | ||||||
| 			.on('failed', (job, err) => deliverLogger.warn(`failed(${err.stack}) ${getJobInfo(job)} to=${job ? job.data.to : '-'}`)) | 				.on('failed', (job, err) => { | ||||||
|  | 					deliverLogger.error(`failed(${err.stack}) ${getJobInfo(job)} to=${job ? job.data.to : '-'}`); | ||||||
|  | 					if (config.sentryForBackend) { | ||||||
|  | 						Sentry.captureMessage(`Queue: Deliver: ${err.message}`, { | ||||||
|  | 							extra: { job, err }, | ||||||
|  | 						}); | ||||||
|  | 					} | ||||||
|  | 				}) | ||||||
| 				.on('error', (err: Error) => deliverLogger.error(`error ${err.stack}`, { e: renderError(err) })) | 				.on('error', (err: Error) => deliverLogger.error(`error ${err.stack}`, { e: renderError(err) })) | ||||||
| 				.on('stalled', (jobId) => deliverLogger.warn(`stalled id=${jobId}`)); | 				.on('stalled', (jobId) => deliverLogger.warn(`stalled id=${jobId}`)); | ||||||
|  | 		} | ||||||
| 		//#endregion | 		//#endregion | ||||||
|  |  | ||||||
| 		//#region inbox | 		//#region inbox | ||||||
| 		this.inboxQueueWorker = new Bull.Worker(QUEUE.INBOX, (job) => this.inboxProcessorService.process(job), { | 		{ | ||||||
|  | 			this.inboxQueueWorker = new Bull.Worker(QUEUE.INBOX, (job) => { | ||||||
|  | 				if (this.config.sentryForBackend) { | ||||||
|  | 					return Sentry.startSpan({ name: 'Queue: Inbox' }, () => this.inboxProcessorService.process(job)); | ||||||
|  | 				} else { | ||||||
|  | 					return this.inboxProcessorService.process(job); | ||||||
|  | 				} | ||||||
|  | 			}, { | ||||||
| 				...baseQueueOptions(this.config, QUEUE.INBOX), | 				...baseQueueOptions(this.config, QUEUE.INBOX), | ||||||
| 				autorun: false, | 				autorun: false, | ||||||
| 				concurrency: this.config.inboxJobConcurrency ?? 16, | 				concurrency: this.config.inboxJobConcurrency ?? 16, | ||||||
| @@ -242,13 +299,28 @@ export class QueueProcessorService implements OnApplicationShutdown { | |||||||
| 			this.inboxQueueWorker | 			this.inboxQueueWorker | ||||||
| 				.on('active', (job) => inboxLogger.debug(`active ${getJobInfo(job, true)}`)) | 				.on('active', (job) => inboxLogger.debug(`active ${getJobInfo(job, true)}`)) | ||||||
| 				.on('completed', (job, result) => inboxLogger.debug(`completed(${result}) ${getJobInfo(job, true)}`)) | 				.on('completed', (job, result) => inboxLogger.debug(`completed(${result}) ${getJobInfo(job, true)}`)) | ||||||
| 			.on('failed', (job, err) => inboxLogger.warn(`failed(${err.stack}) ${getJobInfo(job)} activity=${job ? (job.data.activity ? job.data.activity.id : 'none') : '-'}`, { job, e: renderError(err) })) | 				.on('failed', (job, err) => { | ||||||
|  | 					inboxLogger.error(`failed(${err.stack}) ${getJobInfo(job)} activity=${job ? (job.data.activity ? job.data.activity.id : 'none') : '-'}`, { job, e: renderError(err) }); | ||||||
|  | 					if (config.sentryForBackend) { | ||||||
|  | 						Sentry.captureMessage(`Queue: Inbox: ${err.message}`, { | ||||||
|  | 							extra: { job, err }, | ||||||
|  | 						}); | ||||||
|  | 					} | ||||||
|  | 				}) | ||||||
| 				.on('error', (err: Error) => inboxLogger.error(`error ${err.stack}`, { e: renderError(err) })) | 				.on('error', (err: Error) => inboxLogger.error(`error ${err.stack}`, { e: renderError(err) })) | ||||||
| 				.on('stalled', (jobId) => inboxLogger.warn(`stalled id=${jobId}`)); | 				.on('stalled', (jobId) => inboxLogger.warn(`stalled id=${jobId}`)); | ||||||
|  | 		} | ||||||
| 		//#endregion | 		//#endregion | ||||||
|  |  | ||||||
| 		//#region webhook deliver | 		//#region webhook deliver | ||||||
| 		this.webhookDeliverQueueWorker = new Bull.Worker(QUEUE.WEBHOOK_DELIVER, (job) => this.webhookDeliverProcessorService.process(job), { | 		{ | ||||||
|  | 			this.webhookDeliverQueueWorker = new Bull.Worker(QUEUE.WEBHOOK_DELIVER, (job) => { | ||||||
|  | 				if (this.config.sentryForBackend) { | ||||||
|  | 					return Sentry.startSpan({ name: 'Queue: WebhookDeliver' }, () => this.webhookDeliverProcessorService.process(job)); | ||||||
|  | 				} else { | ||||||
|  | 					return this.webhookDeliverProcessorService.process(job); | ||||||
|  | 				} | ||||||
|  | 			}, { | ||||||
| 				...baseQueueOptions(this.config, QUEUE.WEBHOOK_DELIVER), | 				...baseQueueOptions(this.config, QUEUE.WEBHOOK_DELIVER), | ||||||
| 				autorun: false, | 				autorun: false, | ||||||
| 				concurrency: 64, | 				concurrency: 64, | ||||||
| @@ -266,13 +338,22 @@ export class QueueProcessorService implements OnApplicationShutdown { | |||||||
| 			this.webhookDeliverQueueWorker | 			this.webhookDeliverQueueWorker | ||||||
| 				.on('active', (job) => webhookLogger.debug(`active ${getJobInfo(job, true)} to=${job.data.to}`)) | 				.on('active', (job) => webhookLogger.debug(`active ${getJobInfo(job, true)} to=${job.data.to}`)) | ||||||
| 				.on('completed', (job, result) => webhookLogger.debug(`completed(${result}) ${getJobInfo(job, true)} to=${job.data.to}`)) | 				.on('completed', (job, result) => webhookLogger.debug(`completed(${result}) ${getJobInfo(job, true)} to=${job.data.to}`)) | ||||||
| 			.on('failed', (job, err) => webhookLogger.warn(`failed(${err.stack}) ${getJobInfo(job)} to=${job ? job.data.to : '-'}`)) | 				.on('failed', (job, err) => { | ||||||
|  | 					webhookLogger.error(`failed(${err.stack}) ${getJobInfo(job)} to=${job ? job.data.to : '-'}`); | ||||||
|  | 					if (config.sentryForBackend) { | ||||||
|  | 						Sentry.captureMessage(`Queue: WebhookDeliver: ${err.message}`, { | ||||||
|  | 							extra: { job, err }, | ||||||
|  | 						}); | ||||||
|  | 					} | ||||||
|  | 				}) | ||||||
| 				.on('error', (err: Error) => webhookLogger.error(`error ${err.stack}`, { e: renderError(err) })) | 				.on('error', (err: Error) => webhookLogger.error(`error ${err.stack}`, { e: renderError(err) })) | ||||||
| 				.on('stalled', (jobId) => webhookLogger.warn(`stalled id=${jobId}`)); | 				.on('stalled', (jobId) => webhookLogger.warn(`stalled id=${jobId}`)); | ||||||
|  | 		} | ||||||
| 		//#endregion | 		//#endregion | ||||||
|  |  | ||||||
| 		//#region relationship | 		//#region relationship | ||||||
| 		this.relationshipQueueWorker = new Bull.Worker(QUEUE.RELATIONSHIP, (job) => { | 		{ | ||||||
|  | 			const processer = (job: Bull.Job) => { | ||||||
| 				switch (job.name) { | 				switch (job.name) { | ||||||
| 					case 'follow': return this.relationshipProcessorService.processFollow(job); | 					case 'follow': return this.relationshipProcessorService.processFollow(job); | ||||||
| 					case 'unfollow': return this.relationshipProcessorService.processUnfollow(job); | 					case 'unfollow': return this.relationshipProcessorService.processUnfollow(job); | ||||||
| @@ -280,6 +361,14 @@ export class QueueProcessorService implements OnApplicationShutdown { | |||||||
| 					case 'unblock': return this.relationshipProcessorService.processUnblock(job); | 					case 'unblock': return this.relationshipProcessorService.processUnblock(job); | ||||||
| 					default: throw new Error(`unrecognized job type ${job.name} for relationship`); | 					default: throw new Error(`unrecognized job type ${job.name} for relationship`); | ||||||
| 				} | 				} | ||||||
|  | 			}; | ||||||
|  |  | ||||||
|  | 			this.relationshipQueueWorker = new Bull.Worker(QUEUE.RELATIONSHIP, (job) => { | ||||||
|  | 				if (this.config.sentryForBackend) { | ||||||
|  | 					return Sentry.startSpan({ name: 'Queue: Relationship: ' + job.name }, () => processer(job)); | ||||||
|  | 				} else { | ||||||
|  | 					return processer(job); | ||||||
|  | 				} | ||||||
| 			}, { | 			}, { | ||||||
| 				...baseQueueOptions(this.config, QUEUE.RELATIONSHIP), | 				...baseQueueOptions(this.config, QUEUE.RELATIONSHIP), | ||||||
| 				autorun: false, | 				autorun: false, | ||||||
| @@ -295,18 +384,35 @@ export class QueueProcessorService implements OnApplicationShutdown { | |||||||
| 			this.relationshipQueueWorker | 			this.relationshipQueueWorker | ||||||
| 				.on('active', (job) => relationshipLogger.debug(`active id=${job.id}`)) | 				.on('active', (job) => relationshipLogger.debug(`active id=${job.id}`)) | ||||||
| 				.on('completed', (job, result) => relationshipLogger.debug(`completed(${result}) id=${job.id}`)) | 				.on('completed', (job, result) => relationshipLogger.debug(`completed(${result}) id=${job.id}`)) | ||||||
| 			.on('failed', (job, err) => relationshipLogger.warn(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) })) | 				.on('failed', (job, err) => { | ||||||
|  | 					relationshipLogger.error(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) }); | ||||||
|  | 					if (config.sentryForBackend) { | ||||||
|  | 						Sentry.captureMessage(`Queue: Relationship: ${job?.name ?? '?'}: ${err.message}`, { | ||||||
|  | 							extra: { job, err }, | ||||||
|  | 						}); | ||||||
|  | 					} | ||||||
|  | 				}) | ||||||
| 				.on('error', (err: Error) => relationshipLogger.error(`error ${err.stack}`, { e: renderError(err) })) | 				.on('error', (err: Error) => relationshipLogger.error(`error ${err.stack}`, { e: renderError(err) })) | ||||||
| 				.on('stalled', (jobId) => relationshipLogger.warn(`stalled id=${jobId}`)); | 				.on('stalled', (jobId) => relationshipLogger.warn(`stalled id=${jobId}`)); | ||||||
|  | 		} | ||||||
| 		//#endregion | 		//#endregion | ||||||
|  |  | ||||||
| 		//#region object storage | 		//#region object storage | ||||||
| 		this.objectStorageQueueWorker = new Bull.Worker(QUEUE.OBJECT_STORAGE, (job) => { | 		{ | ||||||
|  | 			const processer = (job: Bull.Job) => { | ||||||
| 				switch (job.name) { | 				switch (job.name) { | ||||||
| 					case 'deleteFile': return this.deleteFileProcessorService.process(job); | 					case 'deleteFile': return this.deleteFileProcessorService.process(job); | ||||||
| 					case 'cleanRemoteFiles': return this.cleanRemoteFilesProcessorService.process(job); | 					case 'cleanRemoteFiles': return this.cleanRemoteFilesProcessorService.process(job); | ||||||
| 					default: throw new Error(`unrecognized job type ${job.name} for objectStorage`); | 					default: throw new Error(`unrecognized job type ${job.name} for objectStorage`); | ||||||
| 				} | 				} | ||||||
|  | 			}; | ||||||
|  |  | ||||||
|  | 			this.objectStorageQueueWorker = new Bull.Worker(QUEUE.OBJECT_STORAGE, (job) => { | ||||||
|  | 				if (this.config.sentryForBackend) { | ||||||
|  | 					return Sentry.startSpan({ name: 'Queue: ObjectStorage: ' + job.name }, () => processer(job)); | ||||||
|  | 				} else { | ||||||
|  | 					return processer(job); | ||||||
|  | 				} | ||||||
| 			}, { | 			}, { | ||||||
| 				...baseQueueOptions(this.config, QUEUE.OBJECT_STORAGE), | 				...baseQueueOptions(this.config, QUEUE.OBJECT_STORAGE), | ||||||
| 				autorun: false, | 				autorun: false, | ||||||
| @@ -318,16 +424,32 @@ export class QueueProcessorService implements OnApplicationShutdown { | |||||||
| 			this.objectStorageQueueWorker | 			this.objectStorageQueueWorker | ||||||
| 				.on('active', (job) => objectStorageLogger.debug(`active id=${job.id}`)) | 				.on('active', (job) => objectStorageLogger.debug(`active id=${job.id}`)) | ||||||
| 				.on('completed', (job, result) => objectStorageLogger.debug(`completed(${result}) id=${job.id}`)) | 				.on('completed', (job, result) => objectStorageLogger.debug(`completed(${result}) id=${job.id}`)) | ||||||
| 			.on('failed', (job, err) => objectStorageLogger.warn(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) })) | 				.on('failed', (job, err) => { | ||||||
|  | 					objectStorageLogger.error(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) }); | ||||||
|  | 					if (config.sentryForBackend) { | ||||||
|  | 						Sentry.captureMessage(`Queue: ObjectStorage: ${job?.name ?? '?'}: ${err.message}`, { | ||||||
|  | 							extra: { job, err }, | ||||||
|  | 						}); | ||||||
|  | 					} | ||||||
|  | 				}) | ||||||
| 				.on('error', (err: Error) => objectStorageLogger.error(`error ${err.stack}`, { e: renderError(err) })) | 				.on('error', (err: Error) => objectStorageLogger.error(`error ${err.stack}`, { e: renderError(err) })) | ||||||
| 				.on('stalled', (jobId) => objectStorageLogger.warn(`stalled id=${jobId}`)); | 				.on('stalled', (jobId) => objectStorageLogger.warn(`stalled id=${jobId}`)); | ||||||
|  | 		} | ||||||
| 		//#endregion | 		//#endregion | ||||||
|  |  | ||||||
| 		//#region ended poll notification | 		//#region ended poll notification | ||||||
| 		this.endedPollNotificationQueueWorker = new Bull.Worker(QUEUE.ENDED_POLL_NOTIFICATION, (job) => this.endedPollNotificationProcessorService.process(job), { | 		{ | ||||||
|  | 			this.endedPollNotificationQueueWorker = new Bull.Worker(QUEUE.ENDED_POLL_NOTIFICATION, (job) => { | ||||||
|  | 				if (this.config.sentryForBackend) { | ||||||
|  | 					return Sentry.startSpan({ name: 'Queue: EndedPollNotification' }, () => this.endedPollNotificationProcessorService.process(job)); | ||||||
|  | 				} else { | ||||||
|  | 					return this.endedPollNotificationProcessorService.process(job); | ||||||
|  | 				} | ||||||
|  | 			}, { | ||||||
| 				...baseQueueOptions(this.config, QUEUE.ENDED_POLL_NOTIFICATION), | 				...baseQueueOptions(this.config, QUEUE.ENDED_POLL_NOTIFICATION), | ||||||
| 				autorun: false, | 				autorun: false, | ||||||
| 			}); | 			}); | ||||||
|  | 		} | ||||||
| 		//#endregion | 		//#endregion | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -53,7 +53,7 @@ export class FileServerService { | |||||||
| 		private internalStorageService: InternalStorageService, | 		private internalStorageService: InternalStorageService, | ||||||
| 		private loggerService: LoggerService, | 		private loggerService: LoggerService, | ||||||
| 	) { | 	) { | ||||||
| 		this.logger = this.loggerService.getLogger('server', 'gray', false); | 		this.logger = this.loggerService.getLogger('server', 'gray'); | ||||||
|  |  | ||||||
| 		//this.createServer = this.createServer.bind(this); | 		//this.createServer = this.createServer.bind(this); | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -68,7 +68,7 @@ export class ServerService implements OnApplicationShutdown { | |||||||
| 		private loggerService: LoggerService, | 		private loggerService: LoggerService, | ||||||
| 		private oauth2ProviderService: OAuth2ProviderService, | 		private oauth2ProviderService: OAuth2ProviderService, | ||||||
| 	) { | 	) { | ||||||
| 		this.logger = this.loggerService.getLogger('server', 'gray', false); | 		this.logger = this.loggerService.getLogger('server', 'gray'); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	@bindThis | 	@bindThis | ||||||
|   | |||||||
| @@ -93,7 +93,7 @@ export class ApiCallService implements OnApplicationShutdown { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	#onExecError(ep: IEndpoint, data: any, err: Error): void { | 	#onExecError(ep: IEndpoint, data: any, err: Error, userId?: MiUser['id']): void { | ||||||
| 		if (err instanceof ApiError || err instanceof AuthenticationError) { | 		if (err instanceof ApiError || err instanceof AuthenticationError) { | ||||||
| 			throw err; | 			throw err; | ||||||
| 		} else { | 		} else { | ||||||
| @@ -108,10 +108,12 @@ export class ApiCallService implements OnApplicationShutdown { | |||||||
| 					id: errId, | 					id: errId, | ||||||
| 				}, | 				}, | ||||||
| 			}); | 			}); | ||||||
| 			console.error(err, errId); |  | ||||||
|  |  | ||||||
| 			if (this.config.sentryForBackend) { | 			if (this.config.sentryForBackend) { | ||||||
| 				Sentry.captureMessage(`Internal error occurred in ${ep.name}: ${err.message}`, { | 				Sentry.captureMessage(`Internal error occurred in ${ep.name}: ${err.message}`, { | ||||||
|  | 					user: { | ||||||
|  | 						id: userId, | ||||||
|  | 					}, | ||||||
| 					extra: { | 					extra: { | ||||||
| 						ep: ep.name, | 						ep: ep.name, | ||||||
| 						ps: data, | 						ps: data, | ||||||
| @@ -410,9 +412,13 @@ export class ApiCallService implements OnApplicationShutdown { | |||||||
|  |  | ||||||
| 		// API invoking | 		// API invoking | ||||||
| 		if (this.config.sentryForBackend) { | 		if (this.config.sentryForBackend) { | ||||||
| 			return await Sentry.startSpan({ name: 'API: ' + ep.name }, () => ep.exec(data, user, token, file, request.ip, request.headers).catch((err: Error) => this.#onExecError(ep, data, err))); | 			return await Sentry.startSpan({ | ||||||
|  | 				name: 'API: ' + ep.name, | ||||||
|  | 			}, () => ep.exec(data, user, token, file, request.ip, request.headers) | ||||||
|  | 				.catch((err: Error) => this.#onExecError(ep, data, err, user?.id))); | ||||||
| 		} else { | 		} else { | ||||||
| 			return await ep.exec(data, user, token, file, request.ip, request.headers).catch((err: Error) => this.#onExecError(ep, data, err)); | 			return await ep.exec(data, user, token, file, request.ip, request.headers) | ||||||
|  | 				.catch((err: Error) => this.#onExecError(ep, data, err, user?.id)); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -102,13 +102,16 @@ SPDX-License-Identifier: AGPL-3.0-only | |||||||
| 					<template #label>Special thanks</template> | 					<template #label>Special thanks</template> | ||||||
| 					<div style="display:grid;grid-template-columns:repeat(auto-fill, minmax(130px, 1fr));grid-gap:24px;align-items:center;"> | 					<div style="display:grid;grid-template-columns:repeat(auto-fill, minmax(130px, 1fr));grid-gap:24px;align-items:center;"> | ||||||
| 						<div> | 						<div> | ||||||
| 							<a style="display: inline-block;" class="masknetwork" title="Mask Network" href="https://mask.io/" target="_blank"><img style="width: 100%;" src="https://misskey-hub.net/sponsors/masknetwork.png" alt="Mask Network"></a> | 							<a style="display: inline-block;" class="masknetwork" title="Mask Network" href="https://mask.io/" target="_blank"><img style="width: 100%;" src="https://assets.misskey-hub.net/sponsors/masknetwork.png" alt="Mask Network"></a> | ||||||
| 						</div> | 						</div> | ||||||
| 						<div> | 						<div> | ||||||
| 							<a style="display: inline-block;" class="xserver" title="XServer" href="https://www.xserver.ne.jp/" target="_blank"><img style="width: 100%;" src="https://misskey-hub.net/sponsors/xserver.png" alt="XServer"></a> | 							<a style="display: inline-block;" class="xserver" title="XServer" href="https://www.xserver.ne.jp/" target="_blank"><img style="width: 100%;" src="https://assets.misskey-hub.net/sponsors/xserver.png" alt="XServer"></a> | ||||||
| 						</div> | 						</div> | ||||||
| 						<div> | 						<div> | ||||||
| 							<a style="display: inline-block;" class="skeb" title="Skeb" href="https://skeb.jp/" target="_blank"><img style="width: 100%;" src="https://misskey-hub.net/sponsors/skeb.svg" alt="Skeb"></a> | 							<a style="display: inline-block;" class="skeb" title="Skeb" href="https://skeb.jp/" target="_blank"><img style="width: 100%;" src="https://assets.misskey-hub.net/sponsors/skeb.svg" alt="Skeb"></a> | ||||||
|  | 						</div> | ||||||
|  | 						<div> | ||||||
|  | 							<a style="display: inline-block;" class="pepabo" title="GMO Pepabo" href="https://pepabo.com/" target="_blank"><img style="width: 100%;" src="https://assets.misskey-hub.net/sponsors/gmo_pepabo.svg" alt="GMO Pepabo"></a> | ||||||
| 						</div> | 						</div> | ||||||
| 					</div> | 					</div> | ||||||
| 				</FormSection> | 				</FormSection> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 かっこかり
					かっこかり