Ruby on Rails Logs installation

Contents

posthog-rails forwards Rails.logger output and optional structured Rails notification logs to PostHog Logs over OpenTelemetry (OTLP), correlated with each request's distinct ID and session ID. Requires Ruby 3.3+.

  1. Add the OpenTelemetry gems

    Required

    Add the following gems to your Gemfile with require: false — posthog-rails loads them only when log forwarding is enabled:

    Gemfile
    gem 'opentelemetry-sdk', require: false
    gem 'opentelemetry-logs-sdk', '>= 0.6.0', require: false
    gem 'opentelemetry-exporter-otlp-logs', require: false

    Then run:

    Terminal
    bundle install

    opentelemetry-logs-sdk must be >= 0.6.0. The OTLP exporter requires LogRecordData#event_name, which only exists from that version onward.

  2. Enable log forwarding

    Required

    In config/initializers/posthog.rb, enable log forwarding:

    config/initializers/posthog.rb
    PostHog::Rails.configure do |config|
    config.logs_enabled = true
    end

    Logs reuse the same project token (api_key) and host configured in PostHog.init — nothing extra to set. Records are sent to <host>/i/v1/logs.

    When the OpenTelemetry gems are absent, the integration logs a single warning and no-ops — it's safe to enable conditionally.

  3. Optional configuration

    Optional

    Filter by severity

    logs_level controls which severity levels are forwarded. It never changes what your app logs locally:

    Ruby
    config.logs_level = :info # :debug, :info, :warn, :error, :fatal, :unknown, or a Logger constant

    Rate limiting

    Forwarding is capped at 6,000 records per minute by default. When the cap is reached, one warning record is emitted and further records are dropped for the remainder of that minute:

    Ruby
    config.logs_max_records_per_minute = 6_000 # nil, 0, or any negative value to disable

    Scrub PII

    Set logs_before_send to a proc that receives each record and returns a modified hash to send or nil to drop it:

    Ruby
    config.logs_before_send = proc { |record|
    next nil if record[:severity] == :debug
    record[:body] = record[:body].gsub(/\b[\w.+-]+@[\w-]+\.[\w.]+\b/, '[redacted]')
    record
    }

    The record hash has keys :timestamp, :severity (a symbol such as :warn), :body, and :attributes. If the callback raises, the record is dropped.

    Trace correlation

    If your app uses OpenTelemetry tracing instrumentation gems (for example, opentelemetry-instrumentation-rails or opentelemetry-instrumentation-rack), log records emitted during a traced request automatically carry the active trace_id and span_id — no additional configuration needed.

  4. Test your setup

    Recommended
    1. Start your Rails server and trigger a request that logs output.
    2. Check the PostHog Logs interface for your log entries.
    3. If logs don't appear, check for a startup warning in your server output (a line starting with [PostHog] indicates a configuration issue such as missing gems or an unresolved project token).
    View your logs in PostHog
  5. Next steps

    Checkpoint
    What you can do with your logs

    ActionDescription
    Why you need logsWhat logs show you that nothing else does
    Search logsUse the search interface to find specific log entries
    Filter by levelFilter by INFO, WARN, ERROR, etc.
    Link session replayConnect logs to users and session replays by passing posthogDistinctId and sessionId
    Link logs to a personSurface every log emitted on behalf of a user on their PostHog person profile
    Logging best practicesLearn what to log, how to structure logs, and patterns that make logs useful in production

    Troubleshoot common issues

Structured notification logs

From posthog-rails 3.23.0, use logs_notifications to capture structured controller, SQL, job, and mailer logs:

config/initializers/posthog.rb
PostHog::Rails.configure do |config|
config.logs_enabled = true
config.logs_notifications = [:action_controller, :active_record, :active_job, :action_mailer]
config.logs_level = :debug
end

This defaults to []. Restart your app after changing the selection.

ComponentCaptures
:action_controllerController, action, HTTP method, response status, and filtered parameters.
:active_recordQuery name, cache status, and SQL operation. No SQL statements or bind values.
:active_jobEnqueue, execution, retry, and discard events with job details and filtered arguments.
:action_mailerMailer class and action. No recipients, subject, or message content.

Set logs_level = :debug to include SQL records. Other successful operations use info, controller 4xx responses use warn, and failures use error. A failed retry attempt uses error even if retries remain.

Notification logs share the existing severity filter, rate cap, and logs_before_send hook. Set logs_forward_rails_logger = false to collect only notification logs.

Filter sensitive notification data

Controller and job parameters use Rails' parameter filters. Unkeyed job arguments are redacted. Jobs with log_arguments = false and mail delivery jobs omit arguments. With no parameter filters configured, job hash arguments are redacted.

Review your filters and use logs_before_send to remove sensitive data. These protections do not filter arbitrary text passed to Rails.logger.

Enable auto_instrument_active_job to carry user and session IDs into job logs. Notification logs alone do not enable exception capture.

Configuration reference

OptionTypeDefaultDescription
logs_enabledBooleanfalseMaster switch for log forwarding. Requires Ruby 3.3+ and the three OpenTelemetry gems above.
logs_forward_rails_loggerBooleantrueBroadcast Rails.logger into the PostHog Logs sink. Takes effect only when logs_enabled is true.
logs_notificationsArray of symbols[]Components to log: :action_controller, :active_record, :active_job, and :action_mailer. Requires logs_enabled.
logs_levelSymbol/IntegernilMinimum severity to forward (:debug, :info, :warn, :error, :fatal, :unknown, or a Logger constant). Inherits the Rails logger level when nil.
logs_max_records_per_minuteInteger/String6000Records-per-minute cap. Numeric strings (e.g. from ENV) are coerced. Set to nil, 0, or a negative value to disable.
logs_before_sendProcnilCalled with each record hash (:timestamp, :severity, :body, :attributes) before sending. Return a modified hash to send or nil to drop. Records are dropped if the callback raises.

Troubleshooting

Logs not appearing in PostHog

  1. Check the OTel gems are installed: bundle list | grep opentelemetry. You should see opentelemetry-sdk, opentelemetry-logs-sdk (≥ 0.6.0), and opentelemetry-exporter-otlp-logs.
  2. Check for a startup warning — if the gems are missing or the project token can't be resolved, PostHog logs a warning to stderr at boot (look for a line starting with [PostHog]).
  3. Verify logs_enabled = true is set in your initializer and that the initializer runs before the railtie (it should if it lives in config/initializers/).
  4. Check test mode — PostHog.init with test_mode: true disables the logs pipeline entirely. Ensure test mode is only set in the test environment.

For general Logs troubleshooting (authentication, connection issues, format problems), see the Logs troubleshooting guide.

Still have questions?

Was this page useful?