effective_logging 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +280 -0
  4. data/Rakefile +20 -0
  5. data/app/assets/javascripts/effective_logging.js +1 -0
  6. data/app/assets/javascripts/effective_logging/effective_logger.js.coffee.erb +15 -0
  7. data/app/controllers/admin/logs_controller.rb +31 -0
  8. data/app/controllers/effective/logs_controller.rb +71 -0
  9. data/app/helpers/effective_logging_helper.rb +59 -0
  10. data/app/models/effective/access_denied.rb +17 -0
  11. data/app/models/effective/datatables/logs.rb +49 -0
  12. data/app/models/effective/log.rb +59 -0
  13. data/app/models/effective/user_logger.rb +7 -0
  14. data/app/models/effective_logger.rb +38 -0
  15. data/app/views/active_admin/effective_logging/logs/index.html.haml +8 -0
  16. data/app/views/active_admin/effective_logging/logs/show.html.haml +4 -0
  17. data/app/views/admin/logs/index.html.haml +7 -0
  18. data/app/views/admin/logs/show.html.haml +3 -0
  19. data/app/views/effective/logs/_log.html.haml +59 -0
  20. data/app/views/effective/logs/index.html.haml +7 -0
  21. data/app/views/effective/logs/show.html.haml +2 -0
  22. data/config/routes.rb +18 -0
  23. data/db/migrate/01_create_effective_logging.rb.erb +29 -0
  24. data/lib/effective_logging.rb +32 -0
  25. data/lib/effective_logging/email_logger.rb +28 -0
  26. data/lib/effective_logging/engine.rb +50 -0
  27. data/lib/effective_logging/log_page_views.rb +68 -0
  28. data/lib/effective_logging/version.rb +3 -0
  29. data/lib/generators/effective_logging/install_generator.rb +33 -0
  30. data/lib/generators/templates/README +1 -0
  31. data/lib/generators/templates/effective_logging.rb +46 -0
  32. data/spec/effective_logging_spec.rb +7 -0
  33. data/spec/spec_helper.rb +33 -0
  34. metadata +150 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6ffec40703187b420ab31ea83ccb1c7d54f480c5
4
+ data.tar.gz: 1d61908e31c154fb6b61250f6b9ca8a521eae13f
5
+ SHA512:
6
+ metadata.gz: 9dcf971011fb02748255f89d58f5c534bbe7a5b8c694e39077589db39775efc4f41ee57a74b6590cefda73dcba32fb1b7453adfc5afdcbd420338ff1ea47b316
7
+ data.tar.gz: 29e2c73f69b919bb725ea5a15727dd8667017f66754e5b208ae77cbd0a4d9f3179226ad0ab8fdbfb233533a6a7c02d05f1a030649ff5243fa27d94cd6435ed6e
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2014 Code and Effect Inc.
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,280 @@
1
+ # Effective Logging
2
+
3
+ Automatically log all sent emails, user logins, and page views. This also will log custom events from Ruby and JavaScript.
4
+
5
+ A Rails3 / Rails4 engine to completely handle logging of events.
6
+
7
+ Logs are stored in a single database table. A Log (one logged event) can have many children Logs, nested to any depth.
8
+
9
+ Provides a ruby one-liner to log a message, status, user, associated object and any number of additional details.
10
+
11
+ Works great for single fire-and-forget events and long running tasks.
12
+
13
+ Provides a simple Javascript API for logging front-end events.
14
+
15
+ Automatically logs any email sent by the application, any successful user login via Devise, and all page views.
16
+
17
+ Has an effective_datatables driven admin interface to display and search/sort/filter all logs.
18
+
19
+
20
+ ## Getting Started
21
+
22
+ Add to your Gemfile:
23
+
24
+ ```ruby
25
+ gem 'effective_logging'
26
+ ```
27
+
28
+ Run the bundle command to install it:
29
+
30
+ ```console
31
+ bundle install
32
+ ```
33
+
34
+ Then run the generator:
35
+
36
+ ```ruby
37
+ rails generate effective_logging:install
38
+ ```
39
+
40
+ The generator will install an initializer which describes all configuration options and creates a database migration.
41
+
42
+ If you want to tweak the table name (to use something other than the default 'logs'), manually adjust both the configuration file and the migration now.
43
+
44
+ Then migrate the database:
45
+
46
+ ```ruby
47
+ rake db:migrate
48
+ ```
49
+
50
+ ## Usage
51
+
52
+ ### Basic
53
+
54
+ Log an event from anywhere in the application:
55
+
56
+ ```ruby
57
+ EffectiveLogger.info 'something happened'
58
+ ```
59
+
60
+ Each status, as per the config/initializers/effective_logging.rb initializer, will be created as a class level method:
61
+
62
+ ```ruby
63
+ EffectiveLogger.info 'something happened'
64
+ EffectiveLogger.success 'it worked!'
65
+ EffectiveLogger.error 'now we are in trouble'
66
+ ```
67
+
68
+ The :user and :associated options may be passed to indicate the user and associated (ActiveRecord) object which belong to this event:
69
+
70
+ ```ruby
71
+ EffectiveLogger.info 'product purchased', :user => current_user, :associated => @product
72
+ ```
73
+
74
+ Any other passed options will be serialized and stored as additional details:
75
+
76
+ ```ruby
77
+ EffectiveLogger.info 'feedback given', :user => current_user, :feedback => 'your app is great!', :rating => 'good'
78
+ ```
79
+
80
+ ### Sub Logs
81
+
82
+ Any log can have children logs. This is perfect for long-running tasks where there are multiple sub events:
83
+
84
+ ```ruby
85
+ log = EffectiveLogger.info('importing important records')
86
+
87
+ log.success('record 1 imported', :associated => @record1)
88
+ log.error('record 2 failed to import', :associated => @record2)
89
+ log.success('record 3 imported', :associated => @record3)
90
+ ```
91
+
92
+
93
+ ### Automatic Logging of E-mails
94
+
95
+ Any email sent by the application will be automatically logged.
96
+
97
+ This behaviour can be disabled in the config/initializers/effective_logging.rb initializer.
98
+
99
+ If the TO email address match a User, the :user will be set appropriately.
100
+
101
+
102
+ ### Automatic Logging of User Logins
103
+
104
+ Any successful User logins via Devise will be automatically logged.
105
+
106
+ This behaviour can be disabled in the config/initializers/effective_logging.rb initializer.
107
+
108
+ The User's IP address will also be logged.
109
+
110
+
111
+ ### Logging Page Views
112
+
113
+ All page views, whether initiated by a logged in user or not, may be logged.
114
+
115
+ To enable application wide logging for every request, add the following to your ApplicationController:
116
+
117
+ ```ruby
118
+ class ApplicationController < ActionController::Base
119
+ log_page_views
120
+ end
121
+ ```
122
+
123
+ The above command sets an after_filter to log every request. This is probably too much. Try instead:
124
+
125
+ ```ruby
126
+ class ApplicationController < ActionController::Base
127
+ log_page_views :except => [:new, :create, :edit, :update, :destroy], :skip_namespace => [Admin, Devise], :details => false
128
+ end
129
+ ```
130
+
131
+ The above command will log all requests to index, show and non-RESTful controller actions which are not from controllers in the Admin or Devise namespaces.
132
+
133
+ Page views always log the current_user when present.
134
+
135
+ By default, the request.params, request.format, request.referrer and request.user_agent information is also logged, unless :details => false is set.
136
+
137
+
138
+ Instead of logging all requests as per the ApplicationController, it may be easier to selectively log page views on just one or more controllers:
139
+
140
+ ```ruby
141
+ class ProductsController < ApplicationController
142
+ log_page_views
143
+ end
144
+ ```
145
+
146
+ However, if log_page_views is set by the ApplicationController, you can still opt out of logging specific actions in two different ways:
147
+
148
+ ```ruby
149
+ class ApplicationController < ActionController::Base
150
+ log_page_views
151
+ end
152
+
153
+ class ProductsController < ApplicationController
154
+ skip_log_page_views :only => [:show] # Skip logging with a before_filter
155
+
156
+ def index
157
+ @products = Product.all
158
+
159
+ skip_log_page_view if current_user.admin? # Skip logging with a method
160
+ end
161
+ end
162
+ ```
163
+
164
+ The above command will skip logging of the :show action and will skip logging of the :index action if the current_user is an admin.
165
+
166
+ log_page_views accepts any options that an after_filter would accept and has access to the request object
167
+
168
+ ```ruby
169
+ class ProductsController < ApplicationController
170
+ log_page_views :if => Proc.new { request.get? } # Only log GET requests
171
+ end
172
+ ```
173
+
174
+ Similarly, skip_log_page_views also accepts any options that a before_filter would accept.
175
+
176
+
177
+ ### Logging From JavaScript
178
+
179
+ First, require the javascript in your application.js:
180
+
181
+ ```ruby
182
+ //= require effective_logging
183
+ ```
184
+
185
+ then logging an event from JavaScript is almost the same one-liner as from ruby:
186
+
187
+ ```javascript
188
+ EffectiveLogger.success('clicked start on a video');
189
+ ```
190
+
191
+ the above command sends an AJAX request that creates the Log. If the current_user is present :user will be automatically set.
192
+
193
+ The syntax to attach (any number of) additional information fields is very forgiving:
194
+
195
+ ```javascript
196
+ EffectiveLogger.info('clicked start on a video', {video_title: 'cool video', video_time: '5:00'});
197
+ ```
198
+
199
+ and
200
+
201
+ ```javascript
202
+ EffectiveLogger.success('some other event', 'additional', 'information', {subject: 'my subject', from: 'someone'}, 'and more');
203
+ ```
204
+
205
+ The same statuses available to ruby will be available to JavaScript.
206
+
207
+ Creating child logs via JavaScript is not yet supported.
208
+
209
+
210
+ ## Admin Screen
211
+
212
+ To use the Admin screen, please also install the [effective_datatables](https://github.com/code-and-effect/effective_datatables/) gem:
213
+
214
+ ```ruby
215
+ gem 'effective_datatables'
216
+ ```
217
+
218
+ Then you should be able to visit:
219
+
220
+ ```ruby
221
+ link_to 'Logs', effective_logging.admin_logs_path # /admin/logs
222
+ ```
223
+
224
+ ### Build an All Logs Screen
225
+
226
+ If you don't want to use the builtin Admin screen, and would rather render the effective_datatable of logs elsewhere
227
+
228
+ In your controller:
229
+
230
+ ```ruby
231
+ @datatable = Effective::Datatables::Logs.new()
232
+ ```
233
+
234
+ And then in your view:
235
+
236
+ ```ruby
237
+ render_datatable(@datatable)
238
+ ```
239
+
240
+ ### Build a User Specific Logs Screen
241
+
242
+ We can also use a similar method to create a datatable of logs for just one user.
243
+
244
+ When initialized with :user_id, the 'User' column is hidden and the Logs are scoped to the User.
245
+
246
+ In your controller:
247
+
248
+ ```ruby
249
+ @user = User.find(params[:id])
250
+ @datatable = Effective::Datatables::Logs.new(:user_id => @user.id)
251
+ ```
252
+
253
+
254
+ ## License
255
+
256
+ MIT License. Copyright [Code and Effect Inc.](http://www.codeandeffect.com/)
257
+
258
+ Code and Effect is the product arm of [AgileStyle](http://www.agilestyle.com/), an Edmonton-based shop that specializes in building custom web applications with Ruby on Rails.
259
+
260
+
261
+ ## Testing
262
+
263
+ The test suite for this gem is unfortunately not yet complete.
264
+
265
+ Run tests by:
266
+
267
+ ```ruby
268
+ rake spec
269
+ ```
270
+
271
+
272
+ ## Contributing
273
+
274
+ 1. Fork it
275
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
276
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
277
+ 4. Push to the branch (`git push origin my-new-feature`)
278
+ 5. Bonus points for test coverage
279
+ 6. Create new Pull Request
280
+
data/Rakefile ADDED
@@ -0,0 +1,20 @@
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
+
8
+ # Testing tasks
9
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
10
+ load 'rails/tasks/engine.rake'
11
+
12
+ Bundler::GemHelper.install_tasks
13
+
14
+ require 'rspec/core'
15
+ require 'rspec/core/rake_task'
16
+
17
+ desc "Run all specs in spec directory (excluding plugin specs)"
18
+ RSpec::Core::RakeTask.new(:spec => 'app:db:test:prepare')
19
+
20
+ task :default => :spec
@@ -0,0 +1 @@
1
+ //= require_tree ./effective_logging
@@ -0,0 +1,15 @@
1
+ class @EffectiveLogger
2
+ @log = (message, status = 'info', details...) ->
3
+ $.ajax
4
+ type: 'POST',
5
+ url: '/logs.json'
6
+ data:
7
+ effective_log:
8
+ message: message
9
+ status: status
10
+ details: JSON.stringify(details)
11
+ true
12
+
13
+ <% EffectiveLogging.statuses.each do |status| %>
14
+ <%= "@#{status} = (message, details...) -> @log(message, '#{status}', details)" %>
15
+ <% end %>
@@ -0,0 +1,31 @@
1
+ module Admin
2
+ class LogsController < ApplicationController
3
+ before_filter :authenticate_user! # This is devise, ensure we're logged in.
4
+
5
+ layout (EffectiveLogging.layout.kind_of?(Hash) ? EffectiveLogging.layout[:admin_logs] : EffectiveLogging.layout)
6
+
7
+ skip_log_page_views :quiet => true
8
+ helper EffectiveLoggingHelper
9
+
10
+ def index
11
+ @datatable = Effective::Datatables::Logs.new() if defined?(EffectiveDatatables)
12
+ @page_title = 'Logs'
13
+
14
+ EffectiveLogging.authorized?(self, :index, Effective::Log)
15
+ end
16
+
17
+ def show
18
+ @log = Effective::Log.includes(:logs).find(params[:id])
19
+ @log.next_log = Effective::Log.unscoped.order(:id).where(:parent_id => @log.parent_id).where('id > ?', @log.id).first
20
+ @log.prev_log = Effective::Log.unscoped.order(:id).where(:parent_id => @log.parent_id).where('id < ?', @log.id).last
21
+
22
+ @page_title = "Log ##{@log.to_param}"
23
+
24
+ if @log.logs.present?
25
+ @log.datatable = Effective::Datatables::Logs.new(:log_id => @log.id) if defined?(EffectiveDatatables)
26
+ end
27
+
28
+ EffectiveLogging.authorized?(self, :show, @log)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,71 @@
1
+ module Effective
2
+ class LogsController < ApplicationController
3
+ skip_log_page_views
4
+ before_filter :authenticate_user!, :only => [:index, :show] # Devise ensure logged in
5
+
6
+ # This is a post from our Javascript
7
+ def create
8
+ EffectiveLogging.authorized?(self, :create, Effective::Log.new())
9
+
10
+ @log = Effective::Log.new().tap do |log|
11
+ log.message = log_params[:message]
12
+ log.status = (EffectiveLogging.statuses.include?(log_params[:status]) ? log_params[:status] : 'info')
13
+ log.user = (current_user rescue nil)
14
+
15
+ #log.parent = options.delete(:parent)
16
+ #log.associated = options.delete(:associated)
17
+
18
+ count = -1
19
+ Array((JSON.parse(log_params[:details]) rescue [])).flatten(1).each do |obj|
20
+ if obj.kind_of?(Hash)
21
+ obj.each { |k, v| log.details[k] = v if v.present? }
22
+ else
23
+ log.details["param_#{(count += 1)}"] = obj if obj.present?
24
+ end
25
+ end
26
+
27
+ log.details[:referrer] = request.referrer
28
+
29
+ log.save
30
+ end
31
+
32
+ render :text => "ok", :status => :ok
33
+ end
34
+
35
+ # This is the User index event
36
+ def index
37
+ @datatable = Effective::Datatables::Logs.new(:user_id => current_user.id) if defined?(EffectiveDatatables)
38
+ @page_title = 'My Activity'
39
+
40
+ EffectiveLogging.authorized?(self, :index, Effective::Log.new(:user_id => current_user.id))
41
+ end
42
+
43
+ # This is the User show event
44
+ def show
45
+ @log = Effective::Log.where(:user_id => current_user.id).includes(:logs).find(params[:id])
46
+ @log.next_log = Effective::Log.unscoped.where(:user_id => current_user.id).order(:id).where(:parent_id => @log.parent_id).where('id > ?', @log.id).first
47
+ @log.prev_log = Effective::Log.unscoped.where(:user_id => current_user.id).order(:id).where(:parent_id => @log.parent_id).where('id < ?', @log.id).last
48
+
49
+ @page_title = "Log ##{@log.to_param}"
50
+
51
+ if @log.logs.present?
52
+ @log.datatable = Effective::Datatables::Logs.new(:user_id => current_user.id, :log_id => @log.id) if defined?(EffectiveDatatables)
53
+ end
54
+
55
+ EffectiveLogging.authorized?(self, :show, @log)
56
+ end
57
+
58
+
59
+ private
60
+
61
+ # StrongParameters
62
+ def log_params
63
+ begin
64
+ params.require(:effective_log).permit(:message, :status, :details)
65
+ rescue => e
66
+ params[:effective_log] || {}
67
+ end
68
+ end
69
+
70
+ end
71
+ end