rails-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.
Files changed (76) hide show
  1. checksums.yaml +15 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +100 -0
  4. data/Rakefile +38 -0
  5. data/lib/rails_logstasher.rb +16 -0
  6. data/lib/rails_logstasher/action_controller/log_subscriber.rb +75 -0
  7. data/lib/rails_logstasher/action_view/log_subscriber.rb +31 -0
  8. data/lib/rails_logstasher/active_record/log_subscriber.rb +88 -0
  9. data/lib/rails_logstasher/active_resource/log_subscriber.rb +34 -0
  10. data/lib/rails_logstasher/core_ext/object/blank.rb +110 -0
  11. data/lib/rails_logstasher/event.rb +44 -0
  12. data/lib/rails_logstasher/logger.rb +55 -0
  13. data/lib/rails_logstasher/rack/logger.rb +53 -0
  14. data/lib/rails_logstasher/railtie.rb +70 -0
  15. data/lib/rails_logstasher/tagged_logging.rb +108 -0
  16. data/lib/rails_logstasher/version.rb +3 -0
  17. data/test/action_controller/log_subscriber_test.rb +165 -0
  18. data/test/action_view/log_subscriber_test.rb +89 -0
  19. data/test/active_record/log_subscriber_test.rb +87 -0
  20. data/test/active_resource/log_subscriber_test.rb +42 -0
  21. data/test/core_ext/blank_test.rb +31 -0
  22. data/test/dummy/README.rdoc +261 -0
  23. data/test/dummy/Rakefile +7 -0
  24. data/test/dummy/app/assets/javascripts/application.js +13 -0
  25. data/test/dummy/app/assets/javascripts/widgets.js +2 -0
  26. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  27. data/test/dummy/app/assets/stylesheets/scaffold.css +56 -0
  28. data/test/dummy/app/assets/stylesheets/widgets.css +4 -0
  29. data/test/dummy/app/controllers/application_controller.rb +3 -0
  30. data/test/dummy/app/controllers/log_subscriber_controller.rb +56 -0
  31. data/test/dummy/app/controllers/widgets_controller.rb +83 -0
  32. data/test/dummy/app/helpers/application_helper.rb +2 -0
  33. data/test/dummy/app/helpers/widgets_helper.rb +2 -0
  34. data/test/dummy/app/models/widget.rb +3 -0
  35. data/test/dummy/app/views/customers/_customer.html.erb +1 -0
  36. data/test/dummy/app/views/good_customers/_good_customer.html.erb +1 -0
  37. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  38. data/test/dummy/app/views/test/_customer.erb +1 -0
  39. data/test/dummy/app/views/test/hello_world.erb +1 -0
  40. data/test/dummy/app/views/widgets/_form.html.erb +17 -0
  41. data/test/dummy/app/views/widgets/edit.html.erb +6 -0
  42. data/test/dummy/app/views/widgets/index.html.erb +21 -0
  43. data/test/dummy/app/views/widgets/new.html.erb +5 -0
  44. data/test/dummy/app/views/widgets/show.html.erb +5 -0
  45. data/test/dummy/config.ru +4 -0
  46. data/test/dummy/config/application.rb +69 -0
  47. data/test/dummy/config/boot.rb +10 -0
  48. data/test/dummy/config/database.yml +22 -0
  49. data/test/dummy/config/environment.rb +5 -0
  50. data/test/dummy/config/environments/development.rb +31 -0
  51. data/test/dummy/config/environments/production.rb +64 -0
  52. data/test/dummy/config/environments/test.rb +35 -0
  53. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  54. data/test/dummy/config/initializers/inflections.rb +15 -0
  55. data/test/dummy/config/initializers/mime_types.rb +5 -0
  56. data/test/dummy/config/initializers/secret_token.rb +7 -0
  57. data/test/dummy/config/initializers/session_store.rb +8 -0
  58. data/test/dummy/config/initializers/wrap_parameters.rb +10 -0
  59. data/test/dummy/config/locales/en.yml +5 -0
  60. data/test/dummy/config/routes.rb +69 -0
  61. data/test/dummy/db/migrate/20120927084605_create_widgets.rb +8 -0
  62. data/test/dummy/db/schema.rb +16 -0
  63. data/test/dummy/public/404.html +26 -0
  64. data/test/dummy/public/422.html +26 -0
  65. data/test/dummy/public/500.html +25 -0
  66. data/test/dummy/public/favicon.ico +0 -0
  67. data/test/dummy/script/rails +6 -0
  68. data/test/logger_test.rb +126 -0
  69. data/test/rack/logger_test.rb +68 -0
  70. data/test/rails_logstasher_test.rb +7 -0
  71. data/test/support/fake_models.rb +12 -0
  72. data/test/support/integration_case.rb +5 -0
  73. data/test/support/multibyte_test_helpers.rb +19 -0
  74. data/test/tagged_logging_test.rb +155 -0
  75. data/test/test_helper.rb +40 -0
  76. metadata +219 -0
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NDk3MDhmNmExY2Y2NDM5MmFiY2Y1OTRhZjBmNzU3YzU5NGRjMmM2Ng==
5
+ data.tar.gz: !binary |-
6
+ ZjI1YTkzZWQzZDQ3MzRmYWFmNzZlMGE0ZmIwOWJiMzkwYmE0Y2EyYw==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ OGMwMDBlNjMyOWM2MTEyYTY3NjYxOTAzMmVmNzAxZWM4MzQwNGZkYzVhMjZj
10
+ MTk0MjhjYzNiNDM4M2FjY2ZlOTM4ZjQ3MzU3N2VmZTIwMzBiMzc2MGVmN2Qw
11
+ YTY1MzlmYTA5ZjQyNTdmOGNjYzkyYmVkMDQ3YzA5MGQzNDE4MzI=
12
+ data.tar.gz: !binary |-
13
+ ZDQyZmUyZmJkMjFmOTRjMWVhNTljYmZhOGVlM2UzOTQyMzQyMGQ4NzNiNWJm
14
+ YTdmYjI1NmJiNmE0ZDA2MDg4ZmZkNmQ3NjFkZTMyNDNiMTQ2MWY4ZmI2ODQz
15
+ M2RkYjQ0MjBlMmY2MTU4OTdjOTY0NjcyN2MxODQyNzNhM2RhY2Q=
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2012 Jeffrey Jones
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,100 @@
1
+ # RailsLogstasher
2
+
3
+ [![Build Status](https://secure.travis-ci.org/capriza/rails-logstasher.png)](http://travis-ci.org/capriza/rails-logstasher)
4
+ [![Coverage Status](https://coveralls.io/repos/capriza/rails-logstasher/badge.png?branch=master)](https://coveralls.io/r/capriza/rails-logstasher)
5
+ [![Code Climate](https://codeclimate.com/github/capriza/rails-logstasher.png)](https://codeclimate.com/github/capriza/rails-logstasher)
6
+ [![Dependency Status](https://gemnasium.com/capriza/rails-logstasher.png)](https://gemnasium.com/capriza/rails-logstasher)
7
+
8
+ Logstash logging system for Ruby on Rails.
9
+ This is a fork from https://github.com/rurounijones/yarder.
10
+
11
+ This gem will create JSON based log entries designed for consumption by Logstash version 1.2.
12
+ The JSON will contain the same information as can be found in the default rails logging output.
13
+
14
+ ## Current Status
15
+
16
+ All logging in a Rails3 app should be JSON formatted, including ad-hoc logging.
17
+
18
+ RailsLogstasher has been tested against Rails 3.2.16 on Ruby 1.9.3.
19
+
20
+ ## Installation
21
+
22
+ Add this line to your Rails application's Gemfile:
23
+
24
+ ```ruby
25
+ gem 'rails-logstasher'
26
+ ```
27
+
28
+ ## Configuration
29
+
30
+ RailsLogstasher uses the Rails logger (set using config.logger in application.rb)to log output.
31
+
32
+ By default Rails uses the TaggedLogging class to provide this however because RailsLogstasher
33
+ replaces it you will need to change the default to something else.
34
+
35
+ You will need to specify a Ruby Logger compatible logger. RailsLogstasher provides its own
36
+ logger which is a copy of the ActiveSupport::Logger (Formerly known as
37
+ ActiveSupport::BufferedLogger)
38
+
39
+ If you are not sure what you want yet then set the RailsLogstasher::Logger as in the example
40
+ below in your application.rb file.
41
+
42
+ ```ruby
43
+ module MyApp
44
+ class Application < Rails::Application
45
+
46
+ # Set a logger compatible with the standard ruby logger to be used by RailsLogstasher
47
+ config.logger = RailsLogstasher::Logger.new(Rails.root.join('log',"#{Rails.env}.log").to_s)
48
+
49
+ end
50
+ end
51
+ ```
52
+
53
+ ## Logstash Configuration
54
+
55
+ RailsLogstasher creates log entries with a default type of "rails", therefore your Logstash
56
+ configuration file should be as follows:
57
+
58
+ ```
59
+ input {
60
+ file {
61
+ type => "rails"
62
+ path => "/var/www/rails/application-1/log/production.log" # Path to your log file
63
+ format => "json_event"
64
+ }
65
+ }
66
+ ```
67
+
68
+ The type can be configured via the application configuration "log_type" setting, like so:
69
+
70
+ ```
71
+ module MyApp
72
+ class Application < Rails::Application
73
+
74
+ # Set a different type for the events
75
+ config.log_type = 'my_type'
76
+
77
+ end
78
+ end
79
+ ```
80
+
81
+ You will need to edit the path to point to your application's log file. Because RailsLogstasher creates json
82
+ serialized Logstash::Event entries there is no need to setup any filters
83
+
84
+ ### Known issues
85
+
86
+ RailsLogstasher currently creates nested JSON. Kibana has pretty good (With a few small UI problems) support
87
+ for nested JSON but logstash web does not.
88
+
89
+ ## Developers
90
+
91
+ Thoughts, suggestions, opinions and contributions are welcome.
92
+
93
+ When contributing please make sure to run your tests with warnings enabled and make sure that
94
+ rails-logstasher creates no warnings. (Warnings from other libraries like capybara etc. are ok)
95
+
96
+ ```
97
+ RUBYOPT=-w rake
98
+ ```
99
+
100
+
data/Rakefile ADDED
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'RailsLogstasher'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+
24
+
25
+
26
+ Bundler::GemHelper.install_tasks
27
+
28
+ require 'rake/testtask'
29
+
30
+ Rake::TestTask.new(:test) do |t|
31
+ t.libs << 'lib'
32
+ t.libs << 'test'
33
+ t.pattern = 'test/**/*_test.rb'
34
+ t.verbose = false
35
+ end
36
+
37
+
38
+ task :default => :test
@@ -0,0 +1,16 @@
1
+ require 'rails_logstasher/railtie' if defined?(Rails)
2
+ require 'rails_logstasher/rack/logger'
3
+ require 'rails_logstasher/event'
4
+ require 'logstash-event'
5
+ require 'rails_logstasher/logger'
6
+ require 'rails_logstasher/tagged_logging'
7
+
8
+ module RailsLogstasher
9
+
10
+ class IncompatibleLogger < StandardError; end
11
+
12
+ def self.log_entries
13
+ @@events ||= {}
14
+ end
15
+
16
+ end
@@ -0,0 +1,75 @@
1
+ require 'rails_logstasher/core_ext/object/blank'
2
+
3
+ module RailsLogstasher
4
+ module ActionController
5
+ class LogSubscriber < ::ActiveSupport::LogSubscriber
6
+ INTERNAL_PARAMS = %w(controller action format _method only_path)
7
+
8
+ def start_processing(event)
9
+ payload = event.payload
10
+
11
+ entry.fields['controller'] = payload[:controller]
12
+ entry.fields['action'] = payload[:action]
13
+
14
+ format = payload[:format]
15
+ entry.fields['format'] = format.to_s.downcase if format.is_a?(Symbol)
16
+
17
+ end
18
+
19
+ def process_action(event)
20
+
21
+ payload = event.payload
22
+ #TODO Think about additions. Comment out for the moment to shut up warnings
23
+ #additions = ::ActionController::Base.log_process_action(payload)
24
+
25
+ params = payload[:params].except(*INTERNAL_PARAMS)
26
+ entry.fields['parameters'] = params unless params.empty?
27
+
28
+ entry.fields['controller_duration'] = event.duration
29
+
30
+ #TODO What on earth are additions and how should we handle them?
31
+ # message << " (#{additions.join(" | ")})" unless additions.blank?
32
+
33
+ end
34
+
35
+ def halted_callback(event)
36
+ entry.fields['halted_callback'] = event.payload[:filter]
37
+ end
38
+
39
+ def send_file(event)
40
+ entry.fields['send_file'] = event.payload[:path]
41
+ entry.fields['send_file_duration'] = event.duration
42
+ end
43
+
44
+ def redirect_to(event)
45
+ entry.fields['redirect_to'] = event.payload[:location]
46
+ end
47
+
48
+ def send_data(event)
49
+ entry.fields['send_data'] = event.payload[:filename]
50
+ entry.fields['send_data_duration'] = event.duration
51
+ end
52
+
53
+ %w(write_fragment read_fragment exist_fragment?
54
+ expire_fragment expire_page write_page).each do |method|
55
+ class_eval <<-METHOD, __FILE__, __LINE__ + 1
56
+ def #{method}(event)
57
+ entry.fields['cache'] ||= []
58
+ cache_event = {}
59
+ cache_event['key_or_path'] = event.payload[:key] || event.payload[:path]
60
+ cache_event['type'] = #{method.to_s.humanize.inspect}
61
+ cache_event['duration'] = event.duration
62
+ entry.fields['cache'] << cache_event
63
+ end
64
+ METHOD
65
+ end
66
+
67
+ private
68
+
69
+ def entry
70
+ RailsLogstasher.log_entries[Thread.current]
71
+ end
72
+
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,31 @@
1
+ module RailsLogstasher
2
+ module ActionView
3
+
4
+ class LogSubscriber < ::ActiveSupport::LogSubscriber
5
+
6
+ def render_template(event)
7
+ entry.fields['rendering'] ||= []
8
+ render_entry = {}
9
+ render_entry['identifier'] = from_rails_root(event.payload[:identifier])
10
+ render_entry['layout'] = from_rails_root(event.payload[:layout]) if event.payload[:layout]
11
+ render_entry['duration'] = event.duration
12
+
13
+ entry.fields['rendering'] << render_entry
14
+
15
+ end
16
+ alias :render_partial :render_template
17
+ alias :render_collection :render_template
18
+
19
+ private
20
+
21
+ def from_rails_root(string)
22
+ string.sub("#{Rails.root}/", "").sub(/^app\/views\//, "")
23
+ end
24
+
25
+ def entry
26
+ RailsLogstasher.log_entries[Thread.current]
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,88 @@
1
+ require "securerandom"
2
+
3
+ module RailsLogstasher
4
+ module ActiveRecord
5
+ class LogSubscriber < ::ActiveSupport::LogSubscriber
6
+
7
+ def self.runtime=(value)
8
+ Thread.current["active_record_sql_runtime"] = value
9
+ end
10
+
11
+ def self.runtime
12
+ Thread.current["active_record_sql_runtime"] ||= 0
13
+ end
14
+
15
+ def self.reset_runtime
16
+ rt, self.runtime = runtime, 0
17
+ rt
18
+ end
19
+
20
+ def initialize
21
+ super
22
+ end
23
+
24
+ def sql(event)
25
+ self.class.runtime += event.duration
26
+ return unless logger.debug?
27
+
28
+ payload = event.payload
29
+
30
+ return if 'SCHEMA' == payload[:name]
31
+
32
+ sql_entry = {}
33
+ sql_entry['name'] = payload[:name]
34
+ sql_entry['duration'] = event.duration
35
+ sql_entry['sql']= payload[:sql].squeeze(' ')
36
+
37
+ binds = nil
38
+
39
+ unless (payload[:binds] || []).empty?
40
+ binds = " " + payload[:binds].map { |col,v|
41
+ [col.name, v]
42
+ }.inspect
43
+ end
44
+
45
+ sql_entry['binds'] = binds unless binds.nil?
46
+
47
+ write_entry sql_entry
48
+ end
49
+
50
+ def identity(event)
51
+ return unless logger.debug?
52
+
53
+ payload = event.payload
54
+
55
+ sql_entry = {}
56
+ sql_entry['name'] = payload[:name]
57
+ sql_entry['line'] = payload[:line]
58
+ sql_entry['duration'] = payload[:duration]
59
+
60
+ write_entry sql_entry
61
+ end
62
+
63
+ private
64
+
65
+ def write_entry(sql_entry)
66
+ entry = log_entry
67
+ entry.fields['sql'] ||= []
68
+ entry.fields['sql'] << sql_entry
69
+ entry.write(false)
70
+ end
71
+
72
+ def logger
73
+ ::ActiveRecord::Base.logger
74
+ end
75
+
76
+ def log_entry
77
+ RailsLogstasher.log_entries[Thread.current] ||
78
+ RailsLogstasher::Event.new(Rails.logger, false).tap do |entry|
79
+ entry.fields['uuid'] = SecureRandom.uuid
80
+ #TODO Should really move this into the base logger
81
+ entry.source = Socket.gethostname
82
+ entry.type = "rails_json_log"
83
+ end
84
+ end
85
+
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,34 @@
1
+ module RailsLogstasher
2
+ module ActiveResource
3
+ class LogSubscriber < ::ActiveSupport::LogSubscriber
4
+
5
+ def request(event)
6
+
7
+ #TODO Think of a better name for this!
8
+ entry.fields['active_resource'] ||= []
9
+
10
+ request_entry = {}
11
+
12
+ request_entry['method'] = event.payload[:method].to_s.upcase
13
+ request_entry['uri'] = event.payload[:request_uri]
14
+
15
+ result = event.payload[:result]
16
+
17
+ request_entry['code'] = result.code
18
+ request_entry['message'] = result.message
19
+ request_entry['length'] = result.body.to_s.length
20
+ request_entry['duration'] = event.duration
21
+
22
+ entry.fields['active_resource'] << request_entry
23
+ end
24
+
25
+ private
26
+
27
+ def entry
28
+ RailsLogstasher.log_entries[Thread.current]
29
+ end
30
+
31
+ end
32
+ end
33
+ end
34
+
@@ -0,0 +1,110 @@
1
+ # encoding: utf-8
2
+
3
+ # This file is copied verbatim from ActiveSupport for compatibility
4
+ # should rails_logstasher be used outside of a Rails project. Therefor we
5
+ # won't process this file if ActiveSupport is already defined
6
+ unless defined?(ActiveSupport)
7
+ class Object
8
+ # An object is blank if it's false, empty, or a whitespace string.
9
+ # For example, '', ' ', +nil+, [], and {} are all blank.
10
+ #
11
+ # This simplifies:
12
+ #
13
+ # if address.nil? || address.empty?
14
+ #
15
+ # ...to:
16
+ #
17
+ # if address.blank?
18
+ def blank?
19
+ respond_to?(:empty?) ? empty? : !self
20
+ end
21
+
22
+ # An object is present if it's not <tt>blank?</tt>.
23
+ def present?
24
+ !blank?
25
+ end
26
+
27
+ # Returns object if it's <tt>present?</tt> otherwise returns +nil+.
28
+ # <tt>object.presence</tt> is equivalent to <tt>object.present? ? object : nil</tt>.
29
+ #
30
+ # This is handy for any representation of objects where blank is the same
31
+ # as not present at all. For example, this simplifies a common check for
32
+ # HTTP POST/query parameters:
33
+ #
34
+ # state = params[:state] if params[:state].present?
35
+ # country = params[:country] if params[:country].present?
36
+ # region = state || country || 'US'
37
+ #
38
+ # ...becomes:
39
+ #
40
+ # region = params[:state].presence || params[:country].presence || 'US'
41
+ def presence
42
+ self if present?
43
+ end
44
+ end
45
+
46
+ class NilClass
47
+ # +nil+ is blank:
48
+ #
49
+ # nil.blank? # => true
50
+ def blank?
51
+ true
52
+ end
53
+ end
54
+
55
+ class FalseClass
56
+ # +false+ is blank:
57
+ #
58
+ # false.blank? # => true
59
+ def blank?
60
+ true
61
+ end
62
+ end
63
+
64
+ class TrueClass
65
+ # +true+ is not blank:
66
+ #
67
+ # true.blank? # => false
68
+ def blank?
69
+ false
70
+ end
71
+ end
72
+
73
+ class Array
74
+ # An array is blank if it's empty:
75
+ #
76
+ # [].blank? # => true
77
+ # [1,2,3].blank? # => false
78
+ alias_method :blank?, :empty?
79
+ end
80
+
81
+ class Hash
82
+ # A hash is blank if it's empty:
83
+ #
84
+ # {}.blank? # => true
85
+ # { key: 'value' }.blank? # => false
86
+ alias_method :blank?, :empty?
87
+ end
88
+
89
+ class String
90
+ # A string is blank if it's empty or contains whitespaces only:
91
+ #
92
+ # ''.blank? # => true
93
+ # ' '.blank? # => true
94
+ # ' '.blank? # => true
95
+ # ' something here '.blank? # => false
96
+ def blank?
97
+ self !~ /[^[:space:]]/
98
+ end
99
+ end
100
+
101
+ class Numeric #:nodoc:
102
+ # No number is blank:
103
+ #
104
+ # 1.blank? # => false
105
+ # 0.blank? # => false
106
+ def blank?
107
+ false
108
+ end
109
+ end
110
+ end