Class: PostHog::Rails::Logs::Appender Private
- Inherits:
-
Logger
- Object
- Logger
- PostHog::Rails::Logs::Appender
- Defined in:
- lib/posthog/rails/logs/appender.rb
Overview
This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.
A ‘Logger`-compatible sink that forwards each log record to an OpenTelemetry logger as an OTLP log record.
It is designed to be broadcast alongside the app’s existing ‘Rails.logger` so that ordinary `Rails.logger.info(…)` calls flow to PostHog Logs in addition to the normal output. Each record is stamped with the request-scoped PostHog identity captured by RequestContext.
Thread-safety: intentionally lock-free apart from the optional rate limiter’s counter. Emitting touches no shared mutable state (‘@otel_logger` is assigned once, attributes are built per call, and `Internal::Context.current` is thread/fiber-local), and the OTel BatchLogRecordProcessor synchronizes its buffer internally — the same split as stdlib `Logger`, which locks in `LogDevice`, not `Logger#add`. A mutex around emit would serialize all app logging needlessly.
Constant Summary collapse
- SELF_LOG_PREFIX =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
'[posthog-ruby]'- SELF_LOG_PROGNAME =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
'PostHog'- REQUEST_ATTRIBUTE_NAMES =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
Maps PostHog event-property names (as stored in Internal::Context) to the OTel semantic-convention attribute names used on log records, matching the web SDK so one filter works across SDKs.
{ '$current_url' => 'url.full', '$request_method' => 'http.request.method', '$request_path' => 'url.path' }.freeze
- REENTRANCY_KEY =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
Re-entrancy guard key. Fiber-local (Thread.current[]), which is what recursion needs: if anything inside #add logs through a broadcast that includes this appender (e.g. a logs_before_send callback calling Rails.logger), the nested call would recurse until SystemStackError —which, as an Exception, escapes the rescue below and breaks the app.
:posthog_rails_logs_emitting
Instance Method Summary collapse
-
#add(severity, message = nil, progname = nil) ⇒ Boolean
private
Mirrors ‘Logger#add` message/progname resolution, then emits to OTel instead of writing to a log device.
-
#initialize(otel_logger, level: nil, rate_limiter: nil, before_send: nil) ⇒ Appender
constructor
private
A new instance of Appender.
Constructor Details
#initialize(otel_logger, level: nil, rate_limiter: nil, before_send: nil) ⇒ Appender
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.
Returns a new instance of Appender.
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/posthog/rails/logs/appender.rb', line 52 def initialize(otel_logger, level: nil, rate_limiter: nil, before_send: nil) super(nil) @otel_logger = otel_logger @rate_limiter = rate_limiter @before_send = before_send # The forwarding threshold deliberately does NOT live in Logger#level. # Rails 7.1+ BroadcastLogger computes #level as the min and #debug? # etc. as the any? across sinks, so storing it there would widen the # app-wide predicates (logs_level = :debug would flip # Rails.logger.debug? true and make e.g. ActiveRecord start # generating SQL debug lines), and a broadcast-wide # `Rails.logger.level =` would clobber the configured logs_level. # Pinning the inherited level to UNKNOWN keeps this sink invisible # to those calculations; filtering happens against @threshold in #add. @threshold = level || ::Logger::DEBUG self.level = ::Logger::UNKNOWN end |
Instance Method Details
#add(severity, message = nil, progname = nil) ⇒ 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.
Mirrors ‘Logger#add` message/progname resolution, then emits to OTel instead of writing to a log device.
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/posthog/rails/logs/appender.rb', line 81 def add(severity, = nil, progname = nil) return true if Thread.current[REENTRANCY_KEY] begin Thread.current[REENTRANCY_KEY] = true severity ||= ::Logger::UNKNOWN return true if severity < @threshold if .nil? if block_given? = yield else = progname progname = nil end end return true if .nil? return true if self_log?(, progname) record = apply_before_send(build_record(severity, , progname)) return true if record.nil? case @rate_limiter&.record when :reject return true when :reject_first emit_rate_cap_notice return true end emit(record) true rescue StandardError => e # Never let log forwarding break the calling code path, but leave # one breadcrumb: a persistent emit failure would otherwise drop # 100% of records with no signal anywhere. warn_emit_error(e) true ensure Thread.current[REENTRANCY_KEY] = nil end end |