Requirements
I would like to keep an audit log of particular events, such as:
User logged in successfully
User could not log in (with reason: wrong password, unconfirmed, etc.)
SuperUser modified another users details (with what they changed)
etc.
This log should include details such as:
The logged in user performing the action (based on the controller's current_user)
The record being modified (such as the other user record in the case of super users)
The IP address of request for that action
This log should also be stored in a file on the filesystem, not a database table so it can be ingested by another service on the machine at a later time.
Some possibilities
Here is a short list of the approaches I have considered so far:
ActiveRecord::Observer
Observers give a nice separated way of watching for these particular events.
I could then get the observer to append to a log file, but I'm not sure how easy I would be able to obtain the result of such a call (such as login failed or worked) and I would need to somehow call the controller method current_user to find out the logged in user and get the HTTP request to obtain the IP address.
An auditing gem (such as audited, auditable, paper_trail, etc.)
These gems have the convenience of knowing how to get access to the controller for the current user and IP address, but they all log to an audit table in the database. Auditable is particularly nice because it can audit any method call on an object, not just an AR callback, but I may need to patch it to write to a file instead of the database.. or something?
ActiveSupport::Notifications
I still need to read up on this, but I believe it offers low-level a way of subscribing to low-level events within rails. This might be too low-level for this situation, but I need to investigate further.
Log4r
It seems this will make a nice log file, but I don't think it has any way of watching for events. This would only be part of the problem.
Any advice?
Is there a best practices way of doing this? Can you recommend any gems or lessons learned from previous experience? Anything else I should consider?
Thanks to everyone for the responses.
Casper, I did decide to build something custom.
I see your point with writing to a local db anyway, but the requirement for this project is to dump log files so a more elaborate log parsing service can interrogate the files and even combine them with information from other sources.
In order to get logging from both models and controllers, I ended up making a module that I would include in both the observers and ApplicationController.
The module looks a little something like this:
module MyEventLogger
mattr_accessor :logged_current_user
mattr_accessor :logged_remote_ip
def log_event(message)
##logger ||= Logger.new(Rails.root.join('log', 'audit.log'))
##logger.info "#{Time.now} | #{logged_current_user}##{logged_remote_ip} | #{message}"
end
def logged_current_user
##logged_current_user || "SYSTEM"
end
def logged_remote_ip
##logged_remote_ip || "NO IP ADDRESS"
end
end
ApplicationController would have:
include MyEventLogger
before_filter :setup_logger
...
def setup_logger
MyEventLogger.logged_current_user = current_user
MyEventLogger.logged_ip_address = request.remote_ip
end
The observer would just have to include MyEventLogger and it would have access to the log_event method and the current user and ip address. For example:
class UserObserver < ActiveRecord::Observer
include MyEventLogger
def after_save(user)
log_event "The User #{user} was saved by #{logged_current_user}"
end
end
Some thoughts:
The auditing gems sound closest to what you want. If you look at their source code they are not that complex, and most seem to actually be built around Rails Observers. You could easily use their code as a base to create your own specialized versions.
If you look at he code of Audited for example you will see it's actually quite simple to store the current_user (if you decide to roll your own implementation):
https://github.com/collectiveidea/audited/blob/master/lib/audited/sweeper.rb
I don't think having the auditing data in a DB is necessarily bad. You might actually find that useful one day when you need to track down some complex problem from thousands of previous transactions. You can always create a simple Rake task to dump the data into a logfile format when needed.
However I will say that Log4r is quite nice, and I've used it myself in a couple of projects. But I did not have an audit-type need. Only basic logging for debugging and troubleshooting.
You COULD also consider combining something like your own Observer-type system with a Log4r driver instead of a DB driver, if that's what you want. Because it does sound a little bit like you might need to trigger logging events outside of what the Observer system provides anyway. Which means you're going to have to either implement extensions to existing gems, or use a gem as a base and extend it with your own functionality.
Anyway - I still see the DB approach as actually a benefit, and kind of cool to be able to run queries on your audit-trail. Stuff like that can't hurt (Log4r btw. supports custom "output drivers" also, so even that could be used to log into a DB).
Just sharing my personal experience with this:
I created something very similar to what you mention here. I used a table in the database, trapped the relevant post request in the application_controller, and passed the info to the model associated with my log table. The logic was quite trivial, and I had all the control I wanted. The only effort was in selecting/ rejecting particular transactions, and restructuring all useful params to fit nicely in a text field.
If you decide to take that route, I will be happy to share more details.
Good luck.
One way of doing that, is wherever you want to run a logged action, run it in a block
I really recommend looking at Chapter 18 of Eloquent Ruby by Russ Olsen http://books.google.com/books?id=-s2xL0pVsLUC&lpg=PA219&ots=l7I3oAK3M2&dq=eloquent%20ruby%20chapter%2018&pg=PA219#v=onepage&q&f=false and maybe 'Using Blocks' in Chapter 2 of Gregory Brown's Ruby Best Practices http://majesticseacreature.com/rbp-book/pdfs/rbp_1-0.pdf
e.g.
def with_logging(description)
begin
#logger.debug( "Starting #{description}" )
yield # this is when the code in the block executes
#logger.debug( "Completed #{description}" )
rescue
#logger.error( "#{description} failed!!")
raise
end
end
with_logging('code example') { puts "just printing something" }
Also: It might be worth looking into tools like graylog http://graylog2.org/about/gelf (and see http://arrrrcamp.be/videos/2011/lennart-koopmann---managing-the-logs-of-your-rails-applications/ ) or these posts http://openmymind.net/2012/4/4/You-Really-Should-Log-Client-Side-Error/ (javascript), https://github.com/TwP/logging , http://amon.cx/
Related
Although experienced with another frameworks, I'm a rails newbie. I've come upon the main twitter gem and want to use it. I understand the code... but I don't understand where exactly I should work it, at all (I've read plenty of rails but lack of practical examples).
I want to fetch the tweet info (post, user, etc)
Save the tweet to the database using postgre
I have a 'publication' (about the tweet) and 'publication' model, amongst helpers and so on.
Could someone please walk me through on how to do this? I'm not asking for you to do my work. Just please explain me the thought process of rails via a lazy example because I'm not understanding how to work with the gem this way... Thank you very much for your help :)
max's comment is exactly right, but to help put it into context, let me illustrate how, in your situation, you can build and apply a service object.
First, consider what your service object will be doing, and pick a name for it (you'll often change this later, as you figure things out better). For example, TweetFetcher. Then, decide what information it needs before it can do its job. I'm not clear on which tweet(s) you want to fetch, from your question, but let's assume it just wants to fetch the latest tweet for a given handle. Then, your object can start out like so:
class TweetFetcher
def initialize(handle)
#handle = handle
end
end
Now, this file can go anywhere Rails will automatically load it. The lib/ folder is pretty standard (e.g. lib/tweet_fetcher.rb), but you may need to add lib to your autoload paths. Even simpler is to throw it into the app/models folder, though that's a bit confusing for future developers.
Next, make it do its job. You'll need to add a new method to your class which "calls" it; a standard name is call but you can pick what you'd like. At this point, I'd suggest you write tests for your service object, just like you would for a model, but I won't get into details on that. At the end of the process, your code will look like:
class TweetFetcher
def initialize(handle)
#handle = handle
end
def call
# not real code at all
tweet = Twitter::Client.fetch_latest_tweet(#handle)
Publication.create!(tweet_id: tweet.id)
end
end
Rock-solid. So, the next question is, when to call it? In general, I'd suggest calling service objects from your controllers. For example, say this is all supposed to happen when a user submits a form that they just entered a twitter handle into... say, POST /publications, which hits PublicationsController#create:
class PublicationsController < ApplicationController
def create
# validate params, w/e
#publication = TweetFetcher.new(params[:handle]).call
flash[:notice] = "aaaand done."
end
end
However, you could call your service object from anywhere -- that's the beauty of building them. It allows you to extract, encapsulate, and re-use code in any context. It also makes it way easier to test your code, as I think you'll find. Hope this helped, and good luck!
I know the dogma says to not access current_user in a model but I don't fully agree with it. For example, I want to write a set of logging functions when an action happens via a rails callback. Or simply writing who wrote a change when an object can have multiple people write to it (not like a message which has a single owner). In many ways, I see current_user more as config for an application - in other words make this app respond to this user. I would rather have my logging via the model DSL rather than in the action where it seems REALLY out of place. What am I missing?
This idea seems rather inelegant Access current_user in model
as does this: http://rails-bestpractices.com/posts/47-fetch-current-user-in-models
thx
edit #1
So my question isn't if there are gems that can do auditing / logging. I currently use paper_trail (although moving away from it because I can do same functionality in approx 10 lines of ruby code); it is more about whether current_user should never be accessed in the model - I essentially want to REDUCE my controller code and push down logic to models where it should be. Part of this might be due to the history of ActiveRecord which is essentially a wrapper around database tables for which RoR has added a lot of functionality over the years.
You've given several examples that you'd like to accomplish, I'll go through the solution to each one separately:
I want to write a set of logging functions when an action happens via
a rails callback
Depending on how you want to log (DB vs writing to the logger). If you want to log to the DB, you should have a separate logging model which is given the appropriate information from the controller, or simply with a belongs_to :user type setup. If you want to write to the logger, you should create a method in your application controller which you can call from your create and update methods (or whatever other actions you wanted to have a callback on.)
Or simply writing who wrote a change when an object can have multiple people write to it
class Foo < ActiveRecord::Base
belongs_to :user, as: :edited_by
end
class FooController < ApplicationController
def update
#foo = Foo.find(params[:id])
#foo.attributes = params[:foo]
#foo.edited_by = current_user
end
end
I think you're misunderstanding what the model in Rails does. Its scope is the database. The reason it can't access current_user, is because the current user is not stored in the database, it is a session variable. This has absolutely nothing to do with the model, as this is something that can not exist without a browser.
ActiveRecord::Base is not a class that is designed to work with the browser, it is something that works with the database and only the database. You are using the browser as an interface to that model, but that layer is what needs to access browser specific things such as session variables, as your model is extending a class that is literally incapable of doing so.
This is not a dogma or style choice. This is a fact of the limitations of the class your model is extending from. That means your options basically boil down to extending from something else, handling it in your controller layer, or passing it to the model from your controller layer. ActiveRecord will not do what you want in this case.
The two links you show (each showing imho the same approach) is very similar to a approach I still use. I store the current_user somewhere (indeed thread-context is the safest), and in an observer I can then create a kind of audit-log of all changes to the watched models, and still log the user.
This is imho a really clean approach.
An alternative method, which is more explicit, less clean but more MVC, is that you let the controller create the audit-log, effectively logging the actions of the users, and less the effects on different models. This might also be useful, and in one website we did both. In a controller you know the current-user, and you know the action, but it is more verbose.
I believe your concerns are that somehow this proposed solution is not good enough, or not MVC enough, or ... what?
Another related question: How to create a full Audit log in Rails for every table?
Also check out the audited gem, which solves this problem as well very cleanly.
Hope this helps.
I am looking for a Ruby/Rails tool that will help me accomplish the following:
I would like to store the following string, and ones similar to it, in my database. When an object is created, updated, deleted, etc., I want to run through all the strings, check to see if the CRUD event matches the conditions of the string, and if so, run the actions specified.
When a new ticket is created and it's category=6 then notify user 1234 via email
I am planning to create an interface that builds these strings, so it doesn't need to be a human-readable string. If a JSONish structure is better, or a tool has an existing language, that would be fantastic. I'm kinda thinking something along the lines of:
{
object_types: ['ticket'],
events: ['created', 'updated'],
conditions:'ticket.category=6',
actions: 'notify user',
parameters: {
user:1234,
type:'email'
}
}
So basically, I need the following:
Monitor CRUD events - It would be nice if the tool had a way to do this, but Ican use Rails' ModelObservers here if the tool doesn't natively provide it
Find all matching "rules" - This is my major unknown...
Execute the requested method/parameters - Ideally, this would be defined in my Ruby code as classes/methods
Are there any existing tools that I should investigate?
Edit:
Thanks for the responses so far guys! I really appreciate you pointing me down the right paths.
The use case here is that we have many different clients, with many different business rules. For the rules that apply to all clients, I can easily create those in code (using something like Ruleby), but for all of the client-specific ones, I'd like to store them in the database. Ideally, the rule could be written once, stored either in the code, or in the DB, and then run (using something Resque for performance).
At this point, it looks like I'm going to have to roll my own, so any thoughts as to the best way to do that, or any tools I should investigate, would be greatly appreciated.
Thanks again!
I don't think it would be a major thing to write something yourself to do this, I don't know of any gems which would do this (but it would be good if someone wrote one!)
I would tackle the project in the following way, the way I am thinking is that you don't want to do the rule matching at the point the user saves as it may take a while and could interrupt the user experience and/or slow up the server, so...
Use observers to store a record each time a CRUD event happens, or to make things simpler use the Acts as Audited gem which does this for you.
1.5. Use a rake task, running from your crontab to run through the latest changes, perhaps every minute, or you could use Resque which does a good job of handling lots of jobs
Create a set of tables which define the possible rules a user could select from, perhaps something like
Table: Rule
Name
ForEvent (eg. CRUD)
TableInQuestion
FieldOneName
FieldOneCondition etc.
MethodToExecute
You can use a bit of metaprogramming to execute your method and since your method knows your table name and record id then this can be picked up.
Additional Notes
The best way to get going with this is to start simple then work upwards. To get the simple version working first I'd do the following ...
Install acts as audited
Add an additional field to the created audit table, :when_processed
Create yourself a module in your /lib folder called something like processrules which roughly does this
3.1 Grabs all unprocessed audit entries
3.2 Marks them as processed (perhaps make another small audit table at this point to record events happening)
Now create a rules table which simply has a name and condition statement, perhaps add a few sample ones to get going
Name: First | Rule Statement: 'SELECT 1 WHERE table.value = something'
Adapt your new processrules method to execute that sql for each changed entry (perhaps you want to restrict it to just the tables you are working with)
If the rule matched, add it to your log file.
From here you can extrapolate out the additional functionality you need and perhaps ask another question about the metaprogramaming side of dynamically calling methods as this question is quite broad, am more than happy to help further.
I tend to think the best way to go about task processing is to setup the process nicely first so it will work with any server load and situation then plug in the custom bits.
You could make this abstract enough so that you can specify arbitrary conditions and rules, but then you'd be developing a framework/engine as opposed to solving the specific problems of your app.
There's a good chance that using ActiveRecord::Observer will solve your needs, since you can hardcode all the different types of conditions you expect, and then only put the unknowns in the database. For example, say you know that you'll have people watching categories, then create an association like category_watchers, and use the following Observer:
class TicketObserver < ActiveRecord::Observer
# observe :ticket # not needed here, since it's inferred by the class name
def after_create(ticket)
ticket.category.watchers.each{ |user| notify_user(ticket, user) }
end
# def after_update ... (similar)
private
def notify_user(ticket, user)
# lookup the user's stored email preferences
# send an email if appropriate
end
end
If you want to store the email preference along with the fact that the user is watching the category, then use a join model with a flag indicating that.
If you then want to abstract it a step further, I'd suggest using something like treetop to generate the observers themselves, but I'm not convinced that this adds more value than abstracting similar observers in code.
There's a Ruby & Rules Engines SO post that might have some info that you might find useful. There's another Ruby-based rules engine that you may want to explore that as well - Ruleby.
Hope that this helps you start your investigation.
I am interested in writing a library for a third party API I am using and I need some advice. The average use of the library will involve several api calls in one request. For example, one api call to grab a user from the third party service, and then another call to use that user to grab his/her photos. Each API call will get its own library method wrapper with additional logic to handle errors/timeouts, but my biggest question is whether the library should be made as a singleton that contains state or just as a series of class methods.
For example:
user_id = ThirdParty.get_user("abc#gmail.com")
photos = ThirdParty.get_photos(user_id)
OR
thirdpartyservice = ThirdPartyService.new("abc#gmail.com")
photos = thirdpartyservice.get_photos
These doesn't have to be the exact deseign of the library, but I just am confused about the pros/cons of each approach. Any help would be amazing!
Btw, I am using ruby!
I would have the library contain the state as this reduces the complexity of code on the user side(and that's what API are supposed to do, increase simplicity). With this approach, the user doesn't have to keep track of that user_id since the library is keeping state of that.
If the user really wanted their user_id (or any other data that the library stores), you can just create an attr_reader in your library to expose that data.
To add fleixiblity for the get_photos method, you can do something like:
class ThirdPartyService
def get_photos(user_id=#id_stored_in_library)
# do work
end
end
This way it defaults to the stored id, however it adds flexibility in that the user can specify the userid if he so chooses.
You need state (host, etc) and behavior based on that state so you should use objects, not one singleton object.
As mentioned, you should not name methods like get_photos, just photos.
I believe the best practice is using provider and services for the ability to not be tied down to a specific service provider. Instead of simply wrapping the library you may want to abstract it a bit and by default only allow a single service provider.
Although inheritance isn't really needed in a dynamic/duck typed language like Ruby this may help concrete the 'why'.
class MailProvider
def initialize(args); raise "Not Implemented"; end
def send_mail(args); raise "Not Implemented"; end
end
class SendGridService < MailProvider
def initialize(args)
# Use args here
end
def send_mail(args)
# Use SendGrid's API or Gem here
end
end
Then, in your code you can have something like this:
config.mail_provider = SendGridService.new({
username: ENV['SENDGRID_USERNAME'],
password: ENV['SENDGRID_PASSWORD']
})
config.mail_provider.send_mail({ subject: 'woot', message: 'Look ma, I did it!' })
Then six months later when your users love your Gem/library but want MailChimp support you can simply create a second service 'MailChimpService' and allow your clients to use the new provider.
Instead of get_ set_ methods will be better to use getters and setters. It is ruby standard (not sure, but get_ and set_ methods I saw only once in ruby code).
If you don't need to save some state between requests, make it static. But it depends on many factors. Can you tell us what API you need to wrap ?
We are creating a system in Ruby on Rails and we want to be able to offer our users a bit of control about notifications and actions that can take place when some pre-defined trigger occurs. In addition, we plan on iterating through imported data and allowing our users to configure some actions and triggers based on that data.
Let me give you a few examples to better clarify:
Trigger - Action
------------------------------------------------------------------------
New Ticket is Created - User receives an e-mail
New Ticket Parsed for Keyword 'evil' - Ticket gets auto-assigned to a
particular group
User Missed 3 Meetings - A ticket is automatically created
Ideally, we would like some of the triggers to be configurable. For instance, the last example would possibly let you configure how many meetings were missed before the action took place.
I was wondering what patterns might help me in doing this event/callback situation in Ruby on Rails. Also, the triggers and actions may be configurable, but they will be predefined; so, should they be hard coded or stored in the database?
Any thoughts would be greatly appreciated. Thanks!
Update 1: After looking at it, I noticed that the badges system on SO is somewhat similar, based on these criteria, I want to do this action. It's slightly different, but I want to be able to easily add new criteria and actions and present them to the users. Any thoughts relating to this?
I think that what you are looking for are the Observers.
In your examples the Observers could handle the first and the third example (but not the second one, since an Observer only observes the object, not interact with it, even though it is technically possible).
Some code to show how I mean:
class TicketObserver < ActiveRecord::Observer
def after_create(ticket)
UserMailer.deliver_new_ticket_notification
end
end
class UserObserver < ActiveRecord::Observer
def after_update(user)
Ticket.new if user.recently_missed_a_meeting and user.missed_meetings > 3
end
end
And then add the observers to environment.rb
config.active_record.observers = :user_observer, :ticket_observer
Of course you will have to fill in the logic for the missed_meetings, but one detail to mention.
Since the after_update will trigger after every time that the user is being updated, the recently_missed_a_meeting attribute is useful. I usually follow the thinking of restful-authentication and have an instance variable that is being set to true everytime I want to trigger that row. That can be done in a callback or in some custom logic depends on how you track the meetings.
And for the second example, I would put it in a before_update callback, perhaps having the keywords in a lookup table to let users update which words that should trigger the move to a specific group.
You should look at the "callback" methods in Rails
For docs see - Callbacks
Your first rule would be implemented via the after_create method.
If you want them to be configurable, I would suggest using a model / table to store the possible actions and doing a lookup within the callback.
If this is high volume, be sure to consider caching the configuration since it would end up doing a db lookup on each callback.
Maybe something like a state-machine can help. Try AASM gem for RoR.