-
Notifications
You must be signed in to change notification settings - Fork 2
/
core.clj
108 lines (99 loc) · 4.07 KB
/
core.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
(ns clj-bugsnag.core
(:require [clj-bugsnag.impl :as impl]
[clj-stacktrace.core :refer [parse-exception]]
[clj-stacktrace.repl :refer [method-str]]
[clojure.java.shell :refer [sh]]
[clj-http.client :as http]
[clojure.repl :as repl]
[clojure.string :as string]
[clojure.walk :as walk]))
(defn get-git-rev
[]
(try
(string/trim (:out (sh "git" "rev-parse" "HEAD")))
(catch Throwable _t "git revision not available")))
(def git-rev (memoize get-git-rev))
(defn get-hostname
"Attempt to get the current hostname."
[]
(try
(.. java.net.InetAddress getLocalHost getHostName)
(catch Throwable _t "Hostname could not be resolved")))
(defn- find-source-snippet
[around function-name]
(try
(let [fn-sym (symbol function-name)
fn-var (find-var fn-sym)
source (repl/source-fn fn-sym)
start (-> fn-var meta :line)
indexed-lines (map-indexed (fn [i line]
[(+ i start) (string/trimr line)])
(string/split-lines source))]
(into {} (filter #(<= (- around 3) (first %) (+ around 3)) indexed-lines)))
(catch Exception _ex
nil)))
(defn- transform-stacktrace
[trace-elems project-ns]
(try
(vec (for [{:keys [file line ns] :as elem} trace-elems
:let [project? (string/starts-with? (or ns "_") project-ns)
method (method-str elem)
code (when (string/ends-with? (or file "") ".clj")
(find-source-snippet line (string/replace (or method "") "[fn]" "")))]]
{:file file
:lineNumber line
:method method
:inProject project?
:code code}))
(catch Exception ex
[{:file "clj-bugsnag/core.clj"
:lineNumber 1
:code {1 (str ex)
2 "thrown while building stack trace."}}])))
(defn- stringify
[thing]
(if (or (map? thing) (string? thing) (number? thing) (sequential? thing))
thing
(str thing)))
(defn exception->json
[exception {:keys [project-ns context group severity user version environment meta] :as options}]
(let [ex (parse-exception exception)
message (:message ex)
class-name (.getName ^Class (:class ex))
project-ns (or project-ns "\000")
stacktrace (transform-stacktrace (:trace-elems ex) project-ns)
base-meta (if-let [d (ex-data exception)]
{"ex-data" d}
{})
api-key (impl/load-bugsnag-api-key! options)
grouping-hash (or group
(if (isa? (type exception) clojure.lang.ExceptionInfo)
message
class-name))]
{:apiKey api-key
:notifier {:name "com.splashfinancial/clj-bugsnag"
:version "1.0.0"
:url "https://github.com/SplashFinancial/clj-bugsnag"}
:events [{:payloadVersion "2"
:exceptions [{:errorClass class-name
:message message
:stacktrace stacktrace}]
:context context
:groupingHash grouping-hash
:severity (or severity "error")
:user user
:app {:version (or version (git-rev))
:releaseStage (or environment "production")}
:device {:hostname (get-hostname)}
:metaData (walk/postwalk stringify (merge base-meta meta))}]}))
(defn notify
"Main interface for manually reporting exceptions.
When not :api-key is provided in options,
tries to load BUGSNAG_KEY var from enviroment."
([exception]
(notify exception nil))
([exception options]
(let [params (exception->json exception options)
url "https://notify.bugsnag.com/"]
(http/post url {:form-params params
:content-type :json}))))