78 lines
2.5 KiB
TypeScript
78 lines
2.5 KiB
TypeScript
import { Injectable, Logger, Inject } from '@nestjs/common';
|
|
import { Cron, CronExpression } from '@nestjs/schedule';
|
|
import { MetricsService } from './metrics.service';
|
|
import { PresenceRedisRepository } from '../redis/presence-redis.repository';
|
|
import {
|
|
DAILY_ACTIVE_STATS_REPOSITORY,
|
|
IDailyActiveStatsRepository,
|
|
} from '../../domain/repositories/daily-active-stats.repository.interface';
|
|
import {
|
|
EVENT_LOG_REPOSITORY,
|
|
IEventLogRepository,
|
|
} from '../../domain/repositories/event-log.repository.interface';
|
|
import { format } from 'date-fns';
|
|
|
|
@Injectable()
|
|
export class MetricsCollectorService {
|
|
private readonly logger = new Logger(MetricsCollectorService.name);
|
|
private readonly presenceWindowSeconds: number;
|
|
|
|
constructor(
|
|
private readonly metricsService: MetricsService,
|
|
private readonly presenceRedisRepository: PresenceRedisRepository,
|
|
@Inject(DAILY_ACTIVE_STATS_REPOSITORY)
|
|
private readonly dauRepository: IDailyActiveStatsRepository,
|
|
@Inject(EVENT_LOG_REPOSITORY)
|
|
private readonly eventLogRepository: IEventLogRepository,
|
|
) {
|
|
this.presenceWindowSeconds = parseInt(
|
|
process.env.PRESENCE_WINDOW_SECONDS || '180',
|
|
10,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 每 15 秒更新在线人数指标
|
|
*/
|
|
@Cron('*/15 * * * * *')
|
|
async collectOnlineUsersMetric(): Promise<void> {
|
|
try {
|
|
const now = Math.floor(Date.now() / 1000);
|
|
const threshold = now - this.presenceWindowSeconds;
|
|
const count = await this.presenceRedisRepository.countOnlineUsers(threshold);
|
|
|
|
this.metricsService.setOnlineUsers(count);
|
|
this.logger.debug(`Online users metric updated: ${count}`);
|
|
} catch (error) {
|
|
this.logger.error('Failed to collect online users metric', error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 每 5 分钟更新 DAU 指标
|
|
*/
|
|
@Cron(CronExpression.EVERY_5_MINUTES)
|
|
async collectDauMetric(): Promise<void> {
|
|
try {
|
|
const today = new Date();
|
|
const dateStr = format(today, 'yyyy-MM-dd');
|
|
|
|
const stats = await this.dauRepository.findByDate(today);
|
|
if (stats) {
|
|
this.metricsService.setDau(dateStr, stats.dauCount);
|
|
this.logger.debug(`DAU metric updated: ${dateStr} = ${stats.dauCount}`);
|
|
}
|
|
} catch (error) {
|
|
this.logger.error('Failed to collect DAU metric', error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 启动时立即收集一次
|
|
*/
|
|
async onModuleInit(): Promise<void> {
|
|
await this.collectOnlineUsersMetric();
|
|
await this.collectDauMetric();
|
|
}
|
|
}
|