Skip to content

Add posthog-rails gem for automatic Rails exception tracking#81

Merged
rafaeelaudibert merged 33 commits intoPostHog:mainfrom
popcorn:popcorn/add-rails-activejob-automatic-error-tracking
Feb 5, 2026
Merged

Add posthog-rails gem for automatic Rails exception tracking#81
rafaeelaudibert merged 33 commits intoPostHog:mainfrom
popcorn:popcorn/add-rails-activejob-automatic-error-tracking

Conversation

@popcorn
Copy link
Contributor

@popcorn popcorn commented Oct 28, 2025

What is this?

This PR introduces the posthog-rails gem which provides automatic exception tracking for Rails applications, inspired by Sentry's gem.

Features:

  • Automatic capture of unhandled exceptions via Rails middleware
  • Automatic capture of rescued exceptions (configurable)
  • Automatic instrumentation of ActiveJob failures
  • Integration with Rails 7.0+ error reporter
  • Configurable exception exclusion list
  • User context capture from controllers

Please read posthog-ruby/posthog-rails/IMPLEMENTATION.md to learn more.

How to test this?

Put both gems in your Rails app's Gemfile, and then reference them via path on your local machine:

gem "posthog-ruby", path: "/Users/drago/code/posthog-ruby"
gem "posthog-rails", path: "/Users/drago/code/posthog-ruby/posthog-rails"

PS

  • There's only about ~550 new lines of code, other ~950 lines are readmes, code examples etc.
  • I wrote this for myself to test PostHog so I haven't added any specs. If you are happy with this approach let me know and I will write tests.

@popcorn popcorn force-pushed the popcorn/add-rails-activejob-automatic-error-tracking branch from 2967d45 to 1dfe144 Compare October 28, 2025 14:09
Comment on lines 47 to 60
def extract_distinct_id_from_job
# Try to find a user ID in job arguments
arguments.each do |arg|
if arg.respond_to?(:id)
return arg.id
elsif arg.is_a?(Hash) && arg['user_id']
return arg['user_id']
elsif arg.is_a?(Hash) && arg[:user_id]
return arg[:user_id]
end
end

nil # No user context found
end
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not super-happy with this. Will probably change it to use a proc-based / dynamic configuration.

So you'll be able to define distinct ID in the job itself like this:

class MyJob < ApplicationJob
  posthog_distinct_id ->(user, arg1, arg2) { user.id }

  def perform(user, arg1, arg2)
    # ...
  end
end

@popcorn popcorn force-pushed the popcorn/add-rails-activejob-automatic-error-tracking branch from 1dfe144 to b4d5947 Compare October 28, 2025 22:14
This commit introduces the posthog-rails gem which provides automatic
exception tracking for Rails applications, including:

- Automatic capture of unhandled exceptions via Rails middleware
- Automatic capture of rescued exceptions (configurable)
- Automatic instrumentation of ActiveJob failures
- Integration with Rails 7.0+ error reporter
- Configurable exception exclusion list
- User context capture from controllers

Core library improvements:
- Added comprehensive logging throughout exception capture flow
- Added ExceptionCapture module for standardized exception parsing
- Fixed field handling in Transport to strip internal-only fields
  (type, library, library_version, messageId) that are not part of
  PostHog's RawEvent struct
- Fixed UUID field handling to avoid sending null values
- Added capture_exception method to Client for explicit exception tracking

The posthog-rails gem includes:
- Railtie for automatic initialization and middleware insertion
- Multiple middleware layers for capturing different exception types
- ErrorSubscriber for Rails 7.0+ error reporter integration
- ActiveJob extensions for job failure tracking
- Comprehensive configuration options
@popcorn popcorn force-pushed the popcorn/add-rails-activejob-automatic-error-tracking branch from b4d5947 to e2e4e6d Compare October 28, 2025 22:21
@popcorn
Copy link
Contributor Author

popcorn commented Oct 29, 2025

Hey @rafaeelaudibert, I saw you closing another PR for automating Rails error tracking because it was too big and included many integrations at once.

This one hooks into Rails middleware and ActiveJob, and also lets you integrate with error reporter if you're on Rails >7.0.

Can you take a look?

@popcorn
Copy link
Contributor Author

popcorn commented Nov 3, 2025

cc @dmarticus @daibhin @mariusandra

I cannot add any reviewers for this PR, so I'm tagging you. Please let me know if you're ok with this approach to implementing Rails SDK.

@hpouillot hpouillot requested a review from daibhin November 6, 2025 13:21
@popcorn
Copy link
Contributor Author

popcorn commented Nov 6, 2025

@daibhin, it seems @hpouillot triggered a few CI actions that are failing.

I'll sort it out tomorrow or during the weekend. In the meantime, let me know if you agree with the general approach.

Copy link
Member

@rafaeelaudibert rafaeelaudibert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks very very good! Sorry for taking this long to review, but here it is!

I've left a handful of comments on things I'd like to see changed/improved. This is already 95% of the way there!

@rafaeelaudibert
Copy link
Member

Hey @popcorn pinging you just in case you're still interested in getting this merged :)

@miharekar
Copy link

I'd certainly be interested in getting it merged for my own selfish reasons 😅

@johnnagro
Copy link
Contributor

hey @popcorn i'd be happy to lend a hand

@popcorn
Copy link
Contributor Author

popcorn commented Jan 13, 2026

hey @popcorn i'd be happy to lend a hand

