Module: PostHog::Rails

Defined in:
lib/posthog/rails.rb,
lib/posthog/rails/facade.rb,
lib/posthog/rails/railtie.rb,
lib/posthog/rails/version.rb,
lib/posthog/rails/active_job.rb,
lib/posthog/rails/logs/setup.rb,
lib/posthog/rails/configuration.rb,
lib/posthog/rails/logs/appender.rb,
lib/posthog/rails/logs/severity.rb,
lib/posthog/rails/request_context.rb,
lib/posthog/rails/tracing_headers.rb,
lib/posthog/rails/error_subscriber.rb,
lib/posthog/rails/parameter_filter.rb,
lib/posthog/rails/request_metadata.rb,
lib/posthog/rails/logs/rate_limiter.rb,
lib/posthog/rails/capture_exceptions.rb,
lib/posthog/rails/rescued_exception_interceptor.rb

Defined Under Namespace

Modules: ActiveJobExtensions, Logs, ParameterFilter Classes: CaptureExceptions, Configuration, ErrorSubscriber, InitConfig, Railtie, RequestContext, RescuedExceptionInterceptor

Constant Summary collapse

IN_WEB_REQUEST_KEY =

Thread-local key for tracking web request context

:posthog_in_web_request
VERSION =
'3.18.1'

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.configPostHog::Rails::Configuration

Returns Rails integration configuration.

Returns:



31
32
33
# File 'lib/posthog/rails.rb', line 31

def config
  @config ||= Configuration.new
end

Class Method Details

.active_job_exception_captured?(exception) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Check whether an exception was already captured by ActiveJobExtensions.

Parameters:

  • exception (Exception)

Returns:

  • (Boolean)


85
86
87
88
89
# File 'lib/posthog/rails.rb', line 85

def active_job_exception_captured?(exception)
  exception.instance_variable_get(ACTIVE_JOB_CAPTURED_EXCEPTION_IVAR) == true
rescue StandardError
  false
end

.configure {|config| ... } ⇒ void

This method returns an undefined value.

Configure Rails integration options.

Yield Parameters:



42
43
44
# File 'lib/posthog/rails.rb', line 42

def configure
  yield config if block_given?
end

.enter_web_requestvoid

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Mark that we're in a web request context CaptureExceptions middleware will handle exception capture



50
51
52
# File 'lib/posthog/rails.rb', line 50

def enter_web_request
  Thread.current[IN_WEB_REQUEST_KEY] = true
end

.exit_web_requestvoid

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Clear web request context (called at end of request)



57
58
59
# File 'lib/posthog/rails.rb', line 57

def exit_web_request
  Thread.current[IN_WEB_REQUEST_KEY] = false
end

.in_web_request?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Check if we're currently in a web request context Used by ErrorSubscriber to avoid duplicate captures

Returns:

  • (Boolean)


65
66
67
# File 'lib/posthog/rails.rb', line 65

def in_web_request?
  Thread.current[IN_WEB_REQUEST_KEY] == true
end

.install_posthog_facade!void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Install the Rails singleton-style PostHog facade at load time so Rails app initializers can call PostHog.init before Railtie initializers run.



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/posthog/rails/facade.rb', line 10

def self.install_posthog_facade!
  return if @posthog_facade_installed

  PostHog.class_eval do
    class << self
      attr_accessor :client

      # Initialize the singleton PostHog client used by Rails delegators.
      #
      # @param options [Hash] Core {PostHog::Client} options.
      # @yieldparam config [PostHog::Rails::InitConfig] Block-based core SDK configuration.
      # @return [PostHog::Client]
      def init(options = {})
        # If block given, yield to configuration
        if block_given?
          config = PostHog::Rails::InitConfig.new(options)
          yield config
          options = config.to_client_options
        end

        # Let the PostHog Logs pipeline reuse the same api_key/host without
        # the core client exposing public readers.
        PostHog::Rails::Logs::Setup.remember_client_options(options) if defined?(PostHog::Rails::Logs::Setup)

        # Create the PostHog client. If a client already exists, shut it down
        # after replacement so repeated init calls do not leave background
        # resources from the previous instance running.
        previous_client = @client
        @client = PostHog::Client.new(options)
        begin
          previous_client&.shutdown
        rescue StandardError => e
          PostHog::Logging.logger.warn("Failed to shut down previous PostHog client: #{e.message}")
        end
        @client
      end

      # @return [Boolean] Whether {PostHog.init} has created a client.
      def initialized?
        !@client.nil?
      end

      # Fallback for any client methods not explicitly defined.
      #
      # @api private
      def method_missing(method_name, ...)
        ensure_initialized!

        if client.respond_to?(method_name)
          client.public_send(method_name, ...)
        else
          super
        end
      end

      # @api private
      def respond_to_missing?(method_name, include_private = false)
        ensure_initialized!
        client.respond_to?(method_name, include_private) || super
      end

      private

      def ensure_initialized!
        return if initialized?

        @client = PostHog::Client.new(api_key: nil, silence_disabled_client_error: true)
      end
    end
  end

  %i[
    capture
    capture_exception
    identify
    alias
    group_identify
    is_feature_enabled
    get_feature_flag
    get_all_flags
  ].each do |method_name|
    PostHog.define_singleton_method(method_name) do |*args, **kwargs, &block|
      ensure_initialized!
      client.public_send(method_name, *args, **kwargs, &block)
    end
  end

  @posthog_facade_installed = true
end

.mark_active_job_exception_captured(exception) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Mark an exception as already captured by the ActiveJob integration. Used by ErrorSubscriber to avoid duplicate captures when Rails reports the same re-raised job exception via Rails.error.

Parameters:

  • exception (Exception)


75
76
77
78
79
# File 'lib/posthog/rails.rb', line 75

def mark_active_job_exception_captured(exception)
  exception.instance_variable_set(ACTIVE_JOB_CAPTURED_EXCEPTION_IVAR, true)
rescue StandardError
  nil
end

.mark_web_exception_captured(exception) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Mark an exception as already captured by the CaptureExceptions middleware.

ActionDispatch::Executor sits above our middleware and reports unhandled exceptions to Rails.error after the response has unwound back through CaptureExceptions, so in_web_request? is already false by then. The mark lets ErrorSubscriber recognize the report as a duplicate.

The whole cause chain is marked because ActionDispatch::ExceptionWrapper unwraps ActionView::Template::Error to its cause, meaning Rails can report a different object than the one the middleware captured.

Parameters:

  • exception (Exception)


104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/posthog/rails.rb', line 104

def mark_web_exception_captured(exception)
  current = exception
  seen = {}.compare_by_identity

  while current.is_a?(Exception) && !seen[current]
    seen[current] = true
    current.instance_variable_set(WEB_CAPTURED_EXCEPTION_IVAR, true)
    current = current.cause
  end
rescue StandardError
  nil
end

.web_exception_captured?(exception) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Check whether an exception was already captured by CaptureExceptions.

Parameters:

  • exception (Exception)

Returns:

  • (Boolean)


121
122
123
124
125
# File 'lib/posthog/rails.rb', line 121

def web_exception_captured?(exception)
  exception.instance_variable_get(WEB_CAPTURED_EXCEPTION_IVAR) == true
rescue StandardError
  false
end