-
Notifications
You must be signed in to change notification settings - Fork 2
/
Log.hs
171 lines (170 loc) · 5.19 KB
/
Log.hs
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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
-- | The main ideas of this log library are:
--
-- * we don't want to see all unnecessary trace messages when there are no errors,
--
-- * but we want to have all possible information about error.
--
-- This library is based on scopes. Every scope have a name, and logs traces only if there are some errors. Otherwise it logs only message with 'Info' level.
--
-- Let's start by simple example:
--
-- @
--test :: ReaderT Log IO ()
--test = scope \"test\" $ do
--log Trace \"Trace message\"
-- log Info \"Starting test\"
-- s \<- liftIO T.getLine
-- when (T.null s) $ log Error \"Oh no!\"
-- log Trace $ T.concat [\"Your input: \", s]
-- @
--
-- When you input some valid string, it will produce output:
--
-- @
--08\/10\/12 22:23:34 INFO test> Starting test
--abc
-- @
--
-- wihtout any traces
--
-- But if you input empty strings, you'll get:
--
-- @
--08\/10\/12 22:24:20 INFO test> Starting test
--08\/10\/12 22:24:20 TRACE test> Trace message
--08\/10\/12 22:24:21 ERROR test> Oh no!
--08\/10\/12 22:24:21 TRACE test> Your input:
-- @
--
-- Note, that first 'Trace' is written after 'Info', that's because logger don't know whether 'Trace' message will be written or not, but he must write 'Info' message immediately. But that's not a big problem.
--
-- There are three scope functions: 'scope_', 'scope' and 'scoper'. 'scope_' is basic function. 'scope' catches all exceptions and logs error with it, then rethrows. 'scoper' is like 'scope', but logs (with 'Trace' level) result of do-block.
--
-- Of course, scopes can be nested:
--
-- @
--test :: ReaderT Log IO ()
--test = scope \"test\" $ do
-- log Trace \"test trace\"
-- foo
-- log Info \"some info\"
-- bar
--
--foo :: ReaderT Log IO ()
--foo = scope \"foo\" $ do
-- log Trace \"foo trace\"
--
--bar :: ReaderT Log IO ()
--bar = scope \"bar\" $ do
-- log Trace \"bar trace\"
-- log Error \"bar error\"
-- @
--
-- Output:
--
-- @
--08\/10\/12 22:32:53 INFO test> some info
--08\/10\/12 22:32:53 TRACE test/bar> bar trace
--08\/10\/12 22:32:53 ERROR test/bar> bar error
-- @
--
-- Note, no messages for \"foo\" and no trace messages for \"test\", because error was in \"bar\", not in \"foo\".
--
-- Code to run log:
--
-- @
--rules :: Rules
--rules = []
--
--run :: IO ()
--run = do
-- l <- newLog (constant rules) [logger text console]
-- withLog l test
-- @
--
-- Politics sets 'low' and 'high' levels. By default, 'low' and 'high' are INFO and WARN. Levels below 'low' are "traces" ('Trace' and 'Debug' by default). Levels above 'high' are "errors" ('Warn', 'Error' and 'Fatal' by default).
--
-- If you set 'low' to 'Trace', all messages will be written. If you set 'low' to 'Debug' and 'high' to 'Fatal', "traces" (in this case only 'Trace') will be never written.
--
-- Sometimes we need to trace function, but we don't want to write all traces. We can get this by setting rules. Rules changes politics for specified scope-path (scope-path is list of nested scopes, for example [\"test\"], [\"test\", \"bar\"], [\"test\", \"bar\", \"baz\", \"quux\"] etc.)
--
-- For example, we want to trace function 'foo':
--
-- @
--rules = [
-- rule root $ use defaultPolitics,
-- rule (relative [\"foo\"]) $ low Trace]
-- @
--
-- From now all scope-paths, that contains \"foo\" (all scopes with name "foo") will have politics with 'low' set to Trace.
--
-- We may adjust politics for scope 'foo', that is nested directly in scope 'quux':
--
-- @
--rules = [
-- rule root $ use defaultPolitics,
-- relative [\"quux\", \"foo\"] $ low Trace]
-- @
--
-- And, of course, we may specify absolute path:
--
-- @
--rules = [
-- rule root $ use defaultPolitics,
-- absolute [\"bar\", \"baz\", \"foo\"] $ low Trace]
-- @
--
-- Politics will be changed only for scope \"foo\", which is nested directly in \"baz\", which is nested in \"bar\".
--
-- Another way to define rule is using special functions from "System.Log.Config" module:
--
-- @
--rules = [
-- \"\/\" %= use defaultPolitics,
-- \"\/bar\/baz\/foo\" %= low Trace,
-- \"quux\/foo\" %= low Debug]
-- @
--
-- One more way to use special syntax for rules:
--
-- @
--rules = parseRules_ $ T.unlines [
-- \"\/: use default\",
-- \"\/bar\/baz\/foo: low trace\",
-- \"quux\/foo: low debug\"]
-- @
--
-- Here \"\/\" is for root, \"\/path\" for absolute path, \"path\" for relative and \"path\/\" for child of \"path\" (which may be also prefixed with \"\/\" to be absolute)
--
-- This syntax is useful to config log by file. Having file \"log.cfg\":
--
-- @
--\/: use default
--\/bar\/baz\/foo: low trace
--quux\/foo: low debug
-- @
--
-- We can use it to config log
--
-- @
-- l <- newLog (fileCfg \"log.cfg\" 60) [logger text console]
-- @
--
-- where 60 is period (in seconds) of auto reload or 0 for no reloading.
--
module System.Log (
module System.Log.Base,
module System.Log.Config,
module System.Log.Monad,
module System.Log.Text,
module System.Log.HTML,
module System.Log.Console,
module System.Log.File
) where
import System.Log.Base hiding (entries, flatten, rules, writeLog, scopeLog_, scopeLog, scoperLog)
import System.Log.Config
import System.Log.Monad
import System.Log.Text
import System.Log.HTML
import System.Log.Console
import System.Log.File