yarder 0.0.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.
Files changed (77) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.md +87 -0
  3. data/Rakefile +38 -0
  4. data/lib/tasks/yarder_tasks.rake +4 -0
  5. data/lib/yarder.rb +15 -0
  6. data/lib/yarder/action_controller/log_subscriber.rb +74 -0
  7. data/lib/yarder/action_view/log_subscriber.rb +31 -0
  8. data/lib/yarder/active_record/log_subscriber.rb +75 -0
  9. data/lib/yarder/active_resource/log_subscriber.rb +34 -0
  10. data/lib/yarder/configuration.rb +8 -0
  11. data/lib/yarder/core_ext/object/blank.rb +105 -0
  12. data/lib/yarder/logger.rb +55 -0
  13. data/lib/yarder/rack/logger.rb +72 -0
  14. data/lib/yarder/railtie.rb +69 -0
  15. data/lib/yarder/tagged_logging.rb +102 -0
  16. data/lib/yarder/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/dummy/README.rdoc +261 -0
  22. data/test/dummy/Rakefile +7 -0
  23. data/test/dummy/app/assets/javascripts/application.js +13 -0
  24. data/test/dummy/app/assets/javascripts/widgets.js +2 -0
  25. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  26. data/test/dummy/app/assets/stylesheets/scaffold.css +56 -0
  27. data/test/dummy/app/assets/stylesheets/widgets.css +4 -0
  28. data/test/dummy/app/controllers/application_controller.rb +3 -0
  29. data/test/dummy/app/controllers/log_subscriber_controller.rb +56 -0
  30. data/test/dummy/app/controllers/widgets_controller.rb +83 -0
  31. data/test/dummy/app/helpers/application_helper.rb +2 -0
  32. data/test/dummy/app/helpers/widgets_helper.rb +2 -0
  33. data/test/dummy/app/models/widget.rb +3 -0
  34. data/test/dummy/app/views/customers/_customer.html.erb +1 -0
  35. data/test/dummy/app/views/good_customers/_good_customer.html.erb +1 -0
  36. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  37. data/test/dummy/app/views/test/_customer.erb +1 -0
  38. data/test/dummy/app/views/test/hello_world.erb +1 -0
  39. data/test/dummy/app/views/widgets/_form.html.erb +17 -0
  40. data/test/dummy/app/views/widgets/edit.html.erb +6 -0
  41. data/test/dummy/app/views/widgets/index.html.erb +21 -0
  42. data/test/dummy/app/views/widgets/new.html.erb +5 -0
  43. data/test/dummy/app/views/widgets/show.html.erb +5 -0
  44. data/test/dummy/config.ru +4 -0
  45. data/test/dummy/config/application.rb +69 -0
  46. data/test/dummy/config/boot.rb +10 -0
  47. data/test/dummy/config/database.yml +22 -0
  48. data/test/dummy/config/environment.rb +5 -0
  49. data/test/dummy/config/environments/development.rb +31 -0
  50. data/test/dummy/config/environments/production.rb +64 -0
  51. data/test/dummy/config/environments/test.rb +35 -0
  52. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  53. data/test/dummy/config/initializers/inflections.rb +15 -0
  54. data/test/dummy/config/initializers/mime_types.rb +5 -0
  55. data/test/dummy/config/initializers/secret_token.rb +7 -0
  56. data/test/dummy/config/initializers/session_store.rb +8 -0
  57. data/test/dummy/config/initializers/wrap_parameters.rb +10 -0
  58. data/test/dummy/config/locales/en.yml +5 -0
  59. data/test/dummy/config/routes.rb +69 -0
  60. data/test/dummy/db/migrate/20120927084605_create_widgets.rb +8 -0
  61. data/test/dummy/db/schema.rb +16 -0
  62. data/test/dummy/log/development.log +19 -0
  63. data/test/dummy/log/test.log +10694 -0
  64. data/test/dummy/public/404.html +26 -0
  65. data/test/dummy/public/422.html +26 -0
  66. data/test/dummy/public/500.html +25 -0
  67. data/test/dummy/public/favicon.ico +0 -0
  68. data/test/dummy/script/rails +6 -0
  69. data/test/logger_test.rb +126 -0
  70. data/test/rack/logger_test.rb +68 -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 +31 -0
  76. data/test/yarder_test.rb +7 -0
  77. metadata +236 -0
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2012 YOURNAME
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,87 @@
1
+ # Yarder
2
+
3
+ [![Build Status](https://secure.travis-ci.org/rurounijones/yarder.png)](http://travis-ci.org/rurounijones/yarder)
4
+ [![Dependency Status](https://gemnasium.com/rurounijones/yarder.png)](https://gemnasium.com/rurounijones/yarder)
5
+ [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/rurounijones/yarder)
6
+
7
+ JSON Based Replacement logging system for Ruby on Rails.
8
+
9
+ This is an experimental gem to see how easy / difficult it is to completely replace the default Ruby
10
+ on Rails logging system with one based on outputting JSON messages.
11
+
12
+ This gem will create JSON based log entries designed for consumption by Logstash (although being
13
+ JSON they can be read by other software). The JSON will contain the same information as can be found
14
+ in the default rails logging output.
15
+
16
+ ## Current Status
17
+
18
+ This gem is not production ready however it is probably ready for people interested in seeing the
19
+ results. All logging in a Rails3 app should be JSON formatted, including ad-hoc logging.
20
+
21
+ Yarder has only been tested against Rails 3.2.8 on Ruby 1.9.3 and JRuby running in 1.9 mode. Test
22
+ coverage is reasonable and most of the original Rails3 logging tests are passing. Additional tests
23
+ unique to this gem still need to be created.
24
+
25
+ There may be issues regarding outputting UTF-8 characters in logs on JRuby 1.6 in --1.9 mode. JRuby
26
+ 1.7 is recommended (These same issues exist in the man ruby loggers so use that as a guide).
27
+
28
+ Any help, feedback or pull-requests would be much appreciated, especially related to refactoring and
29
+ test improvement
30
+
31
+ ## Installation
32
+
33
+ Add this line to your Rails application's Gemfile:
34
+
35
+ ```ruby
36
+ gem 'yarder'
37
+ ```
38
+
39
+ ## Configuration
40
+
41
+ Yarder uses the Rails logger (set using config.logger in application.rb)to log output.
42
+
43
+ By default Rails uses the TaggedLogging class to provide this however because Yarder
44
+ replaces it you will need to change the default to something else.
45
+
46
+ You will need to specify a Ruby Logger compatible logger. Yarder provides its own
47
+ logger which is a copy of the ActiveSupport::Logger (Formerly known as
48
+ ActiveSupport::BufferedLogger)
49
+
50
+ If you are not sure what you want yet then set the Yarder::Logger as in the example
51
+ below in your application.rb file.
52
+
53
+ ```ruby
54
+ module MyApp
55
+ class Application < Rails::Application
56
+
57
+ # Set a logger compatible with the standard ruby logger to be used by Yarder
58
+ config.logger = Yarder::Logger.new(Rails.root.join('log',"#{Rails.env}.log").to_s)
59
+
60
+ end
61
+ end
62
+ ```
63
+
64
+ ## Logstash Configuration
65
+
66
+ Yarder currently creates log entries with a hard-coded logtype of "rails_json_log" (This may change
67
+ in future and may become configurable) therefore your Logstash configuration file should be as
68
+ follows:
69
+
70
+ ```
71
+ input {
72
+ file {
73
+ type => "rails_json_log"
74
+ path => "/var/www/rails/application-1/log/production.log" # Path to your log file
75
+ format => "json_event"
76
+ }
77
+ }
78
+ ```
79
+
80
+ You will need to edit the path to point to your application's log file. Because Yarder creates json
81
+ serialized Logstash::Event entries there is no need to setup any filters
82
+
83
+ ### Known issues
84
+
85
+ Yarder currently creates nested JSON. While this is supported in Logstash and Elastic Search the web
86
+ interfaces do not as yet support it. Depending on whether support is possible or not Yarder may
87
+ change to a non-nested format.
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 = 'Yarder'
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,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :yarder do
3
+ # # Task goes here
4
+ # end
data/lib/yarder.rb ADDED
@@ -0,0 +1,15 @@
1
+ require 'yarder/railtie' if defined?(Rails)
2
+ require 'yarder/rack/logger'
3
+ require 'logstash-event'
4
+ require 'yarder/logger'
5
+ require 'yarder/tagged_logging'
6
+
7
+ module Yarder
8
+
9
+ class IncompatibleLogger < StandardError; end
10
+
11
+ def self.log_entries
12
+ @@events ||= {}
13
+ end
14
+
15
+ end
@@ -0,0 +1,74 @@
1
+ require 'yarder/core_ext/object/blank'
2
+
3
+ module Yarder
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
+ additions = ::ActionController::Base.log_process_action(payload)
23
+
24
+ params = payload[:params].except(*INTERNAL_PARAMS)
25
+ entry.fields['parameters'] = params unless params.empty?
26
+
27
+ entry.fields['controller_duration'] = event.duration
28
+
29
+ #TODO What on earth are additions and how should we handle them?
30
+ # message << " (#{additions.join(" | ")})" unless additions.blank?
31
+
32
+ end
33
+
34
+ def halted_callback(event)
35
+ entry.fields['halted_callback'] = event.payload[:filter]
36
+ end
37
+
38
+ def send_file(event)
39
+ entry.fields['send_file'] = event.payload[:path]
40
+ entry.fields['send_file_duration'] = event.duration
41
+ end
42
+
43
+ def redirect_to(event)
44
+ entry.fields['redirect_to'] = event.payload[:location]
45
+ end
46
+
47
+ def send_data(event)
48
+ entry.fields['send_data'] = event.payload[:filename]
49
+ entry.fields['send_data_duration'] = event.duration
50
+ end
51
+
52
+ %w(write_fragment read_fragment exist_fragment?
53
+ expire_fragment expire_page write_page).each do |method|
54
+ class_eval <<-METHOD, __FILE__, __LINE__ + 1
55
+ def #{method}(event)
56
+ entry.fields['cache'] ||= []
57
+ cache_event = {}
58
+ cache_event['key_or_path'] = event.payload[:key] || event.payload[:path]
59
+ cache_event['type'] = #{method.to_s.humanize.inspect}
60
+ cache_event['duration'] = event.duration
61
+ entry.fields['cache'] << cache_event
62
+ end
63
+ METHOD
64
+ end
65
+
66
+ private
67
+
68
+ def entry
69
+ Yarder.log_entries[Thread.current]
70
+ end
71
+
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,31 @@
1
+ module Yarder
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
+ Yarder.log_entries[Thread.current]
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,75 @@
1
+ module Yarder
2
+ module ActiveRecord
3
+ class LogSubscriber < ::ActiveSupport::LogSubscriber
4
+
5
+ def self.runtime=(value)
6
+ Thread.current["active_record_sql_runtime"] = value
7
+ end
8
+
9
+ def self.runtime
10
+ Thread.current["active_record_sql_runtime"] ||= 0
11
+ end
12
+
13
+ def self.reset_runtime
14
+ rt, self.runtime = runtime, 0
15
+ rt
16
+ end
17
+
18
+ def initialize
19
+ super
20
+ end
21
+
22
+ def sql(event)
23
+ self.class.runtime += event.duration
24
+ return unless logger.debug?
25
+
26
+ payload = event.payload
27
+
28
+ return if 'SCHEMA' == payload[:name]
29
+
30
+ entry.fields['sql'] ||= []
31
+ sql_entry = {}
32
+ sql_entry['name'] = payload[:name]
33
+ sql_entry['duration'] = event.duration
34
+ sql_entry['sql']= payload[:sql].squeeze(' ')
35
+
36
+ binds = nil
37
+
38
+ unless (payload[:binds] || []).empty?
39
+ binds = " " + payload[:binds].map { |col,v|
40
+ [col.name, v]
41
+ }.inspect
42
+ end
43
+
44
+ sql_entry['binds'] = binds unless binds.nil?
45
+
46
+ entry.fields['sql'] << sql_entry
47
+ end
48
+
49
+ def identity(event)
50
+ return unless logger.debug?
51
+
52
+ payload = event.payload
53
+
54
+ sql_entry = {}
55
+ sql_entry['name'] = payload[:name]
56
+ sql_entry['line'] = payload[:line]
57
+ sql_entry['duration'] = payload[:duration]
58
+
59
+ entry.fields['sql'] << sql_entry
60
+ end
61
+
62
+ private
63
+
64
+ def logger
65
+ ::ActiveRecord::Base.logger
66
+ end
67
+
68
+
69
+ def entry
70
+ Yarder.log_entries[Thread.current]
71
+ end
72
+
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,34 @@
1
+ module Yarder
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
+ Yarder.log_entries[Thread.current]
29
+ end
30
+
31
+ end
32
+ end
33
+ end
34
+
@@ -0,0 +1,8 @@
1
+ module Yarder
2
+ class Configuration
3
+ attr_accessor :logger
4
+
5
+ def initialize
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,105 @@
1
+ # encoding: utf-8
2
+
3
+ class Object
4
+ # An object is blank if it's false, empty, or a whitespace string.
5
+ # For example, '', ' ', +nil+, [], and {} are all blank.
6
+ #
7
+ # This simplifies:
8
+ #
9
+ # if address.nil? || address.empty?
10
+ #
11
+ # ...to:
12
+ #
13
+ # if address.blank?
14
+ def blank?
15
+ respond_to?(:empty?) ? empty? : !self
16
+ end
17
+
18
+ # An object is present if it's not <tt>blank?</tt>.
19
+ def present?
20
+ !blank?
21
+ end
22
+
23
+ # Returns object if it's <tt>present?</tt> otherwise returns +nil+.
24
+ # <tt>object.presence</tt> is equivalent to <tt>object.present? ? object : nil</tt>.
25
+ #
26
+ # This is handy for any representation of objects where blank is the same
27
+ # as not present at all. For example, this simplifies a common check for
28
+ # HTTP POST/query parameters:
29
+ #
30
+ # state = params[:state] if params[:state].present?
31
+ # country = params[:country] if params[:country].present?
32
+ # region = state || country || 'US'
33
+ #
34
+ # ...becomes:
35
+ #
36
+ # region = params[:state].presence || params[:country].presence || 'US'
37
+ def presence
38
+ self if present?
39
+ end
40
+ end
41
+
42
+ class NilClass
43
+ # +nil+ is blank:
44
+ #
45
+ # nil.blank? # => true
46
+ def blank?
47
+ true
48
+ end
49
+ end
50
+
51
+ class FalseClass
52
+ # +false+ is blank:
53
+ #
54
+ # false.blank? # => true
55
+ def blank?
56
+ true
57
+ end
58
+ end
59
+
60
+ class TrueClass
61
+ # +true+ is not blank:
62
+ #
63
+ # true.blank? # => false
64
+ def blank?
65
+ false
66
+ end
67
+ end
68
+
69
+ class Array
70
+ # An array is blank if it's empty:
71
+ #
72
+ # [].blank? # => true
73
+ # [1,2,3].blank? # => false
74
+ alias_method :blank?, :empty?
75
+ end
76
+
77
+ class Hash
78
+ # A hash is blank if it's empty:
79
+ #
80
+ # {}.blank? # => true
81
+ # { key: 'value' }.blank? # => false
82
+ alias_method :blank?, :empty?
83
+ end
84
+
85
+ class String
86
+ # A string is blank if it's empty or contains whitespaces only:
87
+ #
88
+ # ''.blank? # => true
89
+ # ' '.blank? # => true
90
+ # ' '.blank? # => true
91
+ # ' something here '.blank? # => false
92
+ def blank?
93
+ self !~ /[^[:space:]]/
94
+ end
95
+ end
96
+
97
+ class Numeric #:nodoc:
98
+ # No number is blank:
99
+ #
100
+ # 1.blank? # => false
101
+ # 0.blank? # => false
102
+ def blank?
103
+ false
104
+ end
105
+ end