Class: AllStak::Integrations::Logger

Inherits:
Logger
  • Object
show all
Defined in:
lib/allstak/integrations/logger.rb

Overview

Optional structured-log adapter.

A drop-in Logger-compatible sink that forwards every log record to AllStak’s ‘/ingest/v1/logs` endpoint (via AllStak.log). It is intended to be *broadcast alongside* your existing logger so app logs keep going to STDOUT/file unchanged while also flowing into AllStak — no per-call code.

ERROR PROMOTION: records at or above #error_promotion_level (default ERROR) are additionally captured as AllStak “message” error-group entries (via AllStak.capture_message) so error-level logs surface in the Errors list, not just the log stream. Set ‘error_promotion: false` to disable.

OPT-IN by design — adding this adapter is the only manual step; after that ‘Rails.logger.error(…)` / `logger.warn(…)` ship automatically.

# Rails 7.1+ (BroadcastLogger) — keep existing logging, add AllStak:
AllStak::Integrations::Logger.attach_to_rails!

# Or compose manually with any Ruby Logger:
sink = AllStak::Integrations::Logger.new
Rails.logger.broadcast_to(sink)            # Rails 7.1+
# ...or wrap a single logger so both get every line:
Rails.logger = AllStak::Integrations::Logger.broadcast(Rails.logger)

Fully fail-open: a transport/SDK error never propagates into the host’s logging path, and the adapter is a graceful no-op when the SDK is not configured.

Defined Under Namespace

Classes: BroadcastLogger

Constant Summary collapse

SEVERITY_TO_LEVEL =

Map Ruby Logger severities to AllStak log levels.

{
  ::Logger::DEBUG => "debug",
  ::Logger::INFO  => "info",
  ::Logger::WARN  => "warn",
  ::Logger::ERROR => "error",
  ::Logger::FATAL => "fatal",
  ::Logger::UNKNOWN => "error"
}.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(level: ::Logger::DEBUG, error_promotion: true, error_promotion_level: ::Logger::ERROR) ⇒ Logger

Returns a new instance of Logger.

Parameters:

  • level (Integer) (defaults to: ::Logger::DEBUG)

    minimum severity to forward (default DEBUG).

  • error_promotion (Boolean) (defaults to: true)

    capture >= error_promotion_level records as AllStak message events too (default true).

  • error_promotion_level (Integer) (defaults to: ::Logger::ERROR)

    severity threshold for promotion (default ::Logger::ERROR).



50
51
52
53
54
55
56
57
58
# File 'lib/allstak/integrations/logger.rb', line 50

def initialize(level: ::Logger::DEBUG, error_promotion: true,
               error_promotion_level: ::Logger::ERROR)
  # logdev=nil: this sink does not write to any device of its own; it only
  # forwards into AllStak. Composition (broadcast) keeps the real device.
  super(nil)
  self.level = level
  @error_promotion = error_promotion != false
  @error_promotion_level = error_promotion_level
end

Instance Attribute Details

#error_promotion_levelObject (readonly)

Returns the value of attribute error_promotion_level.



43
44
45
# File 'lib/allstak/integrations/logger.rb', line 43

def error_promotion_level
  @error_promotion_level
end

Class Method Details

.attach_to_rails!(**opts) ⇒ Object

Attach an AllStak sink to the current Rails.logger without replacing the existing logging destinations. Idempotent and fail-open. Returns true when (now) attached, false otherwise (no Rails / no logger).



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/allstak/integrations/logger.rb', line 99

def attach_to_rails!(**opts)
  return false unless defined?(::Rails) && ::Rails.respond_to?(:logger)
  current = ::Rails.logger
  return false if current.nil?
  return true if @attached_to_rails

  sink = new(**opts)
  if current.respond_to?(:broadcast_to)
    # Rails 7.1+ BroadcastLogger — add without disturbing existing sinks.
    current.broadcast_to(sink)
  else
    ::Rails.logger = broadcast(current, **opts)
  end
  @attached_to_rails = true
  true
rescue StandardError
  false
end

.broadcast(existing, **opts) ⇒ Object

Compose ‘existing` with an AllStak sink so both receive every record. Prefers Rails’ BroadcastLogger when available; otherwise returns a BroadcastLogger shim. Returns ‘existing` unchanged on any failure.



85
86
87
88
89
90
91
92
93
94
# File 'lib/allstak/integrations/logger.rb', line 85

def broadcast(existing, **opts)
  sink = new(**opts)
  if defined?(::ActiveSupport::BroadcastLogger)
    ::ActiveSupport::BroadcastLogger.new(existing, sink)
  else
    BroadcastLogger.new([existing, sink])
  end
rescue StandardError
  existing
end

.reset_attached!Object

Test seam.



119
120
121
# File 'lib/allstak/integrations/logger.rb', line 119

def reset_attached!
  @attached_to_rails = false
end

Instance Method Details

#<<(msg) ⇒ Object

‘logger << “raw”` writes an UNKNOWN-severity record in Ruby Logger.



76
77
78
79
# File 'lib/allstak/integrations/logger.rb', line 76

def <<(msg)
  add(::Logger::UNKNOWN, msg.to_s)
  msg
end

#add(severity, message = nil, progname = nil, &block) ⇒ Object

Core Logger entry point. Every severity helper (#debug/#info/#warn/ #error/#fatal/#unknown) and ‘<<` funnel through #add, so overriding it captures the whole surface. Returns true (Logger#add contract) and never raises into the host.



64
65
66
67
68
69
70
71
72
73
# File 'lib/allstak/integrations/logger.rb', line 64

def add(severity, message = nil, progname = nil, &block)
  severity ||= ::Logger::UNKNOWN
  return true if severity < level

  text = resolve_message(message, progname, &block)
  forward(severity, text, progname)
  true
rescue StandardError
  true
end