npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

nestjs-log-decorator

v1.0.1

Published

Decorator that removes try catch boilerplate logging from NestJS service methods

Readme

Build Status npm version Bundle Size NPM Downloads License: AGPL-3.0

TypeScript decorators that eliminate logging boilerplate from NestJS applications.

Quick StartHow It WorksUsageOptionsAPI ReferenceAdvanced Example

Description

@Log() decorator replaces the try-catch logging pattern in NestJS service methods by automatically logging method invocation on return or error.

Key Features

  • By default uses structured output
  • Prettifies Axios errors
  • Zero configuration
  • Zero dependencies

Installation

npm install nestjs-log-decorator @nestjs/common

Quick Start

import { Logger } from '@nestjs/common';
import { Log } from 'nestjs-log-decorator';

class UserService {
  readonly logger = new Logger(UserService.name);

  @Log()
  createUser(name: string, email: string) {
    return { id: 1, name, email };
  }
}

Once a service method is called, it will log the method invocation with all arguments.

const result = service.createUser('John', '[email protected]');
// console output:
// [UserService] { method: 'createUser', state: 'success', args: { name: 'John', email: '[email protected]' } }

Error Logging

If a method throws an error, by default the decorator logs and throws it, preserving the original stack trace.

@Log()
createUser(name: string) {
  throw new Error('Validation failed');
}

Example call with error:

const result = service.createUser('John');
// console output:
// [UserService] { method: 'createUser', state: 'error', args: { name: 'John' }, error: Error: Validation failed }

Invocation Logging

If you want to log the method invocation, you can use the onInvoke option.

@Log({ onInvoke: true })
async createUser(name: string) {
  return await Promise.resolve({ name });
}

Example call with invocation logging:

const resultPromise = service.createUser('John');
// [UserService] { method: 'createUser', state: 'invoked', args: { name: 'John' } }
const result = await resultPromise;
// [UserService] { method: 'createUser', state: 'success', args: { name: 'John' } }

Complete Example

After installation, no additional configuration is needed, just make sure that your class has a public logger property that implements the default NestJS Logger interface.

import { Logger } from '@nestjs/common';
import { Log } from 'nestjs-log-decorator';


class PaymentService {
  // `logger` property will be used by decorator
  readonly logger = new Logger(PaymentService.name);

  @Log()
  async processPayment(amount: number, currency: string) {
    // Automatically logged on success or error
    return await this.gateway.processPayment(amount, currency);
  }

  async refund(transactionId: string) {
    // Not logged without @Log() decorator
    return await this.gateway.refund(transactionId);
  }
}

The logger property is public due to TypeScript validation limitations. TS cannot check if a private property is valid, but it should still work even if the property is private.

How It Works

The @Log() decorator wraps your methods with automatic try-catch logging. It extracts parameter names, captures arguments, and logs structured output on success or error.

┌─────────────────────────────────────────────────────────────────────────────┐
│                            @Log() Decorator Flow                            │
└─────────────────────────────────────────────────────────────────────────────┘

  Method Call                                                        
       │                                                             
       ▼                                                             
┌──────────────────┐                                                 
│ Extract Args     │  ──▶  { id: 1, name: 'John' }                   
│ (auto or custom) │                                                 
└────────┬─────────┘                                                 
         │                                                           
         ▼                                                           
┌──────────────────┐       ┌─────────────────────────────────────┐   
│ onInvoke: true?  │──YES─▶│ logger.log({ state: 'invoked' })    │   
└────────┬─────────┘       └─────────────────────────────────────┘   
         │ NO                                                        
         ▼                                                           
┌──────────────────────┐                                                 
│ Execute Original     │                                                 
│ Method (sync/async)  │                                                 
└────────┬─────────────┘                                                 
         │                                                           
    ┌────┴────┐                                                      
    ▼         ▼                                                      
 SUCCESS    ERROR                                                    
    │         │                                                      
    ▼         ▼                                                      
┌────────┐ ┌──────────────────────────────────────────────────────┐  
│log()   │ │ logger.error({ state: 'error', error: prettify(e) })│  
│success │ │ (Axios errors auto-prettified)                      │  
└────────┘ └──────────────────────────────────────────────────────┘  
    │         │                                                      
    ▼         ▼                                                      
 Return    Re-throw                                                  
 Result    Error                                                     

Usage

Method-Level Decorator

Apply @Log() to specific methods for granular control:

class DataService {
  readonly logger = new Logger(DataService.name);

  @Log()
  async fetchData(id: number) {
    // This method is logged
    return await this.repository.findById(id);
  }

  helperMethod() {
    // This method is NOT logged
    return 'helper';
  }
}

Class-Level Decorator

If you want to log all methods in a class, use the @Log() decorator on its definition:

import { Logger } from '@nestjs/common';
import { Log } from 'nestjs-log-decorator';

@Log()
@Injectable()
class PaymentService {
  readonly logger = new Logger(PaymentService.name);

  processPayment(amount: number, currency: string) {
    // Automatically logged on success or error
    return { status: 'completed', amount, currency };
  }

  async refund(transactionId: string) {
    // Async methods are also logged
    return await this.gateway.refund(transactionId);
  }
}

Optional: Excluding Methods with @NoLog()

When using class-level @Log(), you can exclude specific methods with @NoLog():

import { Log, NoLog } from 'nestjs-log-decorator';

@Log()
class UserService {
  readonly logger = new Logger(UserService.name);

  createUser(name: string) {
    // Logged
    return { name };
  }

