@viniciushrk/nestjs-audit-logger
v1.2.0
Published
NestJS library for automatic API audit logging with PostgreSQL integration
Maintainers
Readme
NestJS Audit Logger
Uma biblioteca completa para auditoria automática de APIs em aplicações NestJS com integração PostgreSQL via TypeORM.
🚀 Características
- ✅ Interceptor Automático: Captura automaticamente requisições HTTP marcadas com
@AuditLog - ✅ Tokens Customizados: Suporte a tokens não-JWT de múltiplas fontes
- ✅ PostgreSQL Integration: Salvamento otimizado no PostgreSQL via TypeORM
- ✅ Configuração Flexível: Configurações globais e por endpoint
- ✅ TypeScript Completo: Tipagem completa para melhor DX
- ✅ Performance Otimizada: Execução assíncrona não-bloqueante
- ✅ IP Real: Captura de IP real considerando proxies
- ✅ Estatísticas Avançadas: Relatórios e análises de uso
📦 Instalação
npm install @viniciushrk/nestjs-audit-loggerDependências Necessárias (Peer Dependencies)
npm install @nestjs/common @nestjs/core @nestjs/typeorm typeorm pg reflect-metadata rxjs🛠️ Configuração
1. Configuração Básica
// app.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AuditLoggerModule } from '@viniciushrk/nestjs-audit-logger';
@Module({
imports: [
// Configuração do PostgreSQL
TypeOrmModule.forRoot({
type: 'postgres',
host: 'localhost',
port: 5432,
username: 'postgres',
password: 'password',
database: 'myapp',
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: true, // Apenas para desenvolvimento
}),
// Configuração do Audit Logger
AuditLoggerModule.forRoot({
logRequestBody: true,
logResponseBody: false,
excludeRoutes: ['/health', '/metrics'],
logLevel: 'info',
database: {
connection: 'default',
},
}),
],
})
export class AppModule {}2. Configuração Assíncrona
// app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { AuditLoggerModule } from '@viniciushrk/nestjs-audit-logger';
@Module({
imports: [
ConfigModule.forRoot(),
AuditLoggerModule.forRootAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => ({
logRequestBody: configService.get('AUDIT_LOG_REQUEST_BODY', false),
logResponseBody: configService.get('AUDIT_LOG_RESPONSE_BODY', false),
excludeRoutes: configService.get('AUDIT_EXCLUDE_ROUTES', '').split(','),
database: {
connection: configService.get('AUDIT_DB_CONNECTION', 'default'),
},
}),
inject: [ConfigService],
}),
],
})
export class AppModule {}3. Setup do Banco de Dados
Execute a migration para criar a tabela de auditoria:
CREATE TABLE audit_logs (
id BIGSERIAL PRIMARY KEY,
timestamp TIMESTAMP WITH TIME ZONE NOT NULL,
user_id VARCHAR(255),
user_email VARCHAR(255),
http_method VARCHAR(10) NOT NULL,
endpoint TEXT NOT NULL,
service_name VARCHAR(255),
action_description TEXT,
request_data JSONB,
response_data JSONB,
user_agent TEXT,
ip_address INET,
token TEXT,
status_code INTEGER,
execution_time_ms INTEGER,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
-- Índices para performance
CREATE INDEX idx_audit_logs_timestamp ON audit_logs (timestamp);
CREATE INDEX idx_audit_logs_user_id ON audit_logs (user_id);
CREATE INDEX idx_audit_logs_endpoint ON audit_logs (endpoint);
CREATE INDEX idx_audit_logs_service_name ON audit_logs (service_name);
CREATE INDEX idx_audit_logs_status_code ON audit_logs (status_code);🎯 Uso
1. Básico - Decorador @AuditLog
// users.controller.ts
import {
Controller,
Post,
Body,
Get,
Param,
UseInterceptors,
} from '@nestjs/common';
import {
AuditLog,
AuditLoggerInterceptor,
} from '@viniciushrk/nestjs-audit-logger';
@Controller('users')
@UseInterceptors(AuditLoggerInterceptor) // Aplicar o interceptor
export class UsersController {
@Post()
@AuditLog({
serviceName: 'UsersService',
description: 'Criação de novo usuário',
logRequestBody: true,
logResponseBody: true,
})
async createUser(@Body() userData: CreateUserDto) {
return this.usersService.create(userData);
}
@Get(':id')
@AuditLog({
serviceName: 'UsersService',
description: 'Busca de usuário por ID',
logRequestBody: false,
logResponseBody: true,
})
async getUserById(@Param('id') id: string) {
return this.usersService.findById(id);
}
@Get() // Sem @AuditLog = não será auditado
async listUsers() {
return this.usersService.findAll();
}
}2. Aplicação Global do Interceptor
// app.module.ts ou main.ts
import { APP_INTERCEPTOR } from '@nestjs/core';
import { AuditLoggerInterceptor } from '@viniciushrk/nestjs-audit-logger';
@Module({
providers: [
{
provide: APP_INTERCEPTOR,
useClass: AuditLoggerInterceptor,
},
],
})
export class AppModule {}3. Tokens Customizados
A biblioteca automaticamente captura tokens de:
- Header Authorization:
Bearer token-value - Headers customizados:
x-api-token,x-auth-token,x-access-token - Query parameters:
token,apikey,api_key,access_token
Extratores Customizados
AuditLoggerModule.forRoot({
// Extrator de token customizado
customTokenExtractor: request => {
return request.headers['my-custom-token'];
},
// Extrator de user ID do token
customUserIdExtractor: token => {
try {
const decoded = JSON.parse(token);
return decoded.customUserId;
} catch {
return undefined;
}
},
// Extrator de email do token
customUserEmailExtractor: token => {
try {
const decoded = JSON.parse(token);
return decoded.userEmail;
} catch {
return undefined;
}
},
});📊 Consultas e Relatórios
1. Serviço de Auditoria
// my.service.ts
import { Injectable } from '@nestjs/common';
import { AuditLoggerService } from '@viniciushrk/nestjs-audit-logger';
@Injectable()
export class ReportsService {
constructor(private readonly auditService: AuditLoggerService) {}
async getUserActivity(userId: string) {
const logs = await this.auditService.getAuditLogs(
{ userId },
{ page: 1, limit: 100, sortBy: 'timestamp', sortOrder: 'DESC' },
);
return logs;
}
async getServiceStats() {
const stats = await this.auditService.getAuditStatistics({
startDate: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000), // 30 dias
});
return stats;
}
}2. Exemplos de Consultas
// Logs de um usuário específico
const userLogs = await auditService.getAuditLogs(
{ userId: 'user-123' },
{ page: 1, limit: 50 },
);
// Logs de erro (status 4xx/5xx)
const errorLogs = await auditService.getAuditLogs(
{ statusCode: 500 },
{ sortBy: 'timestamp', sortOrder: 'DESC' },
);
// Logs de um serviço específico
const serviceLogs = await auditService.getAuditLogs(
{ serviceName: 'UsersService' },
{ page: 1, limit: 100 },
);
// Estatísticas gerais
const stats = await auditService.getAuditStatistics({
startDate: new Date('2024-01-01'),
endDate: new Date('2024-12-31'),
});
console.log({
total: stats.totalRequests,
success: stats.successfulRequests,
errors: stats.failedRequests,
avgTime: stats.averageExecutionTime,
topEndpoints: stats.topEndpoints,
topUsers: stats.topUsers,
});⚙️ Opções de Configuração
AuditLoggerModuleOptions
interface AuditLoggerModuleOptions {
logRequestBody?: boolean; // Logar corpo da requisição (default: false)
logResponseBody?: boolean; // Logar corpo da resposta (default: false)
excludeRoutes?: string[]; // Rotas a excluir (supports wildcards)
logLevel?: 'debug' | 'info' | 'warn' | 'error'; // Nível de log (default: 'info')
database?: {
connection?: string; // Nome da conexão TypeORM (default: 'default')
};
customTokenExtractor?: (request: any) => string | undefined;
customUserIdExtractor?: (token: string) => string | undefined;
customUserEmailExtractor?: (token: string) => string | undefined;
}AuditLogOptions (Decorator)
interface AuditLogOptions {
serviceName?: string; // Nome do serviço
description?: string; // Descrição da ação
logRequestBody?: boolean; // Override do config global
logResponseBody?: boolean; // Override do config global
}🛡️ Segurança
Headers Sensíveis
Os seguintes headers são automaticamente sanitizados nos logs:
authorizationcookiex-api-tokenx-auth-tokenx-access-tokenapi-keyapikey
Exemplo de Log Seguro
{
"requestData": {
"headers": {
"content-type": "application/json",
"authorization": "[REDACTED]",
"x-api-token": "[REDACTED]"
},
"body": { "name": "João", "email": "[email protected]" }
}
}🧹 Manutenção
Limpeza de Logs Antigos
// Executar limpeza (manter apenas 90 dias)
const deletedCount = await auditService.cleanupOldAuditLogs(90);
console.log(`${deletedCount} logs removidos`);Scheduled Cleanup (usando @nestjs/schedule)
import { Injectable } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';
import { AuditLoggerService } from '@viniciushrk/nestjs-audit-logger';
@Injectable()
export class CleanupService {
constructor(private readonly auditService: AuditLoggerService) {}
@Cron(CronExpression.EVERY_WEEK)
async cleanupOldLogs() {
await this.auditService.cleanupOldAuditLogs(90);
}
}📈 Performance
Otimizações Implementadas
- ✅ Execução Assíncrona: Logs são salvos usando
setImmediate()para não bloquear a resposta - ✅ Índices Otimizados: Índices PostgreSQL para consultas rápidas
- ✅ Sanitização Eficiente: Headers sensíveis são sanitizados apenas na memória
- ✅ Configuração Granular: Log apenas o necessário por endpoint
Benchmarks
Em testes com 1000 requisições simultâneas:
- Overhead: < 2ms por requisição
- Throughput: Sem impacto significativo
- Memória: < 50MB adicional
🔧 Desenvolvimento
Estrutura do Projeto
src/
├── lib/
│ ├── audit-logger.module.ts # Módulo principal
│ ├── audit-logger.service.ts # Serviço de auditoria
│ ├── audit-logger.interceptor.ts # Interceptor automático
│ ├── entities/
│ │ └── audit-log.entity.ts # Entidade TypeORM
│ ├── interfaces/
│ │ └── audit-logger.interface.ts # Interfaces TypeScript
│ └── decorators/
│ └── audit-log.decorator.ts # Decorator @AuditLog
├── index.ts # Export principal
└── public-api.ts # API públicaScripts Disponíveis
npm run build # Build da biblioteca
npm run test # Executar testes
npm run lint # Linting do código
npm run format # Formatação com Prettier🤝 Contribuição
- Fork o projeto
- Crie uma branch para sua feature (
git checkout -b feature/AmazingFeature) - Commit suas mudanças (
git commit -m 'Add some AmazingFeature') - Push para a branch (
git push origin feature/AmazingFeature) - Abra um Pull Request
📄 Licença
Este projeto está sob a licença MIT. Veja o arquivo LICENSE para detalhes.
🆘 Suporte
- Issues: GitHub Issues
- Documentação: GitHub Wiki
- Email: [email protected]
Desenvolvido com ❤️
