Module: Beacon

Defined in:
lib/beacon/rails.rb,
lib/beacon.rb,
lib/beacon/lru.rb,
lib/beacon/queue.rb,
lib/beacon/client.rb,
lib/beacon/flusher.rb,
lib/beacon/testing.rb,
lib/beacon/version.rb,
lib/beacon/transport.rb,
lib/beacon/enrichment.rb,
lib/beacon/middleware.rb,
lib/beacon/fingerprint.rb,
lib/beacon/log_throttle.rb,
lib/beacon/configuration.rb,
lib/beacon/path_normalizer.rb,
lib/beacon/integrations/active_job.rb,
lib/beacon/integrations/action_mailer.rb

Overview

ActionMailer integration. Captures delivery errors as Beacon error events via the same fingerprint algorithm the Rack middleware uses.

# config/initializers/beacon.rb
require "beacon/integrations/action_mailer"
Beacon::Integrations::ActionMailer.install

Defined Under Namespace

Modules: Enrichment, Fingerprint, Integrations, PathNormalizer, Testing, Transport Classes: Client, Configuration, Error, Flusher, LRU, LogThrottle, Middleware, Queue, Railtie

Constant Summary collapse

CLIENT_MUTEX =
Mutex.new
EMPTY_FLUSHER_STATS =
{
  sent: 0, last_flush_at: nil, last_flush_status: nil,
  circuit_open: false, consecutive_failures: 0,
}.freeze
VERSION =
ENV.fetch("BEACON_VERSION", "0.0.0-dev")

Class Method Summary collapse

Class Method Details

.clientObject

Thread-safe lazy singleton. Two Puma threads racing on the first request used to be able to create two Clients (each with its own flusher thread) — the second one leaked forever. The Mutex serializes every access.

Why not a double-checked-locking fast path? Under MRI’s GVL it would be safe; under TruffleRuby / JRuby it isn’t — a reader could observe @client as non-nil while the Client’s own fields are still half-written (no memory barrier on the unsynchronized read). Taking the Mutex every call costs ~100ns on MRI, and Beacon.client is called per-Client-lifetime, not per-request.



43
44
45
46
47
# File 'lib/beacon.rb', line 43

def client
  CLIENT_MUTEX.synchronize do
    @client ||= Client.new(config: config)
  end
end

.configObject



28
29
30
# File 'lib/beacon.rb', line 28

def config
  @config ||= Configuration.new
end

.configure {|config| ... } ⇒ Object

Yields:



19
20
21
22
23
24
25
26
# File 'lib/beacon.rb', line 19

def configure
  yield config
  warn_if_unusable_endpoint
  # Drop any existing client so the next Beacon.client call picks
  # up the freshly-configured Configuration.
  shutdown
  config
end

.flushObject



85
86
87
# File 'lib/beacon.rb', line 85

def flush
  client.flush
end

.shutdownObject



89
90
91
92
93
94
# File 'lib/beacon.rb', line 89

def shutdown
  CLIENT_MUTEX.synchronize do
    @client&.shutdown
    @client = nil
  end
end

.statsObject

Operator-visible introspection. Returns a flat hash describing the client’s current internal state — queue depth, the drop counter, flusher counters, and transport counters. Used by smoke tests and rake tasks; also the primary signal operators have when something gets weird at 3am.

Flusher stats are fetched ONCE into a local so the result hash is a consistent snapshot — without this, a flush happening mid-build could leave ‘sent` and `last_flush_at` disagreeing.



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/beacon.rb', line 62

def stats
  c = @client
  return disabled_stats unless c
  fstats = c.flusher&.stats || EMPTY_FLUSHER_STATS
  {
    queue_depth:          c.queue.length,
    queue_max:            config.queue_size,
    dropped:              c.queue.dropped,
    sent:                 fstats[:sent] || 0,
    last_flush_at:        fstats[:last_flush_at],
    last_flush_status:    fstats[:last_flush_status],
    circuit_open:         fstats[:circuit_open] || false,
    consecutive_failures: fstats[:consecutive_failures] || 0,
    reconnects:           c.transport_reconnects,
    enabled:              c.enabled?,
  }
end

.track(name, properties = {}) ⇒ Object



49
50
51
# File 'lib/beacon.rb', line 49

def track(name, properties = {})
  client.track(name, properties)
end