Rack Middleware that logs API calls and sends to Moesif for API analytics and monitoring.
Supports Ruby on Rails, Grape, and other Ruby frameworks built on Rack.
Install the Moesif gem.
gem install moesif_rack
If you're using Bundler, add the gem to your Gemfile
.
gem 'moesif_rack'
Then, run bundle install
Create an options hash containing application_id and any other options.
moesif_options = {
'application_id' => 'Your Moesif Application Id'
}
Your Moesif Application Id can be found in the Moesif Portal. After signing up for a Moesif account, your Moesif Application Id will be displayed during the onboarding steps.
You can always find your Moesif Application Id at any time by logging into the Moesif Portal, click on the top right menu, and then clicking Installation.
Using strings or symbols for middleware class names is deprecated for newer frameworks like Ruby 5.0, so you should pass the class directly.
class Application < Rails::Application
moesif_options = {
'application_id' => 'Your Moesif Application Id'
}
config.middleware.use MoesifRack::MoesifMiddleware, moesif_options
end
For most rack-based frameworks including Rails 4.x or older, add the middleware MoesifRack::MoesifMiddleware
.
within config/application.rb
class Application < Rails::Application
moesif_options = {
'application_id' => 'Your Moesif Application Id'
}
config.middleware.use "MoesifRack::MoesifMiddleware", moesif_options
end
For Grape APIs, we can add the middleware after any custom parsers or formatters.
module Acme
class Ping < Grape::API
format :json
moesif_options = {
'application_id' => 'Your Moesif Application Id'
}
insert_after Grape::Middleware::Formatter, MoesifRack::MoesifMiddleware, moesif_options
get '/ping' do
{ ping: 'pong' }
end
end
end
Since Moesif Rack is a logging middleware, the ordering of middleware matters. The best place for "MoesifRack::MoesifMidleware" is near the top (so it captures the data closest to the wire), but after any body parsers or authentication middleware.
Typically, right above the default logger of Rails apps, "Rails::Rack::Logger" is a good spot. Or if you want to be as close as wire as possible, put it before "ActionDispatch::Static"
To insert the Moesif middleware before "Rails::Rack::Logger", you can use the insert_before
method instead of
use
class Application < Rails::Application
# snip
config.middleware.insert_before Rails::Rack::Logger, MoesifRack::MoesifMiddleware, moesif_options
# snip
end
If you are using "Rack::Deflater" or other compression middleware, make sure Moesif is after it, so it can capture the uncompressed data.
To see your current list of middleware:
bin/rails middleware
Options is a hash with these possible key/value pairs.
Required. String. This is the Moesif application_id under settings from your Moesif account.
Optional. String. Tag requests with the version of your API.
Optional. identify_user is a Proc that takes env, headers, and body as arguments and returns a user_id string. This helps us attribute requests to unique users. Even though Moesif can automatically retrieve the user_id without this, this is highly recommended to ensure accurate attribution.
moesif_options['identify_user'] = Proc.new { |env, headers, body|
# Add your custom code that returns a string for user id
'12345'
}
Optional. identify_company is a Proc that takes env, headers, and body as arguments and returns a company_id string. This helps us attribute requests to unique company.
moesif_options['identify_company'] = Proc.new { |env, headers, body|
# Add your custom code that returns a string for company id
'67890'
}
Optional. A Proc that takes env, headers, body and returns a string.
moesif_options['identify_session'] = Proc.new { |env, headers, body|
# Add your custom code that returns a string for session/API token
'XXXXXXXXX'
}
Optional. get_metadata is a Proc that takes env, headers, and body as arguments and returns a Hash that is representation of a JSON object. This allows you to attach any metadata to this event.
moesif_options['get_metadata'] = Proc.new { |env, headers, body|
# Add your custom code that returns a dictionary
value = {
'datacenter' => 'westus',
'deployment_version' => 'v1.2.3'
}
value
}
Optional. A Proc that takes event_model as an argument and returns event_model. With mask_data, you can make modifications to headers or body of the event before it is sent to Moesif.
moesif_options['mask_data'] = Proc.new { |event_model|
# Add your custom code that returns a event_model after modifying any fields
event_model.response.body.password = nil
event_model
}
For details for the spec of event model, please see the moesifapi-ruby
Optional. A Proc that takes env, headers, body and returns a boolean.
moesif_options['skip'] = Proc.new { |env, headers, body|
# Add your custom code that returns true to skip logging the API call
if env.key?("REQUEST_URI")
# Skip probes to health page
env["REQUEST_URI"].include? "/health"
else
false
end
}
For details for the spec of event model, please see the Moesif Ruby API Documentation
Optional. Boolean. Default false. If true, it will print out debug messages. In debug mode, the processing is not done in backend thread.
Optional. Boolean. Default true. If false, will not log request and response body to Moesif.
Optional. int, default 200, Maximum batch size when sending to Moesif.
Optional. int in seconds Default 2. This is the maximum wait time (approximately) before triggering flushing of the queue and sending to Moesif.
Optional. int, Default 1000, Maximum number of events to hold in queue before sending to Moesif. In case of network issues when not able to connect/send event to Moesif, skips adding new to event to queue to prevent memory overflow.
Optional. Boolean, Default false
. Set to true
to capture all outgoing API calls from your app to third parties like Stripe, Github or to your own dependencies while using Net::HTTP package. The options below is applied to outgoing API calls. When the request is outgoing, for options functions that take request and response as input arguments, the request and response objects passed in are Request request and Response response objects.
Optional. identify_user_outgoing is a Proc that takes request and response as arguments and returns a user_id string. This helps us attribute requests to unique users. Even though Moesif can automatically retrieve the user_id without this, this is highly recommended to ensure accurate attribution.
moesif_options['identify_user_outgoing'] = Proc.new { |request, response|
# Add your custom code that returns a string for user id
'12345'
}
Optional. identify_company_outgoing is a Proc that takes request and response as arguments and returns a company_id string. This helps us attribute requests to unique company.
moesif_options['identify_company_outgoing'] = Proc.new { |request, response|
# Add your custom code that returns a string for company id
'67890'
}
Optional. get_metadata_outgoing is a Proc that takes request and response as arguments and returns a Hash that is representation of a JSON object. This allows you to attach any metadata to this event.
moesif_options['get_metadata_outgoing'] = Proc.new { |request, response|
# Add your custom code that returns a dictionary
value = {
'datacenter' => 'westus',
'deployment_version' => 'v1.2.3'
}
value
}
Optional. A Proc that takes request, response and returns a string.
moesif_options['identify_session_outgoing'] = Proc.new { |request, response|
# Add your custom code that returns a string for session/API token
'XXXXXXXXX'
}
Optional. A Proc that takes request, response and returns a boolean. If true
would skip sending the particular event.
moesif_options['skip_outgoing'] = Proc.new{ |request, response|
# Add your custom code that returns true to skip logging the API call
false
}
Optional. A Proc that takes event_model as an argument and returns event_model. With mask_data_outgoing, you can make modifications to headers or body of the event before it is sent to Moesif.
moesif_options['mask_data_outgoing'] = Proc.new { |event_model|
# Add your custom code that returns a event_model after modifying any fields
event_model.response.body.password = nil
event_model
}
Optional. Boolean. Default true. If false, will not log request and response body to Moesif.
Create or update a user profile in Moesif.
The metadata field can be any customer demographic or other info you want to store.
Only the user_id
field is required.
This method is a convenient helper that calls the Moesif API lib.
For details, visit the Ruby API Reference.
metadata = {
:email => '[email protected]',
:first_name => 'John',
:last_name => 'Doe',
:title => 'Software Engineer',
:salesInfo => {
:stage => 'Customer',
:lifetime_value => 24000,
:accountOwner => '[email protected]',
}
}
# Campaign object is optional, but useful if you want to track ROI of acquisition channels
# See https://www.moesif.com/docs/api#users for campaign schema
campaign = MoesifApi::CampaignModel.new()
campaign.utm_source = "google"
campaign.utm_medium = "cpc"
campaign.utm_campaign = "adwords"
campaign.utm_term = "api+tooling"
campaign.utm_content = "landing"
# Only user_id is required.
# metadata can be any custom object
user = MoesifApi::UserModel.new()
user.user_id = "12345"
user.company_id = "67890" # If set, associate user with a company object
user.campaign = campaign
user.metadata = metadata
update_user = MoesifRack::MoesifMiddleware.new(@app, @options).update_user(user_model)
Similar to update_user, but used to update a list of users in one batch.
Only the user_id
field is required.
This method is a convenient helper that calls the Moesif API lib.
For details, visit the Ruby API Reference.
users = []
metadata = {
:email => '[email protected]',
:first_name => 'John',
:last_name => 'Doe',
:title => 'Software Engineer',
:salesInfo => {
:stage => 'Customer',
:lifetime_value => 24000,
:accountOwner => '[email protected]',
}
}
# Campaign object is optional, but useful if you want to track ROI of acquisition channels
# See https://www.moesif.com/docs/api#users for campaign schema
campaign = MoesifApi::CampaignModel.new()
campaign.utm_source = "google"
campaign.utm_medium = "cpc"
campaign.utm_campaign = "adwords"
campaign.utm_term = "api+tooling"
campaign.utm_content = "landing"
# Only user_id is required.
# metadata can be any custom object
user = MoesifApi::UserModel.new()
user.user_id = "12345"
user.company_id = "67890" # If set, associate user with a company object
user.campaign = campaign
user.metadata = metadata
users << user
response = MoesifRack::MoesifMiddleware.new(@app, @options).update_users_batch(users)
Create or update a company profile in Moesif.
The metadata field can be any company demographic or other info you want to store.
Only the company_id
field is required.
This method is a convenient helper that calls the Moesif API lib.
For details, visit the Ruby API Reference.
metadata = {
:org_name => 'Acme, Inc',
:plan_name => 'Free',
:deal_stage => 'Lead',
:mrr => 24000,
:demographics => {
:alexa_ranking => 500000,
:employee_count => 47
}
}
# Campaign object is optional, but useful if you want to track ROI of acquisition channels
# See https://www.moesif.com/docs/api#update-a-company for campaign schema
campaign = MoesifApi::CampaignModel.new()
campaign.utm_source = "google"
campaign.utm_medium = "cpc"
campaign.utm_campaign = "adwords"
campaign.utm_term = "api+tooling"
campaign.utm_content = "landing"
# Only company_id is required.
# metadata can be any custom object
company = MoesifApi::CompanyModel.new()
company.company_id = "67890"
company.company_domain = "acmeinc.com" # If domain is set, Moesif will enrich your profiles with publicly available info
company.campaign = campaign
company.metadata = metadata
update_company = MoesifRack::MoesifMiddleware.new(@app, @options).update_company(company_model)
Similar to update_company, but used to update a list of companies in one batch.
Only the company_id
field is required.
This method is a convenient helper that calls the Moesif API lib.
For details, visit the Ruby API Reference.
companies = []
metadata = {
:org_name => 'Acme, Inc',
:plan_name => 'Free',
:deal_stage => 'Lead',
:mrr => 24000,
:demographics => {
:alexa_ranking => 500000,
:employee_count => 47
}
}
# Campaign object is optional, but useful if you want to track ROI of acquisition channels
# See https://www.moesif.com/docs/api#update-a-company for campaign schema
campaign = MoesifApi::CampaignModel.new()
campaign.utm_source = "google"
campaign.utm_medium = "cpc"
campaign.utm_campaign = "adwords"
campaign.utm_term = "api+tooling"
campaign.utm_content = "landing"
# Only company_id is required.
# metadata can be any custom object
company = MoesifApi::CompanyModel.new()
company.company_id = "67890"
company.company_domain = "acmeinc.com" # If domain is set, Moesif will enrich your profiles with publicly available info
company.campaign = campaign
company.metadata = metadata
companies << company
response = MoesifRack::MoesifMiddleware.new(@app, @options).update_companies_batch(companies)
- Manually clone the git repo
- From terminal/cmd navigate to the root directory of the middleware.
- Invoke 'gem install moesif_rack'
- Add your own application id to 'test/moesif_rack_test.rb'. You can find your Application Id from Moesif Dashboard -> Bottom Left Menu -> Installation
- Invoke 'ruby test/moesif_rack_test.rb'
- Invoke 'ruby -I test test/moesif_rack_test.rb -n test_capture_outgoing' to test capturing outgoing API calls from your app to third parties like Stripe, Github or to your own dependencies.
- Moesif Rails 5 Example is an example of Moesif with a Ruby on Rails 5 application.
- Moesif Rails 4 Example is an example of Moesif with a Ruby on Rails 4 application.
- Moesif Rack Example is an example of Moesif applied to a Rack application.
To view more documentation on integration options, please visit the Integration Options Documentation.