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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@ghosthaise/payload-audit-log

v1.1.3

Published

Audit log plugin for PayloadCMS with changes tracking

Readme

PayloadCMS Audit Log Plugin

A comprehensive audit logging plugin for PayloadCMS that tracks document changes with intelligent filtering, relationship handling, and cascading prevention.

✨ Features

  • Smart Change Detection: Only logs meaningful changes, excluding system fields
  • Automatic Relationship Handling: Detects and properly handles relationship fields without noise
  • Cascading Prevention: Prevents multiple audit logs when updating related documents
  • System Field Exclusion: Automatically excludes sensitive fields like passwords, hashes, tokens
  • Empty Change Prevention: Won't create audit logs for empty or meaningless changes
  • Flexible Configuration: Extensive options for customization while maintaining sensible defaults

🚀 Installation

npm install @ghosthaise/payload-audit-log

📖 Basic Usage

import { buildConfig } from "payload/config";
import { auditLogPlugin } from "@ghosthaise/payload-audit-log";

export default buildConfig({
    plugins: [
        auditLogPlugin({
            collections: ["posts", "products", "users"],
            includeAuth: true,
        }),
    ],
    // ... rest of your config
});

⚙️ Configuration Options

Basic Options

| Option | Type | Default | Description | |--------|------|---------|-------------| | collections | string[] | [] | Array of collection slugs to audit | | includeAuth | boolean | false | Whether to include the auth collection (users) | | allowCascading | boolean | false | Allow cascading audit logs for related documents | | maxCascadeDepth | number | 0 | Maximum depth for cascading operations | | allowSystemFieldOverride | boolean | false | Allow overriding system field exclusions |

Advanced Options

| Option | Type | Default | Description | |--------|------|---------|-------------| | columnsToIgnore | string[] | See below | Fields to exclude from change comparison | | changeFormatter | object | See below | Change formatting configuration |

Default Excluded Fields

The plugin automatically excludes these system fields:

[
  'id', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy',
  'sessions', 'password', 'token', 'secret', 'hash', 'salt',
  'lockUntil', 'loginAttempts', 'resetPasswordToken', 'resetPasswordExpiration',
  'lastLogin', 'lastLoginAt', 'emailVerified', 'emailVerificationToken',
  'emailVerificationExpiration', 'forgotPasswordToken', 'forgotPasswordExpiration'
]

🔧 Advanced Configuration

Custom Field Exclusions

auditLogPlugin({
    collections: ["posts", "products"],
    columnsToIgnore: ["customField", "internalNotes"], // Added to defaults
    changeFormatter: {
        excludeFields: ["anotherField"], // Added to defaults
        meaningfulChangesOnly: true,
        maxDepth: 3
    }
})

Override System Field Exclusions

auditLogPlugin({
    collections: ["users"],
    allowSystemFieldOverride: true,
    columnsToIgnore: ["customField"], // Only these will be excluded
    changeFormatter: {
        excludeFields: ["anotherField"] // Only these will be excluded
    }
})

Cascading Control

auditLogPlugin({
    collections: ["posts", "comments"],
    allowCascading: true,
    maxCascadeDepth: 2, // Allow up to 2 levels of cascading
})

🎯 Smart Features

Automatic Relationship Detection

The plugin automatically detects relationship fields by analyzing data structure:

  • String IDs: "550e8400-e29b-41d4-a716-446655440000"
  • Objects with ID: { id: "uuid", name: "Value" }
  • Arrays of relationships: [{ id: "uuid1" }, { id: "uuid2" }]

Intelligent Change Filtering

// Before: Logs everything including system fields
{
  "name": { "old": "John", "new": "Jane" },
  "hash": { "old": "old-hash", "new": "new-hash" }, // System field
  "contract": { 
    "old": "uuid-123", 
    "new": { id: "uuid-123", name: "Contract" } // Same ID, populated
  }
}

// After: Only meaningful changes
{
  "name": { "old": "John", "new": "Jane" }
}

📊 Audit Log Structure

The plugin creates an audit-logs collection with these fields:

| Field | Type | Description | |-------|------|-------------| | collection | string | Name of the audited collection | | action | string | Operation type: create, update, delete | | documentId | string | ID of the document | | timestamp | date | When the operation occurred | | user | relationship | User who performed the operation | | changes | json | Change details (see below) |

Changes Field Structure

For Create Operations:

{
  "title": "New Post",
  "content": "Post content",
  "author": "user-123"
}

For Update Operations:

{
  "title": {
    "old": "Old Title",
    "new": "New Title"
  },
  "status": {
    "old": "draft",
    "new": "published"
  }
}

For Delete Operations:

{
  "title": "Deleted Post",
  "content": "Post content",
  "author": "user-123"
}

🔍 Example Scenarios

Scenario 1: User Profile Update

// User updates their profile
const updatedUser = await payload.update({
  collection: 'users',
  id: 'user-123',
  data: {
    name: 'John Doe', // Changed
    email: '[email protected]', // Changed
    hash: 'new-hash-value', // System field - excluded
    lastLogin: new Date() // System field - excluded
  }
});

// Audit log will only show:
{
  "name": { "old": "John", "new": "John Doe" },
  "email": { "old": "[email protected]", "new": "[email protected]" }
}

Scenario 2: Post with Relationships

// Update post with relationships
const updatedPost = await payload.update({
  collection: 'posts',
  id: 'post-123',
  data: {
    title: 'New Title', // Changed
    author: 'user-456', // Same ID, populated in response
    categories: ['cat-1', 'cat-2'], // Same IDs, populated in response
    content: 'Updated content' // Changed
  }
});

// Audit log will only show:
{
  "title": { "old": "Old Title", "new": "New Title" },
  "content": { "old": "Old content", "new": "Updated content" }
}

🛠️ Development

Building

npm run build

Testing

npm test

📝 License

MIT

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

📞 Support

If you encounter any issues or have questions, please open an issue on GitHub.