Module: Phlex::Reactive::APM

Defined in:
lib/phlex/reactive/apm.rb,
lib/phlex/reactive/apm/sentry.rb,
lib/phlex/reactive/apm/adapter.rb,
lib/phlex/reactive/apm/datadog.rb,
lib/phlex/reactive/apm/appsignal.rb,
lib/phlex/reactive/apm/subscriber.rb

Overview

Turnkey APM integration (issue #207). The gem already emits *.phlex_reactive ActiveSupport::Notifications events (issue #107); this namespace turns them into per-component visibility in AppSignal / Sentry / Datadog with ONE config line — Phlex::Reactive.apm = :appsignal — instead of every app hand-writing the subscribe block and each vendor's transaction-naming / error-tagging API.

The vendor adapters are RUNTIME capability-detected (defined?(::Appsignal) etc.), never a gemspec dependency — the same optionality invariant as pgbus. A set-but-absent SDK logs one warning and no-ops.

Defined Under Namespace

Classes: Adapter, Appsignal, Datadog, Sentry, Subscriber

Constant Summary collapse

BUILT_INS =

Symbol => built-in adapter class. Resolved lazily (at attach time), so the adapter class is only referenced when an app actually opts into it.

{
  appsignal: "Phlex::Reactive::APM::Appsignal",
  sentry: "Phlex::Reactive::APM::Sentry",
  datadog: "Phlex::Reactive::APM::Datadog"
}.freeze

Class Method Summary collapse

Class Method Details

.attach!(apm = Phlex::Reactive.apm) ⇒ Object

Attach the APM Subscriber to the notification bus if an adapter resolves. Called once from the engine's after_initialize when Phlex::Reactive.apm is set. Idempotent within a boot: a second call with the SAME adapter is a no-op. Returns the attached adapter (or nil when nothing resolved).



54
55
56
57
58
59
60
61
62
63
# File 'lib/phlex/reactive/apm.rb', line 54

def attach!(apm = Phlex::Reactive.apm)
  adapter = detect(apm)
  return nil unless adapter

  Subscriber.install(adapter)
  # Hold the adapter so the endpoint's error seam (report_error) can reach
  # record_error without re-running detection per request.
  Phlex::Reactive.resolved_apm_adapter = adapter
  adapter
end

.detect(apm) ⇒ Object

Resolve Phlex::Reactive.apm to a live adapter instance, or nil.

  • nil -> nil (off; no warning)
  • an object -> returned verbatim (a custom adapter)
  • a known Symbol -> the built-in adapter instance IF its SDK is loaded, else nil + one warning
  • an unknown Symbol -> nil + one warning


30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/phlex/reactive/apm.rb', line 30

def detect(apm)
  return nil if apm.nil?
  return apm unless apm.is_a?(Symbol)

  klass_name = BUILT_INS[apm]
  unless klass_name
    known = BUILT_INS.keys.map(&:inspect).join(", ")
    return warn_and_nil("apm = #{apm.inspect} — unknown APM flavour (known: #{known})")
  end

  klass = klass_name.constantize
  # Memoize the instance PER SYMBOL so repeated detect/attach! calls return
  # the SAME object — Subscriber.install keys idempotency on adapter
  # identity (equal?), so a fresh klass.new each time would double-subscribe.
  return (built_in_instances[apm] ||= klass.new) if klass.available?

  warn_and_nil("apm = #{apm.inspect} set but #{apm} is not loaded — no-op. " \
               "Require the SDK (or remove the setting).")
end

.reset!Object

Drop the installed subscriber + adapter AND the memoized built-in instances. Tests only (so a stubbed SDK doesn't leak a stale adapter into the next example).



68
69
70
71
72
# File 'lib/phlex/reactive/apm.rb', line 68

def reset!
  Subscriber.uninstall
  Phlex::Reactive.resolved_apm_adapter = nil
  @built_in_instances = nil
end