<p align="center">
  <img src="https://res.cloudinary.com/domebtgvk/image/upload/v1751872966/free-wood-log-icon-download-in-svg-png-gif-file-formats--wooden-timber-firewood-autumn-pack-nature-icons-5002823_zdbj46.png" alt="Firstlog Logo" width="180" />
</p>

<h1 align="center"><strong>Firstlog</strong></h1>

<p align="center">
  <img src="https://img.shields.io/npm/v/firstlog.svg?style=for-the-badge" alt="NPM version" />
  &nbsp;&nbsp;
  <img src="https://img.shields.io/npm/dm/firstlog.svg?style=for-the-badge" alt="NPM downloads" />
  &nbsp;&nbsp;
  <img src="https://img.shields.io/github/stars/adidecodes/firstlog.svg?style=for-the-badge" alt="GitHub stars" />
  &nbsp;&nbsp;
  <img src="https://img.shields.io/badge/built%20with-TypeScript-blue.svg?style=for-the-badge" alt="Built with TypeScript" />
</p>

<p align="center">
  <em>Firstlog is a flexible and powerful Express.js middleware for advanced request logging with comprehensive features like geographic tracking, performance monitoring, and customizable output formats.</em>
</p>

## Features

- 🚀 **Easy Integration** - Simple Express middleware setup
- 📊 **Performance Monitoring** - Track request duration and identify slow requests
- 🌍 **Geographic Tracking** - Optional GeoIP location tracking
- 🔒 **Security** - Mask sensitive fields in logs
- 📝 **Flexible Logging** - Outputs is JSON object format
- 🎯 **Selective Logging** - Filter by paths, errors, or custom conditions
- 📦 **TypeScript Support** - Full TypeScript definitions included
- 🔧 **Highly Configurable** - Extensive customization options

## Installation

```bash
npm install firstlog
```

To use the optional GeoIP feature, you must also install `geoip-lite`:

```bash
npm install geoip-lite
```

## Quick Start

```typescript
import express from "express";
import { logger } from "firstlog";

const app = express();

// Basic usage
app.use(
  logger({
    logFile: "./logs/access.log",
  })
);

// Your routes
app.get("/", (req, res) => {
  res.json({ message: "Hello World" });
});

app.listen(3000);
```

## Configuration Options

### Basic Options

| Option        | Type       | Default                 | Description                       |
| ------------- | ---------- | ----------------------- | --------------------------------- |
| `logFile`     | `string`   | **Required**            | Path to the log file              |
| `maskFields`  | `string[]` | `['password', 'token']` | Fields to mask in logs            |
| `captureBody` | `boolean`  | `true`                  | Whether to capture request body   |
| `prettyPrint` | `boolean`  | `false`                 | Format JSON logs with indentation |

### Advanced Options

| Option            | Type       | Default          | Description                                        |
| ----------------- | ---------- | ---------------- | -------------------------------------------------- |
| `onlyLogOnError`  | `boolean`  | `false`          | Only log requests that result in errors (4xx, 5xx) |
| `maxBodySize`     | `number`   | `1024`           | Maximum body size to log (in bytes)                |
| `slowThresholdMs` | `number`   | `1000`           | Threshold for marking requests as slow             |
| `excludePaths`    | `string[]` | `[]`             | Paths to exclude from logging                      |
| `requestIdHeader` | `string`   | `'x-request-id'` | Header name for request ID                         |

### Feature Toggles

| Option            | Type      | Default | Description                           |
| ----------------- | --------- | ------- | ------------------------------------- |
| `trackQuery`      | `boolean` | `false` | Include query parameters in logs      |
| `trackOrigin`     | `boolean` | `false` | Track the origin of the request       |
| `enableGeoIP`     | `boolean` | `false` | Enable geographic IP tracking         |
| `logHeaders`      | `boolean` | `false` | Include request headers in logs       |
| `logParams`       | `boolean` | `false` | Include route parameters in logs      |
| `logResponseBody` | `boolean` | `false` | Include response body snippet in logs |

### Callbacks

