Skip to content
Ruby on Rails bindings to automatically write metrics into InfluxDB
Branch: master
Clone or download
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
gemfiles Update supported versions Nov 22, 2018
lib Bump version, document latest changes Jan 16, 2019
spec
.gitignore Allow a Gemfile.local for development, unclutter Rakefile Dec 2, 2018
.rspec
.rubocop.yml
.travis.yml
CHANGELOG.md
Gemfile Allow a Gemfile.local for development, unclutter Rakefile Dec 2, 2018
LICENSE.txt Clarify license Feb 18, 2018
README.md Implement dynamic values Jan 10, 2019
Rakefile Travis: update Rubies, include 2.6 Jan 1, 2019
config.ru Initial commit. A hacked-up fork of the old Errplane gem. Feb 3, 2014
influxdb-rails.gemspec Travis: update Rubies, include 2.6 Jan 1, 2019

README.md

You are looking at the README for the master branch of this gem. The latest released version lives in the stable-04 branch, see here for an online version.

influxdb-rails

Gem Version Build Status

Automatically instrument your Ruby on Rails applications and write the metrics directly into InfluxDB.

This gem is designed for Rails 4.2+, Ruby 2.3+ and InfluxDB 0.9+.

Installation

$ [sudo] gem install influxdb-rails

Or add it to your Gemfile, etc.

Usage

To get things set up, just create an initializer:

$ cd /to/you/rails/application
$ touch config/initializers/influxdb_rails.rb

In this file, you can configure the InfluxDB::Rails adapter. The default config should look something like this:

InfluxDB::Rails.configure do |config|
  config.influxdb_database = "rails"
  config.influxdb_username = "root"
  config.influxdb_password = "root"
  config.influxdb_hosts    = ["localhost"]
  config.influxdb_port     = 8086

  # config.retry = false
  # config.async = false
  # config.open_timeout = 5
  # config.read_timeout = 30
  # config.max_delay = 300
  # config.time_precision = 'ms'

  # config.tags_middleware = ->(tags) { tags }

  # config.series_name_for_controller_runtimes = "rails.controller"
  # config.series_name_for_view_runtimes       = "rails.view"
  # config.series_name_for_db_runtimes         = "rails.db"
  # config.series_name_for_render_template     = "rails.render_template"
  # config.series_name_for_render_partial      = "rails.render_partial"
  # config.series_name_for_render_collection   = "rails.render_collection"
  # config.series_name_for_sql                 = nil
  # config.series_name_for_exceptions          = "rails.exceptions"
  # config.series_name_for_instrumentation     = "instrumentation"

  # Set the application name to something meaningful, by default we
  # infer the app name from the Rails.application class name.
  # config.application_name = Rails.application.class.parent_name
end

To see all default values, take a look into InfluxDB::Rails::Configuration::DEFAULTS, defined in lib/influxdb/rails/configuration.rb

Out of the box, you'll automatically get reporting of your controller, view, and db runtimes and rendering of template, partial and collection for each request. Reporting of SQL queries is disabled by default because it is still in experimental mode and currently requires String parsing which might cause performance issues on query intensive applications. You can enable it by setting the series_name_for_sql configuration.

It is possible to disable the rendering series by setting the series_name to nil.

  # config.series_name_for_render_template     = nil
  # config.series_name_for_render_partial      = nil
  # config.series_name_for_render_collection   = nil

You can also call through to the underlying InfluxDB::Client object to write arbitrary data. If you do that, it might be usefull to add the current context to these custom data points which can get accessed with InfluxDB::Rails.current.location.

InfluxDB::Rails.client.write_point "events",
  tags:   { url: "/foo", user_id: current_user.id, location: InfluxDB::Rails.current.location },
  values: { value: 0 }

Additional documentation for InfluxDB::Client lives in the influxdb-ruby repo.

Tags

You can modify the tags sent to InfluxDB by defining a middleware, which receives the current tag set (Hash with Symbol keys and String values) as argument and returns a hash in the same form. The middleware can be any object, as long it responds to #call (like a Proc):

InfluxDB::Rails.configure do |config|
  config.tags_middleware = lambda do |tags|
    tags.merge(env: Rails.env)
  end
end

If you want to add dynamically tags or fields per request, you can access InfluxDB::Rails.current to do so. For instance, you could add the current user as tag and the request id to every data point.

