taoensso / truss

Assertions micro-library for Clojure/Script
https://www.taoensso.com/truss
Eclipse Public License 1.0
302 stars 14 forks source link

set-error-fn! passes a delay object to my function? #7

Closed daonsh closed 7 years ago

daonsh commented 7 years ago

I called set-error-fn! with my logging function which expects to receive a string. Sometimes it works and displays the exception, on other cases it receives a clojure delay object. When I try to deref (@) it in my error function I get something like:

:{:?data nil, :msg_ #object[clojure.lang.Delay 0x35ff7648 {:status :pending, :val nil}], :elidable? true, :dt #inst "2016-11-07T11:51:28.661-00:00", :val "54717465", :ns-str "crmserver.sync", :val-type java.lang.String, :?err nil, :assert true, :?data nil, :?line 419, :form-str "(integer? parent-row-id)"}

I don't know why the delay is used in truss, but would appreciate a better explanation on how to use et-error-fn! with my logging function, and understand what the above means. (I think it's not really an exception).

Thanks

ptaoussanis commented 7 years ago

The provided error fn should expect to be called with a delayed map, so deref (@error-fn-arg) should give you a map.

Additionally, the map may contain delay keys - in this case :msg_'s value is also a delay.

You might like to do something like this for your error fn:

(defn my-error-fn [delayed-map]
  (let [m @delayed-map ; Realise map
        m (assoc m :msg_ @(:msg_ m)) ; Realise msg_
        ]
    ;; do something with `m` map
    ))

I'll note that I use a pattern of naming vars/keys with an ending underscore if they're dereffable, e.g. :msg_ to imply that msg may require dereffing.

I don't know why the delay is used in truss

For performance. Delays allow the consumer to decide whether costly work is worth doing or not. If you choose not to deref a delay, then you save the cost of computing the value. Actually, the cost of just always auto-realising :msg_ here wouldn't be too high; might change that at some point to keep things easier.

daonsh commented 7 years ago

Thank you very much! I implemented an error function that looks like that. That output does not look that great in my log - something like:

{:?data nil, :msg_ "Invariant violation in crmserver.sync:615. Test form: (integer? parent-row-id) with failing input: 58961524", :elidable? true, :dt #inst "2016-11-07T13:35:03.287-00:00", :val "58961524", :ns-str "crmserver.sync", :val-type java.lang.String, :?err nil, :assert true, :?data nil, :?line 615, :form-str "(integer? parent-row-id)"}

But it's usable. It'd be useful if I could somehow also display the stacktrace (without throwing an exception). Please let me know if it's easy enough to do. Otherwise it's OK. Thanks for the help. Perhaps you could also add that to the docs.

ptaoussanis commented 7 years ago

It'd be useful if I could somehow also display the stacktrace (without throwing an exception).

What stacktrace do you have in mind? In this case, no exception was actually thrown.

You've got a test somewhere of the form (integer? parent-row-id) that's failing for a string input "58961524". I.e. you're expecting an integer at this spot, but are actually seeing a string. Does that make sense?

daonsh commented 7 years ago

I understand the message, it's very clear, it makes sense, it's just that when that kind of error happens I'd like to be able to see the stacktrace without throwing an exception. I'd like to learn more about my error - where it came from. I'm not sure that's possible...

ptaoussanis commented 7 years ago

Not sure I understand what stacktrace you have in mind though? There was no underlying exception in this case.

Could you maybe show me an example of the information that you'd be looking to get from a stacktrace?

daonsh commented 7 years ago

I'm looking for something that looks like a stack trace - which function called my function, which function called that function, and so on, with line numbers. Because my function can be called from many places. I don't know if that's available without an exception.

ptaoussanis commented 7 years ago

Hmm, perhaps you'd like something like (seq (.getStackTrace (Thread/currentThread))) or (seq (.getStackTrace (Throwable.)))?

That'll give you a sequence of the current thread's stack trace entries.

daonsh commented 7 years ago

Will look into these, thanks for your help. (May close)