| Option      | Type                       | Description                          |
| ----------- | -------------------------- | ------------------------------------ |
| `trackUser` | `(req: Request) => string` | Custom function to identify users    |
| `onLog`     | `(logEntry) => void`       | Callback executed for each log entry |

## Usage Examples

### Basic Logging

```typescript
import { logger } from "firstlog";

app.use(
  logger({
    logFile: "./logs/app.log",
  })
);
```

### Advanced Configuration

```typescript
app.use(
  logger({
    logFile: "./logs/app.log",
    maskFields: ["password", "token", "apiKey"],
    captureBody: true,
    trackQuery: true,
    enableGeoIP: true, // Requires geoip-lite to be installed
    slowThresholdMs: 500,
    prettyPrint: true,
    excludePaths: ["/health", "/metrics"],
    trackUser: (req) => req.user?.id || "anonymous",
    onLog: (logEntry) => {
      if (logEntry.slow) {
        console.warn(`Slow request detected: ${logEntry.route}`);
      }
    },
  })
);
```

### Error-Only Logging

```typescript
app.use(
  logger({
    logFile: "./logs/errors.log",
    onlyLogOnError: true,
    logResponseBody: true,
  })
);
```

### External Service Integration

```typescript
app.use(
  logger({
    logFile: "./logs/app.log",
    onLog: (logEntry) => {
      // Send to your monitoring service
      analytics.track("request", logEntry);
    },
  })
);
```

## Log Format

Each log entry contains the following information:

```json
{
  "requestId": "550e8400-e29b-41d4-a716-446655440000",
  "timestamp": "2024-01-15T10:30:45.123Z",
  "method": "POST",
  "route": "/api/users",
  "status": 201,
  "ip": "192.168.1.100",
  "durationMs": 234.56,
  "slow": false,
  "user": "user123",
  "body": { "name": "John Doe", "password": "****" },
  "query": { "page": "1" },
  "headers": { "user-agent": "Mozilla/5.0...", "authorization": "****" },
  "params": { "id": "123" },
  "responseSnippet": "{\"success\": true, \"id\": \"456\"}",
  "location": {
    "country": "US",
    "region": "CA",
    "city": "San Francisco"
  }
}
```

## TypeScript Support

Firstlog is built with TypeScript and includes comprehensive type definitions:

```typescript
import { LoggerOptions, logger } from "firstlog";

const options: LoggerOptions = {
  logFile: "./logs/app.log",
  maskFields: ["password"],
  captureBody: true,
};

app.use(logger(options));
```

## Performance Considerations

- **Body Capture**: Disable `captureBody` for high-throughput applications
- **GeoIP**: GeoIP lookups add latency and require the `geoip-lite` peer dependency. Use only when necessary.
- **Memory Usage**: Set appropriate `maxBodySize` to prevent memory issues

## Security

- Sensitive fields are automatically masked using the `maskFields` option
- Request IDs are generated using `nanoid` package

## License

This project is licensed under the Usage-Only License.

## Dependencies

- **express**: Web framework compatibility
- **nanoid**: Secure request ID generation

### Peer Dependencies

- **geoip-lite** (optional): For geographic IP tracking.

## Authors

- [@adidecodes](https://www.github.com/adidecodes)

## Conclusion

#### If you like this package, show your support & love!

[![buy me a coffee](https://res.cloudinary.com/customzone-app/image/upload/c_pad,w_200/v1712840190/bmc-button_wl78gx.png)](https://www.buymeacoffee.com/adidecodes)

## Changelog

### v0.0.1

- Initial release
- Basic logging functionality
- TypeScript support
- GeoIP integration
- Performance monitoring
- Security features

### v0.0.2

- Minor Fixes
- Updated Readme

### v0.0.3

- Reduced package size under 100KB
- Replaced uuid with nanoid
- Modularized `geoip-lite` as an optional peer dependency to reduce bundle size for users who do not need the GeoIP feature.

### v0.0.4

- Fixed an issue where `geoip-lite` was getting installed automatically

**Made with ❤️ by Aditya**
