Simple, performant, local analytics for Rails. Solves 95% of your needs until your ready to start taking analytics more seriously using another tool.
Out of the box the following request details are tracked:
- day
- total (count per day)
- url_hostname (site)
- url_path (page)
- referrer_hostname
- referrer_path
- platform (ios, android, linux, osx, windows, etc)
- browser_engine (blink, gecko, webkit, or nil)
It is fully customizable to store more details if desired.
# Gemfile
gem "rails_local_analytics"
Add the following migration to your app:
bundle exec rails g migration CreateAnalyticsTables
# db/migrations/..._create_analytics_tables.rb
class CreateAnalyticsTables < ActiveRecord::Migration[6.0]
def up
create_table :tracked_requests_by_day_page do |t|
t.date :day, null: false
t.bigint :total, null: false, default: 1
t.string :url_hostname, null: false
t.string :url_path, null: false
t.string :referrer_hostname
t.string :referrer_path
end
add_index :tracked_requests_by_day_page, :day
create_table :tracked_requests_by_day_site do |t|
t.date :day, null: false
t.bigint :total, null: false, default: 1
t.string :url_hostname, null: false
t.string :platform
t.string :browser_engine
end
add_index :tracked_requests_by_day_site, :day
end
def down
drop_table :tracked_requests_by_day_page
drop_table :tracked_requests_by_day_site
end
end
The reason we store our analytics in two separate tables to keep the cardinality of our data low. You are permitted to store everything in only one table but I recommend you try the 2 table approach first and see if it meets your needs. See the performance optimization section for more details.
Add the route for the analytics dashboard at the desired endpoint:
# config/routes.rb
mount RailsLocalAnalytics::Engine, at: "/admin/analytics"
Its generally recomended to use a background job (especially since we now have solid_queue
). If you would like to disable background jobs you can use the following config option:
# config/initializers/rails_local_analytics.rb
# RailsLocalAnalytics.config.background_jobs = false # defaults to true
The next step is to collect traffic.
There are two types of analytics that we mainly target:
- Site level analytics
- Stored in the table
tracked_requests_by_day_site
- Stored in the table
- Page level analytics
- Stored in the table
tracked_requests_by_day_page
- Stored in the table
Your controllers have to manually call RailsLocalAnalytics.record_request
. For example:
class ApplicationController < ActionController::Base
after_action :record_page_view
private
def record_page_view
return if !request.format.html? && !request.format.json?
### We accept manual overrides of any of the database fields
### For example if you wanted to track bots:
site_based_attrs = {}
if some_custom_bot_detection_method
site_based_attrs[:platform] = "bot"
end
RailsLocalAnalytics.record_request(
request: request,
custom_attributes: { # optional
site: site_based_attrs,
page: {},
},
)
end
end
If you need to add more data to your events you can simply add more columns to the analytics tables and then populate these columns using the :custom_attributes
argument.
Some examples of additional things you may want to track:
- Bot detection
- Bot detection is difficult. As such we dont try to include it by default. Recommended gem for detection is
crawler_detect
- One option is to consider not tracking bots at all in your analytics, just a thought
- You may not need to store this in a new column, one example pattern could be to store this data in the existing
platform
database field
- Bot detection is difficult. As such we dont try to include it by default. Recommended gem for detection is
- Country detection
- Country detection is difficult. As such we dont try to include it by default.
- Consider using language detection instead
- Language detection
- You can gather the language from the
request.env["HTTP_ACCEPT_LANGUAGE"]
orbrowser.accept_language.first.full
- You can gather the language from the
- Users or organizations
- You may want to track your users or another model which is a core tenant to your particular application
There are a few techniques that you can use to tailor the database for your particular needs. Heres a few examples:
- If you drop any database columns from the analytics tables this will not cause any issues. It will continue to function as normal.
url_hostname
column- If you wont ever have multi-site needs then you can consider removing this column
- If storage space is an issue you may consider switching to an enum column as the number of permutations is probably something that can be anticipated.
referrer_host
andreferrer_path
columns- Consider just storing "local" or nil instead if the request originated from your website
platform
andbrowser_engine
columns- Consider dropping either of these if you do not need this information
- If you want to store everything in one table (which I dont think most people actually need) then you can simply only create one table (I recommend
tracked_requests_by_day_page
) with all of the fields from both tables. This gem will automatically populate all the same fields. You should NOT need to use:custom_attributes
in this scenario.
If you are not in a controller or do not have access to the request object then you may pass in a hash representation. For example:
RailsLocalAnalytics.record_request(
request: {
host: "http://example.com",
path: "/some/path",
referrer: "http://example.com/some/other/path",
user_agent: "some-user-agent",
http_accept_language: "some-http-accept-language",
},
# ...
)
By default all data is retained indefinately. If you would like to have automated deletion of the data, you might use the following example technique:
class ApplicationController
after_action :record_page_view
private
def record_page_view
# perform other logic and call RailsLocalAnalytics.record_request
TrackedRequestsByDayPage.where("day < ?", 3.months.ago).delete_all
TrackedRequestsByDaySite.where("day < ?", 3.months.ago).delete_all
end
end
We dont do any page performance tracking (request/response time, etc), this gem only specializes in analytics.
If you are looking for a simple performance tracking solution, I highly recommend the gem inner_performance
Run server using: bin/dev
or cd test/dummy/; rails s
bundle exec rspec
We can locally test different versions of Rails using ENV['RAILS_VERSION']
export RAILS_VERSION=7.0
bundle install
bundle exec rspec
Created & Maintained by Weston Ganger - @westonganger
Imitated some parts of active_analytics
. Thanks to them for the aggregate database schema idea.