Module: LogStasher

Extended by:
LogStasher
Included in:
LogStasher
Defined in:
lib/logstasher.rb,
lib/logstasher/railtie.rb,
lib/logstasher/version.rb,
lib/logstasher/device/redis.rb,
lib/logstasher/log_subscriber.rb

Defined Under Namespace

Modules: Device Classes: MailerLogSubscriber, Railtie, RequestLogSubscriber

Constant Summary collapse

STORE_KEY =
:logstasher_data
REQUEST_CONTEXT_KEY =
:logstasher_request_context
VERSION =
"0.7.0"

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#backtraceObject

Returns the value of attribute backtrace.



13
14
15
# File 'lib/logstasher.rb', line 13

def backtrace
  @backtrace
end

#enabledObject

Returns the value of attribute enabled.



13
14
15
# File 'lib/logstasher.rb', line 13

def enabled
  @enabled
end

#log_controller_parametersObject

Returns the value of attribute log_controller_parameters.



13
14
15
# File 'lib/logstasher.rb', line 13

def log_controller_parameters
  @log_controller_parameters
end

#loggerObject

Returns the value of attribute logger.



13
14
15
# File 'lib/logstasher.rb', line 13

def logger
  @logger
end

#logger_pathObject

Returns the value of attribute logger_path.



13
14
15
# File 'lib/logstasher.rb', line 13

def logger_path
  @logger_path
end

#sourceObject

Returns the value of attribute source.



13
14
15
# File 'lib/logstasher.rb', line 13

def source
  @source
end

Instance Method Details

#add_custom_fields(&block) ⇒ Object



54
55
56
57
58
59
60
61
# File 'lib/logstasher.rb', line 54

def add_custom_fields(&block)
  wrapped_block = Proc.new do |fields|
    LogStasher.custom_fields.concat(LogStasher.store.keys)
    instance_exec(fields, &block)
  end
  ActionController::Metal.send(:define_method, :logtasher_add_custom_fields_to_payload, &wrapped_block)
  ActionController::Base.send(:define_method, :logtasher_add_custom_fields_to_payload, &wrapped_block)
end

#add_custom_fields_to_request_context(&block) ⇒ Object



63
64
65
66
67
68
69
70
# File 'lib/logstasher.rb', line 63

def add_custom_fields_to_request_context(&block)
  wrapped_block = Proc.new do |fields|
    instance_exec(fields, &block)
    LogStasher.custom_fields.concat(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



43
44
45
46
47
48
49
50
51
52
# File 'lib/logstasher.rb', line 43

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']
  self.custom_fields += [:ip, :route, :request_id]
  if self.log_controller_parameters
    payload[:parameters] = payload[:params].except(*ActionController::LogSubscriber::INTERNAL_PARAMS)
    self.custom_fields += [:parameters]
  end
end

#add_default_fields_to_request_context(request) ⇒ Object



72
73
74
# File 'lib/logstasher.rb', line 72

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



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

def build_logstash_event(data, tags)
  LogStash::Event.new(data.merge('source' => self.source, 'tags' => tags))
end

#clear_request_contextObject



76
77
78
# File 'lib/logstasher.rb', line 76

def clear_request_context
  request_context.clear
end

#configured_to_suppress_app_logs?(app) ⇒ Boolean

Returns:

  • (Boolean)


104
105
106
107
# File 'lib/logstasher.rb', line 104

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

#custom_fieldsObject



109
110
111
# File 'lib/logstasher.rb', line 109

def custom_fields
  Thread.current[:logstasher_custom_fields] ||= []
end

#custom_fields=(val) ⇒ Object



113
114
115
# File 'lib/logstasher.rb', line 113

def custom_fields=(val)
  Thread.current[:logstasher_custom_fields] = val
end

#enabled?Boolean

Returns:

  • (Boolean)


161
162
163
# File 'lib/logstasher.rb', line 161

def enabled?
  self.enabled
end

#log(severity, msg) ⇒ Object



117
118
119
120
121
122
123
124
125
126
127
# File 'lib/logstasher.rb', line 117

def log(severity, msg)
  if self.logger && self.logger.send("#{severity}?")
    data = {'level' => severity}
    if msg.respond_to?(:to_hash)
      data.merge!(msg.to_hash)
    else
      data['message'] = msg
    end
    self.logger << build_logstash_event(data, ['log']).to_json + "\n"
  end
end

#remove_existing_log_subscriptionsObject



19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/logstasher.rb', line 19

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)
    end
  end
end

#request_contextObject



141
142
143
# File 'lib/logstasher.rb', line 141

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

#setup(app) ⇒ Object



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/logstasher.rb', line 80

def setup(app)
  app.config.action_dispatch.rack_cache[:verbose] = false if app.config.action_dispatch.rack_cache
  # Path instrumentation class to insert our hook
  require 'logstasher/rails_ext/action_controller/metal/instrumentation'
  require 'logstash-event'
  self.suppress_app_logs(app)
  LogStasher::RequestLogSubscriber.attach_to :action_controller
  LogStasher::MailerLogSubscriber.attach_to :action_mailer
  self.logger_path = app.config.logstasher.logger_path || "#{Rails.root}/log/logstash_#{Rails.env}.log"
  self.logger = app.config.logstasher.logger || new_logger(self.logger_path)
  self.logger.level = app.config.logstasher.log_level || Logger::WARN
  self.source = app.config.logstasher.source unless app.config.logstasher.source.nil?
  self.enabled = true
  self.log_controller_parameters = !! app.config.logstasher.log_controller_parameters
  self.backtrace = !! app.config.logstasher.backtrace unless app.config.logstasher.backtrace.nil?
end

#storeObject



133
134
135
136
137
138
139
# File 'lib/logstasher.rb', line 133

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(app) ⇒ Object



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

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

#unsubscribe(component, subscriber) ⇒ Object



32
33
34
35
36
37
38
39
40
41
# File 'lib/logstasher.rb', line 32

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|
      if listener.instance_variable_get('@delegate') == subscriber
        ActiveSupport::Notifications.unsubscribe listener
      end
    end
  end
end

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



145
146
147
148
149
150
151
# File 'lib/logstasher.rb', line 145

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