In the previous article I’ve written about installing Sentry on premise using Docker. In this post, I show some integration points, to improve the error reporting in our Rails+Webpacker settings. This is the same, if you are using Sentry.io SaaS or self-hosted.
Integration aspects in our case:
- (self-hosted) Gitlab, important for commit/release tracking
- Mattermost chat notifications
- Release creation on deploy
- “normal” git based deploy, having a GIT_REVISION file in our root project after every deployment that sentry can pick up to determine a “release”
Basic procedure / Overview
- Create a new project on your Sentry instance or Sentry.io, note the
dsn
- Install Sentry plugins (Raven, Webpacker), Copy over the
dsn
in your secrets/credentials/production env - Configure … Adjust, see below, this might take a few rounds to get the best result
- Alerting/notification, in our case: Link the Mattermost project channel with the Sentry project
- Create releases / Source Maps during deployment process, this needs an auth-key that you can generate on Sentry
Creating a project
Yeah, that’s simple, create a Sentry project, activate your alert and link to a chat channel, like Mattermost, Slack etc. Also make sure to link your Repository (Gitlab/Github), so you can attach commits to releases and therefore errors, and create a Gitlabhub Issue directly from Sentry.
I’ve written in the previous article on how to add Mattermost to our onprem Sentry instance.
Dependencies / Rails config
$ yarn add @sentry/browser
$ bundle add "sentry-raven"
Now, add a config/initializers/sentry.rb
:
# config/initializers/sentry.rb
## TODO: Some mechanism to get the current git revision into the Rails app,
## that depends on your deploy process.
## e.g. ENV['CI_BUILD_REF'],
## or `git rev-parse`.strip if you are deploying with the whole .git
SENTRY_RELEASE = if File.exist?("GIT_REVISION")
File.read("GIT_REVISION")
else
""
end
Raven.configure do |config|
if Rails.env.production?
config.dsn = 'https://xxxxxxxxxxxxxxxxxxx:xxxxxxxxxxxxxx@sentry.xxxxxxxx.com/xx'
# env: config.dsn = ENV['...']
# secrets: config.dsn = Rails.application.secrets.sentry_dsn
end
# which envs to report, could be staging to
config.environments = %w[production]
config.release = SENTRY_RELEASE
# Do this to send POST data, sentry DOES NOT send POST params by default
config.processors -= [Raven::Processor::PostData]
# Do this to send cookies by default, otherwise session/cookies info will not be send
config.processors -= [Raven::Processor::Cookies]
# What fields do you want to sanitize?
config.sanitize_fields = ["first_name", "last_name", "email", "comment", "telephone", "phone", "password", "password_confirmation"]
config.excluded_exceptions =
Raven::Configuration::IGNORE_DEFAULT + [
'ActiveRecord::RecordNotFound',
'ActionController::RoutingError',
'ActionController::InvalidAuthenticityToken',
'CGI::Session::CookieStore::TamperedWithCookie',
'ActionController::UnknownAction',
'AbstractController::ActionNotFound',
'Mongoid::Errors::DocumentNotFound'
].freeze
end
Integrating Javascript Tracking into Rails
We create:
- a new webpacker
pack
:app/javascripts/packs/error_tracking.js
- a new partial, e.g.
app/views/layouts/_error_tracking.html.erb
- we include that partial in the head of all relevant layouts, e.g. “app/views/layouts/application.html.erb” etc.
1. packs/error_tracking.js
// app/javascript/packs/error_tracking.js
import * as Sentry from '@sentry/browser';
// same DSN like production, but without http passowrd, could also be injected like the SENTRY_RELEASE
Sentry.init({ dsn: 'https://xxxxxxxxxxx', release: window.SENTRY_RELEASE, environment: 'production' });
// to allow easy access from all areas append to window:
window.Sentry = Sentry
2. views/layouts/_error_tracking.html.erb
<!-- app/views/layouts/_error_tracking.html.slim -->
<% if Rails.env.production? %>
<script type='text/javascript'>
window.SENTRY_RELEASE = "<%= SENTRY_RELEASE %>"
</script>
<%= javascript_pack_tag 'error_tracking' %>
<% end %>
3. Include in all layouts
// app/views/layouts/application.html.slim
<head>
...
<%= render 'layouts/error_tracking' %>
Deployment: Generate source maps
This will already work, but the Javascript will have no source maps and we only see the minified stacktraces in our application Javascript.
Sentry provides a webpack plugin that can be included. Using this, will upload all source files just during the usual rake assets:precompile
steps, without any adjustments in our build process, Great!
$ yarn add @sentry/webpack-plugin
Add to the config/webpack/production.js
just before module.exports = environment.toWebpackConfig()
:
const SentryCliPlugin = require('@sentry/webpack-plugin');
const fs = require("fs");
// Again, getting the release here at this point, depends on your setup!
const release = fs.readFileSync("GIT_REVISION").toString();
environment.plugins.append('sentry',
new SentryCliPlugin({
release,
// what folders to scan for sources
include: ['app/javascript', 'public/assets'],
// ignore
ignore: ['node_modules', 'webpack.config.js', 'vendor'],
// also set the last commit for the current release
setCommits: {
commit: release,
// link that to your gitlab/github repository, to get the correct name
// head to Sentry -> Organisation settings -> Repos and take the name verbatim, no url!
// in our case, with self hosted Gitlab, it looked like this
repo: 'developers / Myrepos'
}
})
Deployment: Add creds on build host
To make sure, the SentryCLIPlugin works, we need to add the credentials to the system that builds the assets, e.g. using environment variables, add:
# You must create a new Auth on your Sentry profile. Make sure to have R/W access on releases
SENTRY_AUTH_TOKEN='xxxxxxxxxxxxxxxxxxxxxxxx'
SENTRY_URL="https://sentry.yourcompany.com"
SENTRY_ORG="yourorg"
SENTRY_PROJECT="verbatim-project-name-slug"
Now, we could deploy with the Webpack Plugin. That plugin will upload the sources into a new release. Additionaly, Sentry recommends to tell it about, when you just “deployed” a new release, so you could create a sentry “deploy” at the end your deployment script, e.g.:
./node_modules/.bin/sentry-cli releases deploys `cat GIT_REVISION` new -e production
Improving errors: adding contexts
Your app probably has some kind of User object, I18n locales or other interesting context that you wanna include into the error tracking. Therefore, add a function set_raven_context
into ApplicationController
(or your API base controller), like
class ApplicationController < ActionController::Base
before_action :set_raven_context
# ...
def set_raven_context
Raven.tags_context(
language: I18n.locale,
# timezone?
)
if current_user
# GDPR: you probably wouldn't send PII like name/email at this point
Raven.user_context(id: current_user.id, company: current_user.company.name)
else
# masking ip? https://github.com/ankane/ip_anonymizer
Raven.user_context(ip: request.ip)
end
end
end
Specific controllers could override the method and call super
and then add custom context.
Ad-Hoc error reporting of catched exceptions
Sometimes you have situations, where you rescue an exception that you wanna report, but just want to continue with another program flow (e.g. Elasticsearch is down, falling back to naive SQL search, catching rare PDF conversion exceptions etc.).
When using Airbrake before, that was Airbrake.notify(exception, more: info, here: hash)
. With Sentry Raven, wrap the extra params in a Hash attribute called, …extra
response = HTTP.get(url)
...
rescue StandardError => ex
if Rails.env.production?
Raven.capture_exception(ex, extra: { url: url })
end
false
end
Grape/Sinatra Raw Rack
Include the Rack module. I didn’t test this, yet:
+ require 'raven/integrations/rack'
class Api < Grape::API
+ use Raven::Rack
...
Happy error tracking!