Module: LogStruct::SemanticLogger::Setup
- Extended by:
- T::Sig
- Defined in:
- lib/log_struct/semantic_logger/setup.rb
Overview
Sets up SemanticLogger for a Rails app: its level, appenders and formatters, and the loggers it replaces
Constant Summary collapse
- COMPONENT_LOAD_HOOKS =
Load hooks for the framework components whose loggers LogStruct replaces
T.let( %i[active_record action_controller action_mailer active_job action_view action_cable].freeze, T::Array[Symbol] )
- NOISY_LOGGERS =
/\A(ActionView|ActionController::RoutingError|ActiveRecord::SchemaMigration)/
Class Method Summary collapse
- .add_appenders(app) ⇒ void
-
.configure_semantic_logger(app) ⇒ void
Configures SemanticLogger and replaces Rails' loggers with LogStruct's: - the level from config.log_level (info in production, debug elsewhere) - the appenders for the environment (see add_appenders) - Rails.logger and the framework loggers (ActiveRecord, ActionController, ActiveJob, ...).
- .configured? ⇒ Boolean
-
.determine_filter ⇒ T.proc.params(log: ::SemanticLogger::Log).returns(T::Boolean)?
With filter_noisy_loggers on, an appender filter that leaves out NOISY_LOGGERS' lines.
- .determine_log_level(app) ⇒ Symbol
- .determine_output(app) ⇒ T.untyped
-
.reconfigure_appenders(app) ⇒ void
Adds the appenders again, with the settings config/initializers changed after the logger was set up (prefer_json_in_development, enable_color_output, color_map, filter_noisy_loggers, local_environments).
-
.remove_appenders ⇒ void
Removes the appenders add_appenders added, after writing the lines already queued for them.
-
.replace_rails_logger(app) ⇒ void
Replaces Rails.logger, ActiveSupport::LogSubscriber.logger, config.logger and the framework loggers (ActiveRecord, ActionController, ActionMailer, ActiveJob, ActionView, ActionCable) with LogStruct's SemanticLogger logger, so every Rails line is a LogStruct JSON line.
-
.restore_rails_logger(app) ⇒ void
Puts back the loggers that replace_rails_logger replaced.
Class Method Details
.add_appenders(app) ⇒ void
This method returns an undefined value.
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 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 |
# File 'lib/log_struct/semantic_logger/setup.rb', line 99 def self.add_appenders(app) config = LogStruct.config # Determine output destination io = determine_output(app) # Ruby buffers STDOUT when it isn't a terminal, so in a container lines waited until the buffer # filled or the process exited. (GoodJob's executable only syncs STDOUT when it writes its own # text logs there.) io.sync = true if io.equal?($stdout) appenders = [] appenders << if Rails.env.development? if config.prefer_json_in_development # Default to production-style JSON in development when enabled ::SemanticLogger.add_appender( io: io, formatter: LogStruct::SemanticLogger::Formatter.new, filter: determine_filter ) elsif config.enable_color_output # Opt-in colorful human formatter in development ::SemanticLogger.add_appender( io: io, formatter: LogStruct::SemanticLogger::ColorFormatter.new( color_map: config.color_map ), filter: determine_filter ) else ::SemanticLogger.add_appender( io: io, formatter: LogStruct::SemanticLogger::Formatter.new, filter: determine_filter ) end else # Use our custom JSON formatter in non-development environments ::SemanticLogger.add_appender( io: io, formatter: LogStruct::SemanticLogger::Formatter.new, filter: determine_filter ) end # Log to Rails' log file too in development and test (config.local_environments), unless # RAILS_LOG_TO_STDOUT says not to. Production and other servers log to STDOUT only: nothing # rotates the file, and in a container it only fills the disk. local = config.local_environments.include?(Rails.env.to_sym) if local && app.config.paths["log"].first && ENV["RAILS_LOG_TO_STDOUT"].blank? appenders << ::SemanticLogger.add_appender( file_name: app.config.paths["log"].first, formatter: LogStruct::SemanticLogger::Formatter.new, filter: determine_filter ) end # add_appender returns nil when it ignores a second STDOUT appender @appenders = appenders.compact end |
.configure_semantic_logger(app) ⇒ void
This method returns an undefined value.
Configures SemanticLogger and replaces Rails' loggers with LogStruct's:
- the level from config.log_level (info in production, debug elsewhere)
- the appenders for the environment (see add_appenders)
- Rails.logger and the framework loggers (ActiveRecord, ActionController, ActiveJob, ...)
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/log_struct/semantic_logger/setup.rb', line 46 def self.configure_semantic_logger(app) # Set SemanticLogger configuration ::SemanticLogger.application = Rails.application.class.module_parent_name ::SemanticLogger.environment = Rails.env # Determine log level from Rails config log_level = determine_log_level(app) ::SemanticLogger.default_level = log_level # Add appropriate appenders based on environment remove_appenders add_appenders(app) ReopenAfterFork.install! # Replace Rails.logger with SemanticLogger replace_rails_logger(app) end |
.configured? ⇒ Boolean
35 36 37 |
# File 'lib/log_struct/semantic_logger/setup.rb', line 35 def self.configured? !@logger.nil? end |
.determine_filter ⇒ T.proc.params(log: ::SemanticLogger::Log).returns(T::Boolean)?
With filter_noisy_loggers on, an appender filter that leaves out NOISY_LOGGERS' lines. SemanticLogger writes a line when a Proc filter returns true (a Regexp filter would keep only the names it matches).
178 179 180 181 182 |
# File 'lib/log_struct/semantic_logger/setup.rb', line 178 def self.determine_filter return nil unless LogStruct.config.filter_noisy_loggers ->(log) { !NOISY_LOGGERS.match?(log.name.to_s) } end |
.determine_log_level(app) ⇒ Symbol
84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/log_struct/semantic_logger/setup.rb', line 84 def self.determine_log_level(app) level = if app.config.log_level app.config.log_level elsif Rails.env.production? :info elsif Rails.env.test? :debug else :debug end # Rails config.log_level can be a String or Symbol level.is_a?(String) ? level.to_sym : level end |
.determine_output(app) ⇒ T.untyped
160 161 162 163 164 165 166 167 168 169 170 171 |
# File 'lib/log_struct/semantic_logger/setup.rb', line 160 def self.determine_output(app) # Always honor explicit STDOUT directive return $stdout if ENV["RAILS_LOG_TO_STDOUT"].present? if Rails.env.test? # Use StringIO in test to keep stdout clean StringIO.new else # Use STDOUT for app logs in dev/production $stdout end end |
.reconfigure_appenders(app) ⇒ void
This method returns an undefined value.
Adds the appenders again, with the settings config/initializers changed after the logger was set up (prefer_json_in_development, enable_color_output, color_map, filter_noisy_loggers, local_environments). Appenders the app added in config/initializers stay.
68 69 70 71 |
# File 'lib/log_struct/semantic_logger/setup.rb', line 68 def self.reconfigure_appenders(app) remove_appenders add_appenders(app) end |
.remove_appenders ⇒ void
This method returns an undefined value.
Removes the appenders add_appenders added, after writing the lines already queued for them
75 76 77 78 79 80 81 |
# File 'lib/log_struct/semantic_logger/setup.rb', line 75 def self.remove_appenders return if @appenders.empty? ::SemanticLogger.flush @appenders.each { |appender| ::SemanticLogger.remove_appender(appender) } @appenders = [] end |
.replace_rails_logger(app) ⇒ void
This method returns an undefined value.
Replaces Rails.logger, ActiveSupport::LogSubscriber.logger, config.logger and the framework loggers (ActiveRecord, ActionController, ActionMailer, ActiveJob, ActionView, ActionCable) with LogStruct's SemanticLogger logger, so every Rails line is a LogStruct JSON line
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
# File 'lib/log_struct/semantic_logger/setup.rb', line 190 def self.replace_rails_logger(app) # Create new SemanticLogger instance logger = LogStruct::SemanticLogger::Logger.new("Rails") @logger = logger @original_rails_logger = Rails.logger @original_log_subscriber_logger = ActiveSupport::LogSubscriber.logger @original_app_logger = app.config.logger # Replace Rails.logger Rails.logger = logger # Rails assigns ActiveSupport::LogSubscriber.logger once during boot # (in the active_support.set_log_subscriber_logger initializer), so log # subscribers such as Lograge keep writing to the original boot logger # unless this reference is updated along with Rails.logger. ActiveSupport::LogSubscriber.logger = logger # Also replace the component loggers. Use load hooks rather than referencing the base # classes: loading them this early would apply their on_load hooks before the app's # initializers run, e.g. ActiveRecord would copy an empty filter_parameters into # filter_attributes. COMPONENT_LOAD_HOOKS.each do |hook| ActiveSupport.on_load(hook) { self.logger = logger } end # Store reference in app config app.config.logger = logger end |
.restore_rails_logger(app) ⇒ void
This method returns an undefined value.
Puts back the loggers that replace_rails_logger replaced. Used when an initializer disables LogStruct after the logger was replaced at boot.
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 |
# File 'lib/log_struct/semantic_logger/setup.rb', line 222 def self.restore_rails_logger(app) logger = @logger return unless logger original_logger = @original_rails_logger Rails.logger = original_logger ActiveSupport::LogSubscriber.logger = @original_log_subscriber_logger app.config.logger = @original_app_logger remove_appenders # These hooks run after the ones that installed LogStruct's logger, including for # components that are already loaded COMPONENT_LOAD_HOOKS.each do |hook| ActiveSupport.on_load(hook) do self.logger = original_logger if self.logger.equal?(logger) end end @logger = nil end |