Module: LogStasher

Extended by:
LogStasher
Included in:
LogStasher
Defined in:
lib/logstasher.rb,
lib/logstasher/event.rb,
lib/logstasher/railtie.rb,
lib/logstasher/version.rb,
lib/logstasher/device/redis.rb,
lib/logstasher/custom_fields.rb,
lib/logstasher/active_job/log_subscriber.rb,
lib/logstasher/action_view/log_subscriber.rb,
lib/logstasher/active_record/log_subscriber.rb,
lib/logstasher/active_support/log_subscriber.rb,
lib/logstasher/rails_ext/action_controller/base.rb,
lib/logstasher/active_support/mailer_log_subscriber.rb

Defined Under Namespace

Modules: ActionController, ActionView, ActiveJob, ActiveRecord, ActiveSupport, CustomFields, Device Classes: Event, Railtie

Constant Summary collapse

STORE_KEY =
:logstasher_data
REQUEST_CONTEXT_KEY =
:logstasher_request_context
VERSION =
'2.1.5'

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#backtraceObject

Returns the value of attribute backtrace.



22
23
24
# File 'lib/logstasher.rb', line 22

def backtrace
  @backtrace
end

#backtrace_filterObject

Returns the value of attribute backtrace_filter.



22
23
24
# File 'lib/logstasher.rb', line 22

def backtrace_filter
  @backtrace_filter
end

#controller_monkey_patchObject

Returns the value of attribute controller_monkey_patch.



22
23
24
# File 'lib/logstasher.rb', line 22

def controller_monkey_patch
  @controller_monkey_patch
end

#enabledObject

Returns the value of attribute enabled.



22
23
24
# File 'lib/logstasher.rb', line 22

def enabled
  @enabled
end

#field_renamingObject

Returns the value of attribute field_renaming.



22
23
24
# File 'lib/logstasher.rb', line 22

def field_renaming
  @field_renaming
end

#log_controller_parametersObject

Returns the value of attribute log_controller_parameters.



22
23
24
# File 'lib/logstasher.rb', line 22

def log_controller_parameters
  @log_controller_parameters
end

#loggerObject

Returns the value of attribute logger.



22
23
24
# File 'lib/logstasher.rb', line 22

def logger
  @logger
end

#logger_pathObject

Returns the value of attribute logger_path.



22
23
24
# File 'lib/logstasher.rb', line 22

def logger_path
  @logger_path
end

#sourceObject

Returns the value of attribute source.



22
23
24
# File 'lib/logstasher.rb', line 22

def source
  @source
end

Instance Method Details

#add_custom_fields(&block) ⇒ Object



69
70
71
72
73
74
75
76
# File 'lib/logstasher.rb', line 69

def add_custom_fields(&block)
  wrapped_block = proc do |fields|
    LogStasher::CustomFields.add(*LogStasher.store.keys)
    instance_exec(fields, &block)
  end
  ::ActionController::Metal.send(:define_method, :logstasher_add_custom_fields_to_payload, &wrapped_block)
  ::ActionController::Base.send(:define_method, :logstasher_add_custom_fields_to_payload, &wrapped_block)
end

#add_custom_fields_to_request_context(&block) ⇒ Object



78
79
80
81
82
83
84
85
# File 'lib/logstasher.rb', line 78

def add_custom_fields_to_request_context(&block)
  wrapped_block = proc do |fields|
    instance_exec(fields, &block)
    LogStasher::CustomFields.add(*fields.keys)
  end
  ::ActionController::Metal.send(:define_method, :logstasher_add_custom_fields_to_request_context, &wrapped_block)
  ::ActionController::Base.send(:define_method, :logstasher_add_custom_fields_to_request_context, &wrapped_block)
end

#add_default_fields_to_payload(payload, request) ⇒ Object



58
59
60
61
62
63
64
65
66
67
# File 'lib/logstasher.rb', line 58

def add_default_fields_to_payload(payload, request)
  payload[:ip] = request.remote_ip
  payload[:route] = "#{request.params[:controller]}##{request.params[:action]}"
  payload[:request_id] = request.env['action_dispatch.request_id']
  LogStasher::CustomFields.add(:ip, :route, :request_id)
  if log_controller_parameters
    payload[:parameters] = payload[:params].except(*::ActionController::LogSubscriber::INTERNAL_PARAMS)
    LogStasher::CustomFields.add(:parameters)
  end
end

#add_default_fields_to_request_context(request) ⇒ Object



87
88
89
# File 'lib/logstasher.rb', line 87

def add_default_fields_to_request_context(request)
  request_context[:request_id] = request.env['action_dispatch.request_id']
end

