]> gitweb.factorcode.org Git - factor.git/blob - basis/logging/server/server.factor
logging.server: add support for changing the logging root temporarily.
[factor.git] / basis / logging / server / server.factor
1 ! Copyright (C) 2008 Slava Pestov.\r
2 ! See http://factorcode.org/license.txt for BSD license.\r
3 USING: assocs calendar calendar.format combinators\r
4 concurrency.messaging continuations debugger destructors init io\r
5 io.directories io.encodings.utf8 io.files io.pathnames kernel\r
6 locals math math.parser math.ranges namespaces sequences\r
7 strings threads ;\r
8 IN: logging.server\r
9 \r
10 : log-root ( -- string )\r
11     \ log-root get-global [ "logs" resource-path ] unless* ;\r
12 \r
13 : log-path ( service -- path )\r
14     log-root prepend-path ;\r
15 \r
16 : log# ( path n -- path' )\r
17     number>string ".log" append append-path ;\r
18 \r
19 SYMBOL: log-files\r
20 \r
21 : open-log-stream ( service -- stream )\r
22     log-path\r
23     [ make-directories ]\r
24     [ 1 log# utf8 <file-appender> ] bi ;\r
25 \r
26 : log-stream ( service -- stream )\r
27     log-files get [ open-log-stream ] cache ;\r
28 \r
29 : close-log-streams ( -- )\r
30     log-files get [ values dispose-each ] [ clear-assoc ] bi ;\r
31 \r
32 :: with-log-root ( path quot -- )\r
33     [ close-log-streams path \ log-root set-global quot call ]\r
34     \ log-root get-global\r
35     [ \ log-root set-global close-log-streams ] curry\r
36     [ ] cleanup ; inline\r
37 \r
38 : timestamp-header. ( -- )\r
39     "[" write now (timestamp>rfc3339) "] " write ;\r
40 \r
41 : multiline-header ( -- str ) 20 CHAR: - <string> ; foldable\r
42 \r
43 : multiline-header. ( -- )\r
44     "[" write multiline-header write "] " write ;\r
45 \r
46 : write-message ( msg word-name level -- )\r
47     [ harvest ] 2dip pick empty? [ 3drop ] [\r
48         timestamp-header.\r
49         [ write bl write ": " write print ] 2curry\r
50         [ multiline-header. ] swap interleave\r
51     ] if ;\r
52 \r
53 : (log-message) ( msg -- )\r
54     #! msg: { msg word-name level service }\r
55     first4 log-stream [ write-message flush ] with-output-stream* ;\r
56 \r
57 : try-dispose ( obj -- )\r
58     [ dispose ] curry [ error. ] recover ;\r
59 \r
60 : close-log ( service -- )\r
61     log-files get delete-at*\r
62     [ try-dispose ] [ drop ] if ;\r
63 \r
64 : (close-logs) ( -- )\r
65     log-files get\r
66     [ values [ try-dispose ] each ] [ clear-assoc ] bi ;\r
67 \r
68 CONSTANT: keep-logs 10\r
69 \r
70 : ?delete-file ( path -- )\r
71     dup exists? [ delete-file ] [ drop ] if ;\r
72 \r
73 : delete-oldest ( service -- )\r
74     keep-logs log# ?delete-file ;\r
75 \r
76 : ?move-file ( old new -- )\r
77     over exists? [ move-file ] [ 2drop ] if ;\r
78 \r
79 : advance-log ( path n -- )\r
80     [ 1 - log# ] 2keep log# ?move-file ;\r
81 \r
82 : rotate-log ( service -- )\r
83     [ close-log ]\r
84     [\r
85         log-path\r
86         [ delete-oldest ]\r
87         [ keep-logs 1 [a,b] [ advance-log ] with each ] bi\r
88     ] bi ;\r
89 \r
90 : (rotate-logs) ( -- )\r
91     (close-logs)\r
92     log-root directory-files [ rotate-log ] each ;\r
93 \r
94 : log-server-loop ( -- )\r
95     receive unclip {\r
96         { "log-message" [ (log-message) ] }\r
97         { "rotate-logs" [ drop (rotate-logs) ] }\r
98         { "close-logs" [ drop (close-logs) ] }\r
99     } case log-server-loop ;\r
100 \r
101 : log-server ( -- )\r
102     [\r
103         init-namespaces\r
104         [ log-server-loop ]\r
105         [ error. (close-logs) ]\r
106         recover t\r
107     ]\r
108     "Log server" spawn-server\r
109     "log-server" set-global ;\r
110 \r
111 [\r
112     H{ } clone log-files set-global\r
113     log-server\r
114 ] "logging" add-startup-hook\r