class ApplicationController

  before_action :set_influx_data

  def set_influx_data
    InfluxDB::Rails.current.tags = { user: current_user.id }
    InfluxDB::Rails.current.values = { id: request.request_id }
  end
end

By default, the following tags are sent for non-exception series (rails.controller, rails.view, rails.db and instrumentation):

{
  method:      "#{payload[:controller]}##{payload[:action]}",
  server:      Socket.gethostname,
  app_name:    configuration.application_name,
  http_method: payload[:method],
  format:      payload[:format],
  status:      payload[:status]
}

For the render series (rails.render_partial, rails.render_view and rails.render_collection)

  server:     Socket.gethostname,
  app_name:   configuration.application_name,
  location:   "#{payload[:controller]}##{payload[:action]}",
  filename:   payload[:identifier],
  count:      payload[:count],
  cache_hits: payload[:cache_hits],

For the SQL series (rails.sql, disabled by default)

  server:     Socket.gethostname,
  app_name:   configuration.application_name,
  location:   "#{payload[:controller]}##{payload[:action]}",,
  operation:  "SELECT",
  class_name: "USER",
  name:       payload[:name],

For more information about the payload, have a look at the official ActiveSupport documentation.

For the exceptions (series name rails.exceptions):

{
  application_name:   InfluxDB::Rails.configuration.application_name,
  application_root:   InfluxDB::Rails.configuration.application_root,
  framework:          InfluxDB::Rails.configuration.framework,
  framework_version:  InfluxDB::Rails.configuration.framework_version,
  language:           "Ruby",
  language_version:   "#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}",
  custom_data:        @custom_data,
  class:              @exception.class.to_s,
  method:             "#{@controller}##{@action}",
  filename:           File.basename(@backtrace.lines.first.try(:file)),
  server:             Socket.gethostname,
  status:             "open",
}

Frequently Asked Questions

Q: I'm seeing far less requests recorded in InfluxDB than my logs suggest.

By default, this gem only sends data points with second time precision to the InfluxDB server. If you experience multiple requests per second, only the last point (with the same tag set) is stored.

See InfluxDB server docs for further details. To work around this limitation, set the config.time_precision to one of "ms" (milliseconds, 1·10-3s), "us" (microseconds, 1·10-6s) or "ns" (nanoseconds, 1·10-9s).

Q: How does the measurement influence the response time?

This gem subscribes to the process_action.action_controller controller notification (via ActiveSupport::Notifications · guide · docs · impl), i.e. it runs after a controller action has finished.

The thread processing incoming HTTP requests however is blocked until the notification is processed. By default, this means calculating and enqueueing some data points for later processing (config.async = true), which usually is negligible. The asynchronuous sending happens in a seperate thread, which batches multiple data points.

If you, however, use a synchronous client (config.async = false), the data points are immediately sent to the InfluxDB server. Depending on the network link, this might cause the HTTP thread to block a lot longer.

Q: How does this gem handle an unreachable InfluxDB server?

By default, the InfluxDB client will retry indefinetly, until a write succeedes (see client docs for details). This has two important implcations, depending on the value of config.async:

  • if the client runs asynchronously (i.e. in a seperate thread), the queue might fill up with hundrets of megabytes of data points
  • if the client runs synchronously (i.e. inline in the request/response cycle), it might block all available request threads

In both cases, your application server might become inresponsive and needs to be restarted (which can happen automatically in cgroups contexts).

If you setup a maximum retry value (Integer === config.retry), the client will try upto that amount of times to send the data to the server and (on final error) log an error and discard the values.

Q: What happens with unwritten points, when the application restarts?

The data points are simply discarded.

Q: What happens, when the InfluxDB client or this gem throws an exception? Will the user see 500 errors?

No. The controller instrumentation is wrapped in a rescue StandardError clause, i.e. this gem will only write the error to the client.logger (Rails.logger by default) and not disturb the user experience.

Testing

git clone git@github.com:influxdata/influxdb-rails.git
cd influxdb-rails
bundle
bundle exec rake

Contributing

  • Fork this repository on GitHub.
  • Make your changes.
    • Add tests.
    • Add an entry in the CHANGELOG.md in the "unreleased" section on top.
  • Run the tests:
    • Either run them manually:
      $ rake test:all
    • or wait for Travis to pick up your changes, after you made a pull request.
  • Send a pull request.
    • Please rebase against the master branch.
  • If your changes look good, we'll merge them.
You can’t perform that action at this time.