A layer of sugar on top of raven-clj.
[listora/corax "0.3.0"]
Corax is used to incrementally build a raven event and to eventually submit that event to Sentry.
(require '[corax.core :as err])
A CoraxErrorReporter
is used to wrap state, like the Sentry DSN, and
to report errors to Sentry. Create one with new-error-reporter
:
(def my-dsn "") ;; get your DSN from getsentry.com
(def error-reporter (err/new-error-reporter my-dsn))
An event is built with the thread-first (->
) macro and the various
helper functions provided by Corax:
(-> (err/message "Things went wrong")
(err/exception ex)
(err/culprit ::my-fn)
...)
A built event is submitted to Sentry with report
:
(-> ... ;; build the event like above
(err/report error-reporter))
(require '[corax.core :as err])
(def my-dsn "") ;; get your DSN from getsentry.com
(def error-reporter (err/new-error-reporter my-dsn))
(-> (err/message "Things went wrong")
(err/exception ex)
(err/culprit ::my-fn)
(err/report error-reporter
The Corax API consists of a number of builders used to build an event
incrementally, and a report
function used to submit the built event
to Sentry. All the builders can either create a new event map, or add
new fields to a provided map, which allows the builders to be chained
together with the threading macro (->
).
More details on the format and semantics of a Sentry event can be found in the Sentry documentation on writing a client and the supported interfaces.
The name of the function that was the primary perpetrator of this event. Can be a symbol or a string.
(culprit ::my-fn)
(culprit ev ::my-fn)
(culprit "any random string")
(culprit ev "foo.core/my-fn")
The culprit will show up as the heading of an event in the Sentry web UI.
Add an Exception
interface to an event. The ex
argument must
be an instance of java.lang.Throwable
.
(exception ex)
(exception event ex)
ex-data
is automatically called on ex
and the resulting map is
included in the event under the :ex-data
key in the :extra
map. Any existing :ex-data
field in the :extra
map will be
overwritten.
If the event doesn't contain a :message
field yet, the exception
message will be set as :message
. The :message
field can be
explicitly set by calling message
either before or after calling
exception
.
An exception in the event is rendered as a stack trace under the
Exception heading in the Sentry web UI. The type and message of the
exception are also rendered. Corax uses clj-stacktrace
to
translate a stack trace to something that Sentry can render in the web
UI. We try to provide sensible, language-specific stack frames for
both Clojure and Java.
Include any arbitrary data in the event. The argument must be a
map. When this function is called more than once on an event, the
:extra
fields are merged together. If the multiple calls share keys,
the latter calls will overwrite the keys from earlier calls (see
Clojure's merge
). The extra payload must be serializable to
JSON by Cheshire.
(extra {:foo :bar})
(extra event {:foo "bar"})
The provided data is rendered under the Additional Data heading in the Sentry web UI.
Add an Http
interface to an event. The Http interface is used
to describe an HTTP request. The http
function takes a ring request
and translates that to a map understood by Sentry.
(http ring-req)
(http ev ring-req)
Note: Sentry supports arbitrary data in the :env
map. This facility
is not exposed by Corax at the moment, but the user can add fields to
the :env
map manually if they so wish:
(-> (http ring-req)
(assoc-in [:sentry.interfaces.Http :env :my-key] :my-data))
But it might be simpler to just use extra
.
The severity of the event (a string or a keyword). If not specified,
Corax will default the field to :error
. Accepted values are
:fatal
, :error
, :warning
, :info
, and :debug
.
(level :fatal)
(level ev "fatal")
The name of the logger (string or keyword) which created the
record. Eg "my.logger.name"
. If not specified, Sentry defaults
logger to "root"
.
(logger :my.logger.name)
(logger ev "my.logger.name")
Set the message field in an event. The value should be a user-readable description of the event. Maximum length is 1000 characters. Longer messages are silently truncated.
(message "Failed to froblify the packet.")
(message ev "Failed to froblify the packet.")
A list of relevant modules and their versions.
(modules {:clojure "1.6.0" :clj-stacktrace "0.2.8"})
(modules ev {:clojure "1.6.0" :clj-stacktrace "0.2.8"})
Modules are rendered as a list under the Package Versions heading in the Sentry web UI.
A string or keyword representing the platform the client is submitting
from. Eg :clojure
or :python
. If a platform is not specified,
Corax will default to :clojure
. The platform field is used by the
Sentry interface to customize various components in the web UI, but
it's unlikely that the web UI would have been customized for Clojure.
(platform :clojure)
(platform ev "clojure")
Add a Query
interface to an event. The Query interface consists
of two fields: :query
and :engine
, where the former is mandatory
and the latter is optional. The :query
field value should be a
database query that was being performed when the error occurred. The
:engine
field is used to describe the database driver.
(query {:query "SELECT * FROM users" :engine "JDBC"})
(query ev {:query "SELECT * FROM users"})
Query is not rendered on the Sentry web UI at the moment. The UI allows to search for events with a Query field, but there doesn't seem to be a way of accessing the fields of the Query payload.
A string or keyword representing the server that the client is
submitting from. Eg "web1.example.com"
. If a server-name
is not
specified, it will default to the Corax client's IP address.
(server-name :web1)
(server-name ev \"web1.example.com\")"
A map of tags and values.
(tags {:version "1.2.3" :environment :production})
(tags ev {:version "1.2.3" :environment :production})
Tags will show up in the Tags section of the Sentry web UI. A number of tags are generated via other mechanism in a Raven error report, eg the log level, logger and server name are also treated as tags.
Add a User
interface to an event. The User interface consists
of four fields: :id
, :username
, :email
, and :ip-address
. All
are optional, but the caller should provide at least an :id
or an
:ip-address
.
(user {:id "12345" :email "[email protected]"})
(user ev {:id "12345" :ip-address "127.0.0.1"})
User information is rendered under the User heading in the Sentry web UI. Sentry keeps track of the number of users that have reported the same error.
A new error reporter is instantiated with new-error-reporter
. The
function takes two arguments: dsn
and logger
.
The dsn
argument is mandatory and is used to specify the Sentry DSN
to use when reporting events. If the provided DSN is nil
, or looks
otherwise invalid, new-error-reporter
will throw an
AssertionError
. A DSN looks something like this:
https://12345...:[email protected]/12345
.
The optional logger
argument is an implementation of
corax.log/Logger
. It is used to notify the calling application of
the result of an error report. See corax.log/Logger
for more
details. If a logger is not provided, the default logger
(corax.log/CoraxLogger
) will be used. The default logger logs to
stdout
. corax.core/NullLogger
can be used to turn all logging
off. Please use your own custom implementation to redirect the output
to your logging system.
The report
function is used to submit an event to Sentry. report
takes an event and an error reporter as arguments.
The corax.log/Logger
protocol defines the following methods:
-
log-event-id
- the event was reported to Sentry -
log-failure
- the call to Sentry returned an unexpected HTTP status code -
log-error
- an unexpected exception was thrown when reporting the event
The event
argument in all the above method definitions contains the
event that was being reported.
The event will be serialized to JSON before it is submitted to Sentry (by Cheshire). If the event contains an object that is not serializable, Corax will report an error to Sentry pointing this out, but the original event will have been lost.
The JSON serialization error contains the following message:
An object in the error report could not be serialized to
JSON. This error is masking the real application error. For more
details see https://github.com/listora/corax
The error report will also contain the
com.fasterxml.jackson.core.JsonGenerationException
that was thrown
by Cheshire. The exception message explains which object failed to
serialize, e.g. Cannot JSON encode object of class: class java.lang.Exception: java.lang.Exception: foo
.
The serialization problem can be addressed by:
-
making sure that the offending object is stripped out from the event before
report
ing it to Sentry -
by adding a custom JSON encoder for the offending type
-
by adding a catch-all JSON encoder for
Object
The first approach is sometimes hard to implement since it might not be obvious where the offending object is coming from. The second approach is quite simple, but it will fix the problem only for one type and adding JSON encoders to your application might have unwanted effects if you use Cheshire in your own application. The third option is the nuclear option and will address all JSON serialization problems, but again with possibly unwanted side-effects if you use Cheshire in your own app.
Custom JSON encoders can be added to Cheshire with the add-encoder
fn:
(require '[cheshire.generate :as generate])
(generate/add-encoder Exception (fn [e jg] (.writeString jg (str e))))
Here we simply call str
on the provided Exception
instance to
produce a String
which can be included in the JSON event.
The nuclear option is to enable to str
-based serialization as a
default:
(generate/add-encoder Object (fn [e jg] (.writeString jg (str e))))
Corax provides an example ring middleware for reporting any exceptions thrown by the request handler as errors to Sentry:
(require '[corax.core :as corax])
(require '[corax.middleware :refer [wrap-exception-reporting]])
(def my-dsn "") ;; get your DSN from getsentry.com
(def error-reporter (err/new-error-reporter my-dsn))
(defn apply-middleware
[routes]
(-> routes
...
(wrap-exception-reporting error-reporter)
...))
Note: the middleware will rethrow the caught exception to allow some other middleware to catch it and return an appropriate HTTP response.
The library defines an ErrorReporter
protocol which is implemented
by the CoraxErrorReporter
record. You can mock out the Corax error
reporter in your tests by providing a mock implementation of
ErrorReporter
and passing that to report
.
Copyright © 2014 - 2015 Listora
Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.