mongodb_logger 0.2.6-jruby

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) hide show
  1. data/.gitignore +20 -0
  2. data/.rvmrc +1 -0
  3. data/.travis.yml +19 -0
  4. data/CHANGELOG.md +30 -0
  5. data/Gemfile +10 -0
  6. data/LICENSE +22 -0
  7. data/README.md +207 -0
  8. data/Rakefile +169 -0
  9. data/SUPPORTED_RAILS_VERSIONS +17 -0
  10. data/TESTING.md +24 -0
  11. data/bin/mongodb_logger_web +24 -0
  12. data/config.ru +17 -0
  13. data/examples/server_config.yml +5 -0
  14. data/features/mongodb_logger_web.feature +14 -0
  15. data/features/rails.feature +12 -0
  16. data/features/step_definitions/mongodb_logger_web_steps.rb +45 -0
  17. data/features/step_definitions/rails_application_steps.rb +65 -0
  18. data/features/support/env.rb +15 -0
  19. data/features/support/rails.rb +98 -0
  20. data/features/support/terminal.rb +95 -0
  21. data/lib/mongodb_logger/initializer_mixin.rb +26 -0
  22. data/lib/mongodb_logger/logger.rb +239 -0
  23. data/lib/mongodb_logger/railtie.rb +12 -0
  24. data/lib/mongodb_logger/replica_set_helper.rb +19 -0
  25. data/lib/mongodb_logger/server/coffee/logs.coffee +250 -0
  26. data/lib/mongodb_logger/server/content_for.rb +58 -0
  27. data/lib/mongodb_logger/server/model/additional_filter.rb +104 -0
  28. data/lib/mongodb_logger/server/model/analytic.rb +82 -0
  29. data/lib/mongodb_logger/server/model/filter.rb +84 -0
  30. data/lib/mongodb_logger/server/partials.rb +24 -0
  31. data/lib/mongodb_logger/server/public/images/arrow-down.png +0 -0
  32. data/lib/mongodb_logger/server/public/images/arrow-up.png +0 -0
  33. data/lib/mongodb_logger/server/public/images/date.png +0 -0
  34. data/lib/mongodb_logger/server/public/images/external.png +0 -0
  35. data/lib/mongodb_logger/server/public/images/failure.png +0 -0
  36. data/lib/mongodb_logger/server/public/images/logo.png +0 -0
  37. data/lib/mongodb_logger/server/public/images/mongodb.png +0 -0
  38. data/lib/mongodb_logger/server/public/images/newlog.png +0 -0
  39. data/lib/mongodb_logger/server/public/images/play-icon.png +0 -0
  40. data/lib/mongodb_logger/server/public/images/spinner.gif +0 -0
  41. data/lib/mongodb_logger/server/public/images/spinner2.gif +0 -0
  42. data/lib/mongodb_logger/server/public/images/stop-icon.png +0 -0
  43. data/lib/mongodb_logger/server/public/images/success.png +0 -0
  44. data/lib/mongodb_logger/server/public/javascripts/logs.js +1 -0
  45. data/lib/mongodb_logger/server/public/javascripts/vendors/highlight.pack.js +1 -0
  46. data/lib/mongodb_logger/server/public/javascripts/vendors/jquery-1.7.1.min.js +4 -0
  47. data/lib/mongodb_logger/server/public/javascripts/vendors/jquery-ui-1.8.16.min.js +791 -0
  48. data/lib/mongodb_logger/server/public/javascripts/vendors/jquery.pjax.min.js +6 -0
  49. data/lib/mongodb_logger/server/public/stylesheets/all.css +12 -0
  50. data/lib/mongodb_logger/server/public/stylesheets/grids.css +18 -0
  51. data/lib/mongodb_logger/server/public/stylesheets/group-buttons.css +81 -0
  52. data/lib/mongodb_logger/server/public/stylesheets/group-forms.css +59 -0
  53. data/lib/mongodb_logger/server/public/stylesheets/group-headers.css +8 -0
  54. data/lib/mongodb_logger/server/public/stylesheets/group-tables.css +87 -0
  55. data/lib/mongodb_logger/server/public/stylesheets/highlight/zenburn.css +115 -0
  56. data/lib/mongodb_logger/server/public/stylesheets/humanity/images/ui-bg_flat_75_aaaaaa_40x100.png +0 -0
  57. data/lib/mongodb_logger/server/public/stylesheets/humanity/images/ui-bg_glass_100_f5f0e5_1x400.png +0 -0
  58. data/lib/mongodb_logger/server/public/stylesheets/humanity/images/ui-bg_glass_25_cb842e_1x400.png +0 -0
  59. data/lib/mongodb_logger/server/public/stylesheets/humanity/images/ui-bg_glass_70_ede4d4_1x400.png +0 -0
  60. data/lib/mongodb_logger/server/public/stylesheets/humanity/images/ui-bg_highlight-hard_100_f4f0ec_1x100.png +0 -0
  61. data/lib/mongodb_logger/server/public/stylesheets/humanity/images/ui-bg_highlight-hard_65_fee4bd_1x100.png +0 -0
  62. data/lib/mongodb_logger/server/public/stylesheets/humanity/images/ui-bg_highlight-hard_75_f5f5b5_1x100.png +0 -0
  63. data/lib/mongodb_logger/server/public/stylesheets/humanity/images/ui-bg_inset-soft_100_f4f0ec_1x100.png +0 -0
  64. data/lib/mongodb_logger/server/public/stylesheets/humanity/images/ui-icons_c47a23_256x240.png +0 -0
  65. data/lib/mongodb_logger/server/public/stylesheets/humanity/images/ui-icons_cb672b_256x240.png +0 -0
  66. data/lib/mongodb_logger/server/public/stylesheets/humanity/images/ui-icons_f08000_256x240.png +0 -0
  67. data/lib/mongodb_logger/server/public/stylesheets/humanity/images/ui-icons_f35f07_256x240.png +0 -0
  68. data/lib/mongodb_logger/server/public/stylesheets/humanity/images/ui-icons_ff7519_256x240.png +0 -0
  69. data/lib/mongodb_logger/server/public/stylesheets/humanity/images/ui-icons_ffffff_256x240.png +0 -0
  70. data/lib/mongodb_logger/server/public/stylesheets/humanity/jquery-ui-1.8.16.custom.css +568 -0
  71. data/lib/mongodb_logger/server/public/stylesheets/layout.css +205 -0
  72. data/lib/mongodb_logger/server/public/stylesheets/library.css +330 -0
  73. data/lib/mongodb_logger/server/public/stylesheets/reset.css +43 -0
  74. data/lib/mongodb_logger/server/public/stylesheets/spaces.css +42 -0
  75. data/lib/mongodb_logger/server/view_helpers.rb +113 -0
  76. data/lib/mongodb_logger/server/views/analytics.erb +61 -0
  77. data/lib/mongodb_logger/server/views/error.erb +2 -0
  78. data/lib/mongodb_logger/server/views/layout.erb +47 -0
  79. data/lib/mongodb_logger/server/views/overview.erb +119 -0
  80. data/lib/mongodb_logger/server/views/shared/_collection_stats.erb +14 -0
  81. data/lib/mongodb_logger/server/views/shared/_dynamic_filter.erb +34 -0
  82. data/lib/mongodb_logger/server/views/shared/_log.erb +8 -0
  83. data/lib/mongodb_logger/server/views/shared/_log_info.erb +27 -0
  84. data/lib/mongodb_logger/server/views/shared/_message_tabs.erb +15 -0
  85. data/lib/mongodb_logger/server/views/shared/_tabs.erb +4 -0
  86. data/lib/mongodb_logger/server/views/shared/_tail_panel.erb +13 -0
  87. data/lib/mongodb_logger/server/views/shared/_top_panel.erb +7 -0
  88. data/lib/mongodb_logger/server/views/show_log.erb +105 -0
  89. data/lib/mongodb_logger/server.rb +174 -0
  90. data/lib/mongodb_logger/server_config.rb +77 -0
  91. data/lib/mongodb_logger/version.rb +3 -0
  92. data/lib/mongodb_logger.rb +31 -0
  93. data/mongodb_logger.gemspec +44 -0
  94. data/mongodb_logger.java.gemspec +42 -0
  95. data/spec/javascripts/MongodbLoggerMainSpec.js +13 -0
  96. data/spec/javascripts/helpers/SpecHelper.js +3 -0
  97. data/spec/javascripts/support/jasmine.yml +77 -0
  98. data/spec/javascripts/support/jasmine_config.rb +23 -0
  99. data/spec/javascripts/support/jasmine_runner.rb +32 -0
  100. data/test/active_record.rb +13 -0
  101. data/test/config/samples/database.yml +9 -0
  102. data/test/config/samples/database_no_file_logging.yml +10 -0
  103. data/test/config/samples/database_replica_set.yml +12 -0
  104. data/test/config/samples/database_with_auth.yml +9 -0
  105. data/test/config/samples/database_with_collection.yml +8 -0
  106. data/test/config/samples/mongodb_logger.yml +2 -0
  107. data/test/config/samples/mongoid.yml +30 -0
  108. data/test/config/samples/server_config.yml +3 -0
  109. data/test/rails/app/controllers/order_controller.rb +23 -0
  110. data/test/rails/test/functional/order_controller_test.rb +116 -0
  111. data/test/rails/test/test_helper.rb +10 -0
  112. data/test/rails.rb +22 -0
  113. data/test/shoulda_macros/log_macros.rb +13 -0
  114. data/test/test.sh +6 -0
  115. data/test/test_helper.rb +89 -0
  116. data/test/unit/mongodb_logger_replica_test.rb +56 -0
  117. data/test/unit/mongodb_logger_test.rb +270 -0
  118. metadata +383 -0
@@ -0,0 +1,65 @@
1
+ require 'active_support/core_ext/string/inflections'
2
+
3
+ Given /^I have built and installed the "([^\"]*)" gem$/ do |gem_name|
4
+ @terminal.build_and_install_gem(File.join(PROJECT_ROOT, "#{gem_name}.gemspec"))
5
+ end
6
+
7
+ When /^I generate a new Rails application$/ do
8
+ @terminal.cd(TEMP_DIR)
9
+ version_string = ENV['RAILS_VERSION']
10
+ rails_create_command = 'new'
11
+
12
+ rails_dir_search = (version_string =~ /^3.(0|1)/) ? "rails" : "railties"
13
+
14
+ load_rails = <<-RUBY
15
+ gem "rails", "#{version_string}"; \
16
+ load Gem.bin_path("#{rails_dir_search}", "rails", "#{version_string}")
17
+ RUBY
18
+
19
+ @terminal.run(%{ruby -rrubygems -rthread -e "#{load_rails.gsub("\"", "\\\"").strip!}" #{rails_create_command} rails_root})
20
+ if rails_root_exists?
21
+ @terminal.echo("Generated a Rails #{version_string} application")
22
+ else
23
+ raise "Unable to generate a Rails application:\n#{@terminal.output}"
24
+ end
25
+ #require_thread if rails30?
26
+ end
27
+
28
+ When /^I configure my application to require the "([^\"]*)" gem(?: with version "(.+)")?$/ do |gem_name, version|
29
+ bundle_gem(gem_name, version)
30
+ end
31
+
32
+ When /^I setup mongodb_logger tests$/ do
33
+ copy_tests
34
+ add_routes
35
+ end
36
+
37
+ When /^I setup all gems for rails$/ do
38
+ bundle_gem("therubyracer", nil) if rails31?
39
+ step %{I run "bundle install"}
40
+ @terminal.status.exitstatus.should == 0
41
+ end
42
+
43
+ When /^I prepare rails environment for testing$/ do
44
+ step %{I run "rake db:create db:migrate RAILS_ENV=test --trace"}
45
+ @terminal.status.exitstatus.should == 0
46
+ end
47
+
48
+
49
+ Then /^the tests should have run successfully$/ do
50
+ step %{I run "rake test RAILS_ENV=test --trace"}
51
+ @terminal.status.exitstatus.should == 0
52
+ # show errors
53
+ output_log = @terminal.output
54
+ is_pass = false
55
+ # TODO: fix regexp later, because can PASS in 10 or 100 failures
56
+ is_pass = true if ((1 == output_log.scan(/([.*]?)fail: 0(\D+)error: 0([\D+]?)/i).size) || (1 == output_log.scan(/(.*)0 failures(.*)0 errors(.*)/i).size))
57
+ puts @terminal.output unless is_pass
58
+ # check if have errors
59
+ is_pass.should == true
60
+ end
61
+
62
+ When /^I run "([^\"]*)"$/ do |command|
63
+ @terminal.cd(rails_root)
64
+ @terminal.run(command)
65
+ end
@@ -0,0 +1,15 @@
1
+ require 'active_support'
2
+ require 'rspec'
3
+
4
+ PROJECT_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..', '..')).freeze
5
+ TEMP_DIR = File.join(PROJECT_ROOT, 'tmp').freeze
6
+ LOCAL_RAILS_ROOT = File.join(TEMP_DIR, 'rails_root').freeze
7
+ BUILT_GEM_ROOT = File.join(TEMP_DIR, 'built_gems').freeze
8
+ LOCAL_GEM_ROOT = File.join(TEMP_DIR, 'local_gems').freeze
9
+
10
+ Before do
11
+ FileUtils.mkdir_p(TEMP_DIR)
12
+ FileUtils.rm_rf(BUILT_GEM_ROOT)
13
+ FileUtils.rm_rf(LOCAL_RAILS_ROOT)
14
+ FileUtils.mkdir_p(BUILT_GEM_ROOT)
15
+ end
@@ -0,0 +1,98 @@
1
+ module RailsHelpers
2
+ def rails_root_exists?
3
+ File.exists?(environment_path)
4
+ end
5
+
6
+ def environment_path
7
+ File.join(rails_root, 'config', 'environment.rb')
8
+ end
9
+
10
+ def application_controller_filename
11
+ controller_filename = File.join(rails_root, 'app', 'controllers', "application_controller.rb")
12
+ end
13
+
14
+ def rails_root
15
+ LOCAL_RAILS_ROOT
16
+ end
17
+
18
+ def rails30?
19
+ rails_version =~ /^3.0/
20
+ end
21
+
22
+ def rails31?
23
+ rails_version =~ /^3.1/
24
+ end
25
+
26
+ def rails_version
27
+ @rails_version ||= begin
28
+ rails_version = open(gemfile_path).read.match(/gem.*rails["'].*["'](.+)["']/)[1]
29
+ end
30
+ end
31
+
32
+ def gemfile_path
33
+ File.join(rails_root, 'Gemfile')
34
+ end
35
+
36
+ def rakefile_path
37
+ File.join(rails_root, 'Rakefile')
38
+ end
39
+
40
+ def routes_path
41
+ File.join(rails_root, 'config', 'routes.rb')
42
+ end
43
+
44
+ def logs_path
45
+ File.join(rails_root, 'log')
46
+ end
47
+
48
+ def bundle_gem(gem_name, version = nil)
49
+ File.open(gemfile_path, 'a') do |file|
50
+ gem = "gem '#{gem_name}'"
51
+ gem += ", '#{version}'" if version
52
+ file.puts(gem)
53
+ end
54
+ end
55
+
56
+ def require_thread
57
+ content = File.read(rakefile_path)
58
+ content = "require 'thread'\n#{content}"
59
+ File.open(rakefile_path, 'wb') { |file| file.write(content) }
60
+ end
61
+
62
+ def add_routes
63
+ content = File.read(routes_path)
64
+ flag = Regexp.escape("Application.routes.draw do\n")
65
+ order_routes = <<STR
66
+ resources :order do
67
+ collection do
68
+ post :test_post
69
+ end
70
+ end
71
+ STR
72
+ content.gsub!(/#{flag}/m, '\0 ' + order_routes)
73
+ File.open(routes_path, 'wb') { |file| file.write(content) }
74
+ end
75
+
76
+ def copy_tests
77
+ FileUtils.cp(
78
+ File.join(PROJECT_ROOT, 'test', 'rails', 'app', 'controllers', 'order_controller.rb'),
79
+ File.join(rails_root, 'app', 'controllers', 'order_controller.rb')
80
+ )
81
+ FileUtils.cp(
82
+ File.join(PROJECT_ROOT, 'test', 'config', 'samples', 'database.yml'),
83
+ File.join(rails_root, 'config', 'database.yml')
84
+ )
85
+ FileUtils.cp(
86
+ File.join(PROJECT_ROOT, 'test', 'rails', 'test', 'test_helper.rb'),
87
+ File.join(rails_root, 'test', 'test_helper.rb')
88
+ )
89
+ FileUtils.cp(
90
+ File.join(PROJECT_ROOT, 'test', 'rails', 'test', 'functional', 'order_controller_test.rb'),
91
+ File.join(rails_root, 'test', 'functional', 'order_controller_test.rb')
92
+ )
93
+ FileUtils.chmod 0777, logs_path
94
+ end
95
+
96
+ end
97
+
98
+ World(RailsHelpers)
@@ -0,0 +1,95 @@
1
+ require 'fileutils'
2
+
3
+ Before do
4
+ @terminal = Terminal.new
5
+ end
6
+
7
+ After do |story|
8
+ if story.failed?
9
+ # puts @terminal.output
10
+ end
11
+ end
12
+
13
+ class Terminal
14
+ attr_reader :output, :status
15
+ attr_accessor :environment_variables
16
+
17
+ def initialize
18
+ @cwd = FileUtils.pwd
19
+ @output = ""
20
+ @status = 0
21
+ @logger = Logger.new(File.join(TEMP_DIR, 'terminal.log'))
22
+
23
+ @environment_variables = {
24
+ "GEM_HOME" => LOCAL_GEM_ROOT,
25
+ "GEM_PATH" => "#{LOCAL_GEM_ROOT}:#{BUILT_GEM_ROOT}",
26
+ "PATH" => "#{gem_bin_path}:#{ENV['PATH']}",
27
+ "BUNDLE_GEMFILE" => File.join(LOCAL_RAILS_ROOT, 'Gemfile')
28
+ }
29
+ end
30
+
31
+ def cd(directory)
32
+ @cwd = directory
33
+ end
34
+
35
+ def run(command)
36
+ output << "#{command}\n"
37
+ current_dir = FileUtils.pwd
38
+
39
+ # The ; forces ruby to shell out so the env settings work right
40
+ cmdline = "sh -c 'cd #{@cwd} && #{environment_settings} #{command} 2>&1 ; '"
41
+ logger.debug(cmdline)
42
+ result = `#{cmdline}`
43
+ logger.debug(result)
44
+ output << result
45
+
46
+ `sh -c 'cd #{current_dir}'`
47
+ @status = $?
48
+ end
49
+
50
+ def echo(string)
51
+ logger.debug(string)
52
+ end
53
+
54
+
55
+ def build_and_install_gem(gemspec)
56
+ pkg_dir = File.join(TEMP_DIR, 'pkg')
57
+ FileUtils.mkdir_p(pkg_dir)
58
+ output = `gem build #{gemspec} 2>&1`
59
+ gem_file = Dir.glob("*.gem").first
60
+ unless gem_file
61
+ raise "Gem didn't build:\n#{output}"
62
+ end
63
+ target = File.join(pkg_dir, gem_file)
64
+ FileUtils.mv(gem_file, target)
65
+ install_gem_to(LOCAL_GEM_ROOT, target)
66
+ end
67
+
68
+ def install_gem(gem)
69
+ install_gem_to(LOCAL_GEM_ROOT, gem)
70
+ end
71
+
72
+ def uninstall_gem(gem)
73
+ `gem uninstall -i #{LOCAL_GEM_ROOT} #{gem}`
74
+ end
75
+
76
+ def prepend_path(path)
77
+ @environment_variables['PATH'] = path + ":" + @environment_variables['PATH']
78
+ end
79
+
80
+ private
81
+
82
+ def install_gem_to(root, gem)
83
+ `gem install -i #{root} --no-ri --no-rdoc #{gem}`
84
+ end
85
+
86
+ def environment_settings
87
+ @environment_variables.map { |key, value| "#{key}=#{value}" }.join(' ')
88
+ end
89
+
90
+ def gem_bin_path
91
+ File.join(LOCAL_GEM_ROOT, "bin")
92
+ end
93
+
94
+ attr_reader :logger
95
+ end
@@ -0,0 +1,26 @@
1
+ module MongodbLogger
2
+ module InitializerMixin
3
+
4
+ def rails30?
5
+ 3 == Rails::VERSION::MAJOR && 0 == Rails::VERSION::MINOR
6
+ end
7
+
8
+ def create_logger(config)
9
+ path = rails30? ? config.paths.log.to_a.first : config.paths['log'].first
10
+ level = ActiveSupport::BufferedLogger.const_get(config.log_level.to_s.upcase)
11
+ logger = MongodbLogger::Logger.new(:path => path, :level => level)
12
+ logger.auto_flushing = false if Rails.env.production?
13
+ logger
14
+ rescue StandardError => e
15
+ logger = ActiveSupport::BufferedLogger.new(STDERR)
16
+ logger.level = ActiveSupport::BufferedLogger::WARN
17
+ logger.warn(
18
+ "MongodbLogger Initializer Error: Unable to access log file. Please ensure that #{path} exists and is chmod 0666. " +
19
+ "The log level has been raised to WARN and the output directed to STDERR until the problem is fixed." + "\n" +
20
+ e.message + "\n" + e.backtrace.join("\n")
21
+ )
22
+ logger
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,239 @@
1
+ require 'erb'
2
+ require 'mongo'
3
+ require 'active_support'
4
+ require 'active_support/core_ext'
5
+ require 'action_dispatch/http/upload'
6
+ require 'mongodb_logger/replica_set_helper'
7
+
8
+ module MongodbLogger
9
+ class Logger < ActiveSupport::BufferedLogger
10
+ include ReplicaSetHelper
11
+
12
+ DEFAULT_COLLECTION_SIZE = 250.megabytes
13
+ # Looks for configuration files in this order
14
+ CONFIGURATION_FILES = ["mongodb_logger.yml", "mongoid.yml", "database.yml"]
15
+ LOG_LEVEL_SYM = [:debug, :info, :warn, :error, :fatal, :unknown]
16
+
17
+ attr_reader :db_configuration, :mongo_connection, :mongo_collection_name, :mongo_collection, :mongo_connection_type
18
+
19
+ def initialize(options={})
20
+ path = options[:path] || File.join(Rails.root, "log/#{Rails.env}.log")
21
+ level = options[:level] || DEBUG
22
+ internal_initialize
23
+ rescue => e
24
+ # should use a config block for this
25
+ Rails.env.production? ? (raise e) : (puts "MongodbLogger WARNING: Using BufferedLogger due to exception: " + e.message)
26
+ ensure
27
+ if disable_file_logging?
28
+ @level = level
29
+ @buffer = {}
30
+ @auto_flushing = 1
31
+ @guard = Mutex.new
32
+ else
33
+ super(path, level)
34
+ end
35
+ end
36
+
37
+ def add_metadata(options={})
38
+ options.each do |key, value|
39
+ unless [:messages, :request_time, :ip, :runtime, :application_name, :is_exception, :params, :method].include?(key.to_sym)
40
+ @mongo_record[key] = value
41
+ else
42
+ raise ArgumentError, ":#{key} is a reserved key for the mongodb logger. Please choose a different key"
43
+ end
44
+ end
45
+ end
46
+
47
+ def add(severity, message = nil, progname = nil, &block)
48
+ if @level && @level <= severity && message.present? && @mongo_record.present?
49
+ # do not modify the original message used by the buffered logger
50
+ msg = logging_colorized? ? message.to_s.gsub(/(\e(\[([\d;]*[mz]?))?)?/, '').strip : message
51
+ @mongo_record[:messages][LOG_LEVEL_SYM[severity]] << msg
52
+ end
53
+ # may modify the original message
54
+ disable_file_logging? ? message : (@level ? super : message)
55
+ end
56
+
57
+ # Drop the capped_collection and recreate it
58
+ def reset_collection
59
+ if @mongo_connection && @mongo_collection
60
+ @mongo_collection.drop
61
+ create_collection
62
+ end
63
+ end
64
+
65
+ def mongoize(options={})
66
+ @mongo_record = options.merge({
67
+ :messages => Hash.new { |hash, key| hash[key] = Array.new },
68
+ :request_time => Time.now.getutc,
69
+ :application_name => @application_name
70
+ })
71
+
72
+ runtime = Benchmark.measure{ yield }.real if block_given?
73
+ rescue Exception => e
74
+ add(3, e.message + "\n" + e.backtrace.join("\n"))
75
+ # log exceptions
76
+ @mongo_record[:is_exception] = true
77
+ # Reraise the exception for anyone else who cares
78
+ raise e
79
+ ensure
80
+ # In case of exception, make sure runtime is set
81
+ @mongo_record[:runtime] = ((runtime ||= 0) * 1000).ceil
82
+ begin
83
+ @insert_block.call
84
+ rescue
85
+ begin
86
+ # try to nice serialize record
87
+ nice_serialize @mongo_record
88
+ @insert_block.call
89
+ rescue
90
+ # do extra work to inpect (and flatten)
91
+ force_serialize @mongo_record
92
+ @insert_block.call rescue nil
93
+ end
94
+ end
95
+ end
96
+
97
+ def authenticated?
98
+ @authenticated
99
+ end
100
+
101
+ private
102
+ # facilitate testing
103
+ def internal_initialize
104
+ configure
105
+ connect
106
+ check_for_collection
107
+ end
108
+
109
+ def disable_file_logging?
110
+ @db_configuration.fetch('disable_file_logging', false)
111
+ end
112
+
113
+ def configure
114
+ default_capsize = DEFAULT_COLLECTION_SIZE
115
+ @authenticated = false
116
+ @db_configuration = {
117
+ 'host' => 'localhost',
118
+ 'port' => 27017,
119
+ 'capsize' => default_capsize}.merge(resolve_config)
120
+ @mongo_collection_name = @db_configuration['collection'] || "#{Rails.env}_log"
121
+ @application_name = resolve_application_name
122
+ @safe_insert = @db_configuration['safe_insert'] || false
123
+
124
+ @insert_block = @db_configuration.has_key?('replica_set') && @db_configuration['replica_set'] ?
125
+ lambda { rescue_connection_failure{ insert_log_record(@safe_insert) } } :
126
+ lambda { insert_log_record }
127
+ end
128
+
129
+ def resolve_application_name
130
+ if @db_configuration.has_key?('application_name')
131
+ @db_configuration['application_name']
132
+ else
133
+ Rails.application.class.to_s.split("::").first
134
+ end
135
+ end
136
+
137
+ def resolve_config
138
+ config = {}
139
+ CONFIGURATION_FILES.each do |filename|
140
+ config_file = Rails.root.join("config", filename)
141
+ if config_file.file?
142
+ config = YAML.load(ERB.new(config_file.read).result)[Rails.env]
143
+ config = config['mongodb_logger'] if config && config.has_key?('mongodb_logger')
144
+ break unless config.blank?
145
+ end
146
+ end
147
+ config
148
+ end
149
+
150
+ def mongo_connection_object
151
+ if @db_configuration['hosts']
152
+ conn = Mongo::ReplSetConnection.new(*(@db_configuration['hosts'] <<
153
+ {:auto_reconnect => true, :pool_timeout => 6}))
154
+ @db_configuration['replica_set'] = true
155
+ else
156
+ conn = Mongo::Connection.new(@db_configuration['host'],
157
+ @db_configuration['port'],
158
+ :auto_reconnect => true,
159
+ :pool_timeout => 6)
160
+ end
161
+ @mongo_connection_type = conn.class
162
+ conn
163
+ end
164
+
165
+ def connect
166
+ @mongo_connection ||= mongo_connection_object.db(@db_configuration['database'])
167
+ if @db_configuration['username'] && @db_configuration['password']
168
+ # the driver stores credentials in case reconnection is required
169
+ @authenticated = @mongo_connection.authenticate(@db_configuration['username'],
170
+ @db_configuration['password'])
171
+ end
172
+ end
173
+
174
+ def create_collection
175
+ @mongo_connection.create_collection(@mongo_collection_name,
176
+ {:capped => true, :size => @db_configuration['capsize'].to_i})
177
+ end
178
+
179
+ def check_for_collection
180
+ # setup the capped collection if it doesn't already exist
181
+ create_collection unless @mongo_connection.collection_names.include?(@mongo_collection_name)
182
+ @mongo_collection = @mongo_connection[@mongo_collection_name]
183
+ end
184
+
185
+ def insert_log_record(safe = false)
186
+ @mongo_collection.insert(@mongo_record, :safe => safe)
187
+ end
188
+
189
+ def logging_colorized?
190
+ # Cache it since these ActiveRecord attributes are assigned after logger initialization occurs in Rails boot
191
+ @colorized ||= Object.const_defined?(:ActiveRecord) && ActiveRecord::LogSubscriber.colorize_logging
192
+ end
193
+
194
+ # try to serialyze data by each key and found invalid object
195
+ def nice_serialize(rec)
196
+ if msgs = rec[:messages]
197
+ msgs.each do |i, j|
198
+ msgs[i] = nice_serialize_object(j)
199
+ end
200
+ end
201
+ if pms = rec[:params]
202
+ pms.each do |i, j|
203
+ pms[i] = nice_serialize_object(j)
204
+ end
205
+ end
206
+ end
207
+
208
+ def nice_serialize_object(data)
209
+ case data
210
+ when NilClass, String, Fixnum, Bignum, Float, TrueClass, FalseClass, Time, Regexp, Symbol
211
+ data
212
+ when Hash
213
+ hvalues = Hash.new
214
+ data.each{|k,v| hvalues[k] = nice_serialize_object(v) }
215
+ hvalues
216
+ when Array
217
+ data.map{|v| nice_serialize_object(v) }
218
+ when ActionDispatch::Http::UploadedFile, Rack::Test::UploadedFile # uploaded files
219
+ hvalues = {
220
+ :original_filename => data.original_filename,
221
+ :content_type => data.content_type
222
+ }
223
+ else
224
+ data.inspect
225
+ end
226
+ end
227
+
228
+ # force the data in the db by inspecting each top level array and hash element
229
+ # this will flatten other hashes and arrays
230
+ def force_serialize(rec)
231
+ if msgs = rec[:messages]
232
+ msgs.each { |i, j| msgs[i] = j.inspect }
233
+ end
234
+ if pms = rec[:params]
235
+ pms.each { |i, j| pms[i] = j.inspect }
236
+ end
237
+ end
238
+ end
239
+ end
@@ -0,0 +1,12 @@
1
+ require 'mongodb_logger/initializer_mixin'
2
+ module MongodbLogger
3
+ class Railtie < Rails::Railtie
4
+ include MongodbLogger::InitializerMixin
5
+
6
+ initializer :initialize_mongodb_logger, :before => :initialize_logger do
7
+ app_config = Rails.application.config
8
+ Rails.logger = config.logger = create_logger(app_config)
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,19 @@
1
+ module MongodbLogger
2
+ module ReplicaSetHelper
3
+ # Use retry alg from mongodb to gobble up connection failures during replica set master vote
4
+ # Defaults to a 10 second wait
5
+ def rescue_connection_failure(max_retries=40)
6
+ success = false
7
+ retries = 0
8
+ while !success
9
+ begin
10
+ yield
11
+ success = true
12
+ rescue Mongo::ConnectionFailure => e
13
+ raise e if (retries += 1) >= max_retries
14
+ sleep 0.25
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end