All files / src/common/interceptors logging.interceptor.ts

27.9% Statements 12/43
25% Branches 4/16
50% Functions 2/4
22.5% Lines 9/40

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 9514x           14x 14x 14x 14x     14x 14x     298x 298x                                                                                                                                                          
import {
  Injectable,
  NestInterceptor,
  ExecutionContext,
  CallHandler,
} from '@nestjs/common';
import { Observable, tap } from 'rxjs';
import { LoggerService } from '@app/common/services/logger.service';
import { v4 as uuidv4 } from 'uuid';
import { GqlExecutionContext } from '@nestjs/graphql';
 
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  constructor(private readonly logger: LoggerService) {}
 
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const isProd = process.env.NODE_ENV === 'production';
    if (!isProd) return next.handle(); // dev => no duplicate line
 
    let req: any;
    let method: string;
    let urlOrField: string;
    let headers: any;
    let gqlInfo: any = null;
 
    // HTTP route?
    if (context.getType() === 'http') {
      req = context.switchToHttp().getRequest();
      method = req.method;
      urlOrField = req.url;
      headers = req.headers;
    }
    // GraphQL resolver?
    else if (context.getType<'graphql'>() === 'graphql') {
      const gqlCtx = GqlExecutionContext.create(context);
      req = gqlCtx.getContext().req;
      gqlInfo = gqlCtx.getInfo();
      method = 'GQL';
      urlOrField = gqlInfo.fieldName;
      headers = req?.headers || {};
    }
    else {
      method = 'UNKNOWN';
      urlOrField = 'unknown';
      headers = {};
      req = {};
    }
 
    const requestId = req?.headers?.['x-request-id'] || uuidv4();
    const startTime = Date.now();
 
    this.logger.setRequestId(requestId);
    this.logger.setContext(method === 'GQL' ? 'GraphQL' : 'HTTP');
 
    this.logger.info(`Incoming ${method} ${urlOrField}`, {
      method,
      urlOrField,
      headers,
      body: req?.body,
    });
 
    return next.handle().pipe(
      tap({
        next: (data) => {
          const duration = Date.now() - startTime;
          let statusCode = undefined;
          Iif (context.getType() === 'http') {
            const response = context.switchToHttp().getResponse();
            statusCode = response.statusCode;
          }
          this.logger.info(`Outgoing ${method} ${urlOrField}`, {
            method,
            urlOrField,
            statusCode,
            duration,
            response: data,
          });
        },
        error: (error) => {
          const duration = Date.now() - startTime;
          this.logger.error(`Error in ${method} ${urlOrField}`, {
            method,
            urlOrField,
            duration,
            error: {
              message: error.message,
              stack: error.stack,
              name: error.name,
            },
          });
        },
      }),
    );
  }
}