Skip to content

Conversation

@popcorn
Copy link

@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
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
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
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
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!

Comment on lines +11 to +12
# Your PostHog project API key (required)
# Get this from: PostHog Project Settings > API Keys
Copy link
Member

Choose a reason for hiding this comment

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

# ============================================================================

# For PostHog Cloud, use: https://us.i.posthog.com or https://eu.i.posthog.com
config.host = ENV.fetch('POSTHOG_HOST', 'https://app.posthog.com')
Copy link
Member

Choose a reason for hiding this comment

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

We always use us.i.posthog.com as a default

Suggested change
config.host = ENV.fetch('POSTHOG_HOST', 'https://app.posthog.com')
config.host = ENV.fetch('POSTHOG_HOST', 'https://us.i.posthog.com')

config.host = ENV.fetch('POSTHOG_HOST', 'https://app.posthog.com')

# Personal API key (optional, but required for local feature flag evaluation)
# Get this from: PostHog Settings > Personal API Keys
Copy link
Member

Choose a reason for hiding this comment

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

Comment on lines +36 to +38
config.on_error = proc { |status, message|
Rails.logger.error("[PostHog] Error #{status}: #{message}")
}
Copy link
Member

Choose a reason for hiding this comment

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

Should probably be commented out by default


# Controller method name to get current user (default: :current_user)
# Change this if your app uses a different method name
config.current_user_method = :current_user
Copy link
Member

Choose a reason for hiding this comment

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

Expanding on the docs on why enabling this is a good idea would be nice!

require 'posthog'

# Load Rails integration
require 'posthog/rails' if defined?(Rails)
Copy link
Member

Choose a reason for hiding this comment

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

Should probably abort loading if Rails is not defined, right? People shouldn't use this without Rails

Copy link
Member

Choose a reason for hiding this comment

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

This is very LLM-y. Do we need this here?

# Exceptions are automatically captured with job context
end
end
```
Copy link
Member

Choose a reason for hiding this comment

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

Add a comment here explaining we don't support other job runners yet, and they're coming in the future. Also add a note saying we accept contributions - like yours!

# Code that might raise an error
end

Rails.error.record(exception, context: { user_id: current_user.id })
Copy link
Member

Choose a reason for hiding this comment

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

Make it clear we'll detect the user id from both user_id and distinct_id inside the context.

Copy link
Member

Choose a reason for hiding this comment

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

We'd benefit from a section here explaining how we detect distinct_id in the different runtimes (Autorescue, Rails rescueing, Rails error reporter, Active Job)

@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 😅

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.

3 participants