Skip to content

Instantly share code, notes, and snippets.

@krivochenko
Last active January 29, 2020 14:46
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save krivochenko/a4d53a1a1c69e727cad10a1d42e48393 to your computer and use it in GitHub Desktop.
Save krivochenko/a4d53a1a1c69e727cad10a1d42e48393 to your computer and use it in GitHub Desktop.
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
import { StatisticsService } from './statistics.service';
@Injectable()
export class StatisticsInterceptor implements NestInterceptor {
constructor(
private readonly statisticsService: StatisticsService,
) {
}
intercept(context: ExecutionContext, next: CallHandler<any>): Observable<any> | Promise<Observable<any>> {
const controllerClass = context.getClass();
const method = context.getHandler();
const controllerPath = Reflect.getMetadata('path', controllerClass).replace(/(^\/)|(\/$)/g, '');
const methodPath = Reflect.getMetadata('path', method).replace(/(^\/)|(\/$)/g, '');
const parts = [controllerPath, methodPath].filter(part => !!part);
const fullPath = parts.join('/') || '/';
const startTime = Date.now();
return next
.handle()
.pipe(tap(async () => {
const endTime = Date.now();
const time = endTime - startTime;
await this.statisticsService.add(fullPath, time);
}));
}
}
import { Injectable } from '@nestjs/common';
import { RedisService } from '../redis/redis.service';
import { ago, now } from '../utils/helpers';
import { AppConfig } from '../../config/app.config';
import { InjectConfig } from '@shakajs/nestjs-config';
@Injectable()
export class StatisticsService {
constructor(
private readonly redisService: RedisService,
@InjectConfig() private readonly config: AppConfig,
) {}
async add(path: string, time: number) {
const records = await this.getRecords(path);
records.push({ path, time, createdAt: now() });
await this.client.set(path, JSON.stringify(records), 'EX', this.config.statisticsPeriod);
}
getStatistics() {
return this.client.keys('*').then(async (paths) => {
const statistics = {};
for (const path of paths) {
const records = await this.getRecords(path);
const fullTime = records.reduce((previous, record) => previous + record.time, 0);
statistics[path] = {
requestsCount: records.length,
avgTime: fullTime / records.length,
}
}
return statistics;
});
}
getRecords(path: string) {
return this.client.get(path).then(async (result) => {
const records: StatisticRecord[] = !!result ? JSON.parse(result) : [];
return records.filter(record => record.createdAt >= ago(this.config.statisticsPeriod * 24));
});
}
get client() {
return this.redisService.getClient('statistics');
}
}
export interface StatisticRecord {
path: string;
time: number;
createdAt: number;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment