{"id":19302395,"url":"https://github.com/dryewo/hystrix-plus","last_synced_at":"2026-06-11T01:31:37.646Z","repository":{"id":62433581,"uuid":"149982867","full_name":"dryewo/hystrix-plus","owner":"dryewo","description":"Stack trace enhancer for hystrix-clj.","archived":false,"fork":false,"pushed_at":"2019-01-23T20:23:42.000Z","size":15,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-11-15T21:02:50.218Z","etag":null,"topics":["clojure","exceptions","hystrix","hystrix-clj","stack-trace"],"latest_commit_sha":null,"homepage":"https://github.com/Netflix/Hystrix/tree/master/hystrix-contrib/hystrix-clj","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"epl-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dryewo.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-09-23T12:35:50.000Z","updated_at":"2019-01-23T20:23:44.000Z","dependencies_parsed_at":"2022-11-01T21:16:01.833Z","dependency_job_id":null,"html_url":"https://github.com/dryewo/hystrix-plus","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/dryewo/hystrix-plus","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dryewo%2Fhystrix-plus","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dryewo%2Fhystrix-plus/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dryewo%2Fhystrix-plus/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dryewo%2Fhystrix-plus/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dryewo","download_url":"https://codeload.github.com/dryewo/hystrix-plus/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dryewo%2Fhystrix-plus/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34178819,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-10T02:00:07.152Z","response_time":89,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clojure","exceptions","hystrix","hystrix-clj","stack-trace"],"created_at":"2024-11-09T23:21:48.188Z","updated_at":"2026-06-11T01:31:37.619Z","avatar_url":"https://github.com/dryewo.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"# hystrix-plus\n[![Build Status](https://travis-ci.org/dryewo/hystrix-plus.svg?branch=master)](https://travis-ci.org/dryewo/hystrix-plus)\n[![Clojars Project](https://img.shields.io/clojars/v/me.dryewo/hystrix-plus.svg)](https://clojars.org/me.dryewo/hystrix-plus)\n\nA companion library for [hystrix-clj] to enhance `HystrixRuntimeException`'s stack traces.\n\n```clj\n[me.dryewo/hystrix-plus \"0.1.0\"]\n```\n\nPlease refer to _CHANGELOG.md_ for latest changes.\n\n## Usage\n\nJust add this library and put this call somewhere next to `(defn -main)`:\n\n```clj\n(ns my.main.ns\n  (:require [hystrix-plus.core :as hystrix-plus]))\n\n(hystrix-plus/enable-full-command-stack-traces!)\n```\n\nThis will make stack traces from exceptions thrown by Hystrix commands include both caller's and command's threads' stack frames.\n\n## Rationale\n\nPlease get familiar with [Hystrix] purpose and main concepts before reading this.\n\nImagine that you have the following code:\n\n```clj\n(defn inner3 [] (/ 1 0))       ; Throws an exception\n(defn inner2 [] (inner3))\n(defn inner1 [] (inner2))\n\n(defcommand my-command []      ; Executes the body on a separate thread\n  (inner1))\n\n(defn outer1 [] (my-command))\n(defn outer2 [] (outer1))\n(defn outer3 [] (outer2))\n```\n\nAnd then you call `(outer3)`:\n\n```\n=\u003e (outer3)\n                                       ...                   \nhystrix-plus.core-test/eval2542/my-command  core_test.clj: 16\n             hystrix-plus.core-test/inner1  core_test.clj: 13\n             hystrix-plus.core-test/inner2  core_test.clj: 12\n             hystrix-plus.core-test/inner3  core_test.clj: 11\n                                       ...                   \n                        java.lang.ArithmeticException: Divide by zero\ncom.netflix.hystrix.exception.HystrixRuntimeException: hystrix-plus.core-test/my-command failed and no fallback available.\n          failureType: #object[com.netflix.hystrix.exception.HystrixRuntimeException$FailureType 0x77688a30 \"COMMAND_EXCEPTION\"]\n    implementingClass: com.netflix.hystrix.core.proxy$com.netflix.hystrix.HystrixCommand$ff19274a\n              clojure.lang.Compiler$CompilerException: com.netflix.hystrix.exception.HystrixRuntimeException: hystrix-plus.core-test/my-command failed and no fallback available., compiling:(/projects/hystrix-plus/test/hystrix_plus/core_test.clj:24:3)\n```\n\n(This example assumes that you use [io.aviso/pretty] for pretty printed stack traces).\n\nNotice that `outer1`, `outer2`, `outer3` are missing from the `ArithmeticException` stack trace,\nbecause the exception was created on a separate thread, where Hystrix executed its commands.\nThey are also not present in the wrapping `HystrixRuntimeException`'s stack trace, because [AbstractCommand.execute()](https://github.com/Netflix/Hystrix/blob/7f5a0afc23aa5ff82320560a04d4c81a45efd67c/hystrix-core/src/main/java/com/netflix/hystrix/HystrixCommand.java#L342)\njust rethrows whatever the command thread has thrown.\n\nRead more about [exception wrapping].\n\nIn the end, if someone sees this stack trace in logs, it would be very hard to find that `my-command`\nwas called by `outer1`, which in turn was called by `outer2`, which in turn was called by `outer3`.\n\n### Semi-workaround\n\nIf instead of directly executing the command you `queue` it (obtaining a `Future`) and then immediately dereference it,\nthe resulting exception will include `outer1`, `outer2` and `outer3` in the stack trace of one of the wrapping exceptions.\n\n```clj\n(defn outer1 [] @(com.netflix.hystrix.core/queue #'my-command))\n```\n\nHowever, `outer1`, `outer2` and `outer3` are still not visible in [io.aviso/pretty] printout, because it does not show the stack trace of `ExecutionException`.\n\n```\n=\u003e (outer3)\n                                       ...                   \nhystrix-plus.core-test/eval2739/my-command  core_test.clj: 16\n             hystrix-plus.core-test/inner1  core_test.clj: 13\n             hystrix-plus.core-test/inner2  core_test.clj: 12\n             hystrix-plus.core-test/inner3  core_test.clj: 11\n                                       ...                   \n                        java.lang.ArithmeticException: Divide by zero\ncom.netflix.hystrix.exception.HystrixRuntimeException: hystrix-plus.core-test/my-command failed and no fallback available.\n          failureType: #object[com.netflix.hystrix.exception.HystrixRuntimeException$FailureType 0x77688a30 \"COMMAND_EXCEPTION\"]\n    implementingClass: com.netflix.hystrix.core.proxy$com.netflix.hystrix.HystrixCommand$ff19274a\n              java.util.concurrent.ExecutionException: Observable onError\n              clojure.lang.Compiler$CompilerException: java.util.concurrent.ExecutionException: Observable onError, compiling:(/projects/hystrix-plus/test/hystrix_plus/core_test.clj:24:3)\n```\n\nWith standard exception printing you can see `outer1`, `outer2` and `outer3` (~75% of the output omitted for brevity):\n\n```\njava.util.concurrent.ExecutionException: Observable onError, compiling:(/projects/hystrix-plus/test/hystrix_plus/core_test.clj:24:3)\n\t...\nCaused by: java.util.concurrent.ExecutionException: Observable onError\n\t...\n\tat hystrix_plus.core_test$outer1.invokeStatic(core_test.clj:18)\n\tat hystrix_plus.core_test$outer1.invoke(core_test.clj:18)\n\tat hystrix_plus.core_test$outer2.invokeStatic(core_test.clj:19)\n\tat hystrix_plus.core_test$outer2.invoke(core_test.clj:19)\n\tat hystrix_plus.core_test$outer3.invokeStatic(core_test.clj:20)\n\tat hystrix_plus.core_test$outer3.invoke(core_test.clj:20)\n\tat hystrix_plus.core_test$eval1751.invokeStatic(core_test.clj:24)\n\tat hystrix_plus.core_test$eval1751.invoke(core_test.clj:24)\n\t...\nCaused by: com.netflix.hystrix.exception.HystrixRuntimeException: hystrix-plus.core-test/my-command failed and no fallback available.\n\t...\nCaused by: java.lang.ArithmeticException: Divide by zero\n\t...\n\tat hystrix_plus.core_test$inner3.invokeStatic(core_test.clj:11)\n\tat hystrix_plus.core_test$inner3.invoke(core_test.clj:11)\n\tat hystrix_plus.core_test$inner2.invokeStatic(core_test.clj:12)\n\tat hystrix_plus.core_test$inner2.invoke(core_test.clj:12)\n\tat hystrix_plus.core_test$inner1.invokeStatic(core_test.clj:13)\n\tat hystrix_plus.core_test$inner1.invoke(core_test.clj:13)\n\tat hystrix_plus.core_test$eval1722$my_command__1723.invoke(core_test.clj:16)\n\t...\n```\n\nNote that the order of functions appears \"inside-out\".  \nThis is better than nothing, but still doesn't make it too easy to find how the command was called.\n\nSo, this workaround has drawbacks:\n\n* It is not compatible with [io.aviso/pretty], making it hard to read through verbose standard printout.\n* It requires to use `@(com.netflix.hystrix.core/queue #'my-command ...)` form instead of `(my-command ...)`\n  in order to get full stack trace information.\n\n### Solution\n\nAdd this line to one of your project's namespaces:\n\n```clj\n(hystrix-plus/enable-full-command-stack-traces!)\n```\n\nNow, when any Hystrix command throws an exception, the full call stack will be visible in the printout:\n\n```\n=\u003e (outer3)\n                                            ...                   \n                                  user/eval4088      REPL Input   \n                                            ...                   \n                hystrix-plus.core-test/eval4092  core_test.clj: 26\n                  hystrix-plus.core-test/outer3  core_test.clj: 20\n                  hystrix-plus.core-test/outer2  core_test.clj: 19\n                  hystrix-plus.core-test/outer1  core_test.clj: 18\n                                            ...                   \n     hystrix-plus.core-test/eval3988/my-command  core_test.clj: 15\n                                            ...                   \nhystrix-plus.core/execute-and-join-stack-traces       core.clj: 37\n                                            ...                   \n     hystrix-plus.core-test/eval3988/my-command  core_test.clj: 16\n                  hystrix-plus.core-test/inner1  core_test.clj: 13\n                  hystrix-plus.core-test/inner2  core_test.clj: 12\n                  hystrix-plus.core-test/inner3  core_test.clj: 11\n                                            ...                   \n                        java.lang.ArithmeticException: Divide by zero\ncom.netflix.hystrix.exception.HystrixRuntimeException: hystrix-plus.core-test/my-command failed and no fallback available.\n          failureType: #object[com.netflix.hystrix.exception.HystrixRuntimeException$FailureType 0x14ff2fc4 \"COMMAND_EXCEPTION\"]\n    implementingClass: com.netflix.hystrix.core.proxy$com.netflix.hystrix.HystrixCommand$ff19274a\n              clojure.lang.Compiler$CompilerException: com.netflix.hystrix.exception.HystrixRuntimeException: hystrix-plus.core-test/my-command failed and no fallback available., compiling:(/projects/hystrix-plus/test/hystrix_plus/core_test.clj:26:7)\n```\n\nThe `hystrix-plus.core/execute-and-join-stack-traces` line in the middle indicates that the stack trace was manipulated in some way.  \n\n## Implementation details\n\nWhen you call a command as a function:\n\n```clj\n(my-command ...)\n```\n\n, internally it gets translated to a call to `com.netflix.hystrix.core/execute`:\n\n```clj\n(com.netflix.hystrix.core/execute #'my-command ...)\n```\n\nThis library achieves the effect by replacing the implementation of `com.netflix.hystrix.core/execute` with a function that\ncatches all exceptions thrown by `AbstractCommand.execute()` and stitches together stack traces from the command's thread\nand the current thread before rethrowing:\n\n```clj\n(defn execute-and-join-stack-traces [definition \u0026 args]\n  (try\n    (.execute ^HystrixExecutable (apply com.netflix.hystrix.core/instantiate definition args))\n    (catch Exception e\n      (extend-cross-thread-stack-trace! e)\n      (throw e))))\n```\n\nIt updates the stack trace of the [innermost exception][exception wrapping], `java.lang.ArithmeticException` in the example above\n(because `io.aviso/pretty` only prints the innermost exception's stack trace).\n\nPlease check out `hystrix-plus.core/execute-and-join-stack-traces` function\n[source code](https://github.com/dryewo/hystrix-plus/blob/master/src/hystrix_plus/core.clj) for more details.\n\n\n## License\n\nCopyright © 2018 Dmitrii Balakhonskii\n\nDistributed under the Eclipse Public License either version 1.0 or (at\nyour option) any later version.\n\n\n[hystrix-clj]: https://github.com/Netflix/Hystrix/tree/master/hystrix-contrib/hystrix-clj\n[Hystrix]: https://github.com/Netflix/Hystrix/wiki\n[io.aviso/pretty]: https://github.com/AvisoNovate/pretty\n[exception wrapping]: http://tutorials.jenkov.com/java-exception-handling/exception-wrapping.html\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdryewo%2Fhystrix-plus","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdryewo%2Fhystrix-plus","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdryewo%2Fhystrix-plus/lists"}