lib/appsignal/rack/js_exception_catcher.rb
# frozen_string_literal: true
module Appsignal
# @api private
module Rack
# JavaScript error catching middleware.
#
# Listens to the endpoint specified in the `frontend_error_catching_path`
# configuration option.
#
# This is automatically included middleware in Rails apps if the
# `frontend_error_catching_path` configuration option is active.
#
# If AppSignal is not active in the current environment, but does have
# JavaScript error catching turned on, we assume that a JavaScript script
# still sends errors to this endpoint. When AppSignal is not active in this
# scenario this middleware still listens to the endpoint, but won't record
# the error. It will return HTTP status code 202.
#
# @example with a Sinatra app
# Sinatra::Application.use(Appsignal::Rack::JSExceptionCatcher)
#
# @see http://docs.appsignal.com/front-end/error-handling.html
# @api private
class JSExceptionCatcher
include Appsignal::Utils::DeprecationMessage
def initialize(app, _options = nil)
Appsignal.logger.debug \
"Initializing Appsignal::Rack::JSExceptionCatcher"
deprecation_message "The Appsignal::Rack::JSExceptionCatcher is " \
"deprecated and will be removed in a future version. Please use " \
"the official AppSignal JavaScript integration by disabling " \
"`enable_frontend_error_catching` in your configuration and " \
"installing AppSignal for JavaScript instead. " \
"(https://docs.appsignal.com/front-end/)"
@app = app
end
def call(env)
# Ignore other paths than the error catching path.
return @app.call(env) unless error_cathing_endpoint?(env)
# Prevent raising a 404 not found when a non-active environment posts
# to this endpoint.
unless Appsignal.active?
return [
202,
{},
["AppSignal JavaScript error catching endpoint is not active."]
]
end
begin
body = JSON.parse(env["rack.input"].read)
rescue JSON::ParserError
return [400, {}, ["Request payload is not valid JSON."]]
end
if body["name"].is_a?(String) && !body["name"].empty?
transaction = JSExceptionTransaction.new(body)
transaction.complete!
code = 200
else
Appsignal.logger.debug \
"JSExceptionCatcher: Could not send exception, 'name' is empty."
code = 422
end
[code, {}, []]
end
private
def error_cathing_endpoint?(env)
env["PATH_INFO"] == Appsignal.config[:frontend_error_catching_path]
end
end
end
end