Giter VIP home page Giter VIP logo

clj-stacktrace's Introduction

clj-stacktrace

A library for creating more readable stacktraces in Clojure programs.

For example, to print a nice stack trace in a REPL:

=> (use 'clj-stacktrace.repl)
=> ("foo")
java.lang.ClassCastException: java.lang.String cannot be cast to clojure.lang.IFn (NO_SOURCE_FILE:0)
       Compiler.java:5440 clojure.lang.Compiler.eval
       Compiler.java:5391 clojure.lang.Compiler.eval
            core.clj:2382 clojure.core/eval
             main.clj:183 clojure.main/repl[fn]
             main.clj:204 clojure.main/repl[fn]
             main.clj:204 clojure.main/repl
          RestFn.java:422 clojure.lang.RestFn.invoke
             main.clj:262 clojure.main/repl-opt
             main.clj:355 clojure.main/main
          RestFn.java:398 clojure.lang.RestFn.invoke
             Var.java:361 clojure.lang.Var.invoke
             AFn.java:159 clojure.lang.AFn.applyToHelper
             Var.java:482 clojure.lang.Var.applyTo
             main.java:37 clojure.main.main
Caused by: java.lang.String cannot be cast to clojure.lang.IFn
         NO_SOURCE_FILE:2 user/eval100
       Compiler.java:5424 clojure.lang.Compiler.eval

In stack traces printed by pst:

  • Java methods are described with the usual name.space.ClassName.methodName convention and Clojure functions with their own name.space/function-name convention.
  • Anonymous clojure functions are denoted by adding an [fn] to their enclosing, named function.
  • "Caused by" cascades are shown as in regular java stack traces.
  • Elements are vertically aligned for better readability.
  • Printing is directed to *out*.

If you want to direct the printing to somewhere other than *out*, either use pst-on to specify the output location or pst-str to capture the printing as a string.

The library also offers an API for programatically 'parsing' exceptions. This API is used internal for pst and can be used to e.g. improve development tools. Try for example:

(use 'clj-stacktrace.core)
(try
  ("nofn")
  (catch Exception e
    (parse-exception e)))

Leiningen

If you use Leiningen, you can install clj-stacktrace on a user-wide basis. Just add the following to ~/.lein/profiles.clj:

{:user {:dependencies [[clj-stacktrace "0.2.8"]]
        :injections [(let [orig (ns-resolve (doto 'clojure.stacktrace require)
                                            'print-cause-trace)
                           new (ns-resolve (doto 'clj-stacktrace.repl require)
                                           'pst)]
                       (alter-var-root orig (constantly (deref new))))]}}

The :injections clause replaces the built-in stack trace printing with enhanced clj-stacktrace version; you can leave it out if you plan on invoking clj-stacktrace functions directly or are using tools which are already clj-stacktrace-aware.

License

Copyright © 2009-2013 Mark McGranaghan and contributors.

Released under an MIT license.

clj-stacktrace's People

Contributors

derekmansen avatar desertmonad avatar gerrit-hntschl avatar hatnik avatar joegallo avatar jonpither avatar mmcgrana avatar mva avatar nahuel avatar ninjudd avatar technomancy avatar weavejester avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

clj-stacktrace's Issues

De-colorize?

Is there a configuration variable or other way to turn the colors off? I see args like "color?" to a bunch of functions in the source, but I can't figure out where these are being called from, and thus where I could find a lever from which to make it turn the colors off.

It looks awful with colors in a comint-mode buffer in emacs, for example:

http://storage.restivo.org/misc/brokencolors.jpg

I created a hacked version which just simply stubs out the colored function to return text and nothing else, but it seems like very many projects depend on clj-stacktrace, so it's like playing whack-a-mole to replace all the included stock clj-stacktraces with my brutally mangled un-Ted-Turner-ized version. I'd rather figure out how to get the stock version to not use colors.

Apologies if I'm missing something terribly obvious.

clj-stacktrace.rpl/pst-on raises IndexOutOfBoundsException for exceptions with stack traces one or two elements deep

The following code raises a java.lang.ArithmeticException with an one-element stack trace that clj-stacktrace.repl/find-source-width can't handle, so an IndexOutOfBoundsException is raised when trying to print it. Versions used: [clj-stacktrace "0.2.6"] and [org.clojure/tools.trace "0.7.5"].

(try
  (clojure.tools.trace/trace-forms
    (let [a (+ 1 1)
          b (* 2 2)
          c (* a b (/ 4 0))]
      c))
  (catch Exception e
    ;; this will throw IndexOutOfBoundsException:
    (clj-stacktrace.repl/pst-on *err* false e)    
   ))

clojure.tools.trace/trace-forms creates an exception with a single element in his stacktrace (check clojure.tools.trace/trace-compose-exception). When this exception is parsed by clj-stacktrace.core/parse-exception you get something like this:

{:class java.lang.ArithmeticException, 
 :message "Divide by zero\n  Form failed: (/ 4 0)\n  Form failed: (* a b (/ 4 0))\n         Form failed: (let* [a (+ 1 1) b (* 2 2) c (* a b (/ 4 0))] c)\n  Form failed: (let [a (+ 1 1) b (* 2 2) c (* a b (/ 4 0))] c)\n", 
 :trace-elems ({:method "divide", 
                :class "clojure.lang.Numbers", 
                :java true, 
                :file "Numbers.java", :line 156})}

but clj-stacktrace.utils/quartile3 is BROKEN for one-element collections, so when is called (after pst-on -> find-source-width -> utils/fence) it fails on the following expression:

(defn quartile3
  [coll]
  (let [c (count coll)]                    ;=> c = 1
    (nth coll (if (even? c)
                (/ (+ (* 3 c) 2) 4)        ;=> 5/4
                (/ (inc (* 3 c)) 4)))))

;=>  (nth coll 5/4) ==>  IndexOutOfBoundsException

Simple test:

(clj-stacktrace.utils/quartile3 [1])      ;=> IndexOutOfBoundsException
(clj-stacktrace.utils/quartile3 [1 2])    ;=> IndexOutOfBoundsException
(clj-stacktrace.utils/quartile3 [1 2 3])  ;=> ok if count > 2

I think this bug is related to the following closed ticket:

#20

But the @jonpither fix in that ticket didn't resolved this problem. It happens in clj-stacktrace 0.2.5 before the fix and after the fix was merged in 0.2.6.

Reverse the printing of stacktrace

It seems that whenever examining stacktraces, millions of programmers everywhere are scrolling up. Could clj-stacktrace put the error at the bottom instead of the top?

Cut a release

The bug fix provided by #5 hasn't been released yet. I was just bitten by it and it looks like #14 was too.

Class becomes Symbol when going through `str` and `read-string`

I would like to get the stacktrace from an nREPL session using clj-stacktrace's parse-exception.
I would get it back as a String, which I could then apply read-string to and either use as I need, or pass on to pst-str.

However, when the parsed structure is converted to a String and then back again, the value for the top level :class goes from Class to Symbol, which then causes an exception.

This works:
(try (/ 0) (catch Exception e (-> e parse-exception pst-str)))

This does not work:
(try (/ 0) (catch Exception e (-> e parse-exception str read-string pst-str)))

It gives the exception:

ClassCastException clojure.lang.Symbol cannot be cast to java.lang.Class  clj-stacktrace.repl/pst-class-on (repl.clj:59)

I can of course apply this work-around (I convert the symbol to a class):

(try (/ 0) (catch Exception e (-> e parse-exception str read-string (update-in [:class] class) pst-str)))

Suggested fix - alternative 1:
Remove the type-hint in line 58, and to a type-test and conversion or similar before building the pst-line in lin 59.

Suggested fix - alternative 2:
Have parse-exception return a String-value for :class - in the same way as the :class value is a String in the stacktrace-elements.

I would prefer alternative 2, as it would be more consistent in general. On the downside, it might break something for other users, assuming the expect a Class, but now get a String.

Either way, would you like me to submit a pull-request for one of the fix-alternatives?
If so, which one?

IndexOutOfBounds

I can't get any useful information from this strace:

Although looking at utils.clj the implementation for quartile1 probably needs to be checked

java.lang.IndexOutOfBoundsException
RT.java:784 clojure.lang.RT.nthFrom
RT.java:753 clojure.lang.RT.nth
utils.clj:19 clj-stacktrace.utils/quartile1
utils.clj:36 clj-stacktrace.utils/fence
repl.clj:95 clj-stacktrace.repl/find-source-width
repl.clj:107 clj-stacktrace.repl/pst-on
repl.clj:123 clj-stacktrace.repl/pst-str
RestFn.java:408 clojure.lang.RestFn.invoke
stacktrace.clj:17 ring.middleware.stacktrace/wrap-stacktrace-log[fn]
stacktrace.clj:79 ring.middleware.stacktrace/wrap-stacktrace-web[fn]
jetty.clj:18 ring.adapter.jetty/proxy-handler[fn](Unknown Source) ring.adapter.jetty.proxy$org.eclipse.jetty.server.handler.AbstractHandler$0.handle
HandlerWrapper.java:111 org.eclipse.jetty.server.handler.HandlerWrapper.handle
Server.java:349 org.eclipse.jetty.server.Server.handle
AbstractHttpConnection.java:452 org.eclipse.jetty.server.AbstractHttpConnection.handleRequest
AbstractHttpConnection.java:884 org.eclipse.jetty.server.AbstractHttpConnection.headerComplete
AbstractHttpConnection.java:938 org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.headerComplete
HttpParser.java:634 org.eclipse.jetty.http.HttpParser.parseNext
HttpParser.java:230 org.eclipse.jetty.http.HttpParser.parseAvailable
AsyncHttpConnection.java:76 org.eclipse.jetty.server.AsyncHttpConnection.handle
SelectChannelEndPoint.java:609 org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle
SelectChannelEndPoint.java:45 org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run
QueuedThreadPool.java:599 org.eclipse.jetty.util.thread.QueuedThreadPool.runJob
QueuedThreadPool.java:534 org.eclipse.jetty.util.thread.QueuedThreadPool$3.run
Thread.java:679 java.lang.Thread.run

Parsing stack frame can throw NullPointerException

Sometimes parsing a stack frame can cause a NullPointerException, like this:

java.lang.NullPointerException
    at java.util.regex.Matcher.getTextLength(Matcher.java:1151)
    at java.util.regex.Matcher.reset(Matcher.java:308)
    at java.util.regex.Matcher.<init>(Matcher.java:228)
    at java.util.regex.Pattern.matcher(Pattern.java:905)
    at clj_stacktrace.utils$re_gsub.invoke(utils.clj:6)
    at clj_stacktrace.core$clojure_ns.invoke(core.clj:14)
    at clj_stacktrace.core$parse_trace_elem.invoke(core.clj:68)

Since this exception is usually thrown from within a catch block, it often obscures the original exception.

Can't run from REPL or lein using instructions in README.md

I may be missing something obvious: I'm just following the instructions in README.md blindly. I'm using Clojure 1.4.0, Leiningen 2.0.0, Java 1.7.0_09

If I run command:

(use 'clj-stacktrace.repl)

in a lein repl I get

IllegalStateException pst already refers to: #'clojure.repl/pst in namespace: user  
clojure.lang.Namespace.warnOrFailOnReplace (Namespace.java:88)

If I follow the instructions for installing in Leiningen 2.x (just adding the init file) I get:

java.lang.RuntimeException: Unable to resolve symbol: clj-stacktrace in this con
text, compiling:(C:\Users\edward\.lein\init.clj:0)
    at clojure.lang.Compiler.analyze(Compiler.java:6281)
    at clojure.lang.Compiler.analyze(Compiler.java:6223)
    at clojure.lang.Compiler$VectorExpr.parse(Compiler.java:2954)
    at clojure.lang.Compiler.analyze(Compiler.java:6264)
    at clojure.lang.Compiler.analyze(Compiler.java:6223)
    at clojure.lang.Compiler$VectorExpr.parse(Compiler.java:2954)
    at clojure.lang.Compiler.analyze(Compiler.java:6264)
    at clojure.lang.Compiler.analyze(Compiler.java:6223)
    at clojure.lang.Compiler$MapExpr.parse(Compiler.java:2827)
    at clojure.lang.Compiler.analyze(Compiler.java:6270)
    at clojure.lang.Compiler.analyze(Compiler.java:6223)
    at clojure.lang.Compiler$MapExpr.parse(Compiler.java:2827)
    at clojure.lang.Compiler.analyze(Compiler.java:6270)
    at clojure.lang.Compiler.analyze(Compiler.java:6223)
    at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:5618)
    at clojure.lang.Compiler$FnMethod.parse(Compiler.java:5054)
    at clojure.lang.Compiler$FnExpr.parse(Compiler.java:3674)
    at clojure.lang.Compiler.analyzeSeq(Compiler.java:6453)
    at clojure.lang.Compiler.analyze(Compiler.java:6262)
    at clojure.lang.Compiler.eval(Compiler.java:6508)
    at clojure.lang.Compiler.load(Compiler.java:6952)
    at clojure.lang.Compiler.loadFile(Compiler.java:6912)
    at clojure.lang.RT$3.invoke(RT.java:307)
    at leiningen.core.user$fn__301.invoke(user.clj:22)
    at clojure.lang.AFn.applyToHelper(AFn.java:159)
    at clojure.lang.AFn.applyTo(AFn.java:151)
    at clojure.core$apply.invoke(core.clj:601)
    at clojure.core$memoize$fn__4921.doInvoke(core.clj:5620)
    at clojure.lang.RestFn.invoke(RestFn.java:397)
    at leiningen.core.main$_main$fn__1665.invoke(main.clj:222)
    at leiningen.core.main$_main.doInvoke(main.clj:221)
    at clojure.lang.RestFn.invoke(RestFn.java:408)
    at clojure.lang.Var.invoke(Var.java:415)
    at clojure.lang.AFn.applyToHelper(AFn.java:161)
    at clojure.lang.Var.applyTo(Var.java:532)
    at clojure.core$apply.invoke(core.clj:601)
    at clojure.main$main_opt.invoke(main.clj:324)
    at clojure.main$main.doInvoke(main.clj:427)
    at clojure.lang.RestFn.invoke(RestFn.java:436)
    at clojure.lang.Var.invoke(Var.java:423)
    at clojure.lang.AFn.applyToHelper(AFn.java:167)
    at clojure.lang.Var.applyTo(Var.java:532)
    at clojure.main.main(main.java:37)
Caused by: java.lang.RuntimeException: Unable to resolve symbol: clj-stacktrace
in this context
    at clojure.lang.Util.runtimeException(Util.java:170)
    at clojure.lang.Compiler.resolveIn(Compiler.java:6766)
    at clojure.lang.Compiler.resolve(Compiler.java:6710)
    at clojure.lang.Compiler.analyzeSymbol(Compiler.java:6671)
    at clojure.lang.Compiler.analyze(Compiler.java:6244)
    ... 42 more

clj-stacktrace.repl cannot be loaded w/ Clojure 1.4.0

After modifying the project.clj to specify Clojure 1.4.0:

$ lein repl
REPL started; server listening on localhost port 52113
user=> (use 'clj-stacktrace.repl)
IllegalStateException pst already refers to: #'clojure.repl/pst in namespace: user  clojure.lang.Namespace.warnOrFailOnReplace (Namespace.java:88)

...and the form of printed exceptions past that point is unmodified.

IndexOutOfBoundsException for exceptions with no stack trace filled in

(pst (doto (Exception.)
       (.setStackTrace (make-array java.lang.StackTraceElement 0))))
IndexOutOfBoundsException 
    clojure.lang.RT.nthFrom (RT.java:795)
    clojure.lang.RT.nth (RT.java:764)
    clj-stacktrace.utils/quartile1 (utils.clj:19)
    clj-stacktrace.utils/fence (utils.clj:36)
    clj-stacktrace.repl/find-source-width (repl.clj:95)
    clj-stacktrace.repl/pst-on (repl.clj:107)
    clj-stacktrace.repl/pst (repl.clj:117)

Now, how I end up with an exception with no stack trace in production, I am not 100% sure. I suspect it is the jvm optimization that leaves out stacktraces in recurring exceptions:
http://stackoverflow.com/questions/4659151/recurring-exception-without-a-stack-trace-how-to-reset

I should be able to work around the problem with
:jvm-opts ["-XX:-OmitStackTraceInFastThrow"]
but haven't tried it yet.

clj-stacktrace shouldn't be assuming there is a non-zero length array of StackTraceElement.

Fix should be pretty straightforward, will try to submit a pr.

Doesn't seem to be installing from instructions in README

I have followed the instructions in the readme and added the required fields to my profile.clj. It looks like this:

{:user {:plugins [[lein-vanity "0.1.0"]
                  [lein-marginalia "0.7.1"]
                  [lein-difftest "2.0.0"]
                  [lein-try "0.3.2"]
                  [lein-ancient "0.4.4"] 
                  [org.clojure/tools.namespace "0.2.3"]]}
 :dependencies [[clj-stacktrace "0.2.7"]]
 :injections [(let [orig (ns-resolve (doto 'clojure.stacktrace require)
                                     'print-cause-trace)
                    new (ns-resolve (doto 'clj-stacktrace.repl require)
                                    'pst)]
                (alter-var-root orig (constantly (deref new))))]}

When I run a REPL I can't see any differences in the stack trace and trying to use clj-stacktrace results in an error.

contrail.core=> (use 'clj-stacktrace.repl)

FileNotFoundException Could not locate clj_stacktrace/repl__init.class or clj_stacktrace/repl.clj on classpath:   clojure.lang.RT.load (RT.java:443)

Is there something obvious that I'm missing here?

[Noob]: Leiningen Plugin crashes on startup

When I try to follow the instructions on the main page to set this up, I get the following stack on every invocation of lein:

paul@laomedeia:~/Desktop/cljtest% lein
Exception in thread "main" java.io.FileNotFoundException: Could not locate leiningen/hooks/clj_stacktrace_test__init.class or leiningen/hooks/clj_stacktrace_test.clj on classpath:  (init.clj:1)
    at clojure.lang.Compiler.eval(Compiler.java:5440)
    at clojure.lang.Compiler.load(Compiler.java:5857)
    at clojure.lang.Compiler.loadFile(Compiler.java:5820)
    at clojure.lang.RT$3.invoke(RT.java:296)
    at leiningen.core$user_init.invoke(core.clj:38)
    at leiningen.core$_main.doInvoke(core.clj:284)
    at clojure.lang.RestFn.invoke(RestFn.java:411)
    at clojure.lang.AFn.applyToHelper(AFn.java:163)
    at clojure.lang.RestFn.applyTo(RestFn.java:133)
    at clojure.core$apply.invoke(core.clj:542)
    at leiningen.core$_main.invoke(core.clj:297)
    at user$eval42.invoke(NO_SOURCE_FILE:1)
    at clojure.lang.Compiler.eval(Compiler.java:5424)
    at clojure.lang.Compiler.eval(Compiler.java:5391)
    at clojure.core$eval.invoke(core.clj:2382)
    at clojure.main$eval_opt.invoke(main.clj:235)
    at clojure.main$initialize.invoke(main.clj:254)
    at clojure.main$script_opt.invoke(main.clj:270)
    at clojure.main$main.doInvoke(main.clj:354)
    at clojure.lang.RestFn.invoke(RestFn.java:437)
    at clojure.lang.Var.invoke(Var.java:373)
    at clojure.lang.AFn.applyToHelper(AFn.java:169)
    at clojure.lang.Var.applyTo(Var.java:482)
    at clojure.main.main(main.java:37)
Caused by: java.io.FileNotFoundException: Could not locate leiningen/hooks/clj_stacktrace_test__init.class or leiningen/hooks/clj_stacktrace_test.clj on classpath: 
    at clojure.lang.RT.load(RT.java:412)
    at clojure.lang.RT.load(RT.java:381)
    at clojure.core$load$fn__4511.invoke(core.clj:4905)
    at clojure.core$load.doInvoke(core.clj:4904)
    at clojure.lang.RestFn.invoke(RestFn.java:409)
    at clojure.core$load_one.invoke(core.clj:4729)
    at clojure.core$load_lib.doInvoke(core.clj:4766)
    at clojure.lang.RestFn.applyTo(RestFn.java:143)
    at clojure.core$apply.invoke(core.clj:542)
    at clojure.core$load_libs.doInvoke(core.clj:4800)
    at clojure.lang.RestFn.applyTo(RestFn.java:138)
    at clojure.core$apply.invoke(core.clj:542)
    at clojure.core$require.doInvoke(core.clj:4869)
    at clojure.lang.RestFn.invoke(RestFn.java:409)
    at user$eval44.invoke(init.clj:1)
    at clojure.lang.Compiler.eval(Compiler.java:5424)
    ... 23 more

Any clues?

(use 'clj-stacktrace.repl) in :repl-options :init causes ac-nrepl.el to hang caching JVM class names

Apologies as I'm not sure if the problem is with clj-stacktrace or with https://github.com/purcell/ac-nrepl, but it's a bit of a showstopper for using this in the REPL in emacs so thought I'd mention it.

After substituting (use clj-stacktrace.repl) for (use 'clojure.stacktrace) in my .lein/profiles.clj :repl-options :init, ac-nrepl suddenly starts to hang when I nrepl-jack-in on "Caching JVM class names". Only restarting emacs fixes it.

Using lein 2.0.0 with latest nrepl.el and ac-nrepl.

clojure-ns chokes on class names without $

When using aleph in combination with ring's wrap-stacktrace, certain exceptions are silently swallowed by the server. I traced this down to clojure-ns raising a null pointer exception for the class-name "lamina.core.observable.ConstantObservable", which is a defrecord.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.