-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathps.cljs
153 lines (145 loc) · 7.15 KB
/
ps.cljs
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
(ns ps
(:require
["fs/promises" :as fs]
["fs" :refer [readlinkSync writeFileSync]]
["os" :as os]
["path" :as path]
[promesa.core :as p]
[applied-science.js-interop :as j]
["ps-list$default" :as ps]
["vega-lite" :as vega-lite]
["vega" :as vega]
[sitefox.ui :refer [log]]
[sitefox.db :refer [kv]]
[sitefox.util :refer [env]]))
(def keep-for-minutes (* 60 24 31))
(def max-load (env "MAX_LOAD" 80))
(defn get-proc-cwd [proc]
(p/let [pid (j/get proc :pid)
;path (str "/proc/" (j/get proc :pid) "/cwd")
;exists (fs/access path (j/get-in fs [:constants :R_OK]))
;cwd (when exists (fs/readlink path))
cwd (try (readlinkSync (str "/proc/" pid "/cwd")) (catch :default _e nil))]
(j/assoc! proc :cwd cwd)))
(defn get-processes [timestamp & [n]]
(p/let [procs (js->clj (ps) :keywordize-keys true)
cpu-beasts (->> procs
(sort-by #(j/get % :cpu))
reverse
(take (or n 10))
(map get-proc-cwd)
p/all)
memory-beasts (->> procs
(sort-by #(j/get % :memory))
reverse
(take (or n 10))
(map get-proc-cwd)
p/all)]
(clj->js {:timestamp timestamp
:cpu cpu-beasts
:memory memory-beasts})))
(defn update-history
[history value]
(let [history (or history #js [])]
(j/call history :push value)
(j/call history :slice (* -1 keep-for-minutes))))
(defn run-tick []
"Runs every minute and stores the current load, plus processes if 5-minute load exceeds 80%."
[]
(p/let [timestamp (-> (js/Date.) (.getTime) (/ 1000) int)
cpus (os/cpus)
cpu-count (j/get cpus :length)
loadavg (os/loadavg)
scaled-load-5 (-> loadavg (get 1) (/ cpu-count) (* 100) int)
over-loaded (> scaled-load-5 max-load)
store (kv "monitoring")
load-history (.get store "load-history")
load-history (update-history load-history #js {:timestamp timestamp
:load (-> loadavg (get 0) (/ cpu-count) (* 100) int)
:load-5 (-> loadavg (get 1) (/ cpu-count) (* 100) int)})
process-history (.get store "process-history")
processes (when over-loaded (get-processes timestamp))
process-history (if processes
(update-history process-history processes)
process-history)]
(.set store "load-history" load-history)
(.set store "process-history" process-history)
;(print "over load?" over-loaded)
;(print "load-history" (j/get load-history :length))
;(print "process-history" (j/get process-history :length))
;(log load-history)
#_ (doseq [p process-history]
(log (j/get p :timestamp)))))
(defn format-processes [p]
(when p
(.join
(.map (j/get p :cpu)
(fn [c]
(str
(j/get c :cpu) "\n"
(j/get c :cmd) "\n"
(j/get c :cwd))))
"\n")))
(defn update-charts []
(p/let [dest "public"
store (kv "monitoring")
process-history (.get store "process-history")
processes-by-timestamp (clj->js
(into {}
(map (fn [p] {(j/get p :timestamp) p}) process-history)))
load-history (.get store "load-history")
load-history (.map load-history (fn [l]
(let [processes (j/get processes-by-timestamp (j/get l :timestamp))]
(-> l
(j/assoc! :processes processes)
(j/assoc! :process (format-processes processes))
(j/update-in! [:timestamp] #(* % 1000))))))
vl-spec {:width 800 :height 600
:data {:values load-history}
:transform [{:filter (str "datum['timestamp'] > " (-> (js/Date.) .getTime (- (* 1000 60 60 6))))}]
:encoding {:x {:field :timestamp
:type :temporal
;:timeUnit :utcyearmonthdate
:axis {:labelAngle 15
:titleColor "#7e7e7e"
:titleFontSize 14
:labelFontSize 14
:title "Time"}
:timeUnit {:unit :yearmonthdatehoursminutes}}}
:layer [{:mark {:type :bar
:orient :vertical
:tooltip true}
:encoding {:y {:field :load
:aggregate :max
:format ".0%"
:axis {:titleColor "#7e7e7e"
:titleFontSize 14
:labelFontSize 14
:title "Max load %"}}
:color {:condition {:test "datum.processes"
:value "#FF2A2A"}
:value "#2A7FFF"}
:tooltip [;{:field :load :aggregate :max}
{:field :timestamp :timeUnit {:unit :yearmonthdatehoursminutes} :title "time"}
;{:field :process}
{:field :processes}
;{:field :load :type :temporal :title "" :timeUnit {:unit "yearmonth" :step 1}}
;{:field :l :format "$.3s" :title "Predicted" :aggregate :average}
;{:field :p-max :format "$.3s" :title "Max predicted" :aggregate :max}
]}}]}
spec (aget (vega-lite/compile (clj->js vl-spec)) "spec")
view (vega/View. (vega/parse spec) (clj->js {:renderer "none"}))
hosted-vl-spec (-> vl-spec
(assoc :width :container :height :container))
hosted-spec (aget (vega-lite/compile (clj->js hosted-vl-spec)) "spec")]
;(log load-history)
;(log process-history)
;(print (-> vl-spec clj->js (js/JSON.stringify nil 2)))
;(writeFileSync (path/join dest "spec.json") (js/JSON.stringify hosted-spec))
(writeFileSync (path/join dest "spec.json") (js/JSON.stringify (clj->js hosted-vl-spec)))
(-> (.toSVG view)
(.then (fn [svg] (writeFileSync (path/join dest "chart.svg") svg))))))
(p/do!
(when (not (env "UPDATEONLY"))
(run-tick))
(update-charts))