Logidze: PostgreSQL Change Logging for Rails
Database changes log for Rails
At a glance
- What is it?
- Logidze records PostgreSQL model changes in JSONB columns and exposes that history through Active Record. It suits Rails applications that want row-local change logs without maintaining a separate history table.
- Who is it for?
- Logidze fits Rails applications on PostgreSQL that need row-local change history and trigger-based recording. It does not fit databases other than PostgreSQL or teams requiring an independent history table.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 90 days ago.
- What is it written in?
- Mainly Ruby, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Why Logidze Places Each Change Log in Its Model Row
Logidze addresses a specific auditability problem in Rails applications: recording changes to database records without maintaining a separate history table. It is intended for developers using Rails 6.0 or newer, Active Record, and PostgreSQL 10 or newer. The README also lists Ruby `~> 2.7` as a requirement.
Compatibility is version-sensitive. The README directs Rails 4.2 users to Logidze versions no newer than `0.12.0`, and Rails 5.x users to versions no newer than `1.2.3`. Those restrictions matter during an upgrade because the gem version, Rails version, and PostgreSQL version are all part of the supported setup.
Each tracked table receives a `log_data::jsonb` column. PostgreSQL functions and triggers maintain that column, while `has_logidze` adds model methods for working with the log. The history is stored with the record, so no additional history table is required. This simplifies the data layout, but it also connects record size and update cost to the amount of history retained. That trade-off matters for records updated frequently or kept for many years.
The repository is not archived. Its last push was on 2026-07-25. The most recent listed release is `v1.3.0`, published on 2024-01-09. Earlier listed releases are `v1.2.0` from 2021-06-11 and `v1.1.0` from 2021-03-31. The README excerpt does not document a support policy for current or older versions, so release dates should not be treated as a compatibility guarantee.
PostgreSQL Triggers Drive Logidze's Change Capture
Logidze records changes through the database rather than relying only on Active Record callbacks. The installation generator creates a migration that adds a trigger function and enables the `hstore` extension. The model generator then creates the trigger configuration for a selected table. When a record changes, the database trigger records a version containing the relevant difference.
The history is serialized as JSONB in `log_data`. This differs from a design that writes each version to a separate table. With Logidze, an ordinary row update also updates the row's embedded history. The project README links to an older comparison with Audited and PaperTrail and describes Logidze as faster, but the excerpt does not include benchmark details. A team should therefore evaluate the claim against its own update patterns rather than treating it as a measured guarantee.
Logidze supports both differences and full snapshots. By default, an existing row without an initial snapshot receives one the first time it is updated. The backfill option can create initial snapshots during migration instead. The documented SQL helper is `logidze_snapshot(to_jsonb(t))`, and the Ruby API provides corresponding class and instance methods. A snapshot is created only when `log_data` is null, so calling a snapshot method does not replace an existing log.
The timestamp attached to a version normally comes from `updated_at` when appropriate. If the model has no `updated_at` column, Logidze falls back to `statement_timestamp()`. This behavior is database-driven and should be checked against the model's timestamp conventions.
Installing Logidze and Enabling the Post Model
The documented installation path uses Bundler, Rails generators, and database migrations. Add the dependency shown in the README to the application Gemfile:
gem "logidze", "~> 1.1"Install the database functions and extensions with:
bundle exec rails generate logidze:installApply the generated migration with:
bundle exec rails db:migrateBecause Logidze uses database functions and triggers, the README requires SQL schema dumps:
# application.rb
config.active_record.schema_format = :sqlEnable logging for a `Post` model with:
bundle exec rails generate logidze:model Post
bundle exec rails db:migrateThe model generator adds `has_logidze` to the model and creates the migration for the `log_data::jsonb` column and trigger setup. The README warns that Logidze may fail to infer the model path in projects with an unconventional structure. Specify the path when that occurs:
bundle exec rails generate logidze:model Post --path "app/models/custom/post.rb"The `fx` integration offers a separate path for projects continuing to use `schema.rb`. The README says to add the `fx` gem and run the Logidze generators, or pass `--fx` when automatic detection does not work. A project that already uses `fx` but does not want Logidze to use it can pass `--no-fx`. The README does not describe how `fx` changes migration execution beyond this integration, so teams should inspect their schema pipeline before selecting that option.
Creating the First Post Snapshot
Existing records need an initial version if their history should be available before the next update. The model generator supports a `backfill` option:
bundle exec rails generate logidze:model Post --backfillThe generated migration contains an `UPDATE` statement that populates `log_data` with the current state. Apply it with:
bundle exec rails db:migrateWithout backfilling, a full snapshot is created the first time a record is updated. That behavior can leave older records without an initial version until they change, which matters when history is needed for records that remain unchanged.
The Ruby API can create snapshots directly. The README documents a class method and an instance method with a bang variant:
Model.create_logidze_snapshot
Model.create_logidze_snapshot(timestamp: :created_at)
my_model = Model.find(params[:id])
my_model.create_logidze_snapshot!(timestamp: :created_at)A snapshot can be limited to selected columns or exclude specific columns:
Model.create_logidze_snapshot(only: %w[name])
Model.create_logidze_snapshot(except: %w[password])These calls only create a snapshot when `log_data` is null. The `timestamp` option is useful when an existing creation timestamp should represent the first version. Otherwise, the current time is used according to the README.
For existing tables, backfill is a migration decision rather than merely a one-off script. Review the generated SQL before applying it, especially when a table contains records that should not be logged or when column-level privacy requirements apply.
Column Filters, Log Limits, and Version Timestamps
Logidze can track every column, or it can be configured during model generation to track selected fields. To exclude two columns:
bundle exec rails generate logidze:model Post --except=created_at,activeTo retain only two columns:
bundle exec rails generate logidze:model Post --only=title,bodyThe `only` and `except` options are mutually exclusive. An allowlist is more explicit when a row contains sensitive or frequently changing values, but the choice determines what future history can contain. Configuration must be reviewed in the generated migration because the behavior belongs to the database trigger, not only to Ruby code.
Logs are unlimited by default. The README documents a `limit` option for limiting log size:
bundle exec rails generate logidze:model Post --limit=10The excerpt does not explain the retention algorithm, storage format for discarded versions, or any separate archive. Consequently, `limit=10` should not be interpreted as a full compliance archive. The project documentation includes a “Dealing with large logs” section, but its text is not included in the available excerpt.
Timestamp selection can also be changed. By default, Logidze uses `updated_at` when appropriate and falls back to `statement_timestamp()` when the field is unavailable. To use another column:
bundle exec rails generate logidze:model Post --timestamp_column timeTo always use the database statement timestamp, the README documents:
bundle exec rails generate logidze:model Post --timestamp_column nilThe README states that `null` and `false` are also accepted values. These settings should be checked against the application's update semantics. If records can be imported with historical timestamps, the chosen column may not represent when a version actually occurred.
Embedded History and the Limits of the Comparison
The README names Audited and PaperTrail as comparable change-logging projects, but its excerpt does not describe either alternative's implementation. The concrete distinction available here is Logidze's architecture: it creates a database-level log with PostgreSQL triggers and stores the result in a JSONB column on the record. The alternatives are not evaluated in enough detail to make a complete product comparison.
That architecture can be appropriate when a row and its change history should stay together and adding a separate history table is undesirable. It can be inappropriate when audit records must survive deletion of the main row, when history must be retained beyond a fixed row-local limit, or when the application requires a separate retention and archival policy. The README includes a “Handling records deletion” section, but its text is not included here, so deleted-row behavior cannot be confirmed.
There is also a database dependency. Logidze requires PostgreSQL and database functions and triggers. Applications that use another database cannot adopt the documented mechanism without changing the underlying design. Projects using `schema.rb` need either the documented `fx` integration or a different schema workflow. The README does not document rollback instructions, so a deployment should inspect migrations before installing it.
A final limitation is upgrade scope. Version compatibility changes with Rails, and the README gives different version bounds for older Rails releases. The latest listed release is `v1.3.0` from 2024-01-09, while the last repository push was on 2026-07-25. That does not establish the status of master for production use. Pin a release, read `CHANGELOG.md`, and test migrations against the application's PostgreSQL version before upgrading.
Editorial conclusion
Logidze fits Rails applications on PostgreSQL that need row-local change history and trigger-based recording. It does not fit databases other than PostgreSQL or teams requiring an independent history table. Before adoption, inspect the generated migration, verify version compatibility, and test snapshot, retention, and deletion behavior on the relevant models.
Frequently asked questions
Which databases and Rails versions does Logidze support?
The README documents PostgreSQL 10.0 or newer, Rails 6.0 or newer, and Ruby `~> 2.7`. Rails 4.2 requires a version no newer than `0.12.0`, and Rails 5.x requires a version no newer than `1.2.3`.
Does Logidze create a separate table for change history?
No. The README says history is stored as JSONB in the record's `log_data` column. PostgreSQL functions and triggers maintain the log.
How do I create an initial Logidze snapshot for existing records?
Generate the model migration with `--backfill`, then run `bundle exec rails db:migrate`. The generated migration contains an `UPDATE` statement that populates `log_data` with current snapshots.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/palkan-logidze)Was this useful?