Skip to content

Commit

Permalink
feat(ts): loosening up the IncomingMessage and ServerResponse typings
Browse files Browse the repository at this point in the history
The typings on the various instances of `IncomingMessage` and `ServerResponse`
are too strict when coupled with Express and Fastify `Request` instances that
have been extended for custom properties. This loosens them up by allowing the
main `pino` instance to be supplied a generic of the Request and Response
interfaces that you use in your application.

Fixes #294
  • Loading branch information
erunion committed Aug 30, 2023
1 parent d1f226c commit 8b3e277
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 19 deletions.
38 changes: 19 additions & 19 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,41 +12,41 @@ import { IncomingMessage, ServerResponse } from 'http';
import pino from 'pino';
import { err, req, res, SerializedError, SerializedRequest, SerializedResponse } from 'pino-std-serializers';

declare function PinoHttp(opts?: Options, stream?: pino.DestinationStream): HttpLogger;
declare function PinoHttp<IM = IncomingMessage, SR = ServerResponse>(opts?: Options<IM, SR>, stream?: pino.DestinationStream): HttpLogger<IM, SR>;

declare function PinoHttp(stream?: pino.DestinationStream): HttpLogger;
declare function PinoHttp<IM = IncomingMessage, SR = ServerResponse>(stream?: pino.DestinationStream): HttpLogger<IM, SR>;

export interface HttpLogger {
(req: IncomingMessage, res: ServerResponse, next?: () => void): void;
export interface HttpLogger<IM = IncomingMessage, SR = ServerResponse> {
(req: IM, res: SR, next?: () => void): void;
logger: pino.Logger;
}
export type ReqId = number | string | object;

export interface Options extends pino.LoggerOptions {
export interface Options<IM = IncomingMessage, SR = ServerResponse> extends pino.LoggerOptions {
logger?: pino.Logger | undefined;
genReqId?: GenReqId | undefined;
genReqId?: GenReqId<IM, SR> | undefined;
useLevel?: pino.LevelWithSilent | undefined;
stream?: pino.DestinationStream | undefined;
autoLogging?: boolean | AutoLoggingOptions | undefined;
customLogLevel?: ((req: IncomingMessage, res: ServerResponse, error?: Error) => pino.LevelWithSilent) | undefined;
customReceivedMessage?: ((req: IncomingMessage, res: ServerResponse) => string) | undefined;
customSuccessMessage?: ((req: IncomingMessage, res: ServerResponse, responseTime: number) => string) | undefined;
customErrorMessage?: ((req: IncomingMessage, res: ServerResponse, error: Error) => string) | undefined;
customReceivedObject?: ((req: IncomingMessage, res: ServerResponse, val?: any) => any) | undefined;
customSuccessObject?: ((req: IncomingMessage, res: ServerResponse, val: any) => any) | undefined;
customErrorObject?: ((req: IncomingMessage, res: ServerResponse, error: Error, val: any) => any) | undefined;
autoLogging?: boolean | AutoLoggingOptions<IM> | undefined;
customLogLevel?: ((req: IM, res: SR, error?: Error) => pino.LevelWithSilent) | undefined;
customReceivedMessage?: ((req: IM, res: SR) => string) | undefined;
customSuccessMessage?: ((req: IM, res: SR, responseTime: number) => string) | undefined;
customErrorMessage?: ((req: IM, res: SR, error: Error) => string) | undefined;
customReceivedObject?: ((req: IM, res: SR, val?: any) => any) | undefined;
customSuccessObject?: ((req: IM, res: SR, val: any) => any) | undefined;
customErrorObject?: ((req: IM, res: SR, error: Error, val: any) => any) | undefined;
customAttributeKeys?: CustomAttributeKeys | undefined;
wrapSerializers?: boolean | undefined;
customProps?: ((req: IncomingMessage, res: ServerResponse) => object) | undefined;
customProps?: ((req: IM, res: SR) => object) | undefined;
quietReqLogger?: boolean | undefined;
}

export interface GenReqId {
(req: IncomingMessage, res: ServerResponse): ReqId;
export interface GenReqId<IM = IncomingMessage, SR = ServerResponse> {
(req: IM, res: SR): ReqId;
}

export interface AutoLoggingOptions {
ignore?: ((req: IncomingMessage) => boolean);
export interface AutoLoggingOptions<IM = IncomingMessage> {
ignore?: ((req: IM) => boolean);
}

export interface CustomAttributeKeys {
Expand Down
39 changes: 39 additions & 0 deletions index.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,69 @@ import { Writable } from 'stream';
import { err, req, res } from 'pino-std-serializers';
import pinoHttp, { AutoLoggingOptions, CustomAttributeKeys, GenReqId, HttpLogger, Options, ReqId, startTime, StdSerializers, StdSerializedResults } from '.';

interface CustomRequest extends IncomingMessage {
context: number;
}

interface CustomResponse extends ServerResponse {
context: number;
}

const logger = pino();

pinoHttp();
pinoHttp({ logger });
pinoHttp({ logger }).logger = logger;
pinoHttp<CustomRequest, CustomResponse>({ logger });

// #genReqId
pinoHttp({ genReqId: (req: IncomingMessage, res: ServerResponse) => req.statusCode || 200 });
pinoHttp({ genReqId: (req: IncomingMessage, res: ServerResponse) => res.statusCode || 200 });
pinoHttp({ genReqId: (req: IncomingMessage, res: ServerResponse) => 'foo' });
pinoHttp({ genReqId: (req: IncomingMessage, res: ServerResponse) => Buffer.allocUnsafe(16) });
pinoHttp<CustomRequest, CustomResponse>({ genReqId: (req: CustomRequest, res: CustomResponse) => Buffer.allocUnsafe(16) });

// #useLevel
pinoHttp({ useLevel: 'error' });

// #transport
pinoHttp({ transport: { target: 'pino-pretty', options: { colorize: true } } });

// #autologging
pinoHttp({ autoLogging: false });
pinoHttp({ autoLogging: { ignore: (req: IncomingMessage) => req.headers['user-agent'] === 'ELB-HealthChecker/2.0' } });
pinoHttp<CustomRequest>({ autoLogging: { ignore: (req: CustomRequest) => req.headers['user-agent'] === 'ELB-HealthChecker/2.0' } });

// #customSuccessMessage
pinoHttp({ customSuccessMessage: (req: IncomingMessage, res: ServerResponse) => 'Success' });
pinoHttp<CustomRequest, CustomResponse>({ customSuccessMessage: (req: CustomRequest, res: CustomResponse) => 'Success' });

// #customErrorMessage
pinoHttp({ customErrorMessage: (req: IncomingMessage, res: ServerResponse, error: Error) => `Error - ${error}` });
pinoHttp<CustomRequest, CustomResponse>({ customErrorMessage: (req: CustomRequest, res: CustomResponse, error: Error) => `Error - ${error}` });

// #customAttributeKeys
pinoHttp({ customAttributeKeys: { req: 'req' } });
pinoHttp({ customAttributeKeys: { res: 'res' } });
pinoHttp({ customAttributeKeys: { err: 'err' } });
pinoHttp({ customAttributeKeys: { responseTime: 'responseTime' } });
pinoHttp({ customAttributeKeys: { req: 'req', res: 'res', err: 'err', responseTime: 'responseTime' } });

// #customLogLevel
pinoHttp({ customLogLevel: (req: IncomingMessage, res: ServerResponse, error: Error | undefined) => error ? 'error' : 'info' });
pinoHttp<CustomRequest, CustomResponse>({ customLogLevel: (req: CustomRequest, res: CustomResponse, error: Error | undefined) => error ? 'error' : 'info' });

// #customProps
pinoHttp({ customProps: (req: IncomingMessage, res: ServerResponse) => ({ key1: 'value1', 'x-key-2': 'value2' }) });
pinoHttp<CustomRequest, CustomResponse>({ customProps: (req: CustomRequest, res: CustomResponse) => ({ key1: 'value1', 'x-key-2': 'value2' }) });

// #wrapSerializers
pinoHttp({ wrapSerializers: false });

// streams
pinoHttp(new Writable());

// #quietReqLogger + #customAttributeKeys
pinoHttp({ quietReqLogger: true, customAttributeKeys: { reqId: 'reqId' }});

const rand = () => {
Expand Down

0 comments on commit 8b3e277

Please sign in to comment.