npm package: @logispark/nest-js-logger
Description: A reusable NestJS logger module that sends structured logs to a hosted logger API
Latest version: 1.0.3

# @logispark/nest-js-logger

A lightweight NestJS logger middleware that forwards structured logs (errors, warnings, activity) to a hosted Grafana logging service. It does **not** connect to any database — all log delivery is handled by the Grafana backend.

## Features

- **Structured Logging** — Consistent log format with levels (`debug`, `info`, `warn`, `error`, `fatal`), metadata, and contextual fields
- **Automatic HTTP Logging** — Intercepts all HTTP requests/responses and logs them with timing, user info, and tenant context
- **Grafana Integration** — Posts logs directly to your hosted Grafana logging endpoint via HTTP
- **Multi-Tenant Aware** — Automatically extracts tenant ID from user object, `x-tenant-id` header, or subdomain
- **Sensitive Data Redaction** — Automatically redacts passwords, tokens, secrets, credit cards, CVVs, and SSNs from request/response bodies
- **Route Exclusion** — Skip logging for specific routes (e.g., health checks)
- **Global Module** — Register once, inject anywhere in your NestJS application

## Installation

```bash
npm install @logispark/nest-js-logger
```

### Peer Dependencies

```bash
npm install @nestjs/common @nestjs/core @nestjs/axios rxjs
```

## Quick Start

### 1. Register the module

```typescript
import { Module } from '@nestjs/common';
import { NestJsLoggerModule } from '@logispark/nest-js-logger';

@Module({
  imports: [
    NestJsLoggerModule.forRoot({
      loggerApiUrl: 'http://3.140.127.147:3001/your-log-endpoint',
      appId: 'my-app',
      environment: 'dev',
      apiKey: 'your-api-key',       // optional
      timeout: 5000,                // optional, default: 5000ms
      excludeRoutes: ['/health'],   // optional
    }),
  ],
})
export class AppModule {}
```

### 2. Use the logging service manually

```typescript
import { Injectable } from '@nestjs/common';
import { NestJsLoggerService } from '@logispark/nest-js-logger';

@Injectable()
export class OrdersService {
  constructor(private readonly logger: NestJsLoggerService) {}

  async createOrder(order: any) {
    await this.logger.info('Order created', {
      user_id: order.userId,
      new_values: order,
      metadata: { orderId: order.id },
    });
  }

  async handleError(err: Error) {
    await this.logger.error('Order failed', {
      message: err.message,
      metadata: { stack: err.stack },
    });
  }
}
```

### 3. Enable automatic HTTP logging (recommended)

Apply the interceptor globally so every request/response is logged automatically:

```typescript
import { NestJsLoggingInterceptor } from '@logispark/nest-js-logger';
import { APP_INTERCEPTOR } from '@nestjs/core';

@Module({
  providers: [
    {
      provide: APP_INTERCEPTOR,
      useClass: NestJsLoggingInterceptor,
    },
  ],
})
export class AppModule {}
```

## How It Works

This package is a **pure middleware** — it receives log data from your NestJS app and POSTs it as JSON to your configured Grafana endpoint. There is no local database, no ClickHouse client, and no direct storage. All persistence and querying is handled by the Grafana service.

```
Your NestJS App
     │
     │  (HTTP request comes in)
     ▼
NestJsLoggingInterceptor
     │
     │  builds structured log payload
     ▼
NestJsLoggerService.log()
     │
     │  POST /your-log-endpoint  (JSON)
     ▼
Grafana Logging Service  ──►  ClickHouse (managed by Grafana)
```

## API Reference

### `NestJsLoggerOptions`

| Option | Type | Required | Default | Description |
|--------|------|----------|---------|-------------|
| `loggerApiUrl` | `string` | Yes | — | Full URL of your Grafana log ingestion endpoint |
| `appId` | `string` | Yes | — | Application identifier |
| `environment` | `'dev' \| 'prod' \| 'stage'` | No | `'dev'` | Environment name |
| `apiKey` | `string` | No | — | Bearer token for API auth |
| `timeout` | `number` | No | `5000` | HTTP request timeout in milliseconds |
| `excludeRoutes` | `string[]` | No | `[]` | Route prefixes to skip in the interceptor |

### `NestJsLoggerService` methods

| Method | Description |
|--------|-------------|
| `log(payload)` | Send a log with full control over all fields |
| `debug(message, extras?)` | Log at `debug` level |
| `info(message, extras?)` | Log at `info` level |
| `warn(message, extras?)` | Log at `warn` level |
| `error(message, extras?)` | Log at `error` level |

### `NestJsLogPayload` fields

| Field | Type | Description |
|-------|------|-------------|
| `app_id` | `string` | Application identifier |
| `app_type` | `'web' \| 'mobile' \| 'backend'` | Application type |
| `environment` | `'dev' \| 'prod' \| 'stage'` | Environment |
| `level` | `'debug' \| 'info' \| 'warn' \| 'error' \| 'fatal'` | Log level |
| `message` | `string` | Log message |
| `log_type` | `'error' \| 'activity'` | Log category |
| `tenant_id` | `string` | Tenant identifier |
| `user_id` | `string` | User identifier |
| `user_name` | `string` | Username |
| `url` | `string` | Request URL |
| `request_type` | `'GET' \| 'POST' \| 'PUT' \| 'PATCH' \| 'DELETE'` | HTTP method |
| `source` | `string` | Client IP or forwarded address |
| `metadata` | `Record<string, any>` | Additional key-value data |
| `old_values` | `Record<string, any>` | Previous values (for update operations) |
| `new_values` | `Record<string, any>` | New values (for create/update operations) |

## How the Interceptor Works

The `NestJsLoggingInterceptor` automatically captures:

- HTTP method, URL, and response status code
- Request duration in milliseconds
- User info from `request.user` (compatible with Passport or any auth guard)
- Tenant ID from the user object, `x-tenant-id` header, or subdomain
- Request and response bodies (with sensitive fields redacted automatically)
- Error details including stack traces for failed requests

Log levels are assigned based on HTTP status code:
- `info` — 2xx / 3xx
- `warn` — 4xx
- `error` — 5xx or thrown exceptions

## License

MIT License — see [LICENSE](./LICENSE) for details.