PHPackages                             prahsys/laravel-api-logs - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. prahsys/laravel-api-logs

ActiveLibrary

prahsys/laravel-api-logs
========================

Laravel API request logging with idempotency support

0.2.5(6mo ago)0211↓33.3%1MITPHPPHP ^8.1

Since Jul 10Pushed 6mo agoCompare

[ Source](https://github.com/Prahsys/laravel-api-logs)[ Packagist](https://packagist.org/packages/prahsys/laravel-api-logs)[ RSS](/packages/prahsys-laravel-api-logs/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (7)Dependencies (9)Versions (15)Used By (0)

Prahsys Laravel API Logs
========================

[](#prahsys-laravel-api-logs)

A comprehensive Laravel package for logging API requests and responses with idempotency support, model tracking, and configurable data redaction.

Features
--------

[](#features)

- **Request Correlation**: Automatic correlation ID handling for API request tracking and audit trails
- **Comprehensive Logging**: Logs API requests and responses with detailed metadata
- **Data Redaction**: Configurable pipeline-based redaction for sensitive data (PCI, PII, HIPAA, etc.)
- **Model Tracking**: Automatic association of created/updated models with API requests
- **Async Processing**: Event-driven architecture with queue support
- **Multiple Channels**: Support for raw and redacted logging channels
- **Compliance Ready**: Built-in redaction support useful for PCI DSS, SOC 2, and other compliance requirements

Architecture Overview
---------------------

[](#architecture-overview)

The package follows a clean event-driven architecture with a **lightweight database design**:

```
HTTP Request → Middleware → Event → Listener → Pipeline → Log Channels
                    ↓
              Model Tracking → Association → Database (Lightweight References)

```

### Design Philosophy

[](#design-philosophy)

**ApiLogItem** is designed as a **lightweight reference** to API requests, not a full data store. This approach:

- **Keeps database lean**: Stores only essential metadata (correlation ID, path, method, timestamps, status)
- **Enables long-term retention**: Default 365-day database retention for audit trails
- **Separates concerns**: Heavy request/response data goes to log channels, references stay in database
- **Maximizes flexibility**: Users can extend the model to store additional data if needed

The actual request/response data is processed through configurable log channels where it can be:

- Stored in log files with native Laravel rotation
- Sent to external services (Axiom, Sentry, etc.)
- Redacted according to compliance requirements
- Retained for different periods per channel

### Key Components

[](#key-components)

1. **ApiLogMiddleware**: Captures request/response data and manages correlation IDs
2. **CompleteApiLogItemEvent**: Dispatched after request completion
3. **CompleteApiLogItemListener**: Processes model associations and log data
4. **ApiLogPipelineManager**: Registers Monolog processors for automatic redaction
5. **ApiLogProcessor**: Monolog processor that applies redaction pipelines to log records
6. **ApiLogItemTracker**: Tracks models during request processing
7. **Redaction System**: Pipeline-based data redaction with configurable redactors

Installation
------------

[](#installation)

```
composer require prahsys/laravel-api-logs
```

Configuration
-------------

[](#configuration)

### 1. Publish Configuration and Migrations

[](#1-publish-configuration-and-migrations)

```
php artisan vendor:publish --provider="Prahsys\ApiLogs\ApiLogsServiceProvider"
php artisan migrate
```

### 2. Environment Configuration

[](#2-environment-configuration)

```
# API Logs Settings
API_LOGS_TTL=86400

# Logging Channels (configure in config/logging.php)
API_LOGS_RAW_CHANNEL=api_logs_raw
API_LOGS_REDACTED_CHANNEL=api_logs_redacted
```

### 3. Logging Channels

[](#3-logging-channels)

Add to your `config/logging.php`:

```
'channels' => [
    // Raw logs - restricted access, complete data
    'api_logs_raw' => [
        'driver' => 'daily',
        'path' => storage_path('logs/api_logs_raw.log'),
        'level' => 'info',
        'days' => 14,
        'permission' => 0600, // Restricted access
    ],

    // Redacted logs - general monitoring and analytics
    'api_logs_redacted' => [
        'driver' => 'daily',
        'path' => storage_path('logs/api_logs_redacted.log'),
        'level' => 'info',
        'days' => 90,
    ],

    // External monitoring services with tailored redaction
    'api_logs_sentry' => [
        'driver' => 'sentry',
        'level' => 'error',
        'bubble' => true,
    ],

    'api_logs_axiom' => [
        'driver' => 'custom',
        'via' => App\Logging\AxiomLogger::class,
        'level' => 'info',
        'dataset' => 'api_logs',
    ],

    // Stack multiple channels for comprehensive monitoring
    'api_logs_monitoring' => [
        'driver' => 'stack',
        'channels' => ['api_logs_redacted', 'api_logs_sentry'],
        'ignore_exceptions' => false,
    ],
],
```

### 4. Middleware Registration

[](#4-middleware-registration)

Add to your `app/Http/Kernel.php`:

```
protected $middlewareGroups = [
    'api' => [
        // ... other middleware
        \Prahsys\ApiLogs\Http\Middleware\ApiLogMiddleware::class,
    ],
];
```

Usage
-----

[](#usage)

### Basic Usage

[](#basic-usage)

Once configured, the package automatically logs API requests. Include an `Idempotency-Key` header for request correlation:

```
$response = Http::withHeaders([
    'Idempotency-Key' => 'unique-key-123',
])->post('/api/users', ['name' => 'John Doe']);
```

### Outbound API Logging with Guzzle

[](#outbound-api-logging-with-guzzle)

The package includes Guzzle middleware to log outbound HTTP requests your application makes to external APIs.

#### Basic Setup

[](#basic-setup)

Add the middleware to your Guzzle client's handler stack:

```
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use Prahsys\ApiLogs\Http\Middleware\GuzzleApiLogMiddleware;

$stack = HandlerStack::create();
$stack->push(app(GuzzleApiLogMiddleware::class));

$client = new Client([
    'handler' => $stack,
    'base_uri' => 'https://api.example.com',
    'timeout' => 30,
]);
```

#### Adding to Existing Handler Stack

[](#adding-to-existing-handler-stack)

If you already have a handler stack with other middleware:

```
// Get your existing handler stack
$stack = $existingClient->getConfig('handler');

// Add API logging middleware
$stack->push(app(GuzzleApiLogMiddleware::class));
```

#### Skip Logging for Specific Requests

[](#skip-logging-for-specific-requests)

```
// Skip logging by adding request option
$response = $client->get('/status', [
    'prahsys_api_logs_skip' => true
]);
```

#### Configuration

[](#configuration-1)

Configure outbound logging in your environment:

```
# Enable/disable outbound API logging
API_LOGS_OUTBOUND_ENABLED=true
```

Or in `config/api-logs.php`:

```
'outbound' => [
    'enabled' => true,
    'exclude_hosts' => [
        'localhost',
        '*.internal.company.com',
        'monitoring.example.com',
    ],
],
```

### Model Tracking

[](#model-tracking)

Add the `HasApiLogItems` trait to models you want to track:

```
use Prahsys\ApiLogs\Models\HasApiLogItems;

class User extends Model
{
    use HasApiLogItems;

    // ... model code
}
```

Models created or updated during API requests are automatically associated with the API log item. This is particularly useful for:

- **Audit trails**: Understanding which models were affected by a specific API request
- **Impact analysis**: Tracking the full scope of changes made during a request
- **Debugging**: Identifying which models were modified when troubleshooting issues
- **Compliance**: Maintaining detailed records of data modifications for regulatory requirements
- **Data lineage**: Tracing the history of model changes back to their originating API requests

### Accessing Tracked Data

[](#accessing-tracked-data)

```
// Get all API requests for a model
$user = User::find(1);
$requests = $user->apiLogItems;

// Get the latest API request for a model
$latestRequest = $user->latestApiLogItem();

// Get all models associated with an API request
$apiLogItem = ApiLogItem::where('request_id', $requestId)->first();
$users = $apiLogItem->getRelatedModels(User::class)->get();

// Example: Track all models affected by a single API request
$apiLogItem = ApiLogItem::where('request_id', 'abc-123-def')->first();

// Get all users modified in this request
$affectedUsers = $apiLogItem->getRelatedModels(User::class)->get();

// Get all orders created/updated in this request
$affectedOrders = $apiLogItem->getRelatedModels(Order::class)->get();

// Get all affected models regardless of type
$allAffectedModels = $apiLogItem->relatedModels; // Returns collection of all associated models

// Example output for debugging or audit purposes
foreach ($allAffectedModels as $model) {
    echo "Modified {$model->getMorphClass()}: ID {$model->id}";
}
```

Configuration Options
---------------------

[](#configuration-options)

### Channel Configuration

[](#channel-configuration)

Configure different redaction pipelines for different channels in `config/api-logs.php`. Each channel can have its own redaction strategy based on the destination's requirements:

```
'channels' => [
    // Raw logs - no redaction for internal secure storage
    'api_logs_raw' => [],

    // General monitoring - basic redaction for security
    'api_logs_redacted' => [
        \Prahsys\ApiLogs\Redactors\CommonHeaderFieldsRedactor::class,
        \Prahsys\ApiLogs\Redactors\CommonBodyFieldsRedactor::class,
    ],

    // External monitoring services - tailored redaction
    'api_logs_sentry' => [
        \Prahsys\ApiLogs\Redactors\CommonHeaderFieldsRedactor::class,
        \Prahsys\ApiLogs\Redactors\CommonBodyFieldsRedactor::class,
        \Prahsys\ApiLogs\Redactors\DotNotationRedactor::class => [
            'paths' => ['**.email', '**.phone', '**.ssn'],
        ],
    ],

    'api_logs_axiom' => [
        \Prahsys\ApiLogs\Redactors\CommonHeaderFieldsRedactor::class,
        \Prahsys\ApiLogs\Redactors\CommonBodyFieldsRedactor::class,
        \Prahsys\ApiLogs\Redactors\DotNotationRedactor::class => [
            'paths' => ['request.body.internal_id', 'response.body.debug_info'],
        ],
    ],
],
```

### Available Redactors

[](#available-redactors)

- `CommonHeaderFieldsRedactor`: Redacts authentication headers (extends DotNotationRedactor)
- `CommonBodyFieldsRedactor`: Redacts password fields (extends DotNotationRedactor)
- `DotNotationRedactor`: Base redactor using dot notation (supports `*` and `**` wildcards)

### Wildcard Pattern Support

[](#wildcard-pattern-support)

The `DotNotationRedactor` supports powerful wildcard patterns:

- **Single wildcard (`*`)**: Matches one level

    ```
    'users.*.email' // Matches users.1.email, users.john.email, etc.
    ```
- **Deep wildcard (`**`)**: Matches any level of nesting

    ```
    '**.card.number' // Matches card.number anywhere in the data structure
    '**.password'    // Matches password fields at any depth
    ```

Examples:

```
// Traditional specific paths
\Prahsys\ApiLogs\Redactors\DotNotationRedactor::class => [
    'paths' => ['request.body.users.0.password', 'request.body.users.1.password'],
]

// Using single wildcards
\Prahsys\ApiLogs\Redactors\DotNotationRedactor::class => [
    'paths' => ['request.body.users.*.password'],
]

// Using deep wildcards for complex nested data
\Prahsys\ApiLogs\Redactors\DotNotationRedactor::class => [
    'paths' => ['**.password', '**.card.number', '**.ssn'],
]
```

### Extending ApiLogItem for Custom Data Storage

[](#extending-apilogitem-for-custom-data-storage)

The default `ApiLogItem` stores lightweight references. You can extend it to store additional data:

```
// Create custom model
class CustomApiLogItem extends \Prahsys\ApiLogs\Models\ApiLogItem
{
    protected $fillable = [
        // Default fields
        'request_id', 'path', 'method', 'api_version',
        'request_at', 'response_at', 'response_status', 'is_error',

        // Custom fields
        'request_payload', 'response_payload', 'user_id', 'client_ip'
    ];

    protected $casts = [
        // Default casts
        'request_at' => 'datetime:Y-m-d H:i:s.v',
        'response_at' => 'datetime:Y-m-d H:i:s.v',
        'response_status' => 'integer',
        'is_error' => 'boolean',

        // Custom casts
        'request_payload' => 'json',
        'response_payload' => 'json',
    ];
}

// Create custom migration
Schema::table('api_log_items', function (Blueprint $table) {
    $table->json('request_payload')->nullable();
    $table->json('response_payload')->nullable();
    $table->string('user_id')->nullable();
    $table->string('client_ip')->nullable();
});

// Update config
'models' => [
    'api_log_item' => \App\Models\CustomApiLogItem::class,
],
```

**Alternative approaches:**

- **External correlation**: Use correlation IDs to fetch full data from Axiom/Elasticsearch
- **Hybrid storage**: Store critical fields in database, full payloads in object storage
- **Event sourcing**: Store lightweight events, reconstruct full state when needed

### Creating Custom Redactors

[](#creating-custom-redactors)

The easiest way to create custom redactors is by extending `DotNotationRedactor`, just like the built-in `CommonHeaderFieldsRedactor` and `CommonBodyFieldsRedactor`:

#### Example: PCI DSS Redactor

[](#example-pci-dss-redactor)

```