  @NoLog()
  internalHelper() {
    // NOT logged
    return 'helper';
  }
}

Options

onInvoke

Log method invocation (before execution), not just completion:

@Log({ onInvoke: true })
async fetchExternalData(url: string) {
  const response = await fetch(url);
  return response.json();
}
// Logs: { method: 'fetchExternalData', state: 'invoked', args: { url: '...' } }
// Logs: { method: 'fetchExternalData', state: 'success', args: { url: '...' } }

Class-level with onInvoke:

@Log({ onInvoke: true })
class ApiService {
  readonly logger = new Logger(ApiService.name);
  
  // All methods will log invocation + completion
}

args — Custom Argument Formatting

Control what arguments are logged. Useful for:

  • Excluding large objects from logs
  • Hiding sensitive data (passwords, tokens)
  • Logging only specific arguments
interface LargePayload {
  data: Buffer;
  metadata: object;
}

class SyncService {
  readonly logger = new Logger(SyncService.name);

  // Only log the ID, exclude the large payload
  @Log({ args: (id: number, _payload: LargePayload) => ({ id }) })
  async syncData(id: number, payload: LargePayload) {
    return await this.process(id, payload);
  }

  // Log multiple specific args
  @Log({ args: (userId: number, txId: string, _data: object) => ({ userId, txId }) })
  async processTransaction(userId: number, txId: string, data: object) {
    return await this.execute(userId, txId, data);
  }

  // Return a custom string
  @Log({ args: (id: number, name: string) => `${id}:${name}` })
  lookupUser(id: number, name: string) {
    return this.users.find(id, name);
  }
}

Output:

[SyncService] { method: 'syncData', state: 'success', args: { id: 123 } }
[SyncService] { method: 'processTransaction', state: 'success', args: { userId: 1, txId: 'tx_abc' } }
[SyncService] { method: 'lookupUser', state: 'success', args: '1:John' }

Log Format

All logs are structured JSON objects:

Success Log

{
  method: 'methodName',
  state: 'success',
  args: { param1: value1, param2: value2 }
}

Invocation Log (when onInvoke: true)

{
  method: 'methodName',
  state: 'invoked',
  args: { param1: value1, param2: value2 }
}

Error Log

{
  method: 'methodName',
  state: 'error',
  args: { param1: value1, param2: value2 },
  error: Error | PrettifiedAxiosError
}

Methods with No Arguments

{
  method: 'methodName',
  state: 'success',
  args: undefined
}

Error Handling

Standard Errors

Regular JavaScript errors are logged as-is and re-thrown:

@Log()
processPayment(amount: number) {
  if (amount <= 0) {
    throw new Error('Invalid amount');
  }
  return { status: 'success' };
}

Log Output:

{
  method: 'processPayment',
  state: 'error',
  args: { amount: -10 },
  error: {
    message: 'Invalid amount',
    stack: '...',
    ...
  }
}

Axios Errors (Auto-Prettified)

Axios errors are automatically formatted with structured request/response info:

@Log()
async fetchData(url: string) {
  const response = await this.httpClient.get(url);
  return response.data;
}

Prettified Axios Error Output:

{
  method: 'fetchData',
  state: 'error',
  args: { url: 'http://api.example.com/data' },
  error: {
    name: 'AxiosError',
    error: 'Request failed with status code 404',
    code: 'ERR_BAD_REQUEST',
    config: {
      method: 'get',
      url: 'http://api.example.com/data',
      headers: { ... },
    },
    response: {
      status: 404,
      statusText: 'Not Found',
      data: { message: 'Resource not found' },
      headers: { ... }
    }
  }
}

API Reference

Log(options?)

Decorator that can be applied to classes or methods.

| Option | Type | Default | Description | |--------|------|---------|-------------| | onInvoke | boolean | false | Log method invocation before execution | | args | (...args) => any | undefined | Custom function to format logged arguments |

NoLog()

Method decorator that excludes a method from class-level @Log() logging.

Exported Types

import { Log, NoLog, LogOptions, Loggable, isLoggable } from 'nestjs-log-decorator';

| Export | Type | Description | |--------|------|-------------| | Log | Decorator | Main logging decorator | | NoLog | Decorator | Exclude method from logging | | LogOptions | Interface | Options for @Log() decorator | | Loggable | Interface | Interface for classes with a logger property | | isLoggable | Function | Type guard to check if instance has logger |

Advanced Example

import { Injectable, Logger } from '@nestjs/common';
import { Log, NoLog } from 'nestjs-log-decorator';

@Log()
@Injectable()
export class OrderService {
  readonly logger = new Logger(OrderService.name);

  constructor(
    readonly orderRepo: OrderRepository,
    readonly paymentGateway: PaymentGateway,
  ) {}

  // Logged with all args
  async createOrder(userId: number, items: OrderItem[]) {
    const order = await this.orderRepo.create({ userId, items });
    return order;
  }

  // Logged with invocation + custom args (exclude sensitive card data)
  @Log({ 
    onInvoke: true, 
    args: (orderId: number, _cardDetails: CardDetails) => ({ orderId }) 
  })
  async processPayment(orderId: number, cardDetails: CardDetails) {
    const result = await this.paymentGateway.charge(orderId, cardDetails);
    return result;
  }

  // Not logged - internal helper
  @NoLog()
  calculateTotal(items: OrderItem[]): number {
    return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
  }

  // Logged - errors will include prettified Axios details if from HTTP call
  async syncWithExternalSystem(orderId: number) {
    const response = await this.externalApi.post('/orders', { orderId });
    return response.data;
  }
}