Better exception reporting middleware for Ring. Heavily inspired by better_errors for Rails.
See it to believe it: a quick video demoing Prone.
Prone presents your stack traces in a consumable form. It optionally filters out
stack frames that did not originate in your application, allowing you to focus
on your code. It allows you to browse environment data, such as the request map
and exception data (when using ex-info
). Prone also provides a debug function
that enables you to visually browse local bindings and any piece of data you
pass to debug
.
Add [prone "2019-07-08"]
to :dependencies
in your project.clj
.
This project no longer uses Semantic Versioning. Instead we're aiming to never break the API. Feel free to check out the change log.
with lein-ring
Using lein-ring
version 0.9.1
or above, add this to your project.clj
:
{:profiles {:dev {:ring {:stacktrace-middleware prone.middleware/wrap-exceptions}}}
without lein-ring
Add it as a middleware to your Ring stack:
(ns example
(:require [prone.middleware :as prone]))
(def app
(-> my-app
prone/wrap-exceptions))
Please note, with this configuration you should make sure to only enable Prone in development.
with pedestal
See prone-pedestal
with catacumba
See catacumba-prone
Whether you've tripped on an exception or not, you can use Prone to debug your application:
(ns example
(:require [prone.debug :refer [debug]]))
(defn myhandler [req]
;; ...
(let [person (lookup-person (:id (:params req)))]
(debug)))
Calling debug
without any arguments like this will cause Prone to render the
exception page with information about your environment: the request map, and any
local bindings (req
and person
in the above example).
You can call debug
multiple times. To differentiate calls, you can pass a
message as the first argument, but Prone will also indicate the source location
that triggered debugging.
debug
accepts any number of forms to present in a value browser on the
error/debug page:
(debug) ;; Inspect locals
;; Halts the page if there are no exceptions
(debug "Here be trouble") ;; Same as above, with a message
(debug {:id 42}) ;; Inspect locals and the specific map
;; Halts the page if there are no exceptions
(debug person project) ;; Same as above, with multiple values
(debug "What's this?" person project) ;; Same as above, with message
No. You would be exposing your innards to customers, and maybe even to someone with nefarious purposes.
Here's one way to avoid it:
(def prone-enabled? (= "true" (System.getProperty "prone.enable")))
(def app
(cond-> my-app
prone-enabled? prone/wrap-exceptions))
You can chain more optional middlewares in this cond->
too. Pretty nifty.
By default it reads your project.clj
and looks for namespaces starting with
the project name.
You can change this behavior by passing in some options to wrap-exceptions
,
like so:
(-> app
(prone/wrap-exceptions
{:app-namespaces ["our" "app" "namespace" "prefixes"]}))
All frames from namespaces prefixed with the names in the list will be marked as application frames.
Pass a predicate function skip-prone?
to wrap-exceptions
. For example, to
exclude Postman requests check for postman-token
in the headers:
(-> app
(prone/wrap-exceptions
{:skip-prone? (fn [req] (contains? (:headers req) "postman-token"))}))
Yeah, that's a bit trickier. There's no point in serving a beautiful exception page when you have to inspect it in devtools.
The prone response includes a Link
header with a rel=help
attribute. Like this:
Link:</prone/d97fa078-7638-4fd1-8e4a-9a22576a321f>; rel=help
Use this in your frontend code to display the page. Here's an example from one of our sites:
(def rel-help-regex #"<(.+)>; rel=help")
(defn check-err [result]
(if-let [url (->> (get-in result [:headers "link"] "")
(re-find rel-help-regex)
second)]
(set! js/location url)
(do (js/alert "fail")
(prn result))))
(defn GET [url params]
(go
(let [result (<! (http/get url {:query-params params}))]
(if (:success result)
(do-some-successful-stuff)
(check-err result)))))
The latest prone error page can also be found under /prone/latest
, so if you
haven't fixed your frontend code to use the rel=help
header quite yet, you can
always go there to check it out.
Yeah, I guess you already have a logging framework to print errors for you? And then prone goes and prints them as well. Turn it off like so:
(-> app
(prone/wrap-exceptions
{:print-stacktraces? false}))
some-name
and some_name
function names by inspecting the stack trace. Currently, we assume kebab case.browser-connected-repl
for
ClojureScript causes JavaScript errors that partly trips up Pronejava.util.Date
like #inst
Add function to render self-contained page.
This can be used to store prone-pages for later perusal even when the prone process is no longer running.
(spit "my-error.html" (render-self-contained-page (create-exception-page e {})))
:print-stacktraces?
(Max Ovsiankin)/prone/latest
(Daniel Lebrero):app-namespaces
option.:skip-prone?
option./prone/latest
.:print-stacktraces?
option.SQLException getNextException
Thanks!
Yes, please do. And add tests for your feature or fix, or we'll certainly break it later.
Prerequisites:
To start the server:
lein cljsbuild auto
in one terminallein ring server-headless
in another../bin/kaocha
will run all tests. (run lein cljsbuild once
to generate
required js files)
To run tests continuously: ./bin/kaocha --watch
After making changes to static files in dev-resources
, run
./build-js-sources.sh
again to update the concatenated files.
Copyright © 2014-2018 Christian Johansen & Magnar Sveen. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Can you improve this documentation? These fine people already did:
Magnar Sveen, Christian Johansen, Chris McDevitt, Andrew Mcveigh & Stig BrautasetEdit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close