/
app.lisp
91 lines (72 loc) · 2.94 KB
/
app.lisp
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
(uiop:define-package #:reblocks-prometheus/app
(:use #:cl)
(:import-from #:prometheus
#:collector
#:make-registry)
(:import-from #:prometheus.formats.text
#:marshal)
#+sbcl
(:import-from #:prometheus.sbcl
#:make-memory-collector
#:make-threads-collector)
(:import-from #:prometheus.process
#:make-process-collector)
(:import-from #:log4cl-extras/error
#:with-log-unhandled)
(:import-from #:reblocks/session)
(:import-from #:reblocks/variables
#:*current-app*)
(:import-from #:reblocks-prometheus/gauges/number-of-pages
#:number-of-pages-gauge)
(:import-from #:reblocks-prometheus/gauges/number-of-sessions
#:number-of-sessions-gauge
#:number-of-anonymous-sessions-gauge)
(:import-from #:prometheus-gc
#:make-gc-collector)
(:export #:prometheus-app-mixin
#:stats-registry))
(in-package #:reblocks-prometheus/app)
(defclass prometheus-app-mixin ()
((registry :initform (make-registry)
:reader stats-registry))
(:documentation "A mixin which gathers some stats to report in Prometheus format.
Also, this mixin adds a /metrics slot to the app.
Use STATS-REGISTRY to access the registry slot."))
(defmethod initialize-instance :after ((app prometheus-app-mixin) &rest args)
(declare (ignore args))
(let* ((registry (stats-registry app)))
#+sbcl
(progn
(make-memory-collector :registry registry)
(make-threads-collector :registry registry)
(make-gc-collector :registry registry))
(make-process-collector :registry registry)
(make-instance 'number-of-pages-gauge
:registry registry)
(make-instance 'number-of-sessions-gauge
:registry registry)
(make-instance 'number-of-anonymous-sessions-gauge
:registry registry)
(let* ((route (make-instance 'reblocks/routes:route
:template
(routes:parse-template "/metrics")
:handler '/metrics
:content-type "text/plain"))
(app-class-name (typecase app
(symbol app)
(t (type-of app)))))
;; TODO: this place should be refactored when I've refactor
;; the way how application routes are defined in Reblocks
(setf (get '/metrics :route) route)
(pushnew '/metrics (get app-class-name :route-handlers)))))
(defvar *app* nil)
(defgeneric serve-stats (app)
(:documentation "Should return a string with metrics in Prometheus format.")
(:method :after ((app prometheus-app-mixin))
(setf *app* app)
(reblocks/session:expire))
(:method ((app prometheus-app-mixin))
(marshal (stats-registry app))))
(defun /metrics ()
(with-log-unhandled ()
(serve-stats *current-app*)))