Skip to content
Postgres extensions

pgaudit

What it is

pgAudit adds detailed session and object audit logging to PostgreSQL's standard logging facility. It records statements in an audit-oriented format that is easier to filter and review than ordinary diagnostic logs.

What it provides

  • Session auditing by statement class, such as read, write, DDL, role, and function
  • Object-level auditing for selected relations
  • Structured audit records suitable for security and compliance pipelines

Use it for compliance evidence, privileged-access monitoring, change investigations, security incident response, or regulated systems that need a reviewable database activity trail.

Use it with Rent

extension pgaudit {
  name = "pgaudit"
}

The RSL declaration makes installation and migration intent part of the reviewed application schema. Register the typed pack at runtime to call its APIs:

let audit = extensions.register_pack::<PgAudit>()?;
let settings = AuditSettings::new(
    &audit,
    [AuditClass::Read, AuditClass::Write],
);

assert_eq!(settings.class_setting(), "read,write");

audit
    .client(&pool)
    .configure_database(&settings)
    .await?;

This writes database-level defaults with ALTER DATABASE, so newly opened connections receive the audit policy. Existing pooled connections retain their settings until they are replaced. All three settings are applied in one server statement: if the role lacks privileges for any of them, the database keeps every previous default and the call returns an error. The role applying the defaults must have the required PostgreSQL privileges.

Audit one transaction

Use a transaction-bound client for temporary settings:

client
    .transaction_app(async |tx| -> anyhow::Result<()> {
        audit
            .client(tx)
            .configure_transaction(&settings)
            .await?;

        tx.create_post()
            .title("Published post")
            .author_id(author.id)
            .save()
            .await?;

        Ok(())
    })
    .await?;

The settings apply to this transaction's connection and are restored on commit or rollback. Calling configure_transaction on a pool returns an actionable error before executing SQL. PostgreSQL still enforces audit-configuration privileges. This API does not grant them. An empty audit-class list means none.

Audit records go to PostgreSQL's configured log destination, not the query result. Keep log_parameter disabled unless your logging policy permits recording application values. Database auditing records attempted activity. Rolling back an application write does not erase its audit record.

pgaudit is cluster-scoped and may require provider control-plane work. Rent surfaces that during preflight instead of emitting misleading installation SQL.

Run the application

Add the extension declaration above to your application's schema. Confirm that your PostgreSQL provider offers the extension and that your migration role can install it, then apply the migration and run your application:

rent migrate dev --name enable_pgaudit
cargo run

Preload pgaudit on the server and configure PostgreSQL log collection before enabling audit settings. Use a transaction when applying transaction-local settings.

Complete example

Download this project, or copy the files below into an empty directory.

Replace RENT_REPOSITORY_URL in Cargo.toml with the URL supplied with your preview access. Use the matching Rent CLI release.

Use an empty PostgreSQL database with the extension binaries available on its server. Set DATABASE_URL to that database and follow any server setup requirements described above.

rent extension check --provider self-hosted
rent generate
rent migrate dev --name initial
cargo run

If your host preinstalls other extensions, declare those in RSL as well. For a database with existing application tables, baseline it first.

Cargo.toml
[package]
name = "rent-extension-pgaudit"
version = "0.1.0"
edition = "2024"
[workspace]
[[bin]]
name = "rent-extension-pgaudit"
path = "main.rs"
[dependencies]
rent = { git = "RENT_REPOSITORY_URL", tag = "v0.8.6", default-features = false, features = ["postgres", "extensions", "migrate"] }
rent-ext-pgaudit = { git = "RENT_REPOSITORY_URL", tag = "v0.8.6" }
anyhow = "1.0.104"
tokio = { version = "1.53.1", features = ["full"] }
rent.toml
schema = "schema.rsl"
generated = "generated"
migrations = "migrations"

[database]
dialect = "postgres"
backends = ["postgres"]
schema.rsl
datasource db {
  provider = "postgresql"
}

extension pgaudit {
  name = "pgaudit"
}

model Post {
  id    BigInt  @id
  title String
  body  String  @default("")
}
main.rs
use anyhow::Result;
use rent::{driver::DatabasePool, extension::ExtensionRegistry};
#[allow(dead_code, unused_imports)]
#[rustfmt::skip]
#[path = "generated/mod.rs"]
mod generated;
#[tokio::main]
async fn main() -> Result<()> {
    let pool = DatabasePool::connect_url(&std::env::var("DATABASE_URL")?).await?;
    let client = generated::client::Client::new(pool.clone());
    let mut extensions = ExtensionRegistry::default();
    use rent_ext_pgaudit::{AuditClass, AuditSettings, PgAudit, PgAuditClientExt};
    let pack = extensions.register_pack::<PgAudit>()?;
    let settings = AuditSettings::new(&pack, [AuditClass::Read, AuditClass::Write]);
    client
        .transaction_app(async |tx| -> anyhow::Result<()> {
            pack.client(&tx.system()).configure_transaction(&settings).await?;
            tx.create_post().id(1).title("An audited post").save().await?;
            Ok(())
        })
        .await?;
    println!("The write used transaction-local audit settings.");
    Ok(())
}