Module: LogStruct::Integrations::Dotenv
- Extended by:
- IntegrationInterface, T::Sig
- Defined in:
- lib/log_struct/integrations/dotenv.rb
Overview
Dotenv integration: emits structured logs for load/update/save/restore events
Defined Under Namespace
Classes: State
Constant Summary collapse
- STATE =
T.let(State.new(false), State)
- @@boot_subscribed =
Early boot subscription to buffer structured logs until logger is ready
T.let(false, T::Boolean)
Class Method Summary collapse
-
.handle_log_error(error) ⇒ void
A problem with a Dotenv log line is LogStruct's own: raised in development and test, logged in production.
-
.intercept_logger_setter! ⇒ void
Intercept Dotenv::Rails#logger= to defer replay until we resolve policy.
- .relative_env_path(env) ⇒ String
-
.resolve_boot_logs! ⇒ void
Decide which boot logs to emit after user initializers.
- .setup(config) ⇒ Boolean?
- .setup_boot ⇒ void
- .subscribe! ⇒ void
Methods included from IntegrationInterface
Class Method Details
.handle_log_error(error) ⇒ void
This method returns an undefined value.
A problem with a Dotenv log line is LogStruct's own: raised in development and test, logged in production
53 54 55 |
# File 'lib/log_struct/integrations/dotenv.rb', line 53 def handle_log_error(error) LogStruct.handle_exception(error, source: LogStruct::Source::Internal, context: {integration: :dotenv}) end |
.intercept_logger_setter! ⇒ void
This method returns an undefined value.
Intercept Dotenv::Rails#logger= to defer replay until we resolve policy
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
# File 'lib/log_struct/integrations/dotenv.rb', line 172 def self.intercept_logger_setter! return unless Object.const_defined?(:Dotenv) # Do not intercept when LogStruct is disabled; allow original dotenv replay return unless LogStruct.enabled? dotenv_mod = Object.const_get(:Dotenv) return unless dotenv_mod.const_defined?(:Rails) klass = dotenv_mod.const_get(:Rails) return if klass.instance_variable_defined?(:@_logstruct_replay_patched) original = klass.instance_method(:logger=) @original_logger_setter = original mod = Module.new do define_method :logger= do |new_logger| # Defer replay: store desired logger, keep ReplayLogger as current instance_variable_set(:@logstruct_pending_dotenv_logger, new_logger) new_logger end define_method :logstruct_pending_dotenv_logger do instance_variable_get(:@logstruct_pending_dotenv_logger) end end klass.prepend(mod) klass.instance_variable_set(:@_logstruct_replay_patched, true) end |
.relative_env_path(env) ⇒ String
38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/log_struct/integrations/dotenv.rb', line 38 def relative_env_path(env) abs = env.filename begin if defined?(::Rails) && ::Rails.respond_to?(:root) && ::Rails.root Pathname.new(abs).relative_path_from(Pathname.new(::Rails.root.to_s)).to_s else abs end rescue abs end end |
.resolve_boot_logs! ⇒ void
This method returns an undefined value.
Decide which boot logs to emit after user initializers
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 |
# File 'lib/log_struct/integrations/dotenv.rb', line 202 def self.resolve_boot_logs! # If LogStruct is disabled, do not alter dotenv behavior at all return unless LogStruct.enabled? dotenv_mod = Object.const_defined?(:Dotenv) ? Object.const_get(:Dotenv) : nil klass = dotenv_mod&.const_defined?(:Rails) ? dotenv_mod.const_get(:Rails) : nil pending_logger = nil railtie_instance = nil if klass&.respond_to?(:instance) railtie_instance = klass.instance if railtie_instance.respond_to?(:logstruct_pending_dotenv_logger) pending_logger = railtie_instance.logstruct_pending_dotenv_logger end end if LogStruct.enabled? && LogStruct.config.integrations.enable_dotenv # Structured path if pending_logger && railtie_instance # Clear any buffered original logs current_logger = railtie_instance.logger if railtie_instance.respond_to?(:logger) if current_logger && current_logger.class.name.end_with?("ReplayLogger") begin logs = current_logger.instance_variable_get(:@logs) logs.clear if logs.respond_to?(:clear) rescue # best effort end end railtie_instance.config.dotenv.logger = pending_logger end # Detach original subscriber and subscribe runtime structured if dotenv_mod&.const_defined?(:LogSubscriber) dotenv_mod.const_get(:LogSubscriber).detach_from(:dotenv) end LogStruct::Integrations::Dotenv.subscribe! require_relative "../boot_buffer" LogStruct::BootBuffer.flush else # Original path: replay dotenv lines, drop structured buffer if railtie_instance && @original_logger_setter setter = @original_logger_setter new_logger = pending_logger if new_logger.nil? && ENV["RAILS_LOG_TO_STDOUT"].to_s.strip != "" require "logger" require "active_support/tagged_logging" new_logger = ActiveSupport::TaggedLogging.new(::Logger.new($stdout)).tagged("dotenv") end setter.bind_call(railtie_instance, new_logger) if new_logger end require_relative "../boot_buffer" LogStruct::BootBuffer.clear end end |
.setup(config) ⇒ Boolean?
27 28 29 30 31 32 |
# File 'lib/log_struct/integrations/dotenv.rb', line 27 def self.setup(config) # Subscribe regardless of dotenv gem presence so instrumentation via # ActiveSupport::Notifications can be captured during tests and runtime. subscribe! true end |
.setup_boot ⇒ void
This method returns an undefined value.
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/log_struct/integrations/dotenv.rb', line 118 def self.setup_boot return if @@boot_subscribed return unless defined?(::ActiveSupport::Notifications) instrumenter = if Object.const_defined?(:Dotenv) dm = Object.const_get(:Dotenv) dm.respond_to?(:instrumenter) ? dm.instrumenter : ::ActiveSupport::Notifications else ::ActiveSupport::Notifications end instrumenter.subscribe("load.dotenv") do |*args| event = ::ActiveSupport::Notifications::Event.new(*args) env = event.payload[:env] file = relative_env_path(env) ts = event.time ? Time.at(event.time) : Time.now LogStruct::BootBuffer.add(Log::Dotenv::Load.new(file: file, timestamp: ts)) rescue => e handle_log_error(e) end instrumenter.subscribe("update.dotenv") do |*args| event = ::ActiveSupport::Notifications::Event.new(*args) diff = event.payload[:diff] vars = diff.env.keys.map(&:to_s) ts = event.time ? Time.at(event.time) : Time.now LogStruct::BootBuffer.add(Log::Dotenv::Update.new(vars: vars, timestamp: ts)) rescue => e handle_log_error(e) end instrumenter.subscribe("save.dotenv") do |*args| event = ::ActiveSupport::Notifications::Event.new(*args) ts = event.time ? Time.at(event.time) : Time.now LogStruct::BootBuffer.add(Log::Dotenv::Save.new(snapshot: true, timestamp: ts)) rescue => e handle_log_error(e) end instrumenter.subscribe("restore.dotenv") do |*args| event = ::ActiveSupport::Notifications::Event.new(*args) diff = event.payload[:diff] vars = diff.env.keys.map(&:to_s) ts = event.time ? Time.at(event.time) : Time.now LogStruct::BootBuffer.add(Log::Dotenv::Restore.new(vars: vars, timestamp: ts)) rescue => e handle_log_error(e) end @@boot_subscribed = true end |
.subscribe! ⇒ void
This method returns an undefined value.
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 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 |
# File 'lib/log_struct/integrations/dotenv.rb', line 58 def subscribe! # Guard against double subscription return if STATE.subscribed instrumenter = defined?(::ActiveSupport::Notifications) ? ::ActiveSupport::Notifications : nil return unless instrumenter instrumenter.subscribe("load.dotenv") do |*args| # Allow tests to stub Log::Dotenv.new to force an error path LogStruct::Log::Dotenv.new event = ::ActiveSupport::Notifications::Event.new(*args) env = event.payload[:env] file = relative_env_path(env) ts = event.time ? Time.at(event.time) : Time.now LogStruct.info(Log::Dotenv::Load.new(file: file, timestamp: ts)) rescue => e handle_log_error(e) end instrumenter.subscribe("update.dotenv") do |*args| LogStruct::Log::Dotenv.new event = ::ActiveSupport::Notifications::Event.new(*args) diff = event.payload[:diff] vars = diff.env.keys.map(&:to_s) ts = event.time ? Time.at(event.time) : Time.now LogStruct.debug(Log::Dotenv::Update.new(vars: vars, timestamp: ts)) rescue => e handle_log_error(e) end instrumenter.subscribe("save.dotenv") do |*args| LogStruct::Log::Dotenv.new event = ::ActiveSupport::Notifications::Event.new(*args) ts = event.time ? Time.at(event.time) : Time.now LogStruct.info(Log::Dotenv::Save.new(snapshot: true, timestamp: ts)) rescue => e handle_log_error(e) end instrumenter.subscribe("restore.dotenv") do |*args| LogStruct::Log::Dotenv.new event = ::ActiveSupport::Notifications::Event.new(*args) diff = event.payload[:diff] vars = diff.env.keys.map(&:to_s) ts = event.time ? Time.at(event.time) : Time.now LogStruct.info(Log::Dotenv::Restore.new(vars: vars, timestamp: ts)) rescue => e handle_log_error(e) end STATE.subscribed = true end |