import { expectAssignable, expectDeprecated, expectError, expectNotAssignable, expectType } from 'tsd' import fastify, { FastifyLogFn, LogLevel, FastifyBaseLogger, FastifyRequest, FastifyReply } from '../../fastify' import { Server, IncomingMessage, ServerResponse } from 'node:http' import * as fs from 'node:fs' import P from 'pino' import { FastifyLoggerInstance, ResSerializerReply } from '../../types/logger' expectType(fastify().log) class Foo {} ['trace', 'debug', 'info', 'warn', 'error', 'fatal'].forEach(logLevel => { expectType( fastify().log[logLevel as LogLevel] ) expectType( fastify().log[logLevel as LogLevel]('') ) expectType( fastify().log[logLevel as LogLevel]({}) ) expectType( fastify().log[logLevel as LogLevel]({ foo: 'bar' }) ) expectType( fastify().log[logLevel as LogLevel](new Error()) ) expectType( fastify().log[logLevel as LogLevel](new Foo()) ) }) interface CustomLogger extends FastifyBaseLogger { customMethod(msg: string, ...args: unknown[]): void; } class CustomLoggerImpl implements CustomLogger { level = 'info' customMethod (msg: string, ...args: unknown[]) { console.log(msg, args) } // Implementation signature must be compatible with all overloads of FastifyLogFn info (arg1: unknown, arg2?: unknown, ...args: unknown[]): void { console.log(arg1, arg2, ...args) } warn (...args: unknown[]) { console.log(args) } error (...args: unknown[]) { console.log(args) } fatal (...args: unknown[]) { console.log(args) } trace (...args: unknown[]) { console.log(args) } debug (...args: unknown[]) { console.log(args) } silent (...args: unknown[]) { } child (bindings: P.Bindings, options?: P.ChildLoggerOptions): CustomLoggerImpl { return new CustomLoggerImpl() } } const customLogger = new CustomLoggerImpl() const serverWithCustomLogger = fastify< Server, IncomingMessage, ServerResponse, CustomLoggerImpl >({ logger: customLogger }) expectType(serverWithCustomLogger.log) const serverWithPino = fastify< Server, IncomingMessage, ServerResponse, P.Logger >({ logger: P({ level: 'info', redact: ['x-userinfo'] }) }) expectType(serverWithPino.log) serverWithPino.route({ method: 'GET', url: '/', handler (request) { expectType(this.log) expectType(request.log) } }) serverWithPino.get('/', function (request) { expectType(this.log) expectType(request.log) }) const serverWithLogOptions = fastify< Server, IncomingMessage, ServerResponse >({ logger: { level: 'info' } }) expectType(serverWithLogOptions.log) const serverWithFileOption = fastify< Server, IncomingMessage, ServerResponse >({ logger: { level: 'info', file: '/path/to/file' } }) expectType(serverWithFileOption.log) const serverAutoInferringTypes = fastify({ logger: { level: 'info' } }) expectType(serverAutoInferringTypes.log) const serverWithLoggerInstance = fastify({ loggerInstance: P({ level: 'info', redact: ['x-userinfo'] }) }) expectType(serverWithLoggerInstance.log) const serverWithPinoConfig = fastify({ logger: { level: 'info', serializers: { req (IncomingMessage) { expectType(IncomingMessage) return { method: 'method', url: 'url', version: 'version', host: 'hostname', remoteAddress: 'remoteAddress', remotePort: 80, other: '' } }, res (ServerResponse) { expectType>(ServerResponse) expectAssignable & Pick>(ServerResponse) expectNotAssignable(ServerResponse) return { statusCode: 'statusCode' } }, err (FastifyError) { return { other: '', type: 'type', message: 'msg', stack: 'stack' } } } } }) expectType(serverWithPinoConfig.log) const serverAutoInferredFileOption = fastify({ logger: { level: 'info', file: '/path/to/file' } }) expectType(serverAutoInferredFileOption.log) const serverAutoInferredSerializerResponseObjectOption = fastify({ logger: { serializers: { res (ServerResponse) { expectType>(ServerResponse) expectAssignable & Pick>(ServerResponse) expectNotAssignable(ServerResponse) return { status: '200' } } } } }) expectType(serverAutoInferredSerializerResponseObjectOption.log) const serverAutoInferredSerializerObjectOption = fastify({ logger: { serializers: { req (IncomingMessage) { expectType(IncomingMessage) return { method: 'method', url: 'url', version: 'version', host: 'hostname', remoteAddress: 'remoteAddress', remotePort: 80, other: '' } }, res (ServerResponse) { expectType>(ServerResponse) expectAssignable & Pick>(ServerResponse) expectNotAssignable(ServerResponse) return { statusCode: 'statusCode' } }, err (FastifyError) { return { other: '', type: 'type', message: 'msg', stack: 'stack' } } } } }) expectType(serverAutoInferredSerializerObjectOption.log) const passStreamAsOption = fastify({ logger: { stream: fs.createWriteStream('/tmp/stream.out') } }) expectType(passStreamAsOption.log) const passPinoOption = fastify({ logger: { redact: ['custom'], messageKey: 'msg', nestedKey: 'nested', enabled: true } }) expectType(passPinoOption.log) // FastifyLoggerInstance is deprecated expectDeprecated({} as FastifyLoggerInstance) const childParent = fastify().log // we test different option variant here expectType(childParent.child({}, { level: 'info' })) expectType(childParent.child({}, { level: 'silent' })) expectType(childParent.child({}, { redact: ['pass', 'pin'] })) expectType(childParent.child({}, { serializers: { key: () => {} } })) expectType(childParent.child({}, { level: 'info', redact: ['pass', 'pin'], serializers: { key: () => {} } })) // no option pass expectError(childParent.child()) // wrong option expectError(childParent.child({}, { nonExist: true }))