logstasher 0.5.3 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/README.md +10 -1
- data/lib/logstasher.rb +34 -3
- data/lib/logstasher/log_subscriber.rb +31 -0
- data/lib/logstasher/rails_ext/action_controller/metal/instrumentation.rb +11 -0
- data/lib/logstasher/version.rb +1 -1
- data/logstasher.gemspec +1 -1
- data/spec/lib/logstasher/device/redis_spec.rb +11 -11
- data/spec/lib/logstasher/instrumentation_spec.rb +62 -0
- data/spec/lib/logstasher/log_subscriber_spec.rb +109 -34
- data/spec/lib/logstasher_spec.rb +84 -36
- data/spec/spec_helper.rb +3 -1
- data/spec/support/sample_mailer.rb +16 -0
- metadata +9 -5
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
MmUyOWU0M2UwMGY1YzRjZDU0ZDRmNGExM2E2YTZjZDVhMGEyYWRjMQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
NGI2M2M3NGNjYjc2OGZjMWI0NGU3MTM3ZjljMDY5ZGNhYTk5OTYxNQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
YjM5YzhmMGU0YTcyYTAxMWMxYzAxZWE5NTgyN2RmYTM3MWM0ZDE0MTY1YWQ2
|
10
|
+
YzIzOWQ4MjFkODA0NDMwNDk3YWY4MmNkYzY1M2ViNmU4NjZmYzdjMDZlMGU2
|
11
|
+
ZGM2MGY0ZmNjYzk3NTYwODRiMTc3ZDg1OTYzZWFiMTgzNmEyZjA=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
NTk2MDFmNGM4MDFlOGE2OGI1NjE3MDMwZmZmYjRlZTM0NDQwMTU1OGZiMzBl
|
14
|
+
N2VlYzViNzQ2MDFjODk4OWYyYThhZGRjNzUyMDRhNzBiZjU3YTdhM2E0ZjJj
|
15
|
+
ZTRlYTA2Y2UyM2UzN2M0YTY5MDFkY2IyNzk1NmY5MzM5MjJlOGQ=
|
data/README.md
CHANGED
@@ -94,6 +94,15 @@ Since some fields are very specific to your application for e.g. *user_name*, so
|
|
94
94
|
end
|
95
95
|
end
|
96
96
|
|
97
|
+
## Logging ActionMailer events
|
98
|
+
|
99
|
+
Logstasher can easily log messages from `ActionMailer`, such as incoming/outgoing e-mails and e-mail content generation (Rails >= 4.1).
|
100
|
+
This functionality is automatically enabled. Since the relationship between a concrete HTTP request and a mailer invocation is lost
|
101
|
+
once in an `ActionMailer` instance method, global (per-request) state is kept to correlate HTTP requests and events from other parts
|
102
|
+
of rails, such as `ActionMailer`. Every time a request is invoked, a `request_id` key is added which is present on every `ActionMailer` event.
|
103
|
+
|
104
|
+
Note: Since mailers are executed within the lifetime of a request, they will show up in logs prior to the actual request.
|
105
|
+
|
97
106
|
## Listening to `ActiveSupport::Notifications` events
|
98
107
|
|
99
108
|
It is possible to listen to any `ActiveSupport::Notifications` events and store arbitrary data to be included in the final JSON log entry:
|
@@ -135,7 +144,7 @@ You can easily share the same store between different types of notifications, by
|
|
135
144
|
java -jar logstash-1.3.3-flatjar.jar agent -f quickstart.conf -- web
|
136
145
|
```
|
137
146
|
* Visit http://localhost:9292/ to see the Kibana interface and your parsed logs
|
138
|
-
* For advanced options see the latest logstash documentation at [logstash.net](http://www.logstash.net/) or visit my blog at
|
147
|
+
* For advanced options see the latest logstash documentation at [logstash.net](http://www.logstash.net/) or visit my blog at [shadabahmed.com](http://shadabahmed.com/blog/2013/04/30/logstasher-for-awesome-rails-logging) (slightly outdated but will sure give you ideas for distributed setup etc.)
|
139
148
|
|
140
149
|
## Versions
|
141
150
|
All versions require Rails 3.0.x and higher and Ruby 1.9.2+. Tested on Rails 4 and Ruby 2.0
|
data/lib/logstasher.rb
CHANGED
@@ -8,8 +8,9 @@ require 'active_support/ordered_options'
|
|
8
8
|
module LogStasher
|
9
9
|
extend self
|
10
10
|
STORE_KEY = :logstasher_data
|
11
|
+
REQUEST_CONTEXT_KEY = :logstasher_request_context
|
11
12
|
|
12
|
-
attr_accessor :logger, :enabled, :log_controller_parameters, :source
|
13
|
+
attr_accessor :logger, :logger_path, :enabled, :log_controller_parameters, :source
|
13
14
|
# Setting the default to 'unknown' to define the default behaviour
|
14
15
|
@source = 'unknown'
|
15
16
|
|
@@ -20,6 +21,8 @@ module LogStasher
|
|
20
21
|
unsubscribe(:action_view, subscriber)
|
21
22
|
when ActionController::LogSubscriber
|
22
23
|
unsubscribe(:action_controller, subscriber)
|
24
|
+
when ActionMailer::LogSubscriber
|
25
|
+
unsubscribe(:action_mailer, subscriber)
|
23
26
|
end
|
24
27
|
end
|
25
28
|
end
|
@@ -38,7 +41,8 @@ module LogStasher
|
|
38
41
|
def add_default_fields_to_payload(payload, request)
|
39
42
|
payload[:ip] = request.remote_ip
|
40
43
|
payload[:route] = "#{request.params[:controller]}##{request.params[:action]}"
|
41
|
-
|
44
|
+
payload[:request_id] = request.env['action_dispatch.request_id']
|
45
|
+
self.custom_fields += [:ip, :route, :request_id]
|
42
46
|
if self.log_controller_parameters
|
43
47
|
payload[:parameters] = payload[:params].except(*ActionController::LogSubscriber::INTERNAL_PARAMS)
|
44
48
|
self.custom_fields += [:parameters]
|
@@ -54,6 +58,23 @@ module LogStasher
|
|
54
58
|
ActionController::Base.send(:define_method, :logtasher_add_custom_fields_to_payload, &wrapped_block)
|
55
59
|
end
|
56
60
|
|
61
|
+
def add_custom_fields_to_request_context(&block)
|
62
|
+
wrapped_block = Proc.new do |fields|
|
63
|
+
instance_exec(fields, &block)
|
64
|
+
LogStasher.custom_fields.concat(fields.keys)
|
65
|
+
end
|
66
|
+
ActionController::Metal.send(:define_method, :logstasher_add_custom_fields_to_request_context, &wrapped_block)
|
67
|
+
ActionController::Base.send(:define_method, :logstasher_add_custom_fields_to_request_context, &wrapped_block)
|
68
|
+
end
|
69
|
+
|
70
|
+
def add_default_fields_to_request_context(request)
|
71
|
+
request_context[:request_id] = request.env['action_dispatch.request_id']
|
72
|
+
end
|
73
|
+
|
74
|
+
def clear_request_context
|
75
|
+
request_context.clear
|
76
|
+
end
|
77
|
+
|
57
78
|
def setup(app)
|
58
79
|
app.config.action_dispatch.rack_cache[:verbose] = false if app.config.action_dispatch.rack_cache
|
59
80
|
# Path instrumentation class to insert our hook
|
@@ -61,7 +82,9 @@ module LogStasher
|
|
61
82
|
require 'logstash-event'
|
62
83
|
self.suppress_app_logs(app)
|
63
84
|
LogStasher::RequestLogSubscriber.attach_to :action_controller
|
64
|
-
|
85
|
+
LogStasher::MailerLogSubscriber.attach_to :action_mailer
|
86
|
+
self.logger_path = app.config.logstasher.logger_path || "#{Rails.root}/log/logstash_#{Rails.env}.log"
|
87
|
+
self.logger = app.config.logstasher.logger || new_logger(self.logger_path)
|
65
88
|
self.logger.level = app.config.logstasher.log_level || Logger::WARN
|
66
89
|
self.source = app.config.logstasher.source unless app.config.logstasher.source.nil?
|
67
90
|
self.enabled = true
|
@@ -103,6 +126,10 @@ module LogStasher
|
|
103
126
|
RequestStore.store[STORE_KEY]
|
104
127
|
end
|
105
128
|
|
129
|
+
def request_context
|
130
|
+
RequestStore.store[REQUEST_CONTEXT_KEY] ||= {}
|
131
|
+
end
|
132
|
+
|
106
133
|
def watch(event, opts = {}, &block)
|
107
134
|
event_group = opts[:event_group] || event
|
108
135
|
ActiveSupport::Notifications.subscribe(event) do |*args|
|
@@ -119,6 +146,10 @@ module LogStasher
|
|
119
146
|
EOM
|
120
147
|
end
|
121
148
|
|
149
|
+
def enabled?
|
150
|
+
self.enabled
|
151
|
+
end
|
152
|
+
|
122
153
|
private
|
123
154
|
|
124
155
|
def new_logger(path)
|
@@ -93,4 +93,35 @@ module LogStasher
|
|
93
93
|
custom_fields
|
94
94
|
end
|
95
95
|
end
|
96
|
+
|
97
|
+
class MailerLogSubscriber < ActiveSupport::LogSubscriber
|
98
|
+
MAILER_FIELDS = [:mailer, :action, :message_id, :from, :to]
|
99
|
+
|
100
|
+
def deliver(event)
|
101
|
+
process_event(event, ['mailer', 'deliver'])
|
102
|
+
end
|
103
|
+
|
104
|
+
def receive(event)
|
105
|
+
process_event(event, ['mailer', 'receive'])
|
106
|
+
end
|
107
|
+
|
108
|
+
def process(event)
|
109
|
+
process_event(event, ['mailer', 'process'])
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
def process_event(event, tags)
|
114
|
+
data = LogStasher.request_context.merge(extract_metadata(event.payload))
|
115
|
+
event = LogStash::Event.new('@source' => LogStasher.source, '@fields' => data, '@tags' => tags)
|
116
|
+
logger << event.to_json + "\n"
|
117
|
+
end
|
118
|
+
|
119
|
+
def extract_metadata(payload)
|
120
|
+
payload.slice(*MAILER_FIELDS)
|
121
|
+
end
|
122
|
+
|
123
|
+
def logger
|
124
|
+
LogStasher.logger
|
125
|
+
end
|
126
|
+
end
|
96
127
|
end
|
@@ -12,9 +12,16 @@ module ActionController
|
|
12
12
|
|
13
13
|
LogStasher.add_default_fields_to_payload(raw_payload, request)
|
14
14
|
|
15
|
+
LogStasher.clear_request_context
|
16
|
+
LogStasher.add_default_fields_to_request_context(request)
|
17
|
+
|
15
18
|
ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload.dup)
|
16
19
|
|
17
20
|
ActiveSupport::Notifications.instrument("process_action.action_controller", raw_payload) do |payload|
|
21
|
+
if self.respond_to?(:logstasher_add_custom_fields_to_request_context)
|
22
|
+
logstasher_add_custom_fields_to_request_context(LogStasher.request_context)
|
23
|
+
end
|
24
|
+
|
18
25
|
result = super
|
19
26
|
|
20
27
|
if self.respond_to?(:logtasher_add_custom_fields_to_payload)
|
@@ -30,6 +37,10 @@ module ActionController
|
|
30
37
|
LogStasher.store.each do |key, value|
|
31
38
|
payload[key] = value
|
32
39
|
end
|
40
|
+
|
41
|
+
LogStasher.request_context.each do |key, value|
|
42
|
+
payload[key] = value
|
43
|
+
end
|
33
44
|
result
|
34
45
|
end
|
35
46
|
end
|
data/lib/logstasher/version.rb
CHANGED
data/logstasher.gemspec
CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |s|
|
|
21
21
|
s.add_runtime_dependency "request_store"
|
22
22
|
|
23
23
|
# specify any dependencies here; for example:
|
24
|
-
s.add_development_dependency "rspec"
|
24
|
+
s.add_development_dependency "rspec", [">= 2.14"]
|
25
25
|
s.add_development_dependency("bundler", [">= 1.0.0"])
|
26
26
|
s.add_development_dependency("rails", [">= 3.0"])
|
27
27
|
end
|
@@ -13,28 +13,28 @@ describe LogStasher::Device::Redis do
|
|
13
13
|
|
14
14
|
it 'has default options' do
|
15
15
|
device = LogStasher::Device::Redis.new
|
16
|
-
device.options.
|
16
|
+
expect(device.options).to eq(default_options)
|
17
17
|
end
|
18
18
|
|
19
19
|
it 'creates a redis instance' do
|
20
|
-
::Redis.
|
20
|
+
expect(::Redis).to receive(:new).with({})
|
21
21
|
LogStasher::Device::Redis.new()
|
22
22
|
end
|
23
23
|
|
24
24
|
it 'assumes unknown options are for redis' do
|
25
|
-
::Redis.
|
25
|
+
expect(::Redis).to receive(:new).with(hash_including(db: '0'))
|
26
26
|
device = LogStasher::Device::Redis.new(db: '0')
|
27
|
-
device.redis_options.
|
27
|
+
expect(device.redis_options).to eq(db: '0')
|
28
28
|
end
|
29
29
|
|
30
30
|
it 'has a key' do
|
31
31
|
device = LogStasher::Device::Redis.new(key: 'the_key')
|
32
|
-
device.key.
|
32
|
+
expect(device.key).to eq('the_key')
|
33
33
|
end
|
34
34
|
|
35
35
|
it 'has a data_type' do
|
36
36
|
device = LogStasher::Device::Redis.new(data_type: 'channel')
|
37
|
-
device.data_type.
|
37
|
+
expect(device.data_type).to eq('channel')
|
38
38
|
end
|
39
39
|
|
40
40
|
it 'does not allow unsupported data types' do
|
@@ -45,13 +45,13 @@ describe LogStasher::Device::Redis do
|
|
45
45
|
|
46
46
|
it 'quits the redis connection on #close' do
|
47
47
|
device = LogStasher::Device::Redis.new
|
48
|
-
device.redis.
|
48
|
+
expect(device.redis).to receive(:quit)
|
49
49
|
device.close
|
50
50
|
end
|
51
51
|
|
52
52
|
it 'works as a logger device' do
|
53
53
|
device = LogStasher::Device::Redis.new
|
54
|
-
device.
|
54
|
+
expect(device).to receive(:write).with('blargh')
|
55
55
|
logger = Logger.new(device)
|
56
56
|
logger << 'blargh'
|
57
57
|
end
|
@@ -59,19 +59,19 @@ describe LogStasher::Device::Redis do
|
|
59
59
|
describe '#write' do
|
60
60
|
it "rpushes logs onto a list" do
|
61
61
|
device = LogStasher::Device::Redis.new(data_type: 'list')
|
62
|
-
device.redis.
|
62
|
+
expect(device.redis).to receive(:rpush).with('logstash', 'the log')
|
63
63
|
device.write('the log')
|
64
64
|
end
|
65
65
|
|
66
66
|
it "rpushes logs onto a custom key" do
|
67
67
|
device = LogStasher::Device::Redis.new(data_type: 'list', key: 'custom')
|
68
|
-
device.redis.
|
68
|
+
expect(device.redis).to receive(:rpush).with('custom', 'the log')
|
69
69
|
device.write('the log')
|
70
70
|
end
|
71
71
|
|
72
72
|
it "publishes logs onto a channel" do
|
73
73
|
device = LogStasher::Device::Redis.new(data_type: 'channel', key: 'custom')
|
74
|
-
device.redis.
|
74
|
+
expect(device.redis).to receive(:publish).with('custom', 'the log')
|
75
75
|
device.write('the log')
|
76
76
|
end
|
77
77
|
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'logstasher/rails_ext/action_controller/metal/instrumentation'
|
3
|
+
|
4
|
+
describe ActionController::Base do
|
5
|
+
before :each do
|
6
|
+
subject.request = ActionDispatch::TestRequest.new
|
7
|
+
subject.response = ActionDispatch::TestResponse.new
|
8
|
+
|
9
|
+
def subject.index(*args)
|
10
|
+
render text: 'OK'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe ".process_action" do
|
15
|
+
it "adds default fields to payload" do
|
16
|
+
expect(LogStasher).to receive(:add_default_fields_to_payload).once
|
17
|
+
expect(LogStasher).to receive(:add_default_fields_to_request_context).once
|
18
|
+
subject.process_action(:index)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "creates the request context before processing" do
|
22
|
+
LogStasher.request_context[:some_key] = 'value'
|
23
|
+
expect(LogStasher).to receive(:clear_request_context).once.and_call_original
|
24
|
+
expect {
|
25
|
+
subject.process_action(:index)
|
26
|
+
}.to change { LogStasher.request_context }
|
27
|
+
end
|
28
|
+
|
29
|
+
it "notifies rails of a request coming in" do
|
30
|
+
expect(ActiveSupport::Notifications).to receive(:instrument).with("start_processing.action_controller", anything).once
|
31
|
+
expect(ActiveSupport::Notifications).to receive(:instrument).with("process_action.action_controller", anything).once
|
32
|
+
subject.process_action(:index)
|
33
|
+
end
|
34
|
+
|
35
|
+
context "request context has custom fields defined" do
|
36
|
+
before :each do
|
37
|
+
LogStasher.add_custom_fields_to_request_context do |fields|
|
38
|
+
fields[:some_field] = 'value'
|
39
|
+
end
|
40
|
+
|
41
|
+
ActiveSupport::Notifications.subscribe('process_action.action_controller') do |_, _, _, _, payload|
|
42
|
+
@payload = payload
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should retain the value in the request context" do
|
47
|
+
subject.process_action(:index)
|
48
|
+
end
|
49
|
+
|
50
|
+
after :each do
|
51
|
+
expect(@payload[:some_field]).to eq('value')
|
52
|
+
|
53
|
+
ActionController::Metal.class_eval do
|
54
|
+
undef logstasher_add_custom_fields_to_request_context
|
55
|
+
end
|
56
|
+
ActionController::Base.class_eval do
|
57
|
+
undef logstasher_add_custom_fields_to_request_context
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -45,17 +45,17 @@ describe LogStasher::RequestLogSubscriber do
|
|
45
45
|
let(:logger) { double }
|
46
46
|
let(:json) { "{\"@source\":\"unknown\",\"@tags\":[\"request\"],\"@fields\":{\"request\":true,\"status\":true,\"runtimes\":true,\"location\":true,\"exception\":true,\"custom\":true},\"@timestamp\":\"timestamp\"}\n" }
|
47
47
|
before do
|
48
|
-
LogStasher.
|
49
|
-
LogStash::Time.
|
48
|
+
allow(LogStasher).to receive(:logger).and_return(logger)
|
49
|
+
allow(LogStash::Time).to receive(:now).and_return('timestamp')
|
50
50
|
end
|
51
51
|
it 'calls all extractors and outputs the json' do
|
52
|
-
request_subscriber.
|
53
|
-
request_subscriber.
|
54
|
-
request_subscriber.
|
55
|
-
request_subscriber.
|
56
|
-
request_subscriber.
|
57
|
-
request_subscriber.
|
58
|
-
LogStasher.logger.
|
52
|
+
expect(request_subscriber).to receive(:extract_request).with(payload).and_return({:request => true})
|
53
|
+
expect(request_subscriber).to receive(:extract_status).with(payload).and_return({:status => true})
|
54
|
+
expect(request_subscriber).to receive(:runtimes).with(event).and_return({:runtimes => true})
|
55
|
+
expect(request_subscriber).to receive(:location).with(event).and_return({:location => true})
|
56
|
+
expect(request_subscriber).to receive(:extract_exception).with(payload).and_return({:exception => true})
|
57
|
+
expect(request_subscriber).to receive(:extract_custom_fields).with(payload).and_return({:custom => true})
|
58
|
+
expect(LogStasher.logger).to receive(:<<).with(json)
|
59
59
|
request_subscriber.process_action(event)
|
60
60
|
end
|
61
61
|
end
|
@@ -64,47 +64,47 @@ describe LogStasher::RequestLogSubscriber do
|
|
64
64
|
|
65
65
|
it "should contain request tag" do
|
66
66
|
subscriber.process_action(event)
|
67
|
-
log_output.json['@tags'].
|
67
|
+
expect(log_output.json['@tags']).to include 'request'
|
68
68
|
end
|
69
69
|
|
70
70
|
it "should contain HTTP method" do
|
71
71
|
subscriber.process_action(event)
|
72
|
-
log_output.json['@fields']['method'].
|
72
|
+
expect(log_output.json['@fields']['method']).to eq 'GET'
|
73
73
|
end
|
74
74
|
|
75
75
|
it "should include the path in the log output" do
|
76
76
|
subscriber.process_action(event)
|
77
|
-
log_output.json['@fields']['path'].
|
77
|
+
expect(log_output.json['@fields']['path']).to eq '/home'
|
78
78
|
end
|
79
79
|
|
80
80
|
it "should include the format in the log output" do
|
81
81
|
subscriber.process_action(event)
|
82
|
-
log_output.json['@fields']['format'].
|
82
|
+
expect(log_output.json['@fields']['format']).to eq 'application/json'
|
83
83
|
end
|
84
84
|
|
85
85
|
it "should include the status code" do
|
86
86
|
subscriber.process_action(event)
|
87
|
-
log_output.json['@fields']['status'].
|
87
|
+
expect(log_output.json['@fields']['status']).to eq 200
|
88
88
|
end
|
89
89
|
|
90
90
|
it "should include the controller" do
|
91
91
|
subscriber.process_action(event)
|
92
|
-
log_output.json['@fields']['controller'].
|
92
|
+
expect(log_output.json['@fields']['controller']).to eq 'home'
|
93
93
|
end
|
94
94
|
|
95
95
|
it "should include the action" do
|
96
96
|
subscriber.process_action(event)
|
97
|
-
log_output.json['@fields']['action'].
|
97
|
+
expect(log_output.json['@fields']['action']).to eq 'index'
|
98
98
|
end
|
99
99
|
|
100
100
|
it "should include the view rendering time" do
|
101
101
|
subscriber.process_action(event)
|
102
|
-
log_output.json['@fields']['view'].
|
102
|
+
expect(log_output.json['@fields']['view']).to eq 0.01
|
103
103
|
end
|
104
104
|
|
105
105
|
it "should include the database rendering time" do
|
106
106
|
subscriber.process_action(event)
|
107
|
-
log_output.json['@fields']['db'].
|
107
|
+
expect(log_output.json['@fields']['db']).to eq 0.02
|
108
108
|
end
|
109
109
|
|
110
110
|
it "should add a valid status when an exception occurred" do
|
@@ -115,10 +115,10 @@ describe LogStasher::RequestLogSubscriber do
|
|
115
115
|
event.payload[:status] = nil
|
116
116
|
event.payload[:exception] = ['AbstractController::ActionNotFound', 'Route not found']
|
117
117
|
subscriber.process_action(event)
|
118
|
-
log_output.json['@fields']['status'].
|
119
|
-
log_output.json['@fields']['error'].
|
120
|
-
log_output.json['@tags'].
|
121
|
-
log_output.json['@tags'].
|
118
|
+
expect(log_output.json['@fields']['status']).to be >= 400
|
119
|
+
expect(log_output.json['@fields']['error']).to be =~ /AbstractController::ActionNotFound.*Route not found.*logstasher\/spec\/lib\/logstasher\/log_subscriber_spec\.rb/m
|
120
|
+
expect(log_output.json['@tags']).to include 'request'
|
121
|
+
expect(log_output.json['@tags']).to include 'exception'
|
122
122
|
end
|
123
123
|
end
|
124
124
|
|
@@ -126,7 +126,7 @@ describe LogStasher::RequestLogSubscriber do
|
|
126
126
|
event.payload[:status] = nil
|
127
127
|
event.payload[:exception] = nil
|
128
128
|
subscriber.process_action(event)
|
129
|
-
log_output.json['@fields']['status'].
|
129
|
+
expect(log_output.json['@fields']['status']).to eq 0
|
130
130
|
end
|
131
131
|
|
132
132
|
describe "with a redirect" do
|
@@ -136,36 +136,36 @@ describe LogStasher::RequestLogSubscriber do
|
|
136
136
|
|
137
137
|
it "should add the location to the log line" do
|
138
138
|
subscriber.process_action(event)
|
139
|
-
log_output.json['@fields']['location'].
|
139
|
+
expect(log_output.json['@fields']['location']).to eq 'http://www.example.com'
|
140
140
|
end
|
141
141
|
|
142
142
|
it "should remove the thread local variable" do
|
143
143
|
subscriber.process_action(event)
|
144
|
-
Thread.current[:logstasher_location].
|
144
|
+
expect(Thread.current[:logstasher_location]).to be_nil
|
145
145
|
end
|
146
146
|
end
|
147
147
|
|
148
148
|
it "should not include a location by default" do
|
149
149
|
subscriber.process_action(event)
|
150
|
-
log_output.json['@fields']['location'].
|
150
|
+
expect(log_output.json['@fields']['location']).to be_nil
|
151
151
|
end
|
152
152
|
end
|
153
153
|
|
154
154
|
describe "with append_custom_params block specified" do
|
155
|
-
let(:request) { double(:remote_ip => '10.0.0.1')}
|
155
|
+
let(:request) { double(:remote_ip => '10.0.0.1', :env => {})}
|
156
156
|
it "should add default custom data to the output" do
|
157
|
-
request.
|
157
|
+
allow(request).to receive_messages(:params => event.payload[:params])
|
158
158
|
LogStasher.add_default_fields_to_payload(event.payload, request)
|
159
159
|
subscriber.process_action(event)
|
160
|
-
log_output.json['@fields']['ip'].
|
161
|
-
log_output.json['@fields']['route'].
|
162
|
-
log_output.json['@fields']['parameters'].
|
160
|
+
expect(log_output.json['@fields']['ip']).to eq '10.0.0.1'
|
161
|
+
expect(log_output.json['@fields']['route']).to eq'home#index'
|
162
|
+
expect(log_output.json['@fields']['parameters']).to eq 'foo' => 'bar'
|
163
163
|
end
|
164
164
|
end
|
165
165
|
|
166
166
|
describe "with append_custom_params block specified" do
|
167
167
|
before do
|
168
|
-
LogStasher.
|
168
|
+
allow(LogStasher).to receive(:add_custom_fields) do |&block|
|
169
169
|
@block = block
|
170
170
|
end
|
171
171
|
LogStasher.add_custom_fields do |payload|
|
@@ -177,14 +177,89 @@ describe LogStasher::RequestLogSubscriber do
|
|
177
177
|
it "should add the custom data to the output" do
|
178
178
|
@block.call(event.payload)
|
179
179
|
subscriber.process_action(event)
|
180
|
-
log_output.json['@fields']['user'].
|
180
|
+
expect(log_output.json['@fields']['user']).to eq 'user'
|
181
181
|
end
|
182
182
|
end
|
183
183
|
|
184
184
|
describe "when processing a redirect" do
|
185
185
|
it "should store the location in a thread local variable" do
|
186
186
|
subscriber.redirect_to(redirect)
|
187
|
-
Thread.current[:logstasher_location].
|
187
|
+
expect(Thread.current[:logstasher_location]).to eq "http://example.com"
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
describe LogStasher::MailerLogSubscriber do
|
193
|
+
let(:log_output) {StringIO.new}
|
194
|
+
let(:logger) {
|
195
|
+
logger = Logger.new(log_output)
|
196
|
+
logger.formatter = ->(_, _, _, msg) {
|
197
|
+
msg
|
198
|
+
}
|
199
|
+
def log_output.json
|
200
|
+
JSON.parse!(self.string.split("\n").last)
|
201
|
+
end
|
202
|
+
logger
|
203
|
+
}
|
204
|
+
|
205
|
+
before :all do
|
206
|
+
SampleMailer.delivery_method = :test
|
207
|
+
LogStasher::MailerLogSubscriber.attach_to(:action_mailer)
|
208
|
+
end
|
209
|
+
|
210
|
+
before do
|
211
|
+
LogStasher.logger = logger
|
212
|
+
expect(LogStasher.request_context).to receive(:merge).at_most(2).times.and_call_original
|
213
|
+
end
|
214
|
+
|
215
|
+
let :message do
|
216
|
+
Mail.new do
|
217
|
+
from 'some-dude@example.com'
|
218
|
+
to 'some-other-dude@example.com'
|
219
|
+
subject 'Goodbye'
|
220
|
+
body 'LOL'
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
it 'receive an e-mail' do
|
225
|
+
SampleMailer.receive(message.encoded)
|
226
|
+
log_output.json.tap do |json|
|
227
|
+
expect(json['@source']).to eq(LogStasher.source)
|
228
|
+
expect(json['@tags']).to eq(['mailer', 'receive'])
|
229
|
+
json['@fields'].tap do |fields|
|
230
|
+
expect(fields['mailer']).to eq('SampleMailer')
|
231
|
+
expect(fields['from']).to eq(['some-dude@example.com'])
|
232
|
+
expect(fields['to']).to eq(['some-other-dude@example.com'])
|
233
|
+
expect(fields['message_id']).to eq(message.message_id)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
it 'deliver an outgoing e-mail' do
|
239
|
+
email = SampleMailer.welcome
|
240
|
+
|
241
|
+
if version = ENV['RAILS_VERSION'] and version >= '4.1'
|
242
|
+
log_output.json.tap do |json|
|
243
|
+
expect(json['@source']).to eq(LogStasher.source)
|
244
|
+
expect(json['@tags']).to eq(['mailer', 'process'])
|
245
|
+
json['@fields'].tap do |fields|
|
246
|
+
expect(fields['mailer']).to eq('SampleMailer')
|
247
|
+
expect(fields['action']).to eq('welcome')
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
email.deliver
|
253
|
+
log_output.json.tap do |json|
|
254
|
+
expect(json['@source']).to eq(LogStasher.source)
|
255
|
+
expect(json['@tags']).to eq(['mailer', 'deliver'])
|
256
|
+
json['@fields'].tap do |fields|
|
257
|
+
expect(fields['mailer']).to eq('SampleMailer')
|
258
|
+
expect(fields['from']).to eq(['some-dude@example.com'])
|
259
|
+
expect(fields['to']).to eq(['some-other-dude@example.com'])
|
260
|
+
# Message-Id appears not to be yet available at this point in time.
|
261
|
+
expect(fields['message_id']).to be_nil
|
262
|
+
end
|
188
263
|
end
|
189
264
|
end
|
190
265
|
end
|
data/spec/lib/logstasher_spec.rb
CHANGED
@@ -5,6 +5,7 @@ describe LogStasher do
|
|
5
5
|
after do
|
6
6
|
ActionController::LogSubscriber.attach_to :action_controller
|
7
7
|
ActionView::LogSubscriber.attach_to :action_view
|
8
|
+
ActionMailer::LogSubscriber.attach_to :action_mailer
|
8
9
|
end
|
9
10
|
|
10
11
|
it "should remove subscribers for controller events" do
|
@@ -23,19 +24,27 @@ describe LogStasher do
|
|
23
24
|
}
|
24
25
|
end
|
25
26
|
|
27
|
+
it "should remove subscribsers for mailer events" do
|
28
|
+
expect {
|
29
|
+
LogStasher.remove_existing_log_subscriptions
|
30
|
+
}.to change {
|
31
|
+
ActiveSupport::Notifications.notifier.listeners_for('deliver.action_mailer')
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
26
35
|
it "shouldn't remove subscribers that aren't from Rails" do
|
27
36
|
blk = -> {}
|
28
37
|
ActiveSupport::Notifications.subscribe("process_action.action_controller", &blk)
|
29
38
|
LogStasher.remove_existing_log_subscriptions
|
30
39
|
listeners = ActiveSupport::Notifications.notifier.listeners_for('process_action.action_controller')
|
31
|
-
listeners.
|
40
|
+
expect(listeners).to_not be_empty
|
32
41
|
end
|
33
42
|
end
|
34
43
|
|
35
44
|
describe '.appened_default_info_to_payload' do
|
36
45
|
let(:params) { {'a' => '1', 'b' => 2, 'action' => 'action', 'controller' => 'test'}.with_indifferent_access }
|
37
46
|
let(:payload) { {:params => params} }
|
38
|
-
let(:request) { double(:params => params, :remote_ip => '10.0.0.1')}
|
47
|
+
let(:request) { double(:params => params, :remote_ip => '10.0.0.1', :env => {})}
|
39
48
|
after do
|
40
49
|
LogStasher.custom_fields = []
|
41
50
|
LogStasher.log_controller_parameters = false
|
@@ -44,53 +53,75 @@ describe LogStasher do
|
|
44
53
|
LogStasher.log_controller_parameters = true
|
45
54
|
LogStasher.custom_fields = []
|
46
55
|
LogStasher.add_default_fields_to_payload(payload, request)
|
47
|
-
payload[:ip].
|
48
|
-
payload[:route].
|
49
|
-
payload[:parameters].
|
50
|
-
LogStasher.custom_fields.
|
56
|
+
expect(payload[:ip]).to eq '10.0.0.1'
|
57
|
+
expect(payload[:route]).to eq 'test#action'
|
58
|
+
expect(payload[:parameters]).to eq 'a' => '1', 'b' => 2
|
59
|
+
expect(LogStasher.custom_fields).to eq [:ip, :route, :request_id, :parameters]
|
51
60
|
end
|
52
61
|
|
53
62
|
it 'does not include parameters when not configured to' do
|
54
63
|
LogStasher.custom_fields = []
|
55
64
|
LogStasher.add_default_fields_to_payload(payload, request)
|
56
|
-
payload.
|
57
|
-
LogStasher.custom_fields.
|
65
|
+
expect(payload).to_not have_key(:parameters)
|
66
|
+
expect(LogStasher.custom_fields).to eq [:ip, :route, :request_id]
|
58
67
|
end
|
59
68
|
end
|
60
69
|
|
61
70
|
describe '.append_custom_params' do
|
62
|
-
let(:block) { ->{} }
|
71
|
+
let(:block) { ->(_, _){} }
|
63
72
|
it 'defines a method in ActionController::Base' do
|
64
|
-
ActionController::Base.
|
73
|
+
expect(ActionController::Base).to receive(:send).with(:define_method, :logtasher_add_custom_fields_to_payload, &block)
|
65
74
|
LogStasher.add_custom_fields(&block)
|
66
75
|
end
|
67
76
|
end
|
68
77
|
|
78
|
+
describe '.add_custom_fields_to_request_context' do
|
79
|
+
let(:block) { ->(_, _){} }
|
80
|
+
it 'defines a method in ActionController::Base' do
|
81
|
+
expect(ActionController::Base).to receive(:send).with(:define_method, :logstasher_add_custom_fields_to_request_context, &block)
|
82
|
+
expect(ActionController::Metal).to receive(:send).with(:define_method, :logstasher_add_custom_fields_to_request_context, &block)
|
83
|
+
LogStasher.add_custom_fields_to_request_context(&block)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe '.add_default_fields_to_request_context' do
|
88
|
+
it 'adds a request_id to the request context' do
|
89
|
+
LogStasher.clear_request_context
|
90
|
+
LogStasher.add_default_fields_to_request_context(double(env: {'action_dispatch.request_id' => 'lol'}))
|
91
|
+
expect(LogStasher.request_context).to eq({ request_id: 'lol' })
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
69
95
|
shared_examples 'setup' do
|
70
|
-
let(:
|
71
|
-
let(:logstasher_config) { double(:logger => logger, :log_level => 'warn', :log_controller_parameters => nil, :source => logstasher_source) }
|
96
|
+
let(:logstasher_source) { nil }
|
97
|
+
let(:logstasher_config) { double(:logger => logger, :log_level => 'warn', :log_controller_parameters => nil, :source => logstasher_source, :logger_path => logger_path) }
|
72
98
|
let(:config) { double(:logstasher => logstasher_config) }
|
73
99
|
let(:app) { double(:config => config) }
|
74
100
|
before do
|
75
101
|
@previous_source = LogStasher.source
|
76
|
-
config.
|
102
|
+
allow(config).to receive_messages(:action_dispatch => double(:rack_cache => false))
|
103
|
+
allow_message_expectations_on_nil
|
77
104
|
end
|
78
105
|
after { LogStasher.source = @previous_source } # Need to restore old source for specs
|
79
106
|
it 'defines a method in ActionController::Base' do
|
80
|
-
LogStasher.
|
81
|
-
LogStasher.
|
82
|
-
LogStasher.
|
83
|
-
LogStasher::RequestLogSubscriber.
|
84
|
-
|
107
|
+
expect(LogStasher).to receive(:require).with('logstasher/rails_ext/action_controller/metal/instrumentation')
|
108
|
+
expect(LogStasher).to receive(:require).with('logstash-event')
|
109
|
+
expect(LogStasher).to receive(:suppress_app_logs).with(app)
|
110
|
+
expect(LogStasher::RequestLogSubscriber).to receive(:attach_to).with(:action_controller)
|
111
|
+
expect(LogStasher::MailerLogSubscriber).to receive(:attach_to).with(:action_mailer)
|
112
|
+
expect(logger).to receive(:level=).with('warn')
|
85
113
|
LogStasher.setup(app)
|
86
|
-
LogStasher.source.
|
87
|
-
LogStasher.
|
88
|
-
LogStasher.custom_fields.
|
89
|
-
LogStasher.log_controller_parameters.
|
114
|
+
expect(LogStasher.source).to eq (logstasher_source || 'unknown')
|
115
|
+
expect(LogStasher).to be_enabled
|
116
|
+
expect(LogStasher.custom_fields).to be_empty
|
117
|
+
expect(LogStasher.log_controller_parameters).to eq false
|
90
118
|
end
|
91
119
|
end
|
92
120
|
|
93
121
|
describe '.setup' do
|
122
|
+
let(:logger) { double }
|
123
|
+
let(:logger_path) { nil }
|
124
|
+
|
94
125
|
describe 'with source set' do
|
95
126
|
let(:logstasher_source) { 'foo' }
|
96
127
|
it_behaves_like 'setup'
|
@@ -100,21 +131,37 @@ describe LogStasher do
|
|
100
131
|
let(:logstasher_source) { nil }
|
101
132
|
it_behaves_like 'setup'
|
102
133
|
end
|
134
|
+
|
135
|
+
describe 'with customized logging' do
|
136
|
+
let(:logger) { nil }
|
137
|
+
|
138
|
+
context 'with no logger passed in' do
|
139
|
+
before { expect(LogStasher).to receive(:new_logger).with('/log/logstash_test.log') }
|
140
|
+
it_behaves_like 'setup'
|
141
|
+
end
|
142
|
+
|
143
|
+
context 'with custom logger path passed in' do
|
144
|
+
let(:logger_path) { double }
|
145
|
+
|
146
|
+
before { expect(LogStasher).to receive(:new_logger).with(logger_path) }
|
147
|
+
it_behaves_like 'setup'
|
148
|
+
end
|
149
|
+
end
|
103
150
|
end
|
104
151
|
|
105
152
|
describe '.suppress_app_logs' do
|
106
153
|
let(:logstasher_config){ double(:logstasher => double(:suppress_app_log => true))}
|
107
154
|
let(:app){ double(:config => logstasher_config)}
|
108
155
|
it 'removes existing subscription if enabled' do
|
109
|
-
LogStasher.
|
110
|
-
LogStasher.
|
156
|
+
expect(LogStasher).to receive(:require).with('logstasher/rails_ext/rack/logger')
|
157
|
+
expect(LogStasher).to receive(:remove_existing_log_subscriptions)
|
111
158
|
LogStasher.suppress_app_logs(app)
|
112
159
|
end
|
113
160
|
|
114
161
|
context 'when disabled' do
|
115
162
|
let(:logstasher_config){ double(:logstasher => double(:suppress_app_log => false)) }
|
116
163
|
it 'does not remove existing subscription' do
|
117
|
-
LogStasher.
|
164
|
+
expect(LogStasher).to_not receive(:remove_existing_log_subscriptions)
|
118
165
|
LogStasher.suppress_app_logs(app)
|
119
166
|
end
|
120
167
|
|
@@ -122,7 +169,7 @@ describe LogStasher do
|
|
122
169
|
context 'with spelling "supress_app_log"' do
|
123
170
|
let(:logstasher_config){ double(:logstasher => double(:suppress_app_log => nil, :supress_app_log => false)) }
|
124
171
|
it 'does not remove existing subscription' do
|
125
|
-
LogStasher.
|
172
|
+
expect(LogStasher).to_not receive(:remove_existing_log_subscriptions)
|
126
173
|
LogStasher.suppress_app_logs(app)
|
127
174
|
end
|
128
175
|
end
|
@@ -133,14 +180,14 @@ describe LogStasher do
|
|
133
180
|
describe '.appended_params' do
|
134
181
|
it 'returns the stored var in current thread' do
|
135
182
|
Thread.current[:logstasher_custom_fields] = :test
|
136
|
-
LogStasher.custom_fields.
|
183
|
+
expect(LogStasher.custom_fields).to eq :test
|
137
184
|
end
|
138
185
|
end
|
139
186
|
|
140
187
|
describe '.appended_params=' do
|
141
188
|
it 'returns the stored var in current thread' do
|
142
189
|
LogStasher.custom_fields = :test
|
143
|
-
Thread.current[:logstasher_custom_fields].
|
190
|
+
expect(Thread.current[:logstasher_custom_fields]).to eq :test
|
144
191
|
end
|
145
192
|
end
|
146
193
|
|
@@ -148,11 +195,12 @@ describe LogStasher do
|
|
148
195
|
let(:logger) { double() }
|
149
196
|
before do
|
150
197
|
LogStasher.logger = logger
|
151
|
-
LogStash::Time.
|
198
|
+
allow(LogStash::Time).to receive_messages(:now => 'timestamp')
|
199
|
+
allow_message_expectations_on_nil
|
152
200
|
end
|
153
201
|
it 'adds to log with specified level' do
|
154
|
-
logger.
|
155
|
-
logger.
|
202
|
+
expect(logger).to receive(:send).with('warn?').and_return(true)
|
203
|
+
expect(logger).to receive(:send).with('warn',"{\"@source\":\"unknown\",\"@tags\":[\"log\"],\"@fields\":{\"message\":\"WARNING\",\"level\":\"warn\"},\"@timestamp\":\"timestamp\"}")
|
156
204
|
LogStasher.log('warn', 'WARNING')
|
157
205
|
end
|
158
206
|
context 'with a source specified' do
|
@@ -160,8 +208,8 @@ describe LogStasher do
|
|
160
208
|
LogStasher.source = 'foo'
|
161
209
|
end
|
162
210
|
it 'sets the correct source' do
|
163
|
-
logger.
|
164
|
-
logger.
|
211
|
+
expect(logger).to receive(:send).with('warn?').and_return(true)
|
212
|
+
expect(logger).to receive(:send).with('warn',"{\"@source\":\"foo\",\"@tags\":[\"log\"],\"@fields\":{\"message\":\"WARNING\",\"level\":\"warn\"},\"@timestamp\":\"timestamp\"}")
|
165
213
|
LogStasher.log('warn', 'WARNING')
|
166
214
|
end
|
167
215
|
end
|
@@ -171,7 +219,7 @@ describe LogStasher do
|
|
171
219
|
describe ".#{severity}" do
|
172
220
|
let(:message) { "This is a #{severity} message" }
|
173
221
|
it 'should log with specified level' do
|
174
|
-
LogStasher.
|
222
|
+
expect(LogStasher).to receive(:log).with(severity.to_sym, message)
|
175
223
|
LogStasher.send(severity, message )
|
176
224
|
end
|
177
225
|
end
|
@@ -192,7 +240,7 @@ describe LogStasher do
|
|
192
240
|
before(:each) { LogStasher.custom_fields = [] }
|
193
241
|
|
194
242
|
it "subscribes to the required event" do
|
195
|
-
ActiveSupport::Notifications.
|
243
|
+
expect(ActiveSupport::Notifications).to receive(:subscribe).with('event_name')
|
196
244
|
LogStasher.watch('event_name')
|
197
245
|
end
|
198
246
|
|
@@ -208,7 +256,7 @@ describe LogStasher do
|
|
208
256
|
probe = lambda { |*args, store| store[:foo] = :bar }
|
209
257
|
LogStasher.watch('custom.event.bar', &probe)
|
210
258
|
ActiveSupport::Notifications.instrument('custom.event.bar', {})
|
211
|
-
LogStasher.store['custom.event.bar'].
|
259
|
+
expect(LogStasher.store['custom.event.bar']).to eq :foo => :bar
|
212
260
|
end
|
213
261
|
end
|
214
262
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -23,6 +23,8 @@ require 'bundler/setup'
|
|
23
23
|
# part of Rails. You can say :require => false in gemfile to always use explicit requiring
|
24
24
|
Bundler.require(:default, :test)
|
25
25
|
|
26
|
+
Dir[File.join("./spec/support/**/*.rb")].each { |f| require f }
|
27
|
+
|
26
28
|
# Set Rails environment as test
|
27
29
|
ENV['RAILS_ENV'] = 'test'
|
28
30
|
|
@@ -40,11 +42,11 @@ require 'active_support/core_ext/hash/slice'
|
|
40
42
|
require 'active_support/core_ext/string'
|
41
43
|
require 'active_support/core_ext/time/zones'
|
42
44
|
require 'abstract_controller/base'
|
45
|
+
require 'action_mailer'
|
43
46
|
require 'logger'
|
44
47
|
require 'logstash-event'
|
45
48
|
|
46
49
|
RSpec.configure do |config|
|
47
|
-
config.treat_symbols_as_metadata_keys_with_true_values = true
|
48
50
|
config.run_all_when_everything_filtered = true
|
49
51
|
config.filter_run :focus
|
50
52
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'action_mailer'
|
2
|
+
|
3
|
+
class SampleMailer < ActionMailer::Base
|
4
|
+
def welcome
|
5
|
+
mail(from: 'some-dude@example.com', to: 'some-other-dude@example.com', subject: 'Hello, there') do |format|
|
6
|
+
format.text { render plain: 'OK' }
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def receive(email)
|
11
|
+
end
|
12
|
+
|
13
|
+
def _render_template(_)
|
14
|
+
""
|
15
|
+
end
|
16
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: logstasher
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shadab Ahmed
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-08-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: logstash-event
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - ! '>='
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '2.14'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '2.14'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: bundler
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -104,9 +104,11 @@ files:
|
|
104
104
|
- logstasher.gemspec
|
105
105
|
- sample_logstash_configurations/quickstart.conf
|
106
106
|
- spec/lib/logstasher/device/redis_spec.rb
|
107
|
+
- spec/lib/logstasher/instrumentation_spec.rb
|
107
108
|
- spec/lib/logstasher/log_subscriber_spec.rb
|
108
109
|
- spec/lib/logstasher_spec.rb
|
109
110
|
- spec/spec_helper.rb
|
111
|
+
- spec/support/sample_mailer.rb
|
110
112
|
homepage: https://github.com/shadabahmed/logstasher
|
111
113
|
licenses: []
|
112
114
|
metadata: {}
|
@@ -126,12 +128,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
126
128
|
version: '0'
|
127
129
|
requirements: []
|
128
130
|
rubyforge_project: logstasher
|
129
|
-
rubygems_version: 2.
|
131
|
+
rubygems_version: 2.2.2
|
130
132
|
signing_key:
|
131
133
|
specification_version: 4
|
132
134
|
summary: Awesome rails logs
|
133
135
|
test_files:
|
134
136
|
- spec/lib/logstasher/device/redis_spec.rb
|
137
|
+
- spec/lib/logstasher/instrumentation_spec.rb
|
135
138
|
- spec/lib/logstasher/log_subscriber_spec.rb
|
136
139
|
- spec/lib/logstasher_spec.rb
|
137
140
|
- spec/spec_helper.rb
|
141
|
+
- spec/support/sample_mailer.rb
|