Hey @johnnagro, I'd love it if you could take over. I've been super-busy lately and will be for the next few months at least. I can help with a review once you do the changes. Would that work for you? My apologies I can't be more helpful atm.

@johnnagro
Copy link
Contributor

@popcorn no worries, let me see what i can do. thanks for getting a start on this feature.

@johnnagro
Copy link
Contributor

@popcorn mind adding me with write permissions to your fork of the repo? so i can push to your branch. seems like the simplest way to continue the PR and maintain all the history

@popcorn
Copy link
Contributor Author

popcorn commented Jan 15, 2026

@popcorn mind adding me with write permissions to your fork of the repo? so i can push to your branch. seems like the simplest way to continue the PR and maintain all the history

Just added you as collaborator on the forked repo, cheers!

…tracking

Resolve CHANGELOG.md conflict by keeping both the Unreleased
posthog-rails entry and the 3.4.0 release notes.
@johnnagro
Copy link
Contributor

I'm all set up now. Resolved the CHANGELOG conflict. I will begin to address the review feedback.

johnnagro and others added 10 commits January 17, 2026 08:40
Co-authored-by: Rafael Audibert <32079912+rafaeelaudibert@users.noreply.github.com>
Co-authored-by: Rafael Audibert <32079912+rafaeelaudibert@users.noreply.github.com>
Keep it as an example users can uncomment if needed, consistent
with other optional callbacks like before_send.
Move Rails-specific configuration from PostHog.rails_config to
PostHog::Rails.config for cleaner namespacing.
Add configurable `user_id_method` option and support for `posthog_distinct_id`,
`distinct_id`, and `pk` methods when extracting user IDs for exception tracking.
Co-authored-by: Rafael Audibert <32079912+rafaeelaudibert@users.noreply.github.com>
Move Rails-specific options (auto_capture_exceptions, etc.) to
PostHog::Rails.configure, keeping core SDK options in PostHog.init.

This follows the common Rails gem pattern of namespaced configuration.
Replace repetitive method definitions with define_method loop and
add method_missing fallback for future client methods.
- Use request.session.id instead of session_options.dig(:id)
- session_options returns Session::Options, not a Hash
@johnnagro
Copy link
Contributor

@rafaeelaudibert cc @popcorn - please take another look. I think i've addressed all of your feedback and even went a bit further in a couple spots. I've been testing these changes on a local test rails app and noticed some potential issues in the main gem, see #87 and #88. I could work on fixes there too in dedicated PRs if you're interested.

Let me know your thoughts, happy to continue to iterate on this if need be.

Add safe_serialize to handle circular references and complex objects
in exception context. Tracks visited objects, limits depth/size, and
converts non-serializable objects to safe string representations.

Prevents SystemStackError when Rails error reporter context contains
ActiveRecord models or other objects with circular references.
When auto_capture_exceptions was enabled, exceptions in web requests were
being captured twice:
1. By CaptureExceptions middleware (after ShowExceptions)
2. By ErrorSubscriber (via Rails.error.subscribe in Rails 7.0+)

The ErrorSubscriber fires from within DebugExceptions before the
CaptureExceptions middleware catches the exception, causing both to
report the same error to PostHog.

Fix by tracking web request context via thread-local flag:
- CaptureExceptions sets flag at request start, clears in ensure block
- ErrorSubscriber checks flag and skips if in web request context

This ensures web requests are captured only by the middleware (which has
richer context: URL, params, controller, action), while ErrorSubscriber
still handles non-web contexts (ActiveJob, manual Rails.error.record).
@johnnagro
Copy link
Contributor

I was doing some addition testing and saw a couple further changes that needed to be made. I noticed exceptions were being captured twice in PostHog - two [posthog-ruby] Sending request for 1 items log lines for each error. I also noticed at times the context serialization was crashing when more complex objects were being serialized or had circular references. I added patches for both.

Copy link
Member

Thanks, John! I'll be reviewing this tomorrow, this looks very good! If we merge this in, I'll gladly add some docs about it too.

Copy link
Member

@rafaeelaudibert rafaeelaudibert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's only one change I'd like to see here. Can automatic error tracking be off by default? This will match the behavior we have for our other SDKs.

You should update the defaults, docs and examples/posthog.rb to make sure we appropriately clone this

Change auto_capture_exceptions, report_rescued_exceptions, and
auto_instrument_active_job to default to false, requiring explicit
opt-in for automatic error tracking features.
@johnnagro
Copy link
Contributor

@rafaeelaudibert i changed auto_capture_exceptions, report_rescued_exceptions, and auto_instrument_active_job to default to false, requiring explicit opt-in for automatic error tracking features. Updated the example config. Updated the README. Please take a look at your convenience.

@johnnagro
Copy link
Contributor

@rafaeelaudibert This is ready for another look whenever you have time. (I can't use the formal review request since I took over the PR.)

@rafaeelaudibert rafaeelaudibert self-requested a review February 1, 2026 21:03
@rafaeelaudibert
Copy link
Member

@johnnagro I'll be reviewing this on Monday, thank you!

Copy link
Member

@rafaeelaudibert rafaeelaudibert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! I've added a couple fixes and will be merging this now, appreciate your help!

@rafaeelaudibert rafaeelaudibert merged commit 648066a into PostHog:main Feb 5, 2026
10 of 11 checks passed
@rafaeelaudibert rafaeelaudibert mentioned this pull request Feb 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants