mongodb_logger 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 (69) hide show
  1. data/.gitignore +20 -0
  2. data/.rvmrc +1 -0
  3. data/.travis.yml +17 -0
  4. data/Gemfile +4 -0
  5. data/README.md +177 -0
  6. data/Rakefile +110 -0
  7. data/SUPPORTED_RAILS_VERSIONS +13 -0
  8. data/TESTING.md +24 -0
  9. data/bin/mongodb_logger_web +24 -0
  10. data/config.ru +16 -0
  11. data/examples/server_config.yml +4 -0
  12. data/features/rails.feature +10 -0
  13. data/features/step_definitions/rails_application_steps.rb +48 -0
  14. data/features/support/env.rb +15 -0
  15. data/features/support/rails.rb +91 -0
  16. data/features/support/terminal.rb +94 -0
  17. data/lib/mongodb_logger.rb +31 -0
  18. data/lib/mongodb_logger/initializer_mixin.rb +26 -0
  19. data/lib/mongodb_logger/logger.rb +184 -0
  20. data/lib/mongodb_logger/railtie.rb +12 -0
  21. data/lib/mongodb_logger/replica_set_helper.rb +19 -0
  22. data/lib/mongodb_logger/server.rb +136 -0
  23. data/lib/mongodb_logger/server/model/filter.rb +37 -0
  24. data/lib/mongodb_logger/server/partials.rb +24 -0
  25. data/lib/mongodb_logger/server/public/images/ajax-loader.gif +0 -0
  26. data/lib/mongodb_logger/server/public/images/failure.png +0 -0
  27. data/lib/mongodb_logger/server/public/images/logo.png +0 -0
  28. data/lib/mongodb_logger/server/public/images/play-icon.png +0 -0
  29. data/lib/mongodb_logger/server/public/images/stop-icon.png +0 -0
  30. data/lib/mongodb_logger/server/public/images/success.png +0 -0
  31. data/lib/mongodb_logger/server/public/javascripts/jquery-1.7.min.js +4 -0
  32. data/lib/mongodb_logger/server/public/stylesheets/all.css +9 -0
  33. data/lib/mongodb_logger/server/public/stylesheets/grids.css +18 -0
  34. data/lib/mongodb_logger/server/public/stylesheets/group-buttons.css +83 -0
  35. data/lib/mongodb_logger/server/public/stylesheets/group-forms.css +60 -0
  36. data/lib/mongodb_logger/server/public/stylesheets/group-headers.css +8 -0
  37. data/lib/mongodb_logger/server/public/stylesheets/group-tables.css +42 -0
  38. data/lib/mongodb_logger/server/public/stylesheets/layout.css +168 -0
  39. data/lib/mongodb_logger/server/public/stylesheets/library.css +134 -0
  40. data/lib/mongodb_logger/server/public/stylesheets/reset.css +43 -0
  41. data/lib/mongodb_logger/server/public/stylesheets/spaces.css +42 -0
  42. data/lib/mongodb_logger/server/views/application.coffee +54 -0
  43. data/lib/mongodb_logger/server/views/error.erb +2 -0
  44. data/lib/mongodb_logger/server/views/layout.erb +32 -0
  45. data/lib/mongodb_logger/server/views/overview.erb +94 -0
  46. data/lib/mongodb_logger/server/views/shared/_log.erb +8 -0
  47. data/lib/mongodb_logger/server/views/shared/_log_info.erb +25 -0
  48. data/lib/mongodb_logger/server/views/shared/_tabs.erb +4 -0
  49. data/lib/mongodb_logger/server/views/show_log.erb +85 -0
  50. data/lib/mongodb_logger/server_config.rb +45 -0
  51. data/lib/mongodb_logger/version.rb +3 -0
  52. data/mongodb_logger.gemspec +37 -0
  53. data/test/active_record.rb +13 -0
  54. data/test/config/samples/database.yml +9 -0
  55. data/test/config/samples/database_no_file_logging.yml +10 -0
  56. data/test/config/samples/database_replica_set.yml +8 -0
  57. data/test/config/samples/database_with_auth.yml +9 -0
  58. data/test/config/samples/mongodb_logger.yml +2 -0
  59. data/test/config/samples/mongoid.yml +30 -0
  60. data/test/rails.rb +22 -0
  61. data/test/rails/app/controllers/order_controller.rb +20 -0
  62. data/test/rails/test/functional/order_controller_test.rb +56 -0
  63. data/test/rails/test/test_helper.rb +10 -0
  64. data/test/shoulda_macros/log_macros.rb +13 -0
  65. data/test/test.sh +4 -0
  66. data/test/test_helper.rb +88 -0
  67. data/test/unit/mongodb_logger_replica_test.rb +45 -0
  68. data/test/unit/mongodb_logger_test.rb +252 -0
  69. metadata +300 -0
@@ -0,0 +1,20 @@
1
+ class OrderController < ApplicationController
2
+ include MongodbLogger::Base
3
+ LOG_MESSAGE = "FOO"
4
+ LOG_USER_ID = 12345
5
+
6
+ def index
7
+ logger.debug LOG_MESSAGE
8
+ logger.add_metadata(:application_name_again => Rails.root.basename.to_s)
9
+ logger.add_metadata(:user_id => LOG_USER_ID)
10
+ render :text => "nothing"
11
+ end
12
+
13
+ def new
14
+ raise OrderController::LOG_MESSAGE
15
+ end
16
+
17
+ def create
18
+ render :text => "create"
19
+ end
20
+ end
@@ -0,0 +1,56 @@
1
+ require 'test_helper'
2
+
3
+ class OrderControllerTest < ActionController::TestCase
4
+ def setup
5
+ @mongodb_logger = Rails.logger
6
+ @mongodb_logger.reset_collection
7
+ common_setup
8
+ end
9
+
10
+ test "should have log level set" do
11
+ assert_equal ActiveSupport::BufferedLogger.const_get(Rails.configuration.log_level.to_s.upcase), Rails.logger.level
12
+ end
13
+
14
+ test "should have auto flushing set in development" do
15
+ assert @mongodb_logger.auto_flushing
16
+ end
17
+
18
+ test "should log a single record" do
19
+ get :index
20
+ assert_response :success
21
+ assert_equal 1, @collection.find({"controller" => "order","action"=> "index"}).count
22
+ end
23
+
24
+ test "should log a debug message" do
25
+ get :index
26
+ assert_equal OrderController::LOG_MESSAGE, @collection.find_one({}, :fields => ["messages"])["messages"]["debug"][0]
27
+ end
28
+
29
+ test "should log extra metadata" do
30
+ get :index
31
+ assert_equal Rails.root.basename.to_s, @collection.find_one({}, :fields => "application_name_again")["application_name_again"]
32
+ assert_equal OrderController::LOG_USER_ID, @collection.find_one({}, :fields => "user_id")["user_id"]
33
+ end
34
+
35
+ test "should log request parameters" do
36
+ get :index
37
+ log = @collection.find_one()
38
+ http_method = 'GET'
39
+ assert_equal http_method, log['method']
40
+ end
41
+
42
+ test "should log exceptions" do
43
+ assert_raise(RuntimeError, OrderController::LOG_MESSAGE) {get :new}
44
+ assert_equal 1, @collection.find_one({"messages.error" => /^#{OrderController::LOG_MESSAGE}/})["messages"]["error"].count
45
+ assert_equal 1, @collection.find_one({"is_exception" => true})["messages"]["error"].count
46
+ end
47
+
48
+ test "should not log passwords" do
49
+ post :create, :order => {:password => OrderController::LOG_MESSAGE }
50
+ assert_equal 1, @collection.find_one({"params.order.password" => "[FILTERED]"})["params"]["order"].count
51
+ end
52
+
53
+ test "should set the application name" do
54
+ assert_equal 'mongo_foo', @mongodb_logger.instance_variable_get(:@application_name)
55
+ end
56
+ end
@@ -0,0 +1,10 @@
1
+ ENV["RAILS_ENV"] = "test"
2
+ require File.expand_path('../../config/environment', __FILE__)
3
+ require 'rails/test_help'
4
+
5
+ class ActiveSupport::TestCase
6
+ def common_setup
7
+ @con = @mongodb_logger.mongo_connection
8
+ @collection = @con[@mongodb_logger.mongo_collection_name]
9
+ end
10
+ end
@@ -0,0 +1,13 @@
1
+ module LogMacros
2
+ def should_contain_one_log_record
3
+ should "contain a log record" do
4
+ assert_equal 1, @con[@mongodb_logger.mongo_collection_name].count
5
+ end
6
+ end
7
+
8
+ def should_use_database_name_in_config
9
+ should "use the database name in the config file" do
10
+ assert_equal "system_log", @mongodb_logger.db_configuration['database']
11
+ end
12
+ end
13
+ end
data/test/test.sh ADDED
@@ -0,0 +1,4 @@
1
+ #!/bin/bash
2
+ rake vendor_test_gems
3
+ rake test
4
+ rake cucumber:rails:all
@@ -0,0 +1,88 @@
1
+ require 'test/unit'
2
+
3
+ begin
4
+ require 'shoulda'
5
+ rescue LoadError
6
+ require 'rubygems'
7
+ require 'shoulda'
8
+ end
9
+
10
+ require 'mocha'
11
+ # mock rails class
12
+ require 'pathname'
13
+ require 'rails'
14
+ require 'fileutils'
15
+
16
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
17
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
18
+
19
+ Shoulda.autoload_macros("#{File.dirname(__FILE__)}/..")
20
+
21
+ class Test::Unit::TestCase
22
+ CONFIG_DIR = Rails.root.join("config")
23
+ SAMPLE_CONFIG_DIR = File.join(CONFIG_DIR, "samples")
24
+ DEFAULT_CONFIG = "database.yml"
25
+ DEFAULT_CONFIG_WITH_AUTH = "database_with_auth.yml"
26
+ DEFAULT_CONFIG_WITH_NO_FILE_LOGGING = "database_no_file_logging.yml"
27
+ MONGOID_CONFIG = "mongoid.yml"
28
+ REPLICA_SET_CONFIG = "database_replica_set.yml"
29
+ LOGGER_CONFIG = "mongodb_logger.yml"
30
+
31
+ def log(msg)
32
+ @mongodb_logger.mongoize({"id" => 1}) do
33
+ @mongodb_logger.debug(msg)
34
+ end
35
+ end
36
+
37
+ def log_params(msg)
38
+ @mongodb_logger.mongoize({:params => msg})
39
+ end
40
+
41
+ def log_exception(msg)
42
+ @mongodb_logger.mongoize({"id" => 1}) do
43
+ raise msg
44
+ end
45
+ end
46
+
47
+ def setup_for_config(source, dest=source)
48
+ File.delete(File.join(CONFIG_DIR, DEFAULT_CONFIG))
49
+ cp_config(source, dest)
50
+ @mongodb_logger.send(:configure)
51
+ end
52
+
53
+ def cp_config(source, dest=source)
54
+ FileUtils.cp(File.join(SAMPLE_CONFIG_DIR, source), File.join(CONFIG_DIR, dest))
55
+ end
56
+
57
+ def teardown_for_config(file)
58
+ File.delete(File.join(CONFIG_DIR, file))
59
+ end
60
+
61
+ def log_metadata(options)
62
+ @mongodb_logger.mongoize({"id" => 1}) do
63
+ @mongodb_logger.add_metadata(options)
64
+ end
65
+ end
66
+
67
+ def require_bogus_active_record
68
+ require 'active_record'
69
+ end
70
+
71
+ def common_setup
72
+ @con = @mongodb_logger.mongo_connection
73
+ @collection = @con[@mongodb_logger.mongo_collection_name]
74
+ end
75
+
76
+ def create_user
77
+ db_conf = @mongodb_logger.db_configuration
78
+ @user = db_conf['username']
79
+ mongo_connection = Mongo::Connection.new(db_conf['host'],
80
+ db_conf['port']).db(db_conf['database'])
81
+ mongo_connection.add_user(@user, db_conf['password'])
82
+ end
83
+
84
+ def remove_user
85
+ @mongodb_logger.mongo_connection.remove_user(@user)
86
+ end
87
+
88
+ end
@@ -0,0 +1,45 @@
1
+ require 'test_helper'
2
+ require 'mongodb_logger/logger'
3
+
4
+ # test the basic stuff
5
+ class MongodbLogger::MongodbLoggerReplicaTest < Test::Unit::TestCase
6
+ extend LogMacros
7
+
8
+ context "A MongodbLogger::MongoLogger" do
9
+ setup do
10
+ # Can use different configs, but most tests use database.yml
11
+ cp_config(REPLICA_SET_CONFIG, DEFAULT_CONFIG)
12
+ @mongodb_logger = MongodbLogger::Logger.new
13
+ @mongodb_logger.reset_collection
14
+ end
15
+
16
+ context "upon trying to insert into a replica set voting on a new master" do
17
+ setup do
18
+ puts "Please disconnect the current master and hit ENTER"
19
+ STDIN.gets
20
+ end
21
+
22
+ should "insert a record successfully" do
23
+ assert_nothing_raised{ log("Test") }
24
+ @mongodb_logger.rescue_connection_failure do
25
+ assert_equal 1, @mongodb_logger.mongo_collection.count
26
+ end
27
+ end
28
+
29
+ teardown do
30
+ puts "Please reconnect the current master, wait for the vote to complete, then hit ENTER"
31
+ STDIN.gets
32
+ end
33
+ end
34
+
35
+ should "insert a record successfully" do
36
+ assert_nothing_raised{ log("Test") }
37
+ assert_equal 1, @mongodb_logger.mongo_collection.count
38
+ end
39
+
40
+ teardown do
41
+ file = File.join(CONFIG_DIR, DEFAULT_CONFIG)
42
+ File.delete(file) if File.exist?(file)
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,252 @@
1
+ require 'test_helper'
2
+ require 'mongodb_logger/logger'
3
+ require 'tempfile'
4
+ require 'pathname'
5
+
6
+ # test the basic stuff
7
+ class MongodbLogger::LoggerTest < Test::Unit::TestCase
8
+ extend LogMacros
9
+
10
+ EXCEPTION_MSG = "Foo"
11
+
12
+ context "A MongodbLogger::Logger" do
13
+ setup do
14
+ # Can use different configs, but most tests use database.yml
15
+ FileUtils.cp(File.join(SAMPLE_CONFIG_DIR, DEFAULT_CONFIG), CONFIG_DIR)
16
+ end
17
+
18
+ context "in instantiation" do
19
+ setup do
20
+ MongodbLogger::Logger.any_instance.stubs(:internal_initialize).returns(nil)
21
+ MongodbLogger::Logger.any_instance.stubs(:disable_file_logging?).returns(false)
22
+ @mongodb_logger = MongodbLogger::Logger.new
23
+ end
24
+
25
+ context "during configuration when using a separate " + LOGGER_CONFIG do
26
+ setup do
27
+ setup_for_config(LOGGER_CONFIG)
28
+ end
29
+
30
+ should_use_database_name_in_config
31
+
32
+ teardown do
33
+ teardown_for_config(LOGGER_CONFIG)
34
+ end
35
+ end
36
+
37
+ context "during configuration when using a separate " + MONGOID_CONFIG do
38
+ setup do
39
+ setup_for_config(MONGOID_CONFIG)
40
+ end
41
+
42
+ should_use_database_name_in_config
43
+
44
+ teardown do
45
+ teardown_for_config(MONGOID_CONFIG)
46
+ end
47
+ end
48
+
49
+ # this test will work without the --auth mongod arg
50
+ context "upon connecting with authentication settings" do
51
+ setup do
52
+ setup_for_config(DEFAULT_CONFIG_WITH_AUTH, DEFAULT_CONFIG)
53
+ create_user
54
+ end
55
+
56
+ should "authenticate with the credentials in the configuration" do
57
+ @mongodb_logger.send(:connect)
58
+ assert @mongodb_logger.authenticated?
59
+ end
60
+
61
+ teardown do
62
+ # config will be deleted by outer teardown
63
+ remove_user
64
+ end
65
+ end
66
+
67
+ context "after configuration" do
68
+ setup do
69
+ @mongodb_logger.send(:configure)
70
+ end
71
+
72
+ should "set the default host, port, and capsize if not configured" do
73
+ assert_equal 'localhost', @mongodb_logger.db_configuration['host']
74
+ assert_equal 27017, @mongodb_logger.db_configuration['port']
75
+ assert_equal MongodbLogger::Logger::DEFAULT_COLLECTION_SIZE, @mongodb_logger.db_configuration['capsize']
76
+ end
77
+
78
+ should "set the mongo collection name depending on the Rails environment" do
79
+ assert_equal "#{Rails.env}_log", @mongodb_logger.mongo_collection_name
80
+ end
81
+
82
+ should "set the application name when specified in the config file" do
83
+ assert_equal "mongo_foo", @mongodb_logger.instance_variable_get(:@application_name)
84
+ end
85
+
86
+ should "set safe insert when specified in the config file" do
87
+ assert @mongodb_logger.instance_variable_get(:@safe_insert)
88
+ end
89
+
90
+ should "use the database name in the config file" do
91
+ assert_equal "system_log", @mongodb_logger.db_configuration['database']
92
+ end
93
+
94
+ context "upon connecting to an empty database" do
95
+ setup do
96
+ @mongodb_logger.send(:connect)
97
+ common_setup
98
+ @collection.drop
99
+ end
100
+
101
+ should "expose a valid mongo connection" do
102
+ assert_instance_of Mongo::DB, @mongodb_logger.mongo_connection
103
+ end
104
+
105
+ should "not authenticate" do
106
+ assert !@mongodb_logger.authenticated?
107
+ end
108
+
109
+ should "create a capped collection in the database with the configured size" do
110
+ @mongodb_logger.send(:check_for_collection)
111
+ assert @con.collection_names.include?(@mongodb_logger.mongo_collection_name)
112
+ # new capped collections are X MB + 5888 bytes, but don't be too strict in case that changes
113
+ assert @collection.stats["storageSize"] < MongodbLogger::Logger::DEFAULT_COLLECTION_SIZE + 1.megabyte
114
+ end
115
+ end
116
+ end
117
+ end
118
+
119
+ context "after instantiation" do
120
+ setup do
121
+ @mongodb_logger = MongodbLogger::Logger.new
122
+ common_setup
123
+ @mongodb_logger.reset_collection
124
+ end
125
+
126
+ context "upon insertion of a log record when active record is not used" do
127
+ # mock ActiveRecord has not been included
128
+ setup do
129
+ log("Test")
130
+ end
131
+
132
+ should_contain_one_log_record
133
+
134
+ should "allow recreation of the capped collection to remove all records" do
135
+ @mongodb_logger.reset_collection
136
+ assert_equal 0, @collection.count
137
+ end
138
+ end
139
+
140
+ context "upon insertion of a colorized log record when ActiveRecord is used" do
141
+ setup do
142
+ @log_message = "TESTING"
143
+ require_bogus_active_record
144
+ log("\e[31m #{@log_message} \e[0m")
145
+ end
146
+
147
+ should "detect logging is colorized" do
148
+ assert @mongodb_logger.send(:logging_colorized?)
149
+ end
150
+
151
+ should_contain_one_log_record
152
+
153
+ should "strip out colorization from log messages" do
154
+ assert_equal 1, @collection.find({"messages.debug" => @log_message}).count
155
+ end
156
+ end
157
+
158
+ should "add application metadata to the log record" do
159
+ options = {"application" => self.class.name}
160
+ log_metadata(options)
161
+ assert_equal 1, @collection.find({"application" => self.class.name}).count
162
+ end
163
+
164
+ should "not raise an exception when bson-unserializable data is logged in the :messages key" do
165
+ log(Tempfile.new("foo"))
166
+ assert_equal 1, @collection.count
167
+ end
168
+
169
+ should "not raise an exception when bson-unserializable data is logged in the :params key" do
170
+ log_params({:foo => Tempfile.new("bar")})
171
+ assert_equal 1, @collection.count
172
+ end
173
+
174
+ context "when an exception is raised" do
175
+ should "log the exception" do
176
+ assert_raise(RuntimeError, EXCEPTION_MSG) {log_exception(EXCEPTION_MSG)}
177
+ assert_equal 1, @collection.find_one({"messages.error" => /^#{EXCEPTION_MSG}/})["messages"]["error"].count
178
+ assert_equal 1, @collection.find_one({"is_exception" => true})["messages"]["error"].count
179
+ end
180
+ end
181
+ end
182
+
183
+ context "logging at INFO level" do
184
+ setup do
185
+ @mongodb_logger = MongodbLogger::Logger.new(:level => MongodbLogger::Logger::INFO)
186
+ common_setup
187
+ @mongodb_logger.reset_collection
188
+ log("INFO")
189
+ end
190
+
191
+ should_contain_one_log_record
192
+
193
+ should "not log DEBUG messages" do
194
+ assert_equal 0, @collection.find_one({}, :fields => ["messages"])["messages"].count
195
+ end
196
+ end
197
+ teardown do
198
+ file = File.join(CONFIG_DIR, DEFAULT_CONFIG)
199
+ File.delete(file) if File.exist?(file)
200
+ end
201
+ end
202
+
203
+ context "A MongodbLogger::Logger without file logging" do
204
+ setup do
205
+ FileUtils.cp(File.join(SAMPLE_CONFIG_DIR, DEFAULT_CONFIG_WITH_NO_FILE_LOGGING), File.join(CONFIG_DIR, DEFAULT_CONFIG))
206
+ @log_file = Pathname.new('log.out')
207
+ FileUtils.touch(@log_file)
208
+ end
209
+
210
+ context "in instantiation" do
211
+ should "not call super in the initialize method" do
212
+ MongodbLogger::Logger.any_instance.expects(:open).never # Stubbing out super doesn't work, so we use this side effect instead.
213
+ MongodbLogger::Logger.new
214
+ end
215
+
216
+ should "set level" do
217
+ level = stub('level')
218
+ logger = MongodbLogger::Logger.new(:level => level)
219
+ assert_equal level, logger.level
220
+ end
221
+ should "set buffer" do
222
+ assert_equal({}, MongodbLogger::Logger.new.instance_variable_get(:@buffer))
223
+ end
224
+ should "set auto flushing" do
225
+ assert_equal 1, MongodbLogger::Logger.new.instance_variable_get(:@auto_flushing)
226
+ end
227
+ should "set guard" do
228
+ assert MongodbLogger::Logger.new.instance_variable_get(:@guard).is_a?(Mutex)
229
+ end
230
+ end
231
+
232
+ context "after instantiation" do
233
+ context "upon insertion of a log record" do
234
+ setup do
235
+ @mongodb_logger = MongodbLogger::Logger.new(:path => @log_file)
236
+ log("Test")
237
+ end
238
+
239
+ should "not log the record to a file" do
240
+ assert_equal '', open(@log_file).read
241
+ end
242
+ end
243
+ end
244
+
245
+ teardown do
246
+ file = File.join(CONFIG_DIR, DEFAULT_CONFIG)
247
+ File.delete(file) if File.exist?(file)
248
+ File.delete(@log_file)
249
+ end
250
+ end
251
+
252
+ end