Skip to content

Commit 496c197

Browse files
author
Paudi Moriarty
authored
Merge pull request #63 from rymndhng/add-pretty-app-frame
Add custom app frames with bolded clojure frame font
2 parents 48949a0 + a1b34c4 commit 496c197

File tree

5 files changed

+119
-2
lines changed

5 files changed

+119
-2
lines changed

docs/exceptions.rst

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,26 @@ The related function, ``format-exception``, produces the same output, but return
104104

105105
For both ``format-exception`` and ``write-exception``, output of the stack trace is optional, or can be limited to a certain number of stack frames.
106106

107+
Frames can also be highlighted by customizing ``io.aviso.exception/*app-frame-names*``. This adds extra visual clarity to identify frames that belong in your clojure code vs. library code.
108+
109+
Before:
110+
111+
.. image:: images/without-app-frame-names-exceptions.png
112+
:alt: Without app-frame-names
113+
114+
After:
115+
116+
.. image:: images/with-app-frame-names-exception.png
117+
:alt: With custom app-frame-names
118+
119+
Notice with custom app-frame-names, the matched frame names are also bolded. This is customized by re-binding or altering
120+
``*app-frame-names*``, which is a list of string or patterns to match on the frame's name.
121+
122+
::
123+
124+
;; marks any frame that begins with demo
125+
(alter-var-root #'io.aviso.exception/*app-frame-names* (constantly [#"my-app.*"]))
126+
107127
io.aviso.repl
108128
-------------
109129

@@ -124,4 +144,4 @@ io.aviso.logging
124144
----------------
125145

126146
This namespace includes functions to change ``clojure.tools.logging`` to use Pretty to output exceptions, and to add a
127-
default Thread.UncaughtExceptionHandler that uses ``clojure.tools.logging``.
147+
default Thread.UncaughtExceptionHandler that uses ``clojure.tools.logging``.
58.5 KB
Loading
Loading

src/io/aviso/exception.clj

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,16 @@
1919
:message italic-font
2020
:property bold-font
2121
:source green-font
22+
:app-frame bold-yellow-font
2223
:function-name bold-yellow-font
2324
:clojure-frame yellow-font
2425
:java-frame white-font
2526
:omitted-frame white-font})
2627

28+
(def ^:dynamic *app-frame-names*
29+
"Set of filters (belong to your applications) to create more visual clarity."
30+
nil)
31+
2732
(def ^:dynamic *fonts*
2833
"Current set of fonts used in exception formatting"
2934
(when-not (System/getenv "DISABLE_DEFAULT_PRETTY_FONTS")
@@ -325,6 +330,16 @@
325330
(flush)))
326331
elements))
327332

333+
(defn- clj-frame-font
334+
"Returns the font to use for a clojure frame.
335+
336+
When provided a frame matching *app-frame-names*, returns :app-frame, otherwise :clojure-frame
337+
"
338+
[frame]
339+
(-> (keep #(apply-rule frame [:name % :app-frame]) *app-frame-names*)
340+
first
341+
(or :clojure-frame)))
342+
328343
(defn- preformat-stack-frame
329344
[frame]
330345
(cond
@@ -343,7 +358,7 @@
343358
:else
344359
(let [names (:names frame)
345360
formatted-name (str
346-
(:clojure-frame *fonts*)
361+
(get *fonts* (clj-frame-font frame))
347362
(->> names drop-last (str/join "/"))
348363
"/"
349364
(:function-name *fonts*) (last names) (:reset *fonts*))]

test/demo_app_frames.clj

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
(ns demo-app-frames
2+
"This namespace demonstrates how customizing io.aviso.exception/*app-frames*
3+
helps highlight application logic in stacktraces
4+
5+
The `comment` block at end of file demonstrates this feature.
6+
"
7+
(:require [io.aviso.repl :as repl]))
8+
(repl/install-pretty-exceptions)
9+
10+
;; -- provided.* namespaces are libraries we're consuming ---------------------
11+
(ns provided.db
12+
(:import (java.sql SQLException)))
13+
(defn jdbc-update
14+
[]
15+
(throw (SQLException. "Database failure" "ABC" 123)))
16+
17+
18+
(ns provided.worker)
19+
(defprotocol Worker
20+
(do-work [this]))
21+
22+
23+
(ns provided.db-worker)
24+
(defn make-jdbc-update-worker
25+
[]
26+
(reify
27+
provided.worker/Worker
28+
(provided.worker/do-work [this] (provided.db/jdbc-update))))
29+
30+
;; -- my-app are namespaces belonging to our application ----------------------
31+
(ns my-app.db)
32+
(defn update-row
33+
[]
34+
(try
35+
(-> (provided.db-worker/make-jdbc-update-worker) provided.worker/do-work)
36+
(catch Throwable e
37+
(throw (RuntimeException. "Failure updating row" e)))))
38+
39+
40+
(ns my-app.handler)
41+
(defn make-exception
42+
"Creates a sample exception used to test the exception formatting logic."
43+
[]
44+
(try
45+
(my-app.db/update-row)
46+
(catch Throwable e
47+
;; Return it, not rethrow it.
48+
(RuntimeException. "Request handling exception" e))))
49+
50+
(defn make-ex-info
51+
""
52+
[]
53+
(try
54+
(throw (make-exception))
55+
(catch Throwable t
56+
;; Return it, not rethrow it.
57+
(ex-info "Exception in make-ex-info."
58+
{:function 'make-exception}
59+
t))))
60+
61+
62+
(ns my-app.handler-test
63+
(:require [clojure.test :refer [report]]))
64+
65+
(defn test-failure
66+
[]
67+
(report {:type :error :expected nil :actual (my-app.handler/make-ex-info)}))
68+
69+
70+
(comment
71+
;; Run these commands in a REPL
72+
(require '[demo-appframes] :reload)
73+
74+
;; Should show no app-frames highlighted
75+
(alter-var-root #'io.aviso.exception/*app-frame-names* (constantly []))
76+
(my-app.handler-test/test-failure)
77+
78+
;; Should show app-frames (beginning with my-app) highlighted
79+
(alter-var-root #'io.aviso.exception/*app-frame-names* (constantly [#"my-app.*"]))
80+
(my-app.handler-test/test-failure)
81+
82+
)

0 commit comments

Comments
 (0)