effective_logging 1.0.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 (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