Skip to content

Commit e539cc9

Browse files
adding error log to api (Along with admin error log display)
1 parent 85faacd commit e539cc9

File tree

8 files changed

+138
-5
lines changed

8 files changed

+138
-5
lines changed

config/config.default.lisp

+1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858

5959
(defparameter *public-actions*
6060
`((:post . "/api/users")
61+
(:post . "/api/log/error")
6162
(:get . ,(cl-ppcre:create-scanner "/api/invites/codes/([0-9a-f-]+)")))
6263
"A list of public resources/actions that do not require authentication.")
6364

config/schema.lisp

+2-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@
4545
(list (:attr s "user_id")
4646
(:attr s "type")
4747
(:attr s "id") )))
48-
:rel (:version 1 :multi t))))
48+
:rel (:version 1 :multi t)))
49+
:log (:indexes (:hash (:version 1))))
4950
"Holds our entire db/table/index schema. Tables are are created if they don't
5051
exist. Indexes are also created/updated if they don't exist or if the version
5152
doesn't match. Index names store the version in them, ie the index `user_id`

controllers/admin.lisp

+4-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@
77
(defroute (:get "/admin") (req res)
88
"Get the admin page, populated with our data."
99
(catch-errors (res)
10+
;(setf *admin-page* (file-contents (concatenate 'string (namestring *root*) "views/admin.html")))
1011
(alet* ((admin-stats (get-admin-stats))
11-
(html (populate-stats *admin-page* admin-stats)))
12+
(admin-log (get-logs 200))
13+
(html (populate-stats *admin-page* admin-stats))
14+
(html (populate-log html admin-log)))
1215
(send-response res :body html))))
1316

controllers/log.lisp

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
(in-package :turtl)
2+
3+
(defroute (:post "/api/log/error") (req res)
4+
"Log a client error. Used for debugging."
5+
(catch-errors (res)
6+
(alet* ((log-data (post-var req "data"))
7+
(nil (add-log log-data)))
8+
(send-json res "logged."))))
9+

models/admin.lisp

+27
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,33 @@
1818
;; write stat to string
1919
(format nil "~a" stat)))))
2020

21+
(defun populate-log (html logs)
22+
"Push the given logs into the admin HTML."
23+
(let ((log-html (with-output-to-string (s)
24+
(loop for entry across logs do
25+
(let* ((log-data (gethash "data" entry))
26+
(url (gethash "url" log-data)))
27+
(format s "
28+
<li>
29+
<div class=\"summary\">
30+
<span class=\"count\">~a</span>
31+
<span class=\"id\">~a</span>
32+
<span class=\"file\">~a:~a</span>
33+
</div>
34+
<div class=\"expanded\">
35+
~a
36+
</div>
37+
</li>"
38+
(round (gethash "c" entry))
39+
(gethash "id" entry)
40+
url
41+
(gethash "line" log-data)
42+
(gethash "msg" log-data)))))))
43+
(cl-ppcre:regex-replace
44+
"{{logs}}"
45+
html
46+
log-html)))
47+
2148
(defafun get-admin-stats (future) ()
2249
"Grab statistics for the admin page."
2350
(macrolet ((count-not-deleted (table)

models/log.lisp

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
(in-package :turtl)
2+
3+
(defun hash-log (log-data)
4+
"Generate a hash for this log from its data. Used to de-dupe log records."
5+
(md5 (concatenate 'string (gethash "msg" log-data) (gethash "url" log-data) (gethash "line" log-data))))
6+
7+
(defafun get-logs (future) (num-entries)
8+
"Get N log entries. Please never add a controller interface to this without
9+
resticting it."
10+
(alet* ((sock (db-sock))
11+
(query (r:r
12+
(:limit
13+
(:order-by
14+
(:table "log")
15+
(:desc "c"))
16+
num-entries)))
17+
(cursor (r:run sock query))
18+
(entries (r:to-array sock cursor)))
19+
(r:stop/disconnect sock cursor)
20+
(finish future entries)))
21+
22+
(defafun add-log (future) (log-data)
23+
"Add a log entry to the DB."
24+
(alet* ((log-hash (hash-log log-data))
25+
(log-entry (let ((hash (make-hash-table :test #'equal)))
26+
(setf (gethash "id" hash) log-hash
27+
(gethash "data" hash) log-data
28+
(gethash "created" hash) (get-timestamp)
29+
(gethash "c" hash) 1)
30+
hash))
31+
(sock (db-sock))
32+
(query (r:r
33+
(:replace
34+
(:get (:table "log") log-hash)
35+
(r:fn (x)
36+
(:branch
37+
(:== (:default x 0) 0)
38+
log-entry
39+
(:merge
40+
x
41+
`(("c" . ,(:+ (:attr x "c") 1)))))))))
42+
(res (r:run sock query)))
43+
(r:disconnect sock)
44+
(finish future res)))
45+

turtl.asd

+3-1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
(:file "messages")
5353
(:file "invites")
5454
(:file "sync")
55+
(:file "log")
5556
(:file "admin")
5657
(:file "analytics")
5758
(:file "feedback")))
@@ -71,7 +72,8 @@
7172
(:file "sync")
7273
(:file "users")
7374
(:file "keychain")
74-
(:file "feedback")))
75+
(:file "feedback")
76+
(:file "log")))
7577
(:file "routes" :depends-on (lib "init" controllers "crypto" "errors"))
7678
(:file "init-thread" :depends-on (lib "init" "routes"))))
7779

views/admin.html

+47-2
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,72 @@
55
<meta http-equiv="Content-Language" content="en">
66
<title>Turtl admin dashboard</title>
77
<style type="text/css">
8+
.clearMe {display: block; height: 0; clear: both; font-size: 0;}
9+
.clear:after {content: "."; display: block; height: 0; clear: both; visibility: hidden;}
10+
.clear {display: inline-block;}
11+
/* hide IE mac \*/
12+
* html .clear {height: 1%;}
13+
.clear {display: block;}
14+
/* */
15+
816
body {font-size: 14px; font-family: arial; color: #222; background: #fff;}
917
header h1 {font-size: 64px; font-weight: normal; color: #999; text-transform: lowercase;}
1018
header h1 small {font-size: 14px;}
19+
h2 {font-weight: normal; color: #777;}
1120
div.admin {width: 960px; margin: 0 auto;}
12-
ul.stats {margin: 0; padding: 0; list-style: none;}
21+
ul.stats {margin: 0 0 32px 0; padding: 0; list-style: none;}
1322
ul.stats li {float: left; display: block; width: 132px; height: 132px; margin: 0 16px 16px 0; padding: 16px; font-size: 17px; color: #666; text-align: center; background: #f4f4f4;}
1423
ul.stats li span {display: block; margin: 32px 0 0 0; font-size: 32px; color: #79a;}
24+
ul.logs {list-style: none; margin: 0; padding: 0;}
25+
ul.logs li {}
26+
ul.logs li:hover .summary {background: #f4fff4;}
27+
ul.logs li:nth-child(2n) {background: #f4f4f4;}
28+
ul.logs li .summary {padding: 4px; cursor: pointer; font-family: Consolas, "Liberation Mono", Courier, monospace;}
29+
ul.logs li .summary .count {display: inline-block; width: 32px;}
30+
ul.logs li .summary .id {display: inline-block; width: 64px; margin: 0 12px -4px 0; overflow: hidden; text-overflow: ellipsis;}
31+
ul.logs li .expanded {display: none; padding: 16px;}
32+
ul.logs li.open .expanded {display: block;}
1533
</style>
1634
</head>
1735
<body>
1836
<div class="admin">
1937
<header>
2038
<h1>Turtl. <small>admin</small></h1>
2139
</header>
22-
<ul class="stats">
40+
<ul class="stats clear">
2341
<li>Users <span>{{stat:num-users}}</span></li>
2442
<li>Personas <span>{{stat:num-personas}}</span></li>
2543
<li>Personas no RSA <span>{{stat:num-personas-wo-rsa}}</span></li>
2644
<li>Boards <span>{{stat:num-boards}}</span></li>
2745
<li>Notes <span>{{stat:num-notes}}</span></li>
2846
</ul>
47+
48+
<h2>Error log</h2>
49+
<ul class="logs">
50+
{{logs}}
51+
</ul>
2952
</div>
53+
<script>
54+
(function() {
55+
var ul = document.getElementsByClassName('logs')[0];
56+
var lis = ul.children;
57+
for(var i = 0; i < lis.length; i++)
58+
{
59+
(function (li) {
60+
var summary = li.children[0];
61+
summary.addEventListener('click', function() {
62+
if(li.className.match(/open/))
63+
{
64+
li.className = li.className.replace(/open/, '');
65+
}
66+
else
67+
{
68+
li.className += 'open';
69+
}
70+
}, false);
71+
})(lis[i]);
72+
}
73+
})();
74+
</script>
3075
</body>
3176
</html>

0 commit comments

Comments
 (0)