rv-logstasher 1.3.1
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.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/.rspec +2 -0
- data/.travis.yml +12 -0
- data/Gemfile +10 -0
- data/LICENSE.md +21 -0
- data/README.md +119 -0
- data/Rakefile +21 -0
- data/lib/logstasher.rb +75 -0
- data/lib/logstasher/context_wrapper.rb +14 -0
- data/lib/logstasher/log_formatter.rb +63 -0
- data/lib/logstasher/log_subscriber.rb +152 -0
- data/lib/logstasher/rack/common_logger_adapter.rb +15 -0
- data/lib/logstasher/railtie.rb +58 -0
- data/lib/logstasher/silent_logger.rb +16 -0
- data/lib/logstasher/version.rb +3 -0
- data/lib/rv-logstasher.rb +1 -0
- data/log/logstash_development.log +0 -0
- data/log/logstash_test.log +0 -0
- data/log/test.log +0 -0
- data/logstasher.gemspec +26 -0
- data/spec/lib/logstasher/log_formater_spec.rb +144 -0
- data/spec/lib/logstasher/log_subscriber_spec.rb +167 -0
- data/spec/lib/logstasher/railtie_spec.rb +129 -0
- data/spec/lib/logstasher_spec.rb +18 -0
- data/spec/spec_helper.rb +15 -0
- metadata +146 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 27b875bba60f72b2aeec7c597754ab5a6d72d8a0
|
4
|
+
data.tar.gz: f1102f0c57d2e2c6a79749c5e89314f9698f43e0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a6ff67002aefae4731beca3b079f08d80d68d5a5acd92bdfed6f775affeacfd79422a53787fd1d46b3a9f8d8c26ed2807396888b8310c2b1a108ae15978eeed2
|
7
|
+
data.tar.gz: 9ff5f5e8f66f6c140375687b641f09c4a1aa24a0b8cfc4f0e92a9d56530a087c725b141adc0acd4a8250c29da29fdd331afa08690473d4d04758c8796a0b0393
|
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Reevoo Ltd
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
# Logstasher
|
2
|
+
|
3
|
+
**Awesome Logging for Rack applications**
|
4
|
+
|
5
|
+
## History
|
6
|
+
|
7
|
+
This is a fork of
|
8
|
+
[shadabahmed/logstasher](https://github.com/shadabahmed/logstasher). It has
|
9
|
+
been updated to use the [latest event
|
10
|
+
schema](https://logstash.jira.com/browse/LOGSTASH-675) and customized to better
|
11
|
+
fit the needs of Reevoo though it is still generally useful. It is not
|
12
|
+
backward compatible with its progenitor.
|
13
|
+
|
14
|
+
## Purpose
|
15
|
+
|
16
|
+
This gem makes it easy to generate logstash compatible logs for your applications.
|
17
|
+
|
18
|
+
A request that looks like this in your `production.log`:
|
19
|
+
```text
|
20
|
+
Started GET "/login" for 10.109.10.135 at 2013-04-30 08:59:01 -0400
|
21
|
+
Processing by SessionsController#new as HTML
|
22
|
+
Rendered sessions/new.html.haml within layouts/application (4.3ms)
|
23
|
+
Rendered shared/_javascript.html.haml (0.6ms)
|
24
|
+
Rendered shared/_flashes.html.haml (0.2ms)
|
25
|
+
Rendered shared/_header.html.haml (52.9ms)
|
26
|
+
Rendered shared/_title.html.haml (0.2ms)
|
27
|
+
Rendered shared/_footer.html.haml (0.2ms)
|
28
|
+
Completed 200 OK in 532ms (Views: 62.4ms | ActiveRecord: 0.0ms | ND API: 0.0ms)
|
29
|
+
```
|
30
|
+
|
31
|
+
Will look like this in your `production.log`:
|
32
|
+
```json
|
33
|
+
{"tags":["request"],"method":"GET","path":"/","format":"html","controller":"file_servers"
|
34
|
+
,"action":"index","status":200,"duration":28.34,"view":25.96,"db":0.88,"ip":"127.0.0.1","route":"file_servers#index",
|
35
|
+
"parameters":"","ndapi_time":null,"uuid":"e81ecd178ed3b591099f4d489760dfb6","user":"shadab_ahmed@abc.com",
|
36
|
+
"site":"internal","@timestamp":"2013-04-30T13:00:46.354500+00:00","@version":"1"}
|
37
|
+
```
|
38
|
+
|
39
|
+
From there, it's trivial to forward them to your logstash indexer.
|
40
|
+
|
41
|
+
## Installation for Rails Application
|
42
|
+
|
43
|
+
In your Gemfile:
|
44
|
+
|
45
|
+
gem 'rv-logstasher'
|
46
|
+
|
47
|
+
### Configure your `<environment>.rb` e.g. `development.rb`
|
48
|
+
|
49
|
+
# Enable the logstasher logs for the current environment
|
50
|
+
config.logstasher.enabled = true
|
51
|
+
|
52
|
+
# Optionally silience the standard logging to <environment>.log
|
53
|
+
config.logstasher.silence_standard_logging = true
|
54
|
+
|
55
|
+
### Logging params
|
56
|
+
|
57
|
+
By default, Logstasher will add params as a JSON encoded string. To disable,
|
58
|
+
add the following to your `<environment>.rb`
|
59
|
+
|
60
|
+
# Disable logging of request parameters
|
61
|
+
config.logstasher.include_parameters = false
|
62
|
+
|
63
|
+
### Adding custom fields to the log
|
64
|
+
|
65
|
+
Since some fields are very specific to your application, e.g., *user_name*,
|
66
|
+
it is left upto you to add them. Here's how to do it:
|
67
|
+
|
68
|
+
# Create a file - config/initializers/logstasher.rb
|
69
|
+
|
70
|
+
if LogStasher.enabled
|
71
|
+
LogStasher.append_fields do |fields|
|
72
|
+
# This block is run in application_controller context,
|
73
|
+
# so you have access to all controller methods
|
74
|
+
fields[:user] = current_user && current_user.mail
|
75
|
+
fields[:site] = request.path =~ /^\/api/ ? 'api' : 'user'
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
## Installation for Grape Application
|
80
|
+
|
81
|
+
### In your Gemfile:
|
82
|
+
|
83
|
+
gem "rv-logstasher"
|
84
|
+
gem "grape_logging"
|
85
|
+
|
86
|
+
|
87
|
+
### Init logger:
|
88
|
+
|
89
|
+
module TestApp
|
90
|
+
def self.logger
|
91
|
+
@logger ||= LogStasher.logger_for_app('app_name', Rack::Directory.new("").root, STDOUT)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
### Setup Grape request/exception logging
|
96
|
+
|
97
|
+
module TestApp
|
98
|
+
class API < Grape::API
|
99
|
+
logger TestApp.logger
|
100
|
+
use GrapeLogging::Middleware::RequestLogger, logger: TestApp.logger
|
101
|
+
|
102
|
+
rescue_from TestApp::NotFound do |err|
|
103
|
+
# Tag your exception
|
104
|
+
API.logger.info(exception: err, tags: "rescued_exception", status: 404)
|
105
|
+
error_response(message: "Not found", status: 404)
|
106
|
+
end
|
107
|
+
|
108
|
+
rescue_from :all do |e|
|
109
|
+
API.logger.error(e)
|
110
|
+
error_response(message: e.message, status: 500)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
|
117
|
+
## Copyright
|
118
|
+
|
119
|
+
Copyright (c) 2016 Reevoo Ltd, released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
|
4
|
+
desc 'Default: run specs.'
|
5
|
+
task :default => :spec
|
6
|
+
|
7
|
+
desc "Run specs"
|
8
|
+
RSpec::Core::RakeTask.new('spec') do |spec|
|
9
|
+
spec.pattern = "./spec/**/*_spec.rb"
|
10
|
+
end
|
11
|
+
|
12
|
+
require 'rdoc/task'
|
13
|
+
RDoc::Task.new do |rdoc|
|
14
|
+
require 'logstasher/version'
|
15
|
+
version = LogStasher::VERSION
|
16
|
+
|
17
|
+
rdoc.rdoc_dir = 'rdoc'
|
18
|
+
rdoc.title = "logstasher #{version}"
|
19
|
+
rdoc.rdoc_files.include('README*')
|
20
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
21
|
+
end
|
data/lib/logstasher.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'logstasher/log_formatter'
|
4
|
+
|
5
|
+
module LogStasher
|
6
|
+
class << self
|
7
|
+
attr_reader :append_fields_callback
|
8
|
+
attr_writer :enabled
|
9
|
+
attr_writer :include_parameters
|
10
|
+
attr_writer :serialize_parameters
|
11
|
+
attr_writer :silence_standard_logging
|
12
|
+
|
13
|
+
def append_fields(&block)
|
14
|
+
@append_fields_callback = block
|
15
|
+
end
|
16
|
+
|
17
|
+
def enabled?
|
18
|
+
if @enabled.nil?
|
19
|
+
@enabled = false
|
20
|
+
end
|
21
|
+
|
22
|
+
@enabled
|
23
|
+
end
|
24
|
+
|
25
|
+
def include_parameters?
|
26
|
+
if @include_parameters.nil?
|
27
|
+
@include_parameters = true
|
28
|
+
end
|
29
|
+
|
30
|
+
@include_parameters
|
31
|
+
end
|
32
|
+
|
33
|
+
def serialize_parameters?
|
34
|
+
if @serialize_parameters.nil?
|
35
|
+
@serialize_parameters = true
|
36
|
+
end
|
37
|
+
|
38
|
+
@serialize_parameters
|
39
|
+
end
|
40
|
+
|
41
|
+
def initialize_logger(device = STDOUT, level = ::Logger::INFO, formatter = nil)
|
42
|
+
::Logger.new(device).tap do |new_logger|
|
43
|
+
new_logger.level = level
|
44
|
+
new_logger.formatter = formatter if formatter
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def logger
|
49
|
+
@logger ||= initialize_logger
|
50
|
+
end
|
51
|
+
|
52
|
+
def logger=(log)
|
53
|
+
@logger = log
|
54
|
+
end
|
55
|
+
|
56
|
+
def logger_for_app(app_tag, root_dir = nil, device = nil, level = Logger::INFO)
|
57
|
+
if root_dir && !device
|
58
|
+
device = File.join(root_dir, 'log', 'logstasher.log')
|
59
|
+
FileUtils.touch(device)
|
60
|
+
end
|
61
|
+
formatter = LogFormatter.new(app_tag, root_dir)
|
62
|
+
initialize_logger(device || STDOUT, level, formatter)
|
63
|
+
end
|
64
|
+
|
65
|
+
def silence_standard_logging?
|
66
|
+
if @silence_standard_logging.nil?
|
67
|
+
@silence_standard_logging = false
|
68
|
+
end
|
69
|
+
|
70
|
+
@silence_standard_logging
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
require 'logstasher/railtie' if defined?(Rails)
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module LogStasher
|
2
|
+
class LogFormatter
|
3
|
+
attr_reader :base_tags
|
4
|
+
attr_reader :root_dir
|
5
|
+
attr_reader :release
|
6
|
+
|
7
|
+
def initialize(base_tags = [], root_dir = nil)
|
8
|
+
@base_tags = Array(base_tags)
|
9
|
+
@root_dir = root_dir
|
10
|
+
@release = $1 if root_dir && root_dir =~ /releases\/([^\/]+)/
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(severity, datetime, progname, message)
|
14
|
+
event = {
|
15
|
+
'@timestamp' => datetime.utc,
|
16
|
+
'@version' => '1',
|
17
|
+
severity: severity.downcase,
|
18
|
+
}.merge(format(message))
|
19
|
+
|
20
|
+
event.merge!(format(progname)) if progname.is_a?(Exception)
|
21
|
+
event[:tags] = base_tags + Array(event[:tags])
|
22
|
+
event.to_json + "\n"
|
23
|
+
end
|
24
|
+
|
25
|
+
def format(data)
|
26
|
+
return { message: data } if data.is_a?(String)
|
27
|
+
return format_exception(data) if data.is_a?(Exception)
|
28
|
+
return format_hash(data) if data.is_a?(Hash)
|
29
|
+
fail ArgumentError, "Not expected type of log message: #{data} (#{data.class})"
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def format_hash(data)
|
35
|
+
data.merge!(request_path: data[:path]) if data[:path] # logstash overrides the path attribute
|
36
|
+
if data[:exception]
|
37
|
+
format_exception(data.delete(:exception)).merge(data)
|
38
|
+
else
|
39
|
+
data
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def format_exception(exception)
|
44
|
+
result = {
|
45
|
+
tags: 'exception',
|
46
|
+
error_class: exception.class.to_s,
|
47
|
+
error_message: exception.message,
|
48
|
+
error_source: error_source(exception),
|
49
|
+
error_backtrace: exception.backtrace,
|
50
|
+
}
|
51
|
+
|
52
|
+
if exception.respond_to?(:cause) && exception.cause
|
53
|
+
result[:error_cause] = [exception.cause.class.to_s, exception.cause.message].concat(exception.cause.backtrace)
|
54
|
+
end
|
55
|
+
result
|
56
|
+
end
|
57
|
+
|
58
|
+
def error_source(exception)
|
59
|
+
source = exception.backtrace.find { |line| line.match(/\A#{root_dir}/) }
|
60
|
+
source.sub(/\A#{root_dir}/, '') if source
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
require 'action_controller/log_subscriber'
|
2
|
+
require 'active_support/log_subscriber'
|
3
|
+
require 'active_support/core_ext/hash/except'
|
4
|
+
require 'json'
|
5
|
+
require 'logstash/event'
|
6
|
+
|
7
|
+
# active_support monkeypatches Hash#to_json with a version that does not encode
|
8
|
+
# time correctly for LogStash. Using JSON.generate bypasses the monkeypatch.
|
9
|
+
class LogStash::Event
|
10
|
+
def to_json
|
11
|
+
return JSON.generate(@data)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module LogStasher
|
16
|
+
class LogSubscriber < ::ActiveSupport::LogSubscriber
|
17
|
+
|
18
|
+
INTERNAL_PARAMS = ::ActionController::LogSubscriber::INTERNAL_PARAMS
|
19
|
+
|
20
|
+
def process_action(event)
|
21
|
+
payload = event.payload
|
22
|
+
tags = extract_tags(payload)
|
23
|
+
fields = extract_request(payload)
|
24
|
+
|
25
|
+
fields.merge! extract_status(payload)
|
26
|
+
fields.merge! runtimes(event)
|
27
|
+
fields.merge! location
|
28
|
+
fields.merge! extract_exception(payload)
|
29
|
+
fields.merge! extract_parameters(payload)
|
30
|
+
fields.merge! appended_fields
|
31
|
+
|
32
|
+
event = LogStash::Event.new(fields.merge('tags' => tags))
|
33
|
+
|
34
|
+
LogStasher.logger << event.to_json + "\n"
|
35
|
+
end
|
36
|
+
|
37
|
+
def redirect_to(event)
|
38
|
+
Thread.current[:logstasher_context][:location] = event.payload[:location]
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def appended_fields
|
44
|
+
callback = ::LogStasher.append_fields_callback
|
45
|
+
{}.tap do |fields|
|
46
|
+
controller.instance_exec(fields, &callback) if callback
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def controller
|
51
|
+
Thread.current[:logstasher_context][:controller]
|
52
|
+
end
|
53
|
+
|
54
|
+
def extract_request(payload)
|
55
|
+
{
|
56
|
+
action: payload[:action],
|
57
|
+
controller: payload[:controller],
|
58
|
+
format: extract_format(payload),
|
59
|
+
ip: request.remote_ip,
|
60
|
+
method: payload[:method],
|
61
|
+
request_path: extract_path(payload),
|
62
|
+
route: "#{payload[:controller]}##{payload[:action]}"
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
# Monkey patching to enable exception logging
|
67
|
+
def extract_exception(payload)
|
68
|
+
return {} unless payload[:exception]
|
69
|
+
|
70
|
+
exception, message = payload[:exception]
|
71
|
+
status = ActionDispatch::ExceptionWrapper.status_code_for_exception(exception)
|
72
|
+
error_source = $!.backtrace.find { |line| line.match(/\A#{Rails.root}/) }
|
73
|
+
|
74
|
+
result = { status: status }
|
75
|
+
result[:error_class] = exception
|
76
|
+
result[:error_message] = message
|
77
|
+
result[:error_source] = error_source.gsub(/\A#{Dir.pwd}/, '') if error_source
|
78
|
+
result[:error_backtrace] = $!.backtrace
|
79
|
+
if $!.respond_to?(:cause) && $!.cause
|
80
|
+
result[:error_cause] = [$!.cause.class.to_s, $!.cause.message].concat($!.cause.backtrace)
|
81
|
+
end
|
82
|
+
result
|
83
|
+
end
|
84
|
+
|
85
|
+
def extract_format(payload)
|
86
|
+
if ::ActionPack::VERSION::MAJOR == 3 && ::ActionPack::VERSION::MINOR == 0
|
87
|
+
payload[:formats].first
|
88
|
+
else
|
89
|
+
payload[:format]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def extract_parameters(payload)
|
94
|
+
if LogStasher.include_parameters?
|
95
|
+
external_params = payload[:params].except(*INTERNAL_PARAMS)
|
96
|
+
|
97
|
+
if LogStasher.serialize_parameters?
|
98
|
+
{ :params => JSON.generate(external_params) }
|
99
|
+
else
|
100
|
+
{ :params => external_params }
|
101
|
+
end
|
102
|
+
else
|
103
|
+
{}
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def extract_path(payload)
|
108
|
+
payload[:path].split("?").first
|
109
|
+
end
|
110
|
+
|
111
|
+
def extract_status(payload)
|
112
|
+
if payload[:status]
|
113
|
+
{ :status => payload[:status].to_i }
|
114
|
+
else
|
115
|
+
{ :status => 0 }
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def extract_tags(payload)
|
120
|
+
tags = ['request']
|
121
|
+
tags.push('exception') if payload[:exception]
|
122
|
+
tags
|
123
|
+
end
|
124
|
+
|
125
|
+
def location
|
126
|
+
location = Thread.current[:logstasher_context][:location]
|
127
|
+
|
128
|
+
if location
|
129
|
+
{ :location => location }
|
130
|
+
else
|
131
|
+
{}
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def request
|
136
|
+
Thread.current[:logstasher_context][:request]
|
137
|
+
end
|
138
|
+
|
139
|
+
def runtimes(event)
|
140
|
+
{
|
141
|
+
:total => event.duration,
|
142
|
+
:view => event.payload[:view_runtime],
|
143
|
+
:db => event.payload[:db_runtime]
|
144
|
+
}.inject({:runtime => {}}) do |runtimes, (name, runtime)|
|
145
|
+
runtimes[:runtime][name] = runtime.to_f.round(2) if runtime
|
146
|
+
runtimes
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
::LogStasher::LogSubscriber.attach_to :action_controller
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module LogStasher
|
2
|
+
class Railtie < ::Rails::Railtie
|
3
|
+
config.logstasher = ::ActiveSupport::OrderedOptions.new
|
4
|
+
config.logstasher.enabled = false
|
5
|
+
config.logstasher.include_parameters = true
|
6
|
+
config.logstasher.serialize_parameters = true
|
7
|
+
config.logstasher.silence_standard_logging = false
|
8
|
+
config.logstasher.logger = nil
|
9
|
+
config.logstasher.log_level = ::Logger::INFO
|
10
|
+
|
11
|
+
initializer 'logstasher.configure' do
|
12
|
+
options = config.logstasher
|
13
|
+
|
14
|
+
::LogStasher.enabled = options.enabled
|
15
|
+
::LogStasher.include_parameters = options.include_parameters
|
16
|
+
::LogStasher.serialize_parameters = options.serialize_parameters
|
17
|
+
::LogStasher.silence_standard_logging = options.silence_standard_logging
|
18
|
+
::LogStasher.logger = options.logger || default_logger
|
19
|
+
::LogStasher.logger.level = options.log_level
|
20
|
+
end
|
21
|
+
|
22
|
+
initializer 'logstasher.load' do
|
23
|
+
if ::LogStasher.enabled?
|
24
|
+
::ActiveSupport.on_load(:action_controller) do
|
25
|
+
require 'logstasher/log_subscriber'
|
26
|
+
require 'logstasher/context_wrapper'
|
27
|
+
|
28
|
+
include ::LogStasher::ContextWrapper
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
config.after_initialize do
|
34
|
+
if ::LogStasher.enabled? && ::LogStasher.silence_standard_logging?
|
35
|
+
require 'logstasher/silent_logger'
|
36
|
+
|
37
|
+
::Rails::Rack::Logger.send(:include, ::LogStasher::SilentLogger)
|
38
|
+
|
39
|
+
::ActiveSupport::LogSubscriber.log_subscribers.each do |subscriber|
|
40
|
+
if subscriber.is_a?(::ActiveSupport::LogSubscriber)
|
41
|
+
subscriber.class.send(:include, ::LogStasher::SilentLogger)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def default_logger
|
48
|
+
unless @default_logger
|
49
|
+
path = ::Rails.root.join('log', "logstasher.log")
|
50
|
+
::FileUtils.touch(path) # prevent autocreate messages in log
|
51
|
+
|
52
|
+
@default_logger = ::Logger.new(path)
|
53
|
+
end
|
54
|
+
|
55
|
+
@default_logger
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module LogStasher
|
2
|
+
module SilentLogger
|
3
|
+
def self.included(klass)
|
4
|
+
klass.class_eval do
|
5
|
+
def logger
|
6
|
+
unless @logstasher_silent_logger
|
7
|
+
@logstasher_silent_logger = ::Logger.new('/dev/null')
|
8
|
+
@logstasher_silent_logger.level = ::Logger::UNKNOWN
|
9
|
+
end
|
10
|
+
|
11
|
+
@logstasher_silent_logger
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'logstasher'
|
File without changes
|
File without changes
|
data/log/test.log
ADDED
File without changes
|
data/logstasher.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
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 = "rv-logstasher"
|
7
|
+
s.version = LogStasher::VERSION
|
8
|
+
s.authors = ['David Sevcik', 'Alex Malkov']
|
9
|
+
s.email = ['david.sevcik@reevoo.com', 'alex.malkov@reevoo.com']
|
10
|
+
s.homepage = "https://github.com/reevoo/logstasher"
|
11
|
+
s.summary = %q{Produces log in the logstash format}
|
12
|
+
s.description = %q{Produces log in the logstash format}
|
13
|
+
s.license = 'MIT'
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
|
20
|
+
s.add_runtime_dependency "logstash-event", "~> 1.2"
|
21
|
+
|
22
|
+
s.add_development_dependency "redis"
|
23
|
+
s.add_development_dependency "rspec"
|
24
|
+
s.add_development_dependency "bundler", ">= 1.0.0"
|
25
|
+
s.add_development_dependency "pry"
|
26
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'logstasher/log_formatter'
|
3
|
+
|
4
|
+
describe LogStasher::LogFormatter do
|
5
|
+
|
6
|
+
class TestError < StandardError
|
7
|
+
def cause
|
8
|
+
StandardError.new('Cause error message').tap do |err|
|
9
|
+
err.set_backtrace(['/cause/item1.rb', '/cause/item2.rb'])
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:exception_message) { 'Exception message' }
|
15
|
+
let(:exception) do
|
16
|
+
e = TestError.new(exception_message)
|
17
|
+
e.set_backtrace(backtrace)
|
18
|
+
e
|
19
|
+
end
|
20
|
+
let(:backtrace) do
|
21
|
+
[
|
22
|
+
'/foo/bar.rb',
|
23
|
+
'/my/root/releases/12345/foo/broken.rb',
|
24
|
+
'/bar/foo.rb',
|
25
|
+
]
|
26
|
+
end
|
27
|
+
|
28
|
+
subject { described_class.new('base-tag', '/my/root/releases/12345') }
|
29
|
+
|
30
|
+
describe '#release' do
|
31
|
+
it 'returns indetifier of release from the root dir' do
|
32
|
+
expect(subject.release).to eq('12345')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#format' do
|
37
|
+
context 'with string as an argument' do
|
38
|
+
it 'returns hash with message key' do
|
39
|
+
expect(subject.format('foo')).to eq({ message: 'foo' })
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'with exception as an argument' do
|
44
|
+
|
45
|
+
it 'returns hash describing the exception' do
|
46
|
+
expect(subject.format(exception)).to match({
|
47
|
+
tags: 'exception',
|
48
|
+
error_class: 'TestError',
|
49
|
+
error_message: exception_message,
|
50
|
+
error_source: '/foo/broken.rb',
|
51
|
+
error_backtrace: backtrace,
|
52
|
+
error_cause: [
|
53
|
+
'StandardError',
|
54
|
+
'Cause error message',
|
55
|
+
'/cause/item1.rb',
|
56
|
+
'/cause/item2.rb',
|
57
|
+
]
|
58
|
+
})
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'with hash as an argument' do
|
63
|
+
it 'returns hash as it is with path attribute copied to request_path' do
|
64
|
+
# logstash overrides the path attribute
|
65
|
+
expect(subject.format({ foo: 'bar', path: '/my/path' })).to match({
|
66
|
+
foo: 'bar',
|
67
|
+
path: '/my/path',
|
68
|
+
request_path: '/my/path',
|
69
|
+
})
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context 'with hash containing exception key as an argument' do
|
74
|
+
it 'returns hash describing the exception merged with items from origina hash' do
|
75
|
+
expect(subject.format({ exception: exception, tags: 'custom_tag', foo: 'bar' })).to match({
|
76
|
+
tags: 'custom_tag',
|
77
|
+
error_class: 'TestError',
|
78
|
+
error_message: exception_message,
|
79
|
+
error_source: '/foo/broken.rb',
|
80
|
+
error_backtrace: backtrace,
|
81
|
+
error_cause: [
|
82
|
+
'StandardError',
|
83
|
+
'Cause error message',
|
84
|
+
'/cause/item1.rb',
|
85
|
+
'/cause/item2.rb',
|
86
|
+
],
|
87
|
+
foo: 'bar',
|
88
|
+
})
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe '#call' do
|
94
|
+
context 'when progname is nil' do
|
95
|
+
let(:progname) { nil }
|
96
|
+
|
97
|
+
it 'returns hash with message key' do
|
98
|
+
result = JSON.parse(subject.call(
|
99
|
+
:error,
|
100
|
+
Time.new(2016, 01, 02, 03, 04, 05),
|
101
|
+
progname,
|
102
|
+
exception_message)).deep_symbolize_keys!
|
103
|
+
|
104
|
+
expect(result).to include({
|
105
|
+
message: exception_message,
|
106
|
+
severity: "error",
|
107
|
+
tags: ["base-tag"],
|
108
|
+
})
|
109
|
+
|
110
|
+
expect(result.keys).not_to include(:error_class, :error_message, :error_backtrace, :error_cause)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
context 'with progname is an exception' do
|
115
|
+
let(:progname) { exception }
|
116
|
+
|
117
|
+
it 'returns hash describing the exception' do
|
118
|
+
result = JSON.parse(subject.call(
|
119
|
+
:error,
|
120
|
+
Time.new(2016, 01, 02, 03, 04, 05),
|
121
|
+
progname,
|
122
|
+
exception_message)).deep_symbolize_keys!
|
123
|
+
|
124
|
+
expect(result).to include({
|
125
|
+
message: exception_message,
|
126
|
+
tags: 'exception',
|
127
|
+
error_class: 'TestError',
|
128
|
+
error_message: exception_message,
|
129
|
+
error_source: '/foo/broken.rb',
|
130
|
+
error_backtrace: backtrace,
|
131
|
+
error_cause: [
|
132
|
+
'StandardError',
|
133
|
+
'Cause error message',
|
134
|
+
'/cause/item1.rb',
|
135
|
+
'/cause/item2.rb',
|
136
|
+
],
|
137
|
+
severity: "error",
|
138
|
+
tags: ["base-tag", "exception"],
|
139
|
+
|
140
|
+
})
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'securerandom'
|
3
|
+
|
4
|
+
require 'logstasher/log_subscriber'
|
5
|
+
|
6
|
+
class MockController
|
7
|
+
def user_id
|
8
|
+
@user_id ||= SecureRandom.hex(16)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class MockRequest
|
13
|
+
def remote_ip
|
14
|
+
'127.0.0.1'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe LogStasher::LogSubscriber do
|
19
|
+
subject { described_class.new }
|
20
|
+
|
21
|
+
let(:logger) { ::Logger.new('/dev/null') }
|
22
|
+
let(:mock_controller) { MockController.new }
|
23
|
+
let(:mock_request) { MockRequest.new }
|
24
|
+
let(:context) {{ :controller => mock_controller, :request => mock_request }}
|
25
|
+
|
26
|
+
around do |example|
|
27
|
+
backup_logger = LogStasher.logger
|
28
|
+
LogStasher.logger = logger
|
29
|
+
Thread.current[:logstasher_context] = context
|
30
|
+
Timecop.freeze { example.run }
|
31
|
+
Thread.current[:logstasher_context] = nil
|
32
|
+
LogStasher.logger = backup_logger
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '#process_action' do
|
36
|
+
let(:timestamp) { ::Time.new.utc.iso8601(3) }
|
37
|
+
let(:duration) { 12.4 }
|
38
|
+
let(:json_params) { JSON.dump(payload[:params]) }
|
39
|
+
let(:payload) {{
|
40
|
+
:controller => 'users',
|
41
|
+
:action => 'show',
|
42
|
+
:params => { 'foo' => 'bar' },
|
43
|
+
:format => 'text/plain',
|
44
|
+
:method => 'method',
|
45
|
+
:path => '/users/1',
|
46
|
+
:status => 200
|
47
|
+
}}
|
48
|
+
|
49
|
+
let(:event) { double(:payload => payload, :duration => duration) }
|
50
|
+
|
51
|
+
it 'logs the event in logstash format' do
|
52
|
+
expect(logger).to receive(:<<) do |json|
|
53
|
+
expect(JSON.parse(json)).to eq({
|
54
|
+
'@timestamp' => timestamp,
|
55
|
+
'@version' => '1',
|
56
|
+
'tags' => ['request'],
|
57
|
+
'action' => payload[:action],
|
58
|
+
'controller' => payload[:controller],
|
59
|
+
'format' => payload[:format],
|
60
|
+
'params' => json_params,
|
61
|
+
'ip' => mock_request.remote_ip,
|
62
|
+
'method' => payload[:method],
|
63
|
+
'request_path' => payload[:path],
|
64
|
+
'route' => "#{payload[:controller]}##{payload[:action]}",
|
65
|
+
'status' => payload[:status],
|
66
|
+
'runtime' => { 'total' => duration }
|
67
|
+
})
|
68
|
+
end
|
69
|
+
|
70
|
+
subject.process_action(event)
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'appends fields to the log' do
|
74
|
+
::LogStasher.append_fields do |fields|
|
75
|
+
fields['user_id'] = user_id
|
76
|
+
fields['other'] = 'stuff'
|
77
|
+
end
|
78
|
+
|
79
|
+
expect(logger).to receive(:<<) do |json|
|
80
|
+
fields = JSON.parse(json)
|
81
|
+
expect(fields['user_id']).to eq mock_controller.user_id
|
82
|
+
expect(fields['other']).to eq 'stuff'
|
83
|
+
end
|
84
|
+
|
85
|
+
subject.process_action(event)
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'can be configured to remove parameters from the log' do
|
89
|
+
allow(::LogStasher).to receive(:include_parameters?).and_return(false)
|
90
|
+
|
91
|
+
expect(logger).to receive(:<<) do |json|
|
92
|
+
expect(JSON.parse(json)['params']).to be_nil
|
93
|
+
end
|
94
|
+
|
95
|
+
subject.process_action(event)
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'can be configured to skip parameter serialization' do
|
99
|
+
expect(::LogStasher).to receive(:serialize_parameters?).and_return(false)
|
100
|
+
|
101
|
+
expect(logger).to receive(:<<) do |json|
|
102
|
+
expect(JSON.parse(json)['params']).to eq payload[:params]
|
103
|
+
end
|
104
|
+
|
105
|
+
subject.process_action(event)
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'includes redirect location in the log' do
|
109
|
+
redirect_event = double(:payload => {:location => 'new/location'})
|
110
|
+
subject.redirect_to(redirect_event)
|
111
|
+
|
112
|
+
expect(logger).to receive(:<<) do |json|
|
113
|
+
expect(JSON.parse(json)['location']).to eq 'new/location'
|
114
|
+
end
|
115
|
+
|
116
|
+
subject.process_action(event)
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'includes runtimes in the log' do
|
120
|
+
payload.merge!({
|
121
|
+
:view_runtime => 3.3,
|
122
|
+
:db_runtime => 2.1
|
123
|
+
})
|
124
|
+
|
125
|
+
expect(logger).to receive(:<<) do |json|
|
126
|
+
runtime = JSON.parse(json)['runtime']
|
127
|
+
expect(runtime['view']).to eq 3.3
|
128
|
+
expect(runtime['db']).to eq 2.1
|
129
|
+
end
|
130
|
+
|
131
|
+
subject.process_action(event)
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'includes exception info in the log' do
|
135
|
+
begin
|
136
|
+
fail RuntimeError, 'it no work'
|
137
|
+
rescue
|
138
|
+
# Test inside the rescue block so $! is properly set
|
139
|
+
payload.merge!({
|
140
|
+
:exception => ['RuntimeError', 'it no work']
|
141
|
+
})
|
142
|
+
|
143
|
+
expect(logger).to receive(:<<) do |json|
|
144
|
+
log = JSON.parse(json)
|
145
|
+
expect(log['error_class']).to eq('RuntimeError')
|
146
|
+
expect(log['error_message']).to eq('it no work')
|
147
|
+
expect(log['error_backtrace']).to be_instance_of(Array)
|
148
|
+
expect(log['status']).to eq 500
|
149
|
+
expect(log['tags']).to include('exception')
|
150
|
+
end
|
151
|
+
|
152
|
+
subject.process_action(event)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
describe '#redirect_to' do
|
158
|
+
let(:location) { "users/#{SecureRandom.hex(16)}" }
|
159
|
+
let(:payload) {{ :location => location }}
|
160
|
+
let(:event) { double(:payload => payload) }
|
161
|
+
|
162
|
+
it 'copies the location into the thread local logstasher context' do
|
163
|
+
subject.redirect_to(event)
|
164
|
+
expect(Thread.current[:logstasher_context][:location]).to eq location
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'action_controller/railtie'
|
4
|
+
require 'action_controller/log_subscriber'
|
5
|
+
require 'action_view/railtie'
|
6
|
+
require 'action_view/log_subscriber'
|
7
|
+
|
8
|
+
require 'logstasher/context_wrapper'
|
9
|
+
require 'logstasher/log_subscriber'
|
10
|
+
require 'logstasher/railtie'
|
11
|
+
require 'logstasher/silent_logger'
|
12
|
+
|
13
|
+
ENV['RAILS_ENV'] = 'test'
|
14
|
+
|
15
|
+
class ::LogStasher::RailtieApp < ::Rails::Application
|
16
|
+
end
|
17
|
+
|
18
|
+
describe ::LogStasher::Railtie do
|
19
|
+
let(:config) { described_class.config.logstasher }
|
20
|
+
|
21
|
+
describe 'logstasher.configure' do
|
22
|
+
subject do
|
23
|
+
described_class.instance.initializers.find do |initializer|
|
24
|
+
initializer.name == 'logstasher.configure'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should configure LogStasher' do
|
29
|
+
config.logger = ::Logger.new('/dev/null')
|
30
|
+
config.log_level = "log_level"
|
31
|
+
config.enabled = "enabled"
|
32
|
+
config.include_parameters = "include_parameters"
|
33
|
+
config.serialize_parameters = "serialize_parameters"
|
34
|
+
config.silence_standard_logging = "silence_standard_logging"
|
35
|
+
|
36
|
+
expect(::LogStasher).to receive(:enabled=).with("enabled")
|
37
|
+
expect(::LogStasher).to receive(:include_parameters=).with("include_parameters")
|
38
|
+
expect(::LogStasher).to receive(:serialize_parameters=).with("serialize_parameters")
|
39
|
+
expect(::LogStasher).to receive(:silence_standard_logging=).with("silence_standard_logging")
|
40
|
+
expect(::LogStasher).to receive(:logger=).with(config.logger).and_call_original
|
41
|
+
expect(config.logger).to receive(:level=).with("log_level")
|
42
|
+
|
43
|
+
subject.run
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe 'logstasher.load' do
|
48
|
+
subject do
|
49
|
+
described_class.instance.initializers.find do |initializer|
|
50
|
+
initializer.name == 'logstasher.load'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'when logstasher is disabled' do
|
55
|
+
it 'does nothing' do
|
56
|
+
expect(::ActiveSupport).not_to receive(:on_load)
|
57
|
+
|
58
|
+
subject.run
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'when logstasher is enabled' do
|
63
|
+
before { allow(::LogStasher).to receive(:enabled?) { true } }
|
64
|
+
|
65
|
+
it 'should load LogStasher into ActionController' do
|
66
|
+
expect(::ActionController).to receive(:require).with('logstasher/log_subscriber')
|
67
|
+
expect(::ActionController).to receive(:require).with('logstasher/context_wrapper')
|
68
|
+
expect(::ActionController).to receive(:include).with(::LogStasher::ContextWrapper)
|
69
|
+
|
70
|
+
subject.run
|
71
|
+
::ActiveSupport.run_load_hooks(:action_controller, ::ActionController)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe 'config.after_initialize' do
|
77
|
+
context 'when logstasher is enabled' do
|
78
|
+
before { allow(::LogStasher).to receive(:enabled?) { true } }
|
79
|
+
|
80
|
+
context 'and silence_standard_logging is enabled' do
|
81
|
+
before { allow(::LogStasher).to receive(:silence_standard_logging?) { true } }
|
82
|
+
|
83
|
+
it 'should not silence standard logging' do
|
84
|
+
expect(::ActionController::LogSubscriber).to receive(:include).with(::LogStasher::SilentLogger)
|
85
|
+
expect(::ActionView::LogSubscriber).to receive(:include).with(::LogStasher::SilentLogger)
|
86
|
+
expect(::Rails::Rack::Logger).to receive(:include).with(::LogStasher::SilentLogger)
|
87
|
+
::ActiveSupport.run_load_hooks(:after_initialize, ::LogStasher::RailtieApp)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context 'and silence_standard_logging is disabled' do
|
92
|
+
before { allow(::LogStasher).to receive(:silence_standard_logging?) { false } }
|
93
|
+
|
94
|
+
it 'should not silence standard logging' do
|
95
|
+
expect(::ActionController).not_to receive(:include).with(::LogStasher::SilentLogger)
|
96
|
+
expect(::ActionView).not_to receive(:include).with(::LogStasher::SilentLogger)
|
97
|
+
expect(::Rails::Rack::Logger).not_to receive(:include).with(::LogStasher::SilentLogger)
|
98
|
+
::ActiveSupport.run_load_hooks(:after_initialize, ::LogStasher::RailtieApp)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context 'when logstasher is disabled' do
|
104
|
+
before { allow(::LogStasher).to receive(:enabled?) { false } }
|
105
|
+
|
106
|
+
context 'and silence_standard_logging is enabled' do
|
107
|
+
before { allow(::LogStasher).to receive(:silence_standard_logging?) { true } }
|
108
|
+
|
109
|
+
it 'should not silence standard logging' do
|
110
|
+
expect(::ActionController::LogSubscriber).not_to receive(:include).with(::LogStasher::SilentLogger)
|
111
|
+
expect(::ActionView::LogSubscriber).not_to receive(:include).with(::LogStasher::SilentLogger)
|
112
|
+
expect(::Rails::Rack::Logger).not_to receive(:include).with(::LogStasher::SilentLogger)
|
113
|
+
::ActiveSupport.run_load_hooks(:after_initialize, ::LogStasher::RailtieApp)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context 'and silence_standard_logging is disabled' do
|
118
|
+
before { allow(::LogStasher).to receive(:silence_standard_logging?) { false } }
|
119
|
+
|
120
|
+
it 'should not silence standard logging' do
|
121
|
+
expect(::ActionController).not_to receive(:include).with(::LogStasher::SilentLogger)
|
122
|
+
expect(::ActionView).not_to receive(:include).with(::LogStasher::SilentLogger)
|
123
|
+
expect(::Rails::Rack::Logger).not_to receive(:include).with(::LogStasher::SilentLogger)
|
124
|
+
::ActiveSupport.run_load_hooks(:after_initialize, ::LogStasher::RailtieApp)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe LogStasher do
|
4
|
+
it 'has a version' do
|
5
|
+
expect(::LogStasher::VERSION).not_to be_nil
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'has a logger' do
|
9
|
+
expect(::LogStasher.logger).to be_a_kind_of(::Logger)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'stores a callback for appending fields' do
|
13
|
+
callback = proc { |fields| fail 'Did not expect this to run' }
|
14
|
+
|
15
|
+
::LogStasher.append_fields(&callback)
|
16
|
+
expect(::LogStasher.append_fields_callback).to be callback
|
17
|
+
end
|
18
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# Notice there is a .rspec file in the root folder. It defines rspec arguments
|
2
|
+
|
3
|
+
# Modify load path so you can require logstasher directly.
|
4
|
+
$LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
|
5
|
+
|
6
|
+
require 'rubygems'
|
7
|
+
require 'bundler/setup'
|
8
|
+
|
9
|
+
Bundler.require(:default, :development, :test)
|
10
|
+
|
11
|
+
require 'logstasher'
|
12
|
+
|
13
|
+
RSpec.configure do |config|
|
14
|
+
config.run_all_when_everything_filtered = true
|
15
|
+
end
|
metadata
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rv-logstasher
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.3.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- David Sevcik
|
8
|
+
- Alex Malkov
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2016-03-14 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: logstash-event
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '1.2'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '1.2'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: redis
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: rspec
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: bundler
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: 1.0.0
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 1.0.0
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: pry
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
description: Produces log in the logstash format
|
85
|
+
email:
|
86
|
+
- david.sevcik@reevoo.com
|
87
|
+
- alex.malkov@reevoo.com
|
88
|
+
executables: []
|
89
|
+
extensions: []
|
90
|
+
extra_rdoc_files: []
|
91
|
+
files:
|
92
|
+
- ".gitignore"
|
93
|
+
- ".rspec"
|
94
|
+
- ".travis.yml"
|
95
|
+
- Gemfile
|
96
|
+
- LICENSE.md
|
97
|
+
- README.md
|
98
|
+
- Rakefile
|
99
|
+
- lib/logstasher.rb
|
100
|
+
- lib/logstasher/context_wrapper.rb
|
101
|
+
- lib/logstasher/log_formatter.rb
|
102
|
+
- lib/logstasher/log_subscriber.rb
|
103
|
+
- lib/logstasher/rack/common_logger_adapter.rb
|
104
|
+
- lib/logstasher/railtie.rb
|
105
|
+
- lib/logstasher/silent_logger.rb
|
106
|
+
- lib/logstasher/version.rb
|
107
|
+
- lib/rv-logstasher.rb
|
108
|
+
- log/logstash_development.log
|
109
|
+
- log/logstash_test.log
|
110
|
+
- log/test.log
|
111
|
+
- logstasher.gemspec
|
112
|
+
- spec/lib/logstasher/log_formater_spec.rb
|
113
|
+
- spec/lib/logstasher/log_subscriber_spec.rb
|
114
|
+
- spec/lib/logstasher/railtie_spec.rb
|
115
|
+
- spec/lib/logstasher_spec.rb
|
116
|
+
- spec/spec_helper.rb
|
117
|
+
homepage: https://github.com/reevoo/logstasher
|
118
|
+
licenses:
|
119
|
+
- MIT
|
120
|
+
metadata: {}
|
121
|
+
post_install_message:
|
122
|
+
rdoc_options: []
|
123
|
+
require_paths:
|
124
|
+
- lib
|
125
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
126
|
+
requirements:
|
127
|
+
- - ">="
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
version: '0'
|
130
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
131
|
+
requirements:
|
132
|
+
- - ">="
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
version: '0'
|
135
|
+
requirements: []
|
136
|
+
rubyforge_project:
|
137
|
+
rubygems_version: 2.4.5.1
|
138
|
+
signing_key:
|
139
|
+
specification_version: 4
|
140
|
+
summary: Produces log in the logstash format
|
141
|
+
test_files:
|
142
|
+
- spec/lib/logstasher/log_formater_spec.rb
|
143
|
+
- spec/lib/logstasher/log_subscriber_spec.rb
|
144
|
+
- spec/lib/logstasher/railtie_spec.rb
|
145
|
+
- spec/lib/logstasher_spec.rb
|
146
|
+
- spec/spec_helper.rb
|