Skip to content
This repository has been archived by the owner on Apr 19, 2023. It is now read-only.

Commit

Permalink
✨ Log API requests in ElasticSearch
Browse files Browse the repository at this point in the history
  • Loading branch information
AnandChowdhary committed Nov 13, 2020
1 parent 040eabd commit bbb002f
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 20 deletions.
13 changes: 8 additions & 5 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { ScheduleModule } from '@nestjs/schedule';
import { RateLimiterInterceptor, RateLimiterModule } from 'nestjs-rate-limiter';
import configuration from './config/configuration';
import { AuditLogger } from './interceptors/audit-log.interceptor';
import { ApiLoggerMiddleware } from './middleware/api-logger.middleware';
import { JsonBodyMiddleware } from './middleware/json-body.middleware';
import { RawBodyMiddleware } from './middleware/raw-body.middleware';
import { ApiKeysModule } from './modules/api-keys/api-keys.module';
Expand All @@ -18,21 +19,21 @@ import { AuditLogsModule } from './modules/audit-logs/audit-logs.module';
import { AuthModule } from './modules/auth/auth.module';
import { ScopesGuard } from './modules/auth/scope.guard';
import { StaartAuthGuard } from './modules/auth/staart-auth.guard';
import { DnsModule } from './providers/dns/dns.module';
import { DomainsModule } from './modules/domains/domains.module';
import { MailModule } from './providers/mail/mail.module';
import { EmailsModule } from './modules/emails/emails.module';
import { GeolocationModule } from './providers/geolocation/geolocation.module';
import { GroupsModule } from './modules/groups/groups.module';
import { MembershipsModule } from './modules/memberships/memberships.module';
import { MultiFactorAuthenticationModule } from './modules/multi-factor-authentication/multi-factor-authentication.module';
import { PrismaModule } from './providers/prisma/prisma.module';
import { SessionsModule } from './modules/sessions/sessions.module';
import { StripeModule } from './modules/stripe/stripe.module';
import { TasksModule } from './providers/tasks/tasks.module';
import { UsersModule } from './modules/users/users.module';
import { WebhooksModule } from './modules/webhooks/webhooks.module';
import { DnsModule } from './providers/dns/dns.module';
import { ElasticSearchModule } from './providers/elasticsearch/elasticsearch.module';
import { GeolocationModule } from './providers/geolocation/geolocation.module';
import { MailModule } from './providers/mail/mail.module';
import { PrismaModule } from './providers/prisma/prisma.module';
import { TasksModule } from './providers/tasks/tasks.module';

@Module({
imports: [
Expand Down Expand Up @@ -92,6 +93,8 @@ export class AppModule implements NestModule {
method: RequestMethod.POST,
})
.apply(JsonBodyMiddleware)
.forRoutes('*')
.apply(ApiLoggerMiddleware)
.forRoutes('*');
}
}
5 changes: 5 additions & 0 deletions src/config/configuration.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,9 @@ export interface Configuration {
Stripe.Checkout.SessionCreateParams.PaymentMethodType
>;
};

tracking: {
mode: 'all' | 'api-key';
index: string;
};
}
4 changes: 4 additions & 0 deletions src/config/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ const configuration: Configuration = {
stripeEndpointSecret: process.env.STRIPE_ENDPOINT_SECRET ?? '',
paymentMethodTypes: ['card'],
},
tracking: {
mode: process.env.TRACKING_MODE === 'all' ? 'all' : 'api-key',
index: process.env.TRACKING_INDEX ?? 'staart-logs',
},
};

const configFunction: ConfigFactory<Configuration> = () => configuration;
Expand Down
36 changes: 36 additions & 0 deletions src/middleware/api-logger.middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Injectable, NestMiddleware } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { NextFunction, Request, Response } from 'express';
import { Configuration } from '../config/configuration.interface';
import { ElasticSearchService } from '../providers/elasticsearch/elasticsearch.service';

@Injectable()
export class ApiLoggerMiddleware implements NestMiddleware {
constructor(
private configService: ConfigService,
private elasticSearchService: ElasticSearchService,
) {}

use(req: Request, res: Response, next: NextFunction) {
const config = this.configService.get<Configuration['tracking']>(
'tracking',
);
let date = new Date();
res.on('finish', () => {
const obj = {
date,
method: req.method,
protocol: req.protocol,
path: req.path,
authorization: req.headers.authorization ?? req.headers['x-api-key'],
duration: new Date().getTime() - date.getTime(),
status: res.statusCode,
};
if (config.mode === 'all')
this.elasticSearchService.index(config.index, obj);
else if (config.mode === 'api-key' && req.headers['x-api-key'])
this.elasticSearchService.index(config.index, obj);
});
next();
}
}
33 changes: 18 additions & 15 deletions src/providers/elasticsearch/elasticsearch.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,28 +28,31 @@ export class ElasticSearchService {
...createAwsElasticsearchConnector(AWS.config),
node: config.node,
});
} else
} else if (config.node)
this.client = new Client({
auth: config.auth,
node: config.node,
});
else this.logger.warn('ElasticSearch tracking is not enabled');
}

index(index: string, record: Record<string, any>, params?: Index) {
this.queue
.add(() =>
pRetry(() => this.indexRecord(index, record, params), {
retries: this.configService.get<number>('elasticSearch.retries') ?? 3,
onFailedAttempt: (error) => {
this.logger.error(
`Indexing record failed, retrying (${error.retriesLeft} attempts left)`,
error.name,
);
},
}),
)
.then(() => {})
.catch(() => {});
if (this.client)
this.queue
.add(() =>
pRetry(() => this.indexRecord(index, record, params), {
retries:
this.configService.get<number>('elasticSearch.retries') ?? 3,
onFailedAttempt: (error) => {
this.logger.error(
`Indexing record failed, retrying (${error.retriesLeft} attempts left)`,
error.name,
);
},
}),
)
.then(() => {})
.catch(() => {});
}

private async indexRecord(
Expand Down

0 comments on commit bbb002f

Please sign in to comment.