#build_logstash_event(data, tags) ⇒ Object



184
185
186
187
188
189
# File 'lib/logstasher.rb', line 184

def build_logstash_event(data, tags)
  field_renaming.each do |old_name, new_name|
    data[new_name] = data.delete(old_name) if data.key?(old_name)
  end
  Event.new(data.merge('source' => source, 'tags' => tags))
end

#called_as_console?Boolean

Returns:

  • (Boolean)


134
135
136
# File 'lib/logstasher.rb', line 134

def called_as_console?
  defined?(Rails::Console) && true || false
end

#called_as_rake?Boolean

Returns:

  • (Boolean)


130
131
132
# File 'lib/logstasher.rb', line 130

def called_as_rake?
  File.basename($PROGRAM_NAME) == 'rake'
end

#clear_request_contextObject



91
92
93
# File 'lib/logstasher.rb', line 91

def clear_request_context
  request_context.clear
end

#configured_to_suppress_app_logs?(config) ⇒ Boolean

Returns:

  • (Boolean)


149
150
151
152
# File 'lib/logstasher.rb', line 149

def configured_to_suppress_app_logs?(config)
  # This supports both spellings: "suppress_app_log" and "supress_app_log"
  !!(config.suppress_app_log.nil? ? config.supress_app_log : config.suppress_app_log)
end

#default_sourceObject



9
10
11
12
13
14
15
16
17
18
19
20
21
22
# File 'lib/logstasher/railtie.rb', line 9

def default_source
  case RUBY_PLATFORM
  when /darwin/
    # NOTE: MacOS Sierra and later are setting `.local`
    # hostnames that even as real hostnames without the `.local` part,
    # are still unresolvable. One reliable way to get an IP is to
    # get all available IP address lists and use the first one.
    # This will always be `127.0.0.1`.
    address_info = Socket.ip_address_list.first
    address_info&.ip_address
  else
    IPSocket.getaddress(Socket.gethostname)
  end
end

#enabled?Boolean

Returns:

  • (Boolean)


219
220
221
# File 'lib/logstasher.rb', line 219

def enabled?
  enabled || false
end

#has_active_job?Boolean

Returns:

  • (Boolean)


138
139
140
# File 'lib/logstasher.rb', line 138

def has_active_job?
  defined?(ActiveJob)
end

#log(severity, message, additional_fields = {}) ⇒ Object

Log an arbitrary message.

Usually invoked by the level-based wrapper methods defined below.

Examples

LogStasher.info("message")
LogStasher.info("message", tags:"tag1")
LogStasher.info("message", tags:["tag1", "tag2"])
LogStasher.info("message", timing:1234)
LogStasher.info(custom1:"yes", custom2:"no")


165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/logstasher.rb', line 165

def log(severity, message, additional_fields = {})
  if logger&.send("#{severity}?")

    data = { 'level' => severity }
    if message.respond_to?(:to_hash)
      data.merge!(message.to_hash)
    else
      data['message'] = message
    end

    # tags get special handling
    tags = Array(additional_fields.delete(:tags) || 'log')

    data.merge!(additional_fields)
    logger << "#{build_logstash_event(data, tags).to_json}\n"

  end
end

#process_config(config, yml_config) ⇒ Object



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/logstasher/railtie.rb', line 67

def process_config(config, yml_config)
  # Enable the logstasher logs for the current environment
  config.enabled = yml_config[:enabled] if yml_config.key? :enabled
  config.controller_enabled = yml_config[:controller_enabled] if yml_config.key? :controller_enabled
  config.mailer_enabled = yml_config[:mailer_enabled] if yml_config.key? :mailer_enabled
  config.record_enabled = yml_config[:record_enabled] if yml_config.key? :record_enabled
  config.view_enabled = yml_config[:view_enabled] if yml_config.key? :view_enabled
  config.job_enabled = yml_config[:job_enabled] if yml_config.key? :job_enabled

  # This line is optional if you do not want to suppress app logs in your <environment>.log
  config.suppress_app_log = yml_config[:suppress_app_log] if yml_config.key? :suppress_app_log

  # This line is optional, it allows you to set a custom value for the @source field of the log event
  config.source = yml_config.key?(:source) ? yml_config[:source] : default_source

  config.backtrace = yml_config[:backtrace] if yml_config.key? :backtrace
  config.logger_path = yml_config[:logger_path] if yml_config.key? :logger_path
  config.log_level = yml_config[:log_level] if yml_config.key? :log_level
  if yml_config.key? :log_controller_parameters
    config.log_controller_parameters = yml_config[:log_controller_parameters]
  end
end

#remove_existing_log_subscriptionsObject



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

def remove_existing_log_subscriptions
  ::ActiveSupport::LogSubscriber.log_subscribers.each do |subscriber|
    case subscriber.class.name
    when 'ActionView::LogSubscriber'
      unsubscribe(:action_view, subscriber)
    when 'ActionController::LogSubscriber'
      unsubscribe(:action_controller, subscriber)
    when 'ActionMailer::LogSubscriber'
      unsubscribe(:action_mailer, subscriber)
    when 'ActiveRecord::LogSubscriber'
      unsubscribe(:active_record, subscriber)
    when 'ActiveJob::LogSubscriber' # For Rails 6
      unsubscribe(:active_job, subscriber)
    when 'ActiveJob::Logging::LogSubscriber' # For Rails 5
      unsubscribe(:active_job, subscriber)
    end
  end
end

#request_contextObject



199
200
201
# File 'lib/logstasher.rb', line 199

def request_context
  RequestStore.store[REQUEST_CONTEXT_KEY] ||= {}
end

#set_data_for_consoleObject



126
127
128
# File 'lib/logstasher.rb', line 126

def set_data_for_console
  request_context['request_id'] = Process.pid.to_s if called_as_console?
end

#set_data_for_rakeObject



122
123
124
# File 'lib/logstasher.rb', line 122

def set_data_for_rake
  request_context['request_id'] = ::Rake.application.top_level_tasks if called_as_rake?
end

#setup(config) ⇒ Object



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/logstasher.rb', line 104

def setup(config)
  # Path instrumentation class to insert our hook
  if (!config.controller_monkey_patch && config.controller_monkey_patch != false) || config.controller_monkey_patch == true
    require 'logstasher/rails_ext/action_controller/metal/instrumentation'
  end
  suppress_app_logs(config)
  self.logger_path = config.logger_path || "#{Rails.root}/log/logstash_#{Rails.env}.log"
  self.logger = config.logger || new_logger(logger_path)
  logger.level = config.log_level || Logger::WARN
  self.source = config.source unless config.source.nil?
  self.log_controller_parameters = !config.log_controller_parameters.nil?
  self.backtrace = !config.backtrace.nil? unless config.backtrace.nil?
  self.backtrace_filter = config.backtrace_filter
  set_data_for_rake
  set_data_for_console
  self.field_renaming = Hash(config.field_renaming)
end

#setup_before(config) ⇒ Object



95
96
97
98
99
100
101
102
# File 'lib/logstasher.rb', line 95

def setup_before(config)
  self.enabled = config.enabled
  LogStasher::ActiveSupport::LogSubscriber.attach_to :action_controller if config.controller_enabled
  LogStasher::ActiveSupport::MailerLogSubscriber.attach_to :action_mailer if config.mailer_enabled
  LogStasher::ActiveRecord::LogSubscriber.attach_to :active_record if config.record_enabled
  LogStasher::ActionView::LogSubscriber.attach_to :action_view if config.view_enabled
  LogStasher::ActiveJob::LogSubscriber.attach_to :active_job if has_active_job? && config.job_enabled
end

#storeObject



191
192
193
194
195
196
197
# File 'lib/logstasher.rb', line 191

def store
  if RequestStore.store[STORE_KEY].nil?
    # Get each store it's own private Hash instance.
    RequestStore.store[STORE_KEY] = Hash.new { |hash, key| hash[key] = {} }
  end
  RequestStore.store[STORE_KEY]
end

#suppress_app_logs(config) ⇒ Object



142
143
144
145
146
147
# File 'lib/logstasher.rb', line 142

def suppress_app_logs(config)
  if configured_to_suppress_app_logs?(config)
    require 'logstasher/rails_ext/rack/logger'
    LogStasher.remove_existing_log_subscriptions
  end
end

#unsubscribe(component, subscriber) ⇒ Object



49
50
51
52
53
54
55
56
# File 'lib/logstasher.rb', line 49

def unsubscribe(component, subscriber)
  events = subscriber.public_methods(false).reject { |method| method.to_s == 'call' }
  events.each do |event|
    ::ActiveSupport::Notifications.notifier.listeners_for("#{event}.#{component}").each do |listener|
      ::ActiveSupport::Notifications.unsubscribe listener if listener.instance_variable_get('@delegate') == subscriber
    end
  end
end

#watch(event, opts = {}, &block) ⇒ Object



203
204
205
206
207
208
209
# File 'lib/logstasher.rb', line 203

def watch(event, opts = {}, &block)
  event_group = opts[:event_group] || event
  ::ActiveSupport::Notifications.subscribe(event) do |*args|
    # Calling the processing block with the Notification args and the store
    block.call(*args, store[event_group])
  end
end