Cloud Computing: The Digital Shift 2.0 – Understanding the Current Market Trends

Custom event tracking with ActiveSupport::Notifications and Audited

When it comes to logging or tracking changes in Ruby on Rails models, We typically tend to use either paper_trail or audited gems as these are most popular and widely used gems in Ruby on Rails applications for tracking changes in Rails models, but what if you want to track the custom events on controller level(like following) in addition to model changes? 

  • User login and logout 
  • Password change success or fail
  • User login failed or succeeded
  • OR any other user activity

In this post I will share my learnings on how we can track controller level custom events with the help of model changes tracking gem audited and ActiveSupport::Notifications.

Brief about ActiveSupport::Notifications

ActiveSupport::Notifications gives an APIs for event instrumentation which works on pub/sub model i.e. you can publish an event notification/trigger for the particular subscriber which in turn takes necessary actions for the given event trigger.

For example, publishing and subscribing of a ‘login_success‘ event

Publishing an event

[code language=ruby]
ActiveSupport::Notifications.instrument(‘login_failed’, event_data)
#This triggers the notification for login_failed event

Subscribing to an event

[code language=ruby]

ActiveSupport::Notifications.subscribe(‘login_failed’) do |*args|
event =*args)
#Take necessary actions on this event


Using Audited for saving custom events

I have created an EventHandler service which publishes/creates events and is used by controllers and subscribers.

[code language=ruby]
class EventHandler
def initialize(event)
@event = event

def process!
audit_data = {
username: @event.payload[’email’] || @event.payload[‘username’],
comment: @event.payload[:comment]

audit_data[:auditable] = @event.payload[:auditable] if @event.payload[:auditable]
audit_data[:user] = @event.payload[:user] if @event.payload[:user]

def self.trigger_event_audit(event_name, params, resource, comment=nil)
event_data = {}
event_data.merge!(auditable: resource, user: resource, comment: comment)
ActiveSupport::Notifications.instrument(event_name, event_data)


Using in controller action

Let’s say I have the following piece of code in my controller which allows the user to login.

[code language=ruby]
class Users::SessionsController < Devise::SessionsController

#POST /resource/sign_in
def create
self.resource = warden.authenticate(auth_options)
if resource.present?

EventHandler.trigger_event_audit(‘login_success’, params, resource)
respond_with resource, location: after_sign_in_path_for(resource)
EventHandler.trigger_event_audit(‘login_failed’, params, resource, ‘invalid email/username or password address’)
respond_with resource, location: root_path


Subscribing to events

[code language=ruby]
ActiveSupport::Notifications.subscribe(‘login_failed’) do |*args|
event =*args)!

ActiveSupport::Notifications.subscribe(‘login_success’) do |*args|
event =*args)!


Rails ActiveSupport Instrumentation is very handy whenever we need to write the event based triggers for notifications, event tracking, data logging etc. The aim of this post is to demonstrate how we can use the Rails ActiveSupport Instrumentation with audited gem, Specially if implementation of audited gem is in place and on top of it we need to track the custom events without creating any new database schema.

If you have any suggestions or feedback, please feel free to add them in comments section.

Leave a Comment

Your email address will not be published.