logstasher 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +4 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +9 -0
- data/Guardfile +9 -0
- data/README.md +3 -0
- data/Rakefile +1 -0
- data/lib/logstasher.rb +52 -0
- data/lib/logstasher/log_subscriber.rb +92 -0
- data/lib/logstasher/rails_ext/action_controller/metal/instrumentation.rb +31 -0
- data/lib/logstasher/rails_ext/rack/logger.rb +24 -0
- data/lib/logstasher/railtie.rb +14 -0
- data/lib/logstasher/version.rb +3 -0
- data/logstasher.gemspec +27 -0
- data/spec/logstasher_logsubscriber_spec.rb +350 -0
- data/spec/logstasher_spec.rb +40 -0
- data/spec/spec_helper.rb +13 -0
- metadata +145 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
data/README.md
ADDED
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/lib/logstasher.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'logstasher/version'
|
2
|
+
require 'logstasher/log_subscriber'
|
3
|
+
require 'active_support/core_ext/module/attribute_accessors'
|
4
|
+
require 'active_support/core_ext/string/inflections'
|
5
|
+
require 'active_support/ordered_options'
|
6
|
+
|
7
|
+
module Logstasher
|
8
|
+
# Logger for the logstash logs
|
9
|
+
mattr_accessor :logger, :enabled
|
10
|
+
|
11
|
+
# Set the options for the adding cutom data to payload
|
12
|
+
mattr_accessor :payload_appender
|
13
|
+
|
14
|
+
def self.append_payload(&block)
|
15
|
+
self.payload_appender = block
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.remove_existing_log_subscriptions
|
19
|
+
ActiveSupport::LogSubscriber.log_subscribers.each do |subscriber|
|
20
|
+
case subscriber
|
21
|
+
when ActionView::LogSubscriber
|
22
|
+
unsubscribe(:action_view, subscriber)
|
23
|
+
when ActionController::LogSubscriber
|
24
|
+
unsubscribe(:action_controller, subscriber)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.unsubscribe(component, subscriber)
|
30
|
+
events = subscriber.public_methods(false).reject{ |method| method.to_s == 'call' }
|
31
|
+
events.each do |event|
|
32
|
+
ActiveSupport::Notifications.notifier.listeners_for("#{event}.#{component}").each do |listener|
|
33
|
+
if listener.instance_variable_get('@delegate') == subscriber
|
34
|
+
ActiveSupport::Notifications.unsubscribe listener
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.setup(app)
|
41
|
+
Logstasher.enabled = true
|
42
|
+
app.config.action_dispatch.rack_cache[:verbose] = false if app.config.action_dispatch.rack_cache
|
43
|
+
require 'logstasher/rails_ext/rack/logger'
|
44
|
+
require 'logstasher/rails_ext/action_controller/metal/instrumentation'
|
45
|
+
require 'logstash/event'
|
46
|
+
Logstasher.remove_existing_log_subscriptions
|
47
|
+
Logstasher::RequestLogSubscriber.attach_to :action_controller
|
48
|
+
self.logger = app.config.logstasher.logger || Logger.new("#{Rails.root}/log/logstash.log")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
require 'logstasher/railtie' if defined?(Rails)
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'active_support/core_ext/class/attribute'
|
2
|
+
require 'active_support/log_subscriber'
|
3
|
+
|
4
|
+
module Logstasher
|
5
|
+
class RequestLogSubscriber < ActiveSupport::LogSubscriber
|
6
|
+
def process_action(event)
|
7
|
+
payload = event.payload
|
8
|
+
|
9
|
+
data = extract_request(payload)
|
10
|
+
data.merge! extract_status(payload)
|
11
|
+
data.merge! runtimes(event)
|
12
|
+
data.merge! location(event)
|
13
|
+
data.merge! extract_exception(payload)
|
14
|
+
data.merge! extract_appended_params(payload)
|
15
|
+
|
16
|
+
event = LogStash::Event.new("@fields" => data)
|
17
|
+
Logstasher.logger.info event.to_json
|
18
|
+
end
|
19
|
+
|
20
|
+
def redirect_to(event)
|
21
|
+
Thread.current[:logstasher_location] = event.payload[:location]
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def extract_request(payload)
|
27
|
+
{
|
28
|
+
:method => payload[:method],
|
29
|
+
:path => extract_path(payload),
|
30
|
+
:format => extract_format(payload),
|
31
|
+
:controller => payload[:controller],
|
32
|
+
:action => payload[:action]
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
def extract_path(payload)
|
37
|
+
payload[:path].split("?").first
|
38
|
+
end
|
39
|
+
|
40
|
+
def extract_format(payload)
|
41
|
+
if ::ActionPack::VERSION::MAJOR == 3 && ::ActionPack::VERSION::MINOR == 0
|
42
|
+
payload[:formats].first
|
43
|
+
else
|
44
|
+
payload[:format]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def extract_status(payload)
|
49
|
+
if payload[:status]
|
50
|
+
{ :status => payload[:status].to_i }
|
51
|
+
else
|
52
|
+
{}
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def runtimes(event)
|
57
|
+
{
|
58
|
+
:duration => event.duration,
|
59
|
+
:view => event.payload[:view_runtime],
|
60
|
+
:db => event.payload[:db_runtime]
|
61
|
+
}.inject({}) do |runtimes, (name, runtime)|
|
62
|
+
runtimes[name] = runtime.to_f.round(2) if runtime
|
63
|
+
runtimes
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def location(event)
|
68
|
+
if location = Thread.current[:logstasher_location]
|
69
|
+
Thread.current[:logstasher_location] = nil
|
70
|
+
{ :location => location }
|
71
|
+
else
|
72
|
+
{}
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Monkey patching to enable exception logging
|
77
|
+
def extract_exception(payload)
|
78
|
+
if payload[:exception]
|
79
|
+
exception, message = payload[:exception]
|
80
|
+
message = "#{exception} : #{message}\n #{($!.backtrace.join("\n"))}"
|
81
|
+
{ :status => 500, :error => message }
|
82
|
+
else
|
83
|
+
{}
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def extract_appended_params(payload)
|
88
|
+
appended_keys = payload.delete(:log_stasher_appended_param_keys)
|
89
|
+
(appended_keys && payload.extract!(*appended_keys)) || {}
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module ActionController
|
2
|
+
module Instrumentation
|
3
|
+
def process_action(*args)
|
4
|
+
raw_payload = {
|
5
|
+
:controller => self.class.name,
|
6
|
+
:action => self.action_name,
|
7
|
+
:params => request.filtered_parameters,
|
8
|
+
:format => request.format.try(:ref),
|
9
|
+
:method => request.method,
|
10
|
+
:path => (request.fullpath rescue "unknown")
|
11
|
+
}
|
12
|
+
|
13
|
+
if Logstasher.payload_appender
|
14
|
+
before_keys = raw_payload.keys.clone
|
15
|
+
Logstasher.payload_appender.call(self, request, raw_payload)
|
16
|
+
after_keys = raw_payload.keys
|
17
|
+
raw_payload[:log_stasher_appended_param_keys] = after_keys - before_keys
|
18
|
+
end
|
19
|
+
|
20
|
+
ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload.dup)
|
21
|
+
|
22
|
+
ActiveSupport::Notifications.instrument("process_action.action_controller", raw_payload) do |payload|
|
23
|
+
result = super
|
24
|
+
payload[:status] = response.status
|
25
|
+
append_info_to_payload(payload)
|
26
|
+
result
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'rails/rack/logger'
|
2
|
+
|
3
|
+
module Rails
|
4
|
+
module Rack
|
5
|
+
# Overwrites defaults of Rails::Rack::Logger that cause
|
6
|
+
# unnecessary logging.
|
7
|
+
# This effectively removes the log lines from the log
|
8
|
+
# that say:
|
9
|
+
# Started GET / for 192.168.2.1...
|
10
|
+
class Logger
|
11
|
+
# Overwrites Rails 3.2 code that logs new requests
|
12
|
+
def call_app(*args)
|
13
|
+
env = args.last
|
14
|
+
@app.call(env)
|
15
|
+
ensure
|
16
|
+
ActiveSupport::LogSubscriber.flush_all!
|
17
|
+
end
|
18
|
+
|
19
|
+
# Overwrites Rails 3.0/3.1 code that logs new requests
|
20
|
+
def before_dispatch(env)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rails/railtie'
|
2
|
+
require 'action_view/log_subscriber'
|
3
|
+
require 'action_controller/log_subscriber'
|
4
|
+
|
5
|
+
module Logstasher
|
6
|
+
class Railtie < Rails::Railtie
|
7
|
+
config.logstasher = ActiveSupport::OrderedOptions.new
|
8
|
+
config.logstasher.enabled = false
|
9
|
+
|
10
|
+
initializer :logstasher do |app|
|
11
|
+
Logstasher.setup(app) if app.config.logstasher.enabled
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/logstasher.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "logstasher/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "logstasher"
|
7
|
+
s.version = Logstasher::VERSION
|
8
|
+
s.authors = ["Shadab Ahmed"]
|
9
|
+
s.email = ["shadab.ansari@gmail.com"]
|
10
|
+
s.homepage = "https://github.com/shadabahmed/logstasher"
|
11
|
+
s.summary = %q{Awesome rails logs}
|
12
|
+
s.description = %q{Awesome rails logs}
|
13
|
+
|
14
|
+
s.rubyforge_project = "logstasher"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
s.add_runtime_dependency "logstash-event"
|
21
|
+
|
22
|
+
# specify any dependencies here; for example:
|
23
|
+
s.add_development_dependency "rspec"
|
24
|
+
s.add_development_dependency "guard-rspec"
|
25
|
+
s.add_runtime_dependency "activesupport"
|
26
|
+
s.add_runtime_dependency "actionpack"
|
27
|
+
end
|
@@ -0,0 +1,350 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'logstasher'
|
3
|
+
require 'logstasher/log_subscriber'
|
4
|
+
require 'active_support/notifications'
|
5
|
+
require 'active_support/core_ext/string'
|
6
|
+
require 'logger'
|
7
|
+
|
8
|
+
describe Logstasher::RequestLogSubscriber do
|
9
|
+
let(:log_output) {StringIO.new}
|
10
|
+
let(:logger) {
|
11
|
+
logger = Logger.new(log_output)
|
12
|
+
logger.formatter = ->(_, _, _, msg) {
|
13
|
+
msg
|
14
|
+
}
|
15
|
+
logger
|
16
|
+
}
|
17
|
+
before do
|
18
|
+
Logstasher::RequestLogSubscriber.logger = logger
|
19
|
+
end
|
20
|
+
|
21
|
+
let(:subscriber) {Logstasher::RequestLogSubscriber.new}
|
22
|
+
let(:event) {
|
23
|
+
ActiveSupport::Notifications::Event.new(
|
24
|
+
'process_action.action_controller', Time.now, Time.now, 2, {
|
25
|
+
status: 200, format: 'application/json', method: 'GET', path: '/home?foo=bar', params: {
|
26
|
+
'controller' => 'home', 'action' => 'index', 'foo' => 'bar'
|
27
|
+
}, db_runtime: 0.02, view_runtime: 0.01
|
28
|
+
}
|
29
|
+
)
|
30
|
+
}
|
31
|
+
|
32
|
+
let(:redirect) {
|
33
|
+
ActiveSupport::Notifications::Event.new(
|
34
|
+
'redirect_to.action_controller', Time.now, Time.now, 1, location: 'http://example.com', status: 302
|
35
|
+
)
|
36
|
+
}
|
37
|
+
|
38
|
+
describe "when processing an action with logstasher output" do
|
39
|
+
before do
|
40
|
+
Logstasher::log_format = :logstasher
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should include the URL in the log output" do
|
44
|
+
subscriber.process_action(event)
|
45
|
+
log_output.string.should include('/home')
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should not include the query string in the url" do
|
49
|
+
subscriber.process_action(event)
|
50
|
+
log_output.string.should_not include('?foo=bar')
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should start the log line with the HTTP method" do
|
54
|
+
subscriber.process_action(event)
|
55
|
+
log_output.string.starts_with?('method=GET ').should == true
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should include the status code" do
|
59
|
+
subscriber.process_action(event)
|
60
|
+
log_output.string.should include('status=200 ')
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should include the controller and action" do
|
64
|
+
subscriber.process_action(event)
|
65
|
+
log_output.string.should include('controller=home action=index')
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should include the duration" do
|
69
|
+
subscriber.process_action(event)
|
70
|
+
log_output.string.should =~ /duration=[\.0-9]{4,4} /
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should include the view rendering time" do
|
74
|
+
subscriber.process_action(event)
|
75
|
+
log_output.string.should =~ /view=0.01 /
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should include the database rendering time" do
|
79
|
+
subscriber.process_action(event)
|
80
|
+
log_output.string.should =~ /db=0.02/
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should add a 500 status when an exception occurred" do
|
84
|
+
event.payload[:status] = nil
|
85
|
+
event.payload[:exception] = ['AbstractController::ActionNotFound', 'Route not found']
|
86
|
+
subscriber.process_action(event)
|
87
|
+
log_output.string.should =~ /status=500 /
|
88
|
+
log_output.string.should =~ /error='AbstractController::ActionNotFound:Route not found' /
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should return an unknown status when no status or exception is found" do
|
92
|
+
event.payload[:status] = nil
|
93
|
+
event.payload[:exception] = nil
|
94
|
+
subscriber.process_action(event)
|
95
|
+
log_output.string.should =~ /status=0 /
|
96
|
+
end
|
97
|
+
|
98
|
+
describe "with a redirect" do
|
99
|
+
before do
|
100
|
+
Thread.current[:logstasher_location] = "http://www.example.com"
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should add the location to the log line" do
|
104
|
+
subscriber.process_action(event)
|
105
|
+
log_output.string.should =~ %r{ location=http://www.example.com}
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should remove the thread local variable" do
|
109
|
+
subscriber.process_action(event)
|
110
|
+
Thread.current[:logstasher_location].should == nil
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should not include a location by default" do
|
115
|
+
subscriber.process_action(event)
|
116
|
+
log_output.string.should_not =~ /location=/
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
describe "when processing an action with logstash output" do
|
121
|
+
before do
|
122
|
+
require 'logstash-event'
|
123
|
+
Logstasher::log_format = :logstash
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should include the URL in the log output" do
|
127
|
+
subscriber.process_action(event)
|
128
|
+
log_output.string.should include('/home')
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should start include the HTTP method" do
|
132
|
+
subscriber.process_action(event)
|
133
|
+
log_output.string.should include('"method":"GET"')
|
134
|
+
end
|
135
|
+
|
136
|
+
it "should include the status code" do
|
137
|
+
subscriber.process_action(event)
|
138
|
+
log_output.string.should include('"status":200')
|
139
|
+
end
|
140
|
+
|
141
|
+
it "should include the controller and action" do
|
142
|
+
subscriber.process_action(event)
|
143
|
+
log_output.string.should include('"controller":"home"')
|
144
|
+
log_output.string.should include('"action":"index"')
|
145
|
+
end
|
146
|
+
|
147
|
+
it "should include the duration" do
|
148
|
+
subscriber.process_action(event)
|
149
|
+
log_output.string.should =~ /"duration":\d+\.\d{0,2}/
|
150
|
+
end
|
151
|
+
|
152
|
+
it "should include the view rendering time" do
|
153
|
+
subscriber.process_action(event)
|
154
|
+
log_output.string.should =~ /"view":0.01/
|
155
|
+
end
|
156
|
+
|
157
|
+
it "should include the database rendering time" do
|
158
|
+
subscriber.process_action(event)
|
159
|
+
log_output.string.should =~ /"db":0.02/
|
160
|
+
end
|
161
|
+
|
162
|
+
it "should add a 500 status when an exception occurred" do
|
163
|
+
event.payload[:status] = nil
|
164
|
+
event.payload[:exception] = ['AbstractController::ActionNotFound', 'Route not found']
|
165
|
+
subscriber.process_action(event)
|
166
|
+
log_output.string.should =~ /"status":500/
|
167
|
+
log_output.string.should =~ /"error":"AbstractController::ActionNotFound:Route not found"/
|
168
|
+
end
|
169
|
+
|
170
|
+
it "should return an unknown status when no status or exception is found" do
|
171
|
+
event.payload[:status] = nil
|
172
|
+
event.payload[:exception] = nil
|
173
|
+
subscriber.process_action(event)
|
174
|
+
log_output.string.should =~ /"status":0/
|
175
|
+
end
|
176
|
+
|
177
|
+
describe "with a redirect" do
|
178
|
+
before do
|
179
|
+
Thread.current[:logstasher_location] = "http://www.example.com"
|
180
|
+
end
|
181
|
+
|
182
|
+
it "should add the location to the log line" do
|
183
|
+
subscriber.process_action(event)
|
184
|
+
log_output.string.should =~ %r{"location":"http://www.example.com"}
|
185
|
+
end
|
186
|
+
|
187
|
+
it "should remove the thread local variable" do
|
188
|
+
subscriber.process_action(event)
|
189
|
+
Thread.current[:logstasher_location].should == nil
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
it "should not include a location by default" do
|
194
|
+
subscriber.process_action(event)
|
195
|
+
log_output.string.should_not =~ /"location":/
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
describe "when processing an action with graylog2 output" do
|
200
|
+
before do
|
201
|
+
Logstasher::log_format = :graylog2
|
202
|
+
end
|
203
|
+
|
204
|
+
it "should include the URL in the log output" do
|
205
|
+
subscriber.process_action(event)
|
206
|
+
log_output.string.should include(':_path=>"/home"')
|
207
|
+
end
|
208
|
+
|
209
|
+
it "should start include the HTTP method" do
|
210
|
+
subscriber.process_action(event)
|
211
|
+
log_output.string.should include(':_method=>"GET"')
|
212
|
+
end
|
213
|
+
|
214
|
+
it "should include the status code" do
|
215
|
+
subscriber.process_action(event)
|
216
|
+
log_output.string.should include(':_status=>200') end
|
217
|
+
|
218
|
+
it "should include the controller and action" do
|
219
|
+
subscriber.process_action(event)
|
220
|
+
log_output.string.should include(':_controller=>"home"')
|
221
|
+
log_output.string.should include(':_action=>"index"')
|
222
|
+
end
|
223
|
+
|
224
|
+
it "should include the duration" do
|
225
|
+
subscriber.process_action(event)
|
226
|
+
log_output.string.should =~ /:_duration=>\d+\.\d{0,2}/
|
227
|
+
end
|
228
|
+
|
229
|
+
it "should include the view rendering time" do
|
230
|
+
subscriber.process_action(event)
|
231
|
+
log_output.string.should include(':_view=>0.01')
|
232
|
+
end
|
233
|
+
|
234
|
+
it "should include the database rendering time" do
|
235
|
+
subscriber.process_action(event)
|
236
|
+
log_output.string.should include(':_db=>0.02')
|
237
|
+
end
|
238
|
+
|
239
|
+
it "should add a 500 status when an exception occurred" do
|
240
|
+
event.payload[:status] = nil
|
241
|
+
event.payload[:exception] = ['AbstractController::ActionNotFound', 'Route not found']
|
242
|
+
subscriber.process_action(event)
|
243
|
+
log_output.string.should include(':_status=>500')
|
244
|
+
log_output.string.should include(':_error=>"AbstractController::ActionNotFound:Route not found"')
|
245
|
+
end
|
246
|
+
|
247
|
+
it "should return an unknown status when no status or exception is found" do
|
248
|
+
event.payload[:status] = nil
|
249
|
+
event.payload[:exception] = nil
|
250
|
+
subscriber.process_action(event)
|
251
|
+
log_output.string.should include(':_status=>0')
|
252
|
+
end
|
253
|
+
|
254
|
+
describe "with a redirect" do
|
255
|
+
before do
|
256
|
+
Thread.current[:logstasher_location] = "http://www.example.com"
|
257
|
+
end
|
258
|
+
|
259
|
+
it "should add the location to the log line" do
|
260
|
+
subscriber.process_action(event)
|
261
|
+
log_output.string.should include(':_location=>"http://www.example.com"')
|
262
|
+
end
|
263
|
+
|
264
|
+
it "should remove the thread local variable" do
|
265
|
+
subscriber.process_action(event)
|
266
|
+
Thread.current[:logstasher_location].should == nil
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
it "should not include a location by default" do
|
271
|
+
subscriber.process_action(event)
|
272
|
+
log_output.string.should_not =~ /"location":/
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
describe "with custom_options configured for graylog2 output" do
|
277
|
+
before do
|
278
|
+
Logstasher::log_format = :graylog2
|
279
|
+
end
|
280
|
+
|
281
|
+
it "should combine the hash properly for the output" do
|
282
|
+
Logstasher.custom_options = {:data => "value"}
|
283
|
+
subscriber.process_action(event)
|
284
|
+
log_output.string.should include(':_data=>"value"')
|
285
|
+
end
|
286
|
+
|
287
|
+
it "should combine the output of a lambda properly" do
|
288
|
+
Logstasher.custom_options = lambda {|event| {:data => "value"}}
|
289
|
+
subscriber.process_action(event)
|
290
|
+
log_output.string.should include(':_data=>"value"')
|
291
|
+
end
|
292
|
+
|
293
|
+
it "should work if the method returns nil" do
|
294
|
+
Logstasher.custom_options = lambda {|event| nil}
|
295
|
+
subscriber.process_action(event)
|
296
|
+
log_output.string.should be_present
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
describe "with custom_options configured for logstasher output" do
|
301
|
+
before do
|
302
|
+
Logstasher::log_format = :logstasher
|
303
|
+
end
|
304
|
+
|
305
|
+
it "should combine the hash properly for the output" do
|
306
|
+
Logstasher.custom_options = {:data => "value"}
|
307
|
+
subscriber.process_action(event)
|
308
|
+
log_output.string.should =~ / data=value/
|
309
|
+
end
|
310
|
+
it "should combine the output of a lambda properly" do
|
311
|
+
Logstasher.custom_options = lambda {|event| {:data => "value"}}
|
312
|
+
subscriber.process_action(event)
|
313
|
+
log_output.string.should =~ / data=value/
|
314
|
+
end
|
315
|
+
it "should work if the method returns nil" do
|
316
|
+
Logstasher.custom_options = lambda {|event| nil}
|
317
|
+
subscriber.process_action(event)
|
318
|
+
log_output.string.should be_present
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
describe "with custom_options configured for logstash output" do
|
323
|
+
before do
|
324
|
+
Logstasher::log_format = :logstash
|
325
|
+
end
|
326
|
+
|
327
|
+
it "should combine the hash properly for the output" do
|
328
|
+
Logstasher.custom_options = {:data => "value"}
|
329
|
+
subscriber.process_action(event)
|
330
|
+
log_output.string.should =~ /"data":"value"/
|
331
|
+
end
|
332
|
+
it "should combine the output of a lambda properly" do
|
333
|
+
Logstasher.custom_options = lambda {|event| {:data => "value"}}
|
334
|
+
subscriber.process_action(event)
|
335
|
+
log_output.string.should =~ /"data":"value"/
|
336
|
+
end
|
337
|
+
it "should work if the method returns nil" do
|
338
|
+
Logstasher.custom_options = lambda {|event| nil}
|
339
|
+
subscriber.process_action(event)
|
340
|
+
log_output.string.should be_present
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
describe "when processing a redirect" do
|
345
|
+
it "should store the location in a thread local variable" do
|
346
|
+
subscriber.redirect_to(redirect)
|
347
|
+
Thread.current[:logstasher_location].should == "http://example.com"
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'logstasher'
|
3
|
+
require 'active_support/notifications'
|
4
|
+
require 'active_support/core_ext/string'
|
5
|
+
require 'active_support/log_subscriber'
|
6
|
+
require 'action_controller/log_subscriber'
|
7
|
+
require 'action_view/log_subscriber'
|
8
|
+
|
9
|
+
describe Logstasher do
|
10
|
+
describe "when removing Rails' log subscribers" do
|
11
|
+
after do
|
12
|
+
ActionController::LogSubscriber.attach_to :action_controller
|
13
|
+
ActionView::LogSubscriber.attach_to :action_view
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should remove subscribers for controller events" do
|
17
|
+
expect {
|
18
|
+
Logstasher.remove_existing_log_subscriptions
|
19
|
+
}.to change {
|
20
|
+
ActiveSupport::Notifications.notifier.listeners_for('process_action.action_controller')
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should remove subscribers for all events" do
|
25
|
+
expect {
|
26
|
+
Logstasher.remove_existing_log_subscriptions
|
27
|
+
}.to change {
|
28
|
+
ActiveSupport::Notifications.notifier.listeners_for('render_template.action_view')
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
it "shouldn't remove subscribers that aren't from Rails" do
|
33
|
+
blk = -> {}
|
34
|
+
ActiveSupport::Notifications.subscribe("process_action.action_controller", &blk)
|
35
|
+
Logstasher.remove_existing_log_subscriptions
|
36
|
+
listeners = ActiveSupport::Notifications.notifier.listeners_for('process_action.action_controller')
|
37
|
+
listeners.size.should > 0
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
2
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
3
|
+
# Require this file using `require "spec_helper.rb"` to ensure that it is only
|
4
|
+
# loaded once.
|
5
|
+
#
|
6
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
7
|
+
require 'action_pack'
|
8
|
+
|
9
|
+
RSpec.configure do |config|
|
10
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
11
|
+
config.run_all_when_everything_filtered = true
|
12
|
+
config.filter_run :focus
|
13
|
+
end
|
metadata
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: logstasher
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Shadab Ahmed
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-04-06 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: logstash-event
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rspec
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: guard-rspec
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: activesupport
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: actionpack
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :runtime
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
description: Awesome rails logs
|
95
|
+
email:
|
96
|
+
- shadab.ansari@gmail.com
|
97
|
+
executables: []
|
98
|
+
extensions: []
|
99
|
+
extra_rdoc_files: []
|
100
|
+
files:
|
101
|
+
- .gitignore
|
102
|
+
- .rspec
|
103
|
+
- .travis.yml
|
104
|
+
- Gemfile
|
105
|
+
- Guardfile
|
106
|
+
- README.md
|
107
|
+
- Rakefile
|
108
|
+
- lib/logstasher.rb
|
109
|
+
- lib/logstasher/log_subscriber.rb
|
110
|
+
- lib/logstasher/rails_ext/action_controller/metal/instrumentation.rb
|
111
|
+
- lib/logstasher/rails_ext/rack/logger.rb
|
112
|
+
- lib/logstasher/railtie.rb
|
113
|
+
- lib/logstasher/version.rb
|
114
|
+
- logstasher.gemspec
|
115
|
+
- spec/logstasher_logsubscriber_spec.rb
|
116
|
+
- spec/logstasher_spec.rb
|
117
|
+
- spec/spec_helper.rb
|
118
|
+
homepage: https://github.com/shadabahmed/logstasher
|
119
|
+
licenses: []
|
120
|
+
post_install_message:
|
121
|
+
rdoc_options: []
|
122
|
+
require_paths:
|
123
|
+
- lib
|
124
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
125
|
+
none: false
|
126
|
+
requirements:
|
127
|
+
- - ! '>='
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
version: '0'
|
130
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
131
|
+
none: false
|
132
|
+
requirements:
|
133
|
+
- - ! '>='
|
134
|
+
- !ruby/object:Gem::Version
|
135
|
+
version: '0'
|
136
|
+
requirements: []
|
137
|
+
rubyforge_project: logstasher
|
138
|
+
rubygems_version: 1.8.25
|
139
|
+
signing_key:
|
140
|
+
specification_version: 3
|
141
|
+
summary: Awesome rails logs
|
142
|
+
test_files:
|
143
|
+
- spec/logstasher_logsubscriber_spec.rb
|
144
|
+
- spec/logstasher_spec.rb
|
145
|
+
- spec/spec_helper.rb
|