|
1 | 1 | (ns clj-commons.format.exceptions |
2 | 2 | "Format and output exceptions in a pretty (structured, formatted) way." |
3 | | - (:require [clojure.pprint :as pp] |
| 3 | + (:require [clojure.edn :as edn] |
| 4 | + [clojure.pprint :as pp] |
4 | 5 | [clojure.set :as set] |
5 | 6 | [clojure.string :as str] |
6 | 7 | [clj-commons.ansi :refer [compose perr]] |
|
615 | 616 |
|
616 | 617 |
|
617 | 618 | (defn- format-property-value |
618 | | - [indentation value] |
| 619 | + [indentation print-level print-length value] |
619 | 620 | (let [pretty-value (pp/write value |
620 | 621 | :stream nil |
621 | | - :length *print-length* |
622 | | - :level *print-level* |
| 622 | + :length print-length |
| 623 | + :level print-level |
623 | 624 | :dispatch exception-dispatch)] |
624 | 625 | (indented-value indentation pretty-value))) |
625 | 626 |
|
|
642 | 643 | (defn- render-exception |
643 | 644 | [exception-stack options] |
644 | 645 | (let [{show-properties? :properties |
645 | | - :keys [traditional] |
| 646 | + :keys [traditional print-level print-length] |
646 | 647 | :or {show-properties? true |
| 648 | + print-level *print-level* |
| 649 | + print-length *print-length* |
647 | 650 | traditional *traditional*}} options |
648 | 651 | exception-font (:exception *fonts*) |
649 | 652 | message-font (:message *fonts*) |
|
672 | 675 | :font property-font} k] |
673 | 676 | ": " |
674 | 677 | [property-font |
675 | | - (format-property-value value-indent (get properties' k))])) |
| 678 | + (format-property-value value-indent print-level print-length (get properties' k))])) |
676 | 679 | sorted-keys))) |
677 | 680 | "\n")) |
678 | 681 | exceptions (list |
|
704 | 707 |
|
705 | 708 | The options map may have the following keys: |
706 | 709 |
|
707 | | - Key | Description |
708 | | - --- |--- |
709 | | - :filter | The stack frame filter, which defaults to [[*default-stack-frame-filter*]] |
710 | | - :properties | If true (the default) then properties of exceptions will be output |
711 | | - :frame-limit | If non-nil, the number of stack frames to keep when outputting the stack trace of the deepest exception |
712 | | - :traditional | If true, the use the traditional Java ordering of stack frames. |
| 710 | + Key | Description |
| 711 | + --- |--- |
| 712 | + :filter | The stack frame filter, which defaults to [[*default-stack-frame-filter*]] |
| 713 | + :properties | If true (the default) then properties of exceptions will be output |
| 714 | + :frame-limit | If non-nil, the number of stack frames to keep when outputting the stack trace of the deepest exception |
| 715 | + :traditional | If true, the use the traditional Java ordering of stack frames. |
| 716 | + :print-level | Override [[*print-level*]] |
| 717 | + :print-length | Override [[*print-length*]] |
713 | 718 |
|
714 | 719 | Output may be traditional or modern, as controlled by the :traditonal option |
715 | 720 | (which defaults to the value of [[*traditional*]]). |
|
841 | 846 | :line-number line-number} |
842 | 847 | t))))) |
843 | 848 |
|
844 | | -(defn parse-exception |
845 | | - "Given a chunk of text from an exception report (as with `.printStackTrace`), attempts to |
846 | | - piece together the same information provided by [[analyze-exception]]. The result |
847 | | - is ready to pass to [[format-exception*]]. |
848 | | -
|
849 | | - This code does not attempt to recreate properties associated with the exceptions; in most |
850 | | - exception's cases, this is not necessarily written to the output. For clojure.lang.ExceptionInfo, |
851 | | - it is hard to distinguish the message text from the printed exception map. |
852 | | -
|
853 | | - The options are used when processing the stack trace and may include the :filter and :frame-limit keys. |
854 | | -
|
855 | | - Returns a sequence of exception maps; the final map will include the :stack-trace key (a vector |
856 | | - of stack trace element maps). The exception maps are ordered outermost to innermost (that final map |
857 | | - is the root exception). |
858 | | -
|
859 | | - This should be considered experimental code; there are many cases where it may not work properly. |
860 | | -
|
861 | | - It will work quite poorly with exceptions whose message incorporates a nested exception's |
862 | | - .printStackTrace output. This happens too often with JDBC exceptions, for example." |
863 | | - {:added "0.1.21"} |
| 849 | +(defn- edn->exception-map |
| 850 | + [m] |
| 851 | + (let [{:keys [message type data]} m] |
| 852 | + (cond-> {:class-name (name type) |
| 853 | + :message message} |
| 854 | + (seq data) (assoc-in [:properties :data] data)))) |
| 855 | + |
| 856 | +(defn- edn->frame |
| 857 | + [data] |
| 858 | + (let [[class-name method-name source-file line-number] data] |
| 859 | + (StackTraceElement. (name class-name) |
| 860 | + (name method-name) |
| 861 | + source-file |
| 862 | + (int line-number)))) |
| 863 | + |
| 864 | +(defn- parse-edn-stacktrace |
| 865 | + [s options] |
| 866 | + (let [data (try |
| 867 | + (edn/read-string s) |
| 868 | + (catch Throwable _ |
| 869 | + nil)) |
| 870 | + root-trace (:clojure.main/trace data)] |
| 871 | + (when root-trace |
| 872 | + (let [{:keys [via trace]} root-trace |
| 873 | + exceptions (mapv edn->exception-map via) |
| 874 | + *cache (volatile! {}) |
| 875 | + stack-trace (->> trace |
| 876 | + (map edn->frame) |
| 877 | + (map #(transform-stack-trace-element current-dir-prefix *cache %))) |
| 878 | + stack-trace' (filter-stack-trace-maps stack-trace options)] |
| 879 | + (update exceptions (-> exceptions count dec) assoc :stack-trace stack-trace'))))) |
| 880 | + |
| 881 | +(defn- parse-print-stack-trace-output |
864 | 882 | [exception-text options] |
865 | 883 | (loop [state :start |
866 | 884 | lines (str/split-lines exception-text) |
|
876 | 894 | (let [[_ exception-class-name exception-message] (re-matches re-exception-start line)] |
877 | 895 | (when-not exception-class-name |
878 | 896 | (throw (ex-info "Unable to parse start of exception." |
879 | | - {:line line |
| 897 | + {:line line |
880 | 898 | :exception-text exception-text}))) |
881 | 899 |
|
882 | 900 | ;; The exception message may span a couple of lines, so check for that before absorbing |
883 | 901 | ;; more stack trace |
884 | 902 | (recur :exception-message |
885 | 903 | more-lines |
886 | 904 | (conj exceptions {:class-name exception-class-name |
887 | | - :message exception-message}) |
| 905 | + :message exception-message}) |
888 | 906 | stack-trace |
889 | 907 | stack-trace-batch)) |
890 | 908 |
|
|
923 | 941 | (recur :start lines |
924 | 942 | exceptions stack-trace stack-trace-batch))))))) |
925 | 943 |
|
| 944 | +(defn parse-exception |
| 945 | + "Given a chunk of text from an exception report, attempts to |
| 946 | + piece together the same information provided by [[analyze-exception]]. The result |
| 947 | + is ready to pass to [[format-exception*]]. |
| 948 | +
|
| 949 | + An exception report may be in two forms: |
| 950 | +
|
| 951 | + * The output from Exception/printStackTrace |
| 952 | + * The EDN report generated by the `clojure` or `clj` commands |
| 953 | +
|
| 954 | + For printStackTrace output, this does not attempt to recreate properties associated with the exceptions; in most |
| 955 | + exception's cases, this is not necessarily written to the output. For clojure.lang.ExceptionInfo, |
| 956 | + it is hard to distinguish the message text from the printed exception map. |
| 957 | +
|
| 958 | + The options are used when processing the stack trace and may include the :filter and :frame-limit keys. |
| 959 | +
|
| 960 | + Returns a sequence of exception maps; the final map will include the :stack-trace key (a vector |
| 961 | + of stack trace element maps). The exception maps are ordered outermost to innermost (that final map |
| 962 | + is the root exception). |
| 963 | +
|
| 964 | + This should be considered experimental code; there are many cases where it may not work properly. |
| 965 | +
|
| 966 | + It will work quite poorly with exceptions whose message incorporates a nested exception's |
| 967 | + .printStackTrace output. This happens too often with JDBC exceptions, for example." |
| 968 | + {:added "0.1.21"} |
| 969 | + [exception-text options] |
| 970 | + (or (parse-edn-stacktrace exception-text options) |
| 971 | + (parse-print-stack-trace-output exception-text options))) |
| 972 | + |
926 | 973 | (defn format-stack-trace-element |
927 | 974 | "Formats a stack trace element into a single string identifying the Java method or Clojure function being executed." |
928 | 975 | {:added "3.2.0"} |
|
0 commit comments