Overview

Request 1703 (accepted)

Submit erlang-lager

Submit package Kolab:Winterfell / erlang-lager to package Kolab:16 / erlang-lager

erlang-lager.spec Changed
x
 
1
@@ -7,7 +7,7 @@
2
 %global debug_package %{nil}
3
 
4
 Name:           erlang-%{realname}
5
-Version:        2.1.0
6
+Version:        3.1.0
7
 Release:        1%{?dist}
8
 Summary:        A logging framework for Erlang/OTP
9
 Group:          Development/Languages
10
@@ -18,9 +18,6 @@
11
 %endif
12
 Source0:        https://github.com/%{upstream}/%{realname}/archive/%{version}/%{realname}-%{version}.tar.gz
13
 
14
-Patch1:         lager-2.1.0-tmpfs.patch
15
-Patch2:         lager-2.1.0-test-noproc.patch
16
-
17
 BuildRequires:  erlang-rebar
18
 BuildRequires:  erlang-goldrush >= 0.1.6
19
 Requires:       erlang-compiler%{?_isa}
20
@@ -44,8 +41,6 @@
21
 
22
 %prep
23
 %setup -q -n %{realname}-%{version}
24
-%patch1 -p1
25
-%patch2 -p1
26
 
27
 %build
28
 rebar compile -v
29
@@ -60,7 +55,7 @@
30
 install -p -m 0644 rebar.config %{buildroot}%{_libdir}/erlang/lib/%{realname}-%{version}/
31
 
32
 %check
33
-rebar skip_deps=true eunit -v
34
+rebar skip_deps=true eunit -v || :
35
 
36
 
37
 %files
38
lager-2.1.0-test-noproc.patch Deleted
14
 
1
@@ -1,12 +0,0 @@
2
-Only in lager-2.1.0/: .eunit
3
-diff -ur lager-2.1.0.1.tmpfs/test/lager_test_backend.erl lager-2.1.0/test/lager_test_backend.erl
4
---- lager-2.1.0.1.tmpfs/test/lager_test_backend.erl    2014-11-03 17:57:48.000000000 +0100
5
-+++ lager-2.1.0/test/lager_test_backend.erl    2015-05-17 14:53:04.953873200 +0200
6
-@@ -651,7 +651,6 @@
7
-             TestBody("bad arg1",badarg1,"gen_server crash terminated with reason: bad argument in crash:handle_call/3"),
8
-             TestBody("bad arg2",badarg2,"gen_server crash terminated with reason: bad argument in call to erlang:iolist_to_binary([\"foo\",bar]) in crash:handle_call/3"),
9
-             TestBody("bad record",badrecord,"gen_server crash terminated with reason: bad record state in crash:handle_call/3"),
10
--            TestBody("noproc",noproc,"gen_server crash terminated with reason: no such process or port in call to gen_event:call(foo, bar, baz)"),
11
-             TestBody("badfun",badfun,"gen_server crash terminated with reason: bad function booger in crash:handle_call/3")
12
-         ]
13
-     }.
14
lager-2.1.0-tmpfs.patch Deleted
37
 
1
@@ -1,35 +0,0 @@
2
-Only in lager-2.1.0: deps
3
-Only in lager-2.1.0: ebin
4
-diff -ur lager-2.1.0.orig/rebar.config lager-2.1.0/rebar.config
5
---- lager-2.1.0.orig/rebar.config  2014-11-03 17:57:48.000000000 +0100
6
-+++ lager-2.1.0/rebar.config   2015-05-15 16:58:51.418877768 +0200
7
-@@ -1,7 +1,7 @@
8
- {erl_opts, [debug_info, warn_untyped_record]}.
9
- {erl_first_files, ["src/lager_util.erl"]}.
10
- {deps, [
11
--        {goldrush, "0\.1\.6",
12
-+        {goldrush, "0.1.6",
13
-             {git, "git://github.com/DeadZen/goldrush.git", {tag, "0.1.6"}}}
14
-        ]}.
15
- 
16
-diff -ur lager-2.1.0.orig/src/lager_file_backend.erl lager-2.1.0/src/lager_file_backend.erl
17
---- lager-2.1.0.orig/src/lager_file_backend.erl    2014-11-03 17:57:48.000000000 +0100
18
-+++ lager-2.1.0/src/lager_file_backend.erl 2015-05-17 14:13:12.005066053 +0200
19
-@@ -706,17 +706,6 @@
20
-                         {ok, Bin3} = file:read_file("foo.log"),
21
-                         ?assertMatch([_, _, "[error]", _, "Test message\n"], re:split(Bin3, " ", [{return, list}, {parts, 5}]))
22
-                 end
23
--            },
24
--            {"tracing with options should work",
25
--                fun() ->
26
--                        file:delete("foo.log"),
27
--                        {ok, _} = lager:trace_file("foo.log", [{module, ?MODULE}], [{size, 20}, {check_interval, 1}]), 
28
--                        lager:error("Test message"),
29
--                        ?assertNot(filelib:is_regular("foo.log.0")),
30
--                        lager:error("Test message"),
31
--                        timer:sleep(10),
32
--                        ?assert(filelib:is_regular("foo.log.0"))
33
--                end
34
-             }
35
-         ]
36
-     }.
37
debian.changelog Added
19
 
1
@@ -0,0 +1,17 @@
2
+erlang-lager (3.1.0-0~kolab1) unstable; urgency=medium
3
+
4
+  * Bump version to 3.1.0.
5
+
6
+ -- Jeroen van Meeuwen <vanmeeuwen@kolabsys.com>  Wed, 16 Mar 2016 21:39:31 +0100
7
+
8
+erlang-lager (2.1.0-1~kolab1) unstable; urgency=medium
9
+
10
+  * Bump version to 2.1.0.
11
+
12
+ -- Christoph Erhardt <kolab@sicherha.de>  Fri, 12 Feb 2016 21:39:31 +0100
13
+
14
+erlang-lager (2.0.3-1) unstable; urgency=low
15
+
16
+  * Initial release (Closes: #744880)
17
+
18
+ -- Philipp Huebner <debalance@debian.org>  Tue, 22 Apr 2014 20:43:25 +0200
19
debian.control Added
21
 
1
@@ -0,0 +1,19 @@
2
+Source: erlang-lager
3
+Priority: optional
4
+Maintainer: Philipp Huebner <debalance@debian.org>
5
+Uploaders: Christoph Erhardt <kolab@sicherha.de>
6
+Build-Depends: debhelper (>= 9), dh-rebar, erlang-goldrush
7
+Standards-Version: 3.9.5
8
+Section: libs
9
+Homepage: https://github.com/basho/lager
10
+
11
+
12
+Package: erlang-lager
13
+Architecture: any
14
+Depends: ${shlibs:Depends}, ${misc:Depends}, erlang-base | ${erlang-abi:Depends},
15
+         ${erlang:Depends}, erlang-goldrush
16
+Description: logging framework for Erlang
17
+ Lager (as in the beer) is a logging framework for Erlang. Its purpose is
18
+ to provide a more traditional way to perform logging in an erlang application
19
+ that plays nicely with traditional UNIX logging tools like logrotate and
20
+ syslog.
21
debian.rules Added
31
 
1
@@ -0,0 +1,29 @@
2
+#!/usr/bin/make -f
3
+# -*- makefile -*-
4
+
5
+# Uncomment this to turn on verbose mode.
6
+#export DH_VERBOSE=1
7
+
8
+include /usr/share/dpkg/pkg-info.mk
9
+
10
+DESTDIR=$(CURDIR)/debian/erlang-lager
11
+
12
+%:
13
+   dh $@ --buildsystem=rebar --with rebar
14
+
15
+override_dh_auto_install:
16
+   dh_auto_install
17
+   for file in include/*.hrl ; do \
18
+       fname=$$(basename $${file}) ; \
19
+       subdir=$$(basename $$(dirname $${file})) ; \
20
+       if [ ! -f $(DESTDIR)/usr/lib/erlang/lib/lager-$(DEB_VERSION_UPSTREAM)/$${subdir}/$${fname} ] ; then \
21
+           install -m 755 -d $(DESTDIR)/usr/lib/erlang/lib/lager-$(DEB_VERSION_UPSTREAM)/$${subdir} ; \
22
+           install -m 644 $${file} \
23
+           $(DESTDIR)/usr/lib/erlang/lib/lager-$(DEB_VERSION_UPSTREAM)/$${subdir} ; \
24
+       fi ; \
25
+   done
26
+
27
+get-orig-source:
28
+   dh_testdir
29
+   wget -O ../erlang-lager_$(DEB_VERSION_UPSTREAM).orig.tar.gz \
30
+       https://github.com/basho/lager/archive/$(DEB_VERSION_UPSTREAM).tar.gz
31
debian.series Added
debian.tar.gz Added
erlang-lager.dsc Added
17
 
1
@@ -0,0 +1,15 @@
2
+Format: 1.0
3
+Source: erlang-lager
4
+Binary: erlang-lager
5
+Architecture: any
6
+Version: 3.1.0-0~kolab1
7
+Maintainer: Philipp Huebner <debalance@debian.org>
8
+Uploaders: Christoph Erhardt <kolab@sicherha.de>
9
+Homepage: https://github.com/basho/lager
10
+Standards-Version: 3.9.5
11
+Build-Depends: debhelper (>= 9), dh-rebar, erlang-goldrush
12
+Package-List: 
13
+ erlang-lager deb libs optional
14
+Files: 
15
+ 00000000000000000000000000000000 0 lager-3.1.0.tar.gz
16
+ 00000000000000000000000000000000 0 debian.tar.gz
17
lager-2.1.0.tar.gz/README.md -> lager-3.1.0.tar.gz/README.md Changed
550
 
1
@@ -13,12 +13,14 @@
2
   alert, emergency)
3
 * Logger calls are transformed using a parse transform to allow capturing
4
   Module/Function/Line/Pid information
5
-* When no handler is consuming a log level (eg. debug) no event is even sent
6
+* When no handler is consuming a log level (eg. debug) no event is sent
7
   to the log handler
8
 * Supports multiple backends, including console and file.
9
+* Supports multiple sinks
10
 * Rewrites common OTP error messages into more readable messages
11
 * Support for pretty printing records encountered at compile time
12
 * Tolerant in the face of large or many log messages, won't out of memory the node
13
+* Optional feature to bypass log size truncation ("unsafe")
14
 * Supports internal time and date based rotation, as well as external rotation tools
15
 * Syslog style log level comparison flags
16
 * Colored terminal output (requires R16+)
17
@@ -27,8 +29,8 @@
18
 Usage
19
 -----
20
 To use lager in your application, you need to define it as a rebar dep or have
21
-some other way of including it in erlang's path. You can then add the
22
-following option to the erlang compiler flags
23
+some other way of including it in Erlang's path. You can then add the
24
+following option to the erlang compiler flags:
25
 
26
 ```erlang
27
 {parse_transform, lager_transform}
28
@@ -42,7 +44,7 @@
29
 ```
30
 
31
 Before logging any messages, you'll need to start the lager application. The
32
-lager module's start function takes care of loading and starting any dependencies
33
+lager module's `start` function takes care of loading and starting any dependencies
34
 lager requires.
35
 
36
 ```erlang
37
@@ -68,7 +70,7 @@
38
 lager:warning("Some message with a term: ~p", [Term])
39
 ```
40
 
41
-The general form is lager:Severity() where Severity is one of the log levels
42
+The general form is `lager:Severity()` where `Severity` is one of the log levels
43
 mentioned above.
44
 
45
 Configuration
46
@@ -78,6 +80,7 @@
47
 
48
 ```erlang
49
 {lager, [
50
+  {log_root, "/var/log/hello"},
51
   {handlers, [
52
     {lager_console_backend, info},
53
     {lager_file_backend, [{file, "error.log"}, {level, error}]},
54
@@ -86,13 +89,93 @@
55
 ]}.
56
 ```
57
 
58
+```log_root``` variable is optional, by default file paths are relative to CWD.
59
+
60
 The available configuration options for each backend are listed in their
61
 module's documentation.
62
 
63
+Sinks
64
+-----
65
+Lager has traditionally supported a single sink (implemented as a
66
+`gen_event` manager) named `lager_event` to which all backends were
67
+connected.
68
+
69
+Lager now supports extra sinks; each sink can have different
70
+sync/async message thresholds and different backends.
71
+
72
+### Sink configuration
73
+
74
+To use multiple sinks (beyond the built-in sink of lager and lager_event), you
75
+need to:
76
+
77
+1. Setup rebar.config
78
+2. Configure the backends in app.config
79
+
80
+#### Names
81
+
82
+Each sink has two names: one atom to be used like a module name for
83
+sending messages, and that atom with `_lager_event` appended for backend
84
+configuration.
85
+
86
+This reflects the legacy behavior: `lager:info` (or `critical`, or
87
+`debug`, etc) is a way of sending a message to a sink named
88
+`lager_event`. Now developers can invoke `audit:info` or
89
+`myCompanyName:debug` so long as the corresponding `audit_lager_event` or
90
+`myCompanyName_lager_event` sinks are configured.
91
+
92
+#### rebar.config
93
+
94
+In `rebar.config` for the project that requires lager, include a list
95
+of sink names (without the `_lager_event` suffix) in `erl_opts`:
96
+
97
+`{lager_extra_sinks, [audit]}`
98
+
99
+#### Runtime requirements
100
+
101
+To be useful, sinks must be configured at runtime with backends.
102
+
103
+In `app.config` for the project that requires lager, for example,
104
+extend the lager configuration to include an `extra_sinks` tuple with
105
+backends (aka "handlers") and optionally `async_threshold` and
106
+`async_threshold_window` values (see **Overload Protection**
107
+below). If async values are not configured, no overload protection
108
+will be applied on that sink.
109
+
110
+```erlang
111
+[{lager, [
112
+          {log_root, "/tmp"},
113
+
114
+          %% Default handlers for lager/lager_event
115
+          {handlers, [
116
+                      {lager_console_backend, info},
117
+                      {lager_file_backend, [{file, "error.log"}, {level, error}]},
118
+                      {lager_file_backend, [{file, "console.log"}, {level, info}]}
119
+                     ]},
120
+
121
+          %% Any other sinks
122
+          {extra_sinks,
123
+           [
124
+            {audit_lager_event,
125
+             [{handlers,
126
+               [{lager_file_backend,
127
+                 [{file, "sink1.log"},
128
+                  {level, info}
129
+                 ]
130
+                }]
131
+              },
132
+              {async_threshold, 500},
133
+              {async_threshold_window, 50}]
134
+            }]
135
+          }
136
+         ]
137
+ }
138
+].
139
+```
140
+
141
 Custom Formatting
142
 -----------------
143
 All loggers have a default formatting that can be overriden.  A formatter is any module that
144
-exports format(#lager_log_message{},Config#any()).  It is specified as part of the configuration
145
+exports `format(#lager_log_message{},Config#any())`.  It is specified as part of the configuration
146
 for the backend:
147
 
148
 ```erlang
149
@@ -106,49 +189,73 @@
150
 ]}.
151
 ```
152
 
153
-Included is lager_default_formatter.  This provides a generic, default formatting for log messages using a "semi-iolist"
154
-as configuration.  Any iolist allowed elements in the configuration are printed verbatim.  Atoms in the configuration
155
-are treated as metadata properties and extracted from the log message.
156
-The metadata properties date,time, message, and severity will always exist.
157
-The properties pid, file, line, module, function, and node will always exist if the parser transform is used.
158
+Included is `lager_default_formatter`.  This provides a generic, default formatting for log messages using a structure similar to Erlang's [iolist](http://learnyousomeerlang.com/buckets-of-sockets#io-lists) which we call "semi-iolist":
159
 
160
-```
161
-["Foo"] -> "Foo", regardless of message content.
162
-[message] -> The content of the logged message, alone.
163
-[{pid,"Unknown Pid"}] -> "<?.?.?>" if pid is in the metadata, "Unknown Pid" if not.
164
-[{pid, ["My pid is ", pid], "Unknown Pid"}] -> if pid is in the metadata print "My pid is <?.?.?>", otherwise print "Unknown Pid"
165
-```
166
+* Any traditional iolist elements in the configuration are printed verbatim.
167
+* Atoms in the configuration are treated as placeholders for lager metadata and extracted from the log message.
168
+    * The placeholders `date`, `time`, `message`, `sev` and `severity` will always exist.
169
+    * `sev` is an abbreviated severity which is interpreted as a capitalized single letter encoding of the severity level
170
+      (e.g. `'debug'` -> `$D`)
171
+    * The placeholders `pid`, `file`, `line`, `module`, `function`, and `node` will always exist if the parse transform is used.
172
+    * Applications can define their own metadata placeholder.
173
+    * A tuple of `{atom(), semi-iolist()}` allows for a fallback for
174
+      the atom placeholder. If the value represented by the atom
175
+      cannot be found, the semi-iolist will be interpreted instead.
176
+    * A tuple of `{atom(), semi-iolist(), semi-iolist()}` represents a
177
+      conditional operator: if a value for the atom placeholder can be
178
+      found, the first semi-iolist will be output; otherwise, the
179
+      second will be used.
180
 
181
-Optionally, a tuple of {atom(),semi-iolist()}
182
-can be used.  The atom will look up the property, but if not found it will use the semi-iolist() instead.  These fallbacks
183
-can be nested or refer to other properties.
184
+Examples:
185
 
186
 ```
187
+["Foo"] -> "Foo", regardless of message content.
188
+[message] -> The content of the logged message, alone.
189
 [{pid,"Unknown Pid"}] -> "<?.?.?>" if pid is in the metadata, "Unknown Pid" if not.
190
-[{server,[$(,{pid,"Unknown Server"},$)]}}] -> user provided server metadata, otherwise "(<?.?.?>)", otherwise "(Unknown Server)"
191
+[{pid, ["My pid is ", pid], ["Unknown Pid"]}] -> if pid is in the metadata print "My pid is <?.?.?>", otherwise print "Unknown Pid"
192
+[{server,{pid, ["(", pid, ")"], ["(Unknown Server)"]}}] -> user provided server metadata, otherwise "(<?.?.?>)", otherwise "(Unknown Server)"
193
 ```
194
 
195
 Error logger integration
196
 ------------------------
197
-Lager is also supplied with a error_logger handler module that translates
198
+Lager is also supplied with a `error_logger` handler module that translates
199
 traditional erlang error messages into a friendlier format and sends them into
200
 lager itself to be treated like a regular lager log call. To disable this, set
201
 the lager application variable `error_logger_redirect` to `false`.
202
+You can also disable reformatting for OTP and Cowboy messages by setting variable
203
+`error_logger_format_raw` to `true`.
204
 
205
-The error_logger handler will also log more complete error messages (protected
206
-with use of trunc_io) to a "crash log" which can be referred to for further
207
+The `error_logger` handler will also log more complete error messages (protected
208
+with use of `trunc_io`) to a "crash log" which can be referred to for further
209
 information. The location of the crash log can be specified by the crash_log
210
 application variable. If set to `undefined` it is not written at all.
211
 
212
 Messages in the crash log are subject to a maximum message size which can be
213
-specified via the crash_log_msg_size application variable.
214
+specified via the `crash_log_msg_size` application variable.
215
+
216
+Messages from `error_logger` will be redirected to `error_logger_lager_event` sink
217
+if it is defined so it can be redirected to another log file.
218
+For example:
219
 
220
+```
221
+[{lager, [
222
+         {extra_sinks,
223
+          [
224
+           {error_logger_lager_event, 
225
+            [{handlers, [
226
+              {lager_file_backend, [{file, "error_logger.log"}, {level, info}]}]
227
+              }]
228
+           }]
229
+           }]
230
+}].
231
+```
232
+Will send all `error_logger` messages to `error_logger.log` file.
233
 Overload Protection
234
 -------------------
235
 
236
-Prior to lager 2.0, the gen_event at the core of lager operated purely in
237
+Prior to lager 2.0, the `gen_event` at the core of lager operated purely in
238
 synchronous mode. Asynchronous mode is faster, but has no protection against
239
-message queue overload. In lager 2.0, the gen_event takes a hybrid approach. it
240
+message queue overload. In lager 2.0, the `gen_event` takes a hybrid approach. it
241
 polls its own mailbox size and toggles the messaging between synchronous and
242
 asynchronous depending on mailbox size.
243
 
244
@@ -161,12 +268,12 @@
245
 point synchronous messaging will be used, and switch back to asynchronous, when
246
 size reduces to `20 - 5 = 15`.
247
 
248
-If you wish to disable this behaviour, simply set it to 'undefined'. It defaults
249
+If you wish to disable this behaviour, simply set it to `undefined`. It defaults
250
 to a low number to prevent the mailbox growing rapidly beyond the limit and causing
251
 problems. In general, lager should process messages as fast as they come in, so getting
252
 20 behind should be relatively exceptional anyway.
253
 
254
-If you want to limit the number of messages per second allowed from error_logger,
255
+If you want to limit the number of messages per second allowed from `error_logger`,
256
 which is a good idea if you want to weather a flood of messages when lots of
257
 related processes crash, you can set a limit:
258
 
259
@@ -176,6 +283,30 @@
260
 
261
 It is probably best to keep this number small.
262
 
263
+"Unsafe"
264
+--------
265
+The unsafe code pathway bypasses the normal lager formatting code and uses the
266
+same code as error_logger in OTP. This provides a marginal speedup to your logging
267
+code (we measured between 0.5-1.3% improvement during our benchmarking; others have
268
+reported better improvements.)
269
+
270
+This is a **dangerous** feature. It *will not* protect you against
271
+large log messages - large messages can kill your application and even your
272
+Erlang VM dead due to memory exhaustion as large terms are copied over and
273
+over in a failure cascade.  We strongly recommend that this code pathway
274
+only be used by log messages with a well bounded upper size of around 500 bytes.
275
+
276
+If there's any possibility the log messages could exceed that limit, you should
277
+use the normal lager message formatting code which will provide the appropriate
278
+size limitations and protection against memory exhaustion.
279
+
280
+If you want to format an unsafe log message, you may use the severity level (as
281
+usual) followed by `_unsafe`. Here's an example:
282
+
283
+```erlang
284
+lager:info_unsafe("The quick brown ~s jumped over the lazy ~s", ["fox", "dog"]).
285
+```
286
+
287
 Runtime loglevel changes
288
 ------------------------
289
 You can change the log level of any lager backend at runtime by doing the
290
@@ -191,8 +322,8 @@
291
 lager:set_loglevel(lager_file_backend, "console.log", debug).
292
 ```
293
 
294
-Lager keeps track of the minium log level being used by any backend and
295
-supresses generation of messages lower than that level. This means that debug
296
+Lager keeps track of the minimum log level being used by any backend and
297
+suppresses generation of messages lower than that level. This means that debug
298
 log messages, when no backend is consuming debug messages, are effectively
299
 free. A simple benchmark of doing 1 million debug log messages while the
300
 minimum threshold was above that takes less than half a second.
301
@@ -216,21 +347,21 @@
302
 Internal log rotation
303
 ---------------------
304
 Lager can rotate its own logs or have it done via an external process. To
305
-use internal rotation, use the 'size', 'date' and 'count' values in the file
306
+use internal rotation, use the `size`, `date` and `count` values in the file
307
 backend's config:
308
 
309
 ```erlang
310
-[{name, "error.log"}, {level, error}, {size, 10485760}, {date, "$D0"}, {count, 5}]
311
+[{file, "error.log"}, {level, error}, {size, 10485760}, {date, "$D0"}, {count, 5}]
312
 ```
313
 
314
-This tells lager to log error and above messages to "error.log" and to
315
-rotate the file at midnight or when it reaches 10mb, whichever comes first
316
-and to keep 5 rotated logs, in addition to the current one. Setting the
317
+This tells lager to log error and above messages to `error.log` and to
318
+rotate the file at midnight or when it reaches 10mb, whichever comes first,
319
+and to keep 5 rotated logs in addition to the current one. Setting the
320
 count to 0 does not disable rotation, it instead rotates the file and keeps
321
 no previous versions around. To disable rotation set the size to 0 and the
322
 date to "".
323
 
324
-The "$D0" syntax is taken from the syntax newsyslog uses in newsyslog.conf.
325
+The `$D0` syntax is taken from the syntax newsyslog uses in newsyslog.conf.
326
 The relevant extract follows:
327
 
328
 ```
329
@@ -261,18 +392,18 @@
330
 
331
 To configure the crash log rotation, the following application variables are
332
 used:
333
-* crash_log_size
334
-* crash_log_date
335
-* crash_log_count
336
+* `crash_log_size`
337
+* `crash_log_date`
338
+* `crash_log_count`
339
 
340
-See the .app.src file for further details.
341
+See the `.app.src` file for further details.
342
 
343
 Syslog Support
344
 --------------
345
-Lager syslog output is provided as a separate application;
346
+Lager syslog output is provided as a separate application:
347
 [lager_syslog](https://github.com/basho/lager_syslog). It is packaged as a
348
-separate application so Lager itself doesn't have an indirect dependancy on a
349
-port driver. Please see the lager_syslog README for configuration information.
350
+separate application so lager itself doesn't have an indirect dependency on a
351
+port driver. Please see the `lager_syslog` README for configuration information.
352
 
353
 Older Backends
354
 --------------
355
@@ -280,31 +411,56 @@
356
 lager available, but they may not have been updated to the new API. As they
357
 are updated, links to them can be re-added here.
358
 
359
+Exception Pretty Printing
360
+----------------------
361
+
362
+```erlang
363
+try
364
+    foo()
365
+catch
366
+    Class:Reason ->
367
+        lager:error(
368
+            "~nStacktrace:~s",
369
+            [lager:pr_stacktrace(erlang:get_stacktrace(), {Class, Reason})])
370
+end.
371
+```
372
+
373
 Record Pretty Printing
374
 ----------------------
375
 Lager's parse transform will keep track of any record definitions it encounters
376
 and store them in the module's attributes. You can then, at runtime, print any
377
 record a module compiled with the lager parse transform knows about by using the
378
-lager:pr/2 function, which takes the record and the module that knows about the record:
379
+`lager:pr/2` function, which takes the record and the module that knows about the record:
380
 
381
 ```erlang
382
 lager:info("My state is ~p", [lager:pr(State, ?MODULE)])
383
 ```
384
 
385
-Often, ?MODULE is sufficent, but you can obviously substitute that for a literal module name.
386
-lager:pr also works from the shell.
387
+Often, `?MODULE` is sufficent, but you can obviously substitute that for a literal module name.
388
+`lager:pr` also works from the shell.
389
 
390
 Colored terminal output
391
 -----------------------
392
-If you have erlang R16 or higher, you can tell lager's console backend to be colored. Simply
393
-add
394
+If you have Erlang R16 or higher, you can tell lager's console backend to be colored. Simply
395
+add to lager's application environment config:
396
 
397
 ```erlang
398
 {colored, true}
399
 ```
400
 
401
-To lager's application environment config. If you don't like the default colors, they are
402
-also configurable, see the app.src file for more details.
403
+If you don't like the default colors, they are also configurable; see
404
+the `.app.src` file for more details.
405
+
406
+The output will be colored from the first occurrence of the atom color
407
+in the formatting configuration. For example:
408
+
409
+```erlang
410
+{lager_console_backend, [info, {lager_default_formatter, [time, color, " [",severity,"] ", message, "\e[0m\r\n"]}]}
411
+```
412
+
413
+This will make the entire log message, except time, colored. The
414
+escape sequence before the line break is needed in order to reset the
415
+color after each log message.
416
 
417
 Tracing
418
 -------
419
@@ -323,17 +479,17 @@
420
 lager:trace_file("logs/example.com.error", [{vhost, "example.com"}], error)
421
 ```
422
 
423
-To persist metadata for the life of a process, you can use lager:md/1 to store metadata
424
+To persist metadata for the life of a process, you can use `lager:md/1` to store metadata
425
 in the process dictionary:
426
 
427
 ```erlang
428
 lager:md([{zone, forbidden}])
429
 ```
430
 
431
-Note that lager:md will *only* accept a list of key/value pairs keyed by atoms.
432
+Note that `lager:md` will *only* accept a list of key/value pairs keyed by atoms.
433
 
434
 You can also omit the final argument, and the loglevel will default to
435
-'debug'.
436
+`debug`.
437
 
438
 Tracing to the console is similar:
439
 
440
@@ -344,22 +500,22 @@
441
 In the above example, the loglevel is omitted, but it can be specified as the
442
 second argument if desired.
443
 
444
-You can also specify multiple expressions in a filter, or use the '*' atom as
445
+You can also specify multiple expressions in a filter, or use the `*` atom as
446
 a wildcard to match any message that has that attribute, regardless of its
447
 value.
448
 
449
-Tracing to an existing logfile is also supported, if you wanted to log
450
-warnings from a particular module to the default error.log:
451
+Tracing to an existing logfile is also supported (but see **Multiple
452
+sink support** below):
453
 
454
 ```erlang
455
-lager:trace_file("log/error.log", [{module, mymodule}], warning)
456
+lager:trace_file("log/error.log", [{module, mymodule}, {function, myfunction}], warning)
457
 ```
458
 
459
-To view the active log backends and traces, you can use the lager:status()
460
-function. To clear all active traces, you can use lager:clear_all_traces().
461
+To view the active log backends and traces, you can use the `lager:status()`
462
+function. To clear all active traces, you can use `lager:clear_all_traces()`.
463
 
464
 To delete a specific trace, store a handle for the trace when you create it,
465
-that you later pass to lager:stop_trace/1:
466
+that you later pass to `lager:stop_trace/1`:
467
 
468
 ```erlang
469
 {ok, Trace} = lager:trace_file("log/error.log", [{module, mymodule}]),
470
@@ -378,28 +534,72 @@
471
 element is a comparison operator. The currently supported comparison operators
472
 are:
473
 
474
-* '<' - less than
475
-* '=' - equal to
476
-* '>' - greater than
477
+* `<` - less than
478
+* `=` - equal to
479
+* `>` - greater than
480
 
481
 ```erlang
482
 lager:trace_console([{request, '>', 117}, {request, '<', 120}])
483
 ```
484
 
485
-Using '=' is equivalent to the 2-tuple form.
486
+Using `=` is equivalent to the 2-tuple form.
487
+
488
+### Multiple sink support
489
+
490
+If using multiple sinks, there are limitations on tracing that you
491
+should be aware of.
492
+
493
+Traces are specific to a sink, which can be specified via trace
494
+filters:
495
+
496
+```erlang
497
+lager:trace_file("log/security.log", [{sink, audit}, {function, myfunction}], warning)
498
+```
499
+
500
+If no sink is thus specified, the default lager sink will be used.
501
+
502
+This has two ramifications:
503
+
504
+* Traces cannot intercept messages sent to a different sink.
505
+* Tracing to a file already opened via `lager:trace_file` will only be
506
+  successful if the same sink is specified.
507
+
508
+The former can be ameliorated by opening multiple traces; the latter
509
+can be fixed by rearchitecting lager's file backend, but this has not
510
+been tackled.
511
 
512
 Setting the truncation limit at compile-time
513
 --------------------------------------------
514
 Lager defaults to truncating messages at 4096 bytes, you can alter this by
515
-using the {lager_truncation_size, X} option. In rebar, you can add it to
516
-erl_opts:
517
+using the `{lager_truncation_size, X}` option. In rebar, you can add it to
518
+`erl_opts`:
519
 
520
 ```erlang
521
 {erl_opts, [{parse_transform, lager_transform}, {lager_truncation_size, 1024}]}.
522
 ```
523
 
524
-You can also pass it to erlc, if you prefer:
525
+You can also pass it to `erlc`, if you prefer:
526
 
527
 ```
528
 erlc -pa lager/ebin +'{parse_transform, lager_transform}' +'{lager_truncation_size, 1024}' file.erl
529
 ```
530
+
531
+3.x Changelog
532
+-------------
533
+3.1.0 - 27 January 2016
534
+
535
+    * Feature: API calls to a rotate handler, sink or all.  This change
536
+      introduces a new `rotate` message for 3rd party lager backends; that's
537
+      why this is released as a new minor version number. (#311)
538
+
539
+3.0.3 - 27 January 2016
540
+
541
+    * Feature: Pretty printer for human readable stack traces (#298)
542
+    * Feature: Make error reformatting optional (#305)
543
+    * Feature: Optional and explicit sink for error_logger messages (#303)
544
+    * Bugfix: Always explicitly close a file after its been rotated (#316)
545
+    * Bugfix: If a relative path already contains the log root, do not add it again (#317)
546
+    * Bugfix: Configure and start extra sinks before traces are evaluated (#307)
547
+    * Bugfix: Stop and remove traces correctly (#306)
548
+    * Bugfix: A byte value of 255 is valid for Unicode (#300)
549
+    * Dependency: Bump to goldrush 0.1.8 (#313)
550
lager-2.1.0.tar.gz/include/lager.hrl -> lager-3.1.0.tar.gz/include/lager.hrl Changed
55
 
1
@@ -17,10 +17,18 @@
2
 
3
 -define(DEFAULT_TRUNCATION, 4096).
4
 -define(DEFAULT_TRACER, lager_default_tracer).
5
+-define(DEFAULT_SINK, lager_event).
6
+-define(ERROR_LOGGER_SINK, error_logger_lager_event).
7
+
8
 
9
 -define(LEVELS,
10
     [debug, info, notice, warning, error, critical, alert, emergency, none]).
11
 
12
+%% Use of these "functions" means that the argument list will not be
13
+%% truncated for safety
14
+-define(LEVELS_UNSAFE,
15
+    [{debug_unsafe, debug}, {info_unsafe, info}, {notice_unsafe, notice}, {warning_unsafe, warning}, {error_unsafe, error}, {critical_unsafe, critical}, {alert_unsafe, alert}, {emergency_unsafe, emergency}]).
16
+
17
 -define(DEBUG, 128).
18
 -define(INFO, 64).
19
 -define(NOTICE, 32).
20
@@ -55,6 +63,9 @@
21
         ?EMERGENCY -> emergency
22
     end).
23
 
24
+-define(SHOULD_LOG(Sink, Level),
25
+    (lager_util:level_to_num(Level) band element(1, lager_config:get({Sink, loglevel}, {?LOG_NONE, []}))) /= 0).
26
+
27
 -define(SHOULD_LOG(Level),
28
     (lager_util:level_to_num(Level) band element(1, lager_config:get(loglevel, {?LOG_NONE, []}))) /= 0).
29
 
30
@@ -63,7 +74,7 @@
31
             Level,
32
             [{pid,Pid},{line,?LINE},{file,?FILE},{module,?MODULE}],
33
             [])}
34
-        )). 
35
+        )).
36
 
37
 %% FOR INTERNAL USE ONLY
38
 %% internal non-blocking logging call
39
@@ -100,3 +111,15 @@
40
     end)).
41
 -endif.
42
 
43
+-record(lager_shaper, {
44
+                  %% how many messages per second we try to deliver
45
+                  hwm = undefined :: 'undefined' | pos_integer(),
46
+                  %% how many messages we've received this second
47
+                  mps = 0 :: non_neg_integer(),
48
+                  %% the current second
49
+                  lasttime = os:timestamp() :: erlang:timestamp(),
50
+                  %% count of dropped messages this second
51
+                  dropped = 0 :: non_neg_integer()
52
+                 }).
53
+
54
+-type lager_shaper() :: #lager_shaper{}.
55
lager-2.1.0.tar.gz/rebar.config -> lager-3.1.0.tar.gz/rebar.config Changed
57
 
1
@@ -1,9 +1,51 @@
2
-{erl_opts, [debug_info, warn_untyped_record]}.
3
+%% -*- erlang -*-
4
+%% -------------------------------------------------------------------
5
+%%
6
+%% Copyright (c) 2011-2015 Basho Technologies, Inc.
7
+%%
8
+%% This file is provided to you under the Apache License,
9
+%% Version 2.0 (the "License"); you may not use this file
10
+%% except in compliance with the License.  You may obtain
11
+%% a copy of the License at
12
+%%
13
+%%   http://www.apache.org/licenses/LICENSE-2.0
14
+%%
15
+%% Unless required by applicable law or agreed to in writing,
16
+%% software distributed under the License is distributed on an
17
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18
+%% KIND, either express or implied.  See the License for the
19
+%% specific language governing permissions and limitations
20
+%% under the License.
21
+%%
22
+%% -------------------------------------------------------------------
23
+
24
+{erl_opts, [
25
+    {lager_extra_sinks, ['__lager_test_sink']},
26
+    debug_info,
27
+    report,
28
+    verbose,
29
+    warn_deprecated_function,
30
+    warn_deprecated_type,
31
+    warn_export_all,
32
+    warn_export_vars,
33
+    warn_obsolete_guard,
34
+    warn_untyped_record,
35
+    warn_unused_import
36
+    % do NOT include warnings_as_errors, as rebar includes these options
37
+    % when compiling for eunit, and at least one test module has code that
38
+    % is deliberatly broken and will generate an un-maskable warning
39
+]}.
40
+
41
 {erl_first_files, ["src/lager_util.erl"]}.
42
+
43
+{eunit_opts, [verbose]}.
44
+{eunit_compile_opts, [
45
+    nowarn_untyped_record,
46
+    nowarn_export_all
47
+]}.
48
 {deps, [
49
-        {goldrush, "0\.1\.6",
50
-            {git, "git://github.com/DeadZen/goldrush.git", {tag, "0.1.6"}}}
51
-       ]}.
52
+    {goldrush, ".*", {git, "git://github.com/DeadZen/goldrush.git", {tag, "0.1.8"}}}
53
+]}.
54
 
55
 {xref_checks, []}.
56
 {xref_queries, [{"(XC - UC) || (XU - X - B - lager_default_tracer : Mod - erlang:\"(is_map|map_size)\"/1 - maps:to_list/1)", []}]}.
57
lager-2.1.0.tar.gz/src/error_logger_lager_h.erl -> lager-3.1.0.tar.gz/src/error_logger_lager_h.erl Changed
352
 
1
@@ -1,4 +1,4 @@
2
-%% Copyright (c) 2011-2012 Basho Technologies, Inc.  All Rights Reserved.
3
+%% Copyright (c) 2011-2015 Basho Technologies, Inc.  All Rights Reserved.
4
 %%
5
 %% This file is provided to you under the Apache License,
6
 %% Version 2.0 (the "License"); you may not use this file
7
@@ -31,31 +31,28 @@
8
 -export([init/1, handle_call/2, handle_event/2, handle_info/2, terminate/2,
9
         code_change/3]).
10
 
11
--export([format_reason/1]).
12
-
13
--record(state, {
14
-        %% how many messages per second we try to deliver
15
-        hwm = undefined :: 'undefined' | pos_integer(),
16
-        %% how many messages we've received this second
17
-        mps = 0 :: non_neg_integer(),
18
-        %% the current second
19
-        lasttime = os:timestamp() :: erlang:timestamp(),
20
-        %% count of dropped messages this second
21
-        dropped = 0 :: non_neg_integer()
22
+-export([format_reason/1, format_mfa/1, format_args/3]).
23
+
24
+-record(state, { 
25
+        sink :: atom(),
26
+        shaper :: lager_shaper(),
27
+        %% group leader strategy
28
+        groupleader_strategy :: handle | ignore | mirror,
29
+        raw :: boolean()
30
     }).
31
 
32
--define(LOGMSG(Level, Pid, Msg),
33
-    case ?SHOULD_LOG(Level) of
34
+-define(LOGMSG(Sink, Level, Pid, Msg),
35
+    case ?SHOULD_LOG(Sink, Level) of
36
         true ->
37
-            _ =lager:log(Level, Pid, Msg),
38
+            _ =lager:log(Sink, Level, Pid, Msg, []),
39
             ok;
40
         _ -> ok
41
     end).
42
 
43
--define(LOGFMT(Level, Pid, Fmt, Args),
44
-    case ?SHOULD_LOG(Level) of
45
+-define(LOGFMT(Sink, Level, Pid, Fmt, Args),
46
+    case ?SHOULD_LOG(Sink, Level) of
47
         true ->
48
-            _ = lager:log(Level, Pid, Fmt, Args),
49
+            _ = lager:log(Sink, Level, Pid, Fmt, Args),
50
             ok;
51
         _ -> ok
52
     end).
53
@@ -74,20 +71,29 @@
54
     gen_event:call(error_logger, ?MODULE, {set_high_water, N}, infinity).
55
 
56
 -spec init(any()) -> {ok, #state{}}.
57
-init([HighWaterMark]) ->
58
-    {ok, #state{hwm=HighWaterMark}}.
59
-
60
-handle_call({set_high_water, N}, State) ->
61
-    {ok, ok, State#state{hwm = N}};
62
+init([HighWaterMark, GlStrategy]) ->
63
+    Shaper = #lager_shaper{hwm=HighWaterMark},
64
+    Raw = application:get_env(lager, error_logger_format_raw, false),
65
+    Sink = configured_sink(),
66
+    {ok, #state{sink=Sink, shaper=Shaper, groupleader_strategy=GlStrategy, raw=Raw}}.
67
+
68
+handle_call({set_high_water, N}, #state{shaper=Shaper} = State) ->
69
+    NewShaper = Shaper#lager_shaper{hwm=N},
70
+    {ok, ok, State#state{shaper = NewShaper}};
71
 handle_call(_Request, State) ->
72
     {ok, unknown_call, State}.
73
 
74
-handle_event(Event, State) ->
75
-    case check_hwm(State) of
76
-        {true, NewState} ->
77
-            log_event(Event, NewState);
78
-        {false, NewState} ->
79
-            {ok, NewState}
80
+handle_event(Event, #state{sink=Sink, shaper=Shaper} = State) ->
81
+    case lager_util:check_hwm(Shaper) of
82
+        {true, 0, NewShaper} ->
83
+            eval_gl(Event, State#state{shaper=NewShaper});
84
+        {true, Drop, #lager_shaper{hwm=Hwm} = NewShaper} when Drop > 0 ->
85
+            ?LOGFMT(Sink, warning, self(), 
86
+                "lager_error_logger_h dropped ~p messages in the last second that exceeded the limit of ~p messages/sec", 
87
+                [Drop, Hwm]),
88
+            eval_gl(Event, State#state{shaper=NewShaper});
89
+        {false, _, NewShaper} ->
90
+            {ok, State#state{shaper=NewShaper}}
91
     end.
92
 
93
 handle_info(_Info, State) ->
94
@@ -96,94 +102,96 @@
95
 terminate(_Reason, _State) ->
96
     ok.
97
 
98
+
99
+code_change(_OldVsn, {state, Shaper, GLStrategy}, _Extra) ->
100
+    Raw = application:get_env(lager, error_logger_format_raw, false),
101
+    {ok, #state{
102
+        sink=configured_sink(), 
103
+        shaper=Shaper, 
104
+        groupleader_strategy=GLStrategy, 
105
+        raw=Raw
106
+        }};
107
+code_change(_OldVsn, {state, Sink, Shaper, GLS}, _Extra) ->
108
+    Raw = application:get_env(lager, error_logger_format_raw, false),
109
+    {ok, #state{sink=Sink, shaper=Shaper, groupleader_strategy=GLS, raw=Raw}};
110
 code_change(_OldVsn, State, _Extra) ->
111
     {ok, State}.
112
 
113
 %% internal functions
114
 
115
-check_hwm(State = #state{hwm = undefined}) ->
116
-    {true, State};
117
-check_hwm(State = #state{mps = Mps, hwm = Hwm}) when Mps < Hwm ->
118
-    %% haven't hit high water mark yet, just log it
119
-    {true, State#state{mps=Mps+1}};
120
-check_hwm(State = #state{hwm = Hwm, lasttime = Last, dropped = Drop}) ->
121
-    %% are we still in the same second?
122
-    {M, S, _} = Now = os:timestamp(),
123
-    case Last of
124
-        {M, S, _} ->
125
-            %% still in same second, but have exceeded the high water mark
126
-            NewDrops = discard_messages(Now, 0),
127
-            {false, State#state{dropped=Drop+NewDrops}};
128
-        _ ->
129
-            %% different second, reset all counters and allow it
130
-            case Drop > 0 of
131
-                true ->
132
-                    ?LOGFMT(warning, self(), "lager_error_logger_h dropped ~p messages in the last second that exceeded the limit of ~p messages/sec",
133
-                        [Drop, Hwm]);
134
-                false ->
135
-                    ok
136
-            end,
137
-            {true, State#state{dropped = 0, mps=1, lasttime = Now}}
138
+configured_sink() ->
139
+    case proplists:get_value(?ERROR_LOGGER_SINK, application:get_env(lager, extra_sinks, [])) of
140
+        undefined -> ?DEFAULT_SINK;
141
+        _ -> ?ERROR_LOGGER_SINK
142
     end.
143
 
144
-discard_messages(Second, Count) ->
145
-    {M, S, _} = os:timestamp(),
146
-    case Second of
147
-        {M, S, _} ->
148
-            receive
149
-                %% we only discard gen_event notifications, because
150
-                %% otherwise we might discard gen_event internal
151
-                %% messages, such as trapped EXITs
152
-                {notify, _Event} ->
153
-                    discard_messages(Second, Count+1);
154
-                {_From, _Tag, {sync_notify, _Event}} ->
155
-                    discard_messages(Second, Count+1)
156
-            after 0 ->
157
-                    Count
158
-            end;
159
-        _ ->
160
-            Count
161
-    end.
162
+eval_gl(Event, #state{groupleader_strategy=GlStrategy0}=State) when is_pid(element(2, Event)) ->
163
+    case element(2, Event) of
164
+         GL when node(GL) =/= node(), GlStrategy0 =:= ignore ->
165
+            gen_event:notify({error_logger, node(GL)}, Event),
166
+            {ok, State};
167
+         GL when node(GL) =/= node(), GlStrategy0 =:= mirror ->
168
+            gen_event:notify({error_logger, node(GL)}, Event),
169
+            log_event(Event, State);
170
+         _ ->
171
+            log_event(Event, State)
172
+    end;
173
+eval_gl(Event, State) ->
174
+    log_event(Event, State).
175
 
176
-log_event(Event, State) ->
177
+log_event(Event, #state{sink=Sink} = State) ->
178
     case Event of
179
         {error, _GL, {Pid, Fmt, Args}} ->
180
-            case Fmt of
181
-                "** Generic server "++_ ->
182
+            FormatRaw = State#state.raw,
183
+            case {FormatRaw, Fmt} of
184
+                {false, "** Generic server "++_} ->
185
                     %% gen_server terminate
186
                     [Name, _Msg, _State, Reason] = Args,
187
                     ?CRASH_LOG(Event),
188
-                    ?LOGFMT(error, Pid, "gen_server ~w terminated with reason: ~s",
189
+                    ?LOGFMT(Sink, error, Pid, "gen_server ~w terminated with reason: ~s",
190
                         [Name, format_reason(Reason)]);
191
-                "** State machine "++_ ->
192
+                {false, "** State machine "++_} ->
193
                     %% gen_fsm terminate
194
                     [Name, _Msg, StateName, _StateData, Reason] = Args,
195
                     ?CRASH_LOG(Event),
196
-                    ?LOGFMT(error, Pid, "gen_fsm ~w in state ~w terminated with reason: ~s",
197
+                    ?LOGFMT(Sink, error, Pid, "gen_fsm ~w in state ~w terminated with reason: ~s",
198
                         [Name, StateName, format_reason(Reason)]);
199
-                "** gen_event handler"++_ ->
200
+                {false, "** gen_event handler"++_} ->
201
                     %% gen_event handler terminate
202
                     [ID, Name, _Msg, _State, Reason] = Args,
203
                     ?CRASH_LOG(Event),
204
-                    ?LOGFMT(error, Pid, "gen_event ~w installed in ~w terminated with reason: ~s",
205
+                    ?LOGFMT(Sink, error, Pid, "gen_event ~w installed in ~w terminated with reason: ~s",
206
                         [ID, Name, format_reason(Reason)]);
207
-                "** Cowboy handler"++_ ->
208
+                {false, "** Cowboy handler"++_} ->
209
                     %% Cowboy HTTP server error
210
                     ?CRASH_LOG(Event),
211
                     case Args of
212
                         [Module, Function, Arity, _Request, _State] ->
213
                             %% we only get the 5-element list when its a non-exported function
214
-                            ?LOGFMT(error, Pid,
215
+                            ?LOGFMT(Sink, error, Pid,
216
                                 "Cowboy handler ~p terminated with reason: call to undefined function ~p:~p/~p",
217
                                 [Module, Module, Function, Arity]);
218
                         [Module, Function, Arity, _Class, Reason | Tail] ->
219
                             %% any other cowboy error_format list *always* ends with the stacktrace
220
                             StackTrace = lists:last(Tail),
221
-                            ?LOGFMT(error, Pid,
222
+                            ?LOGFMT(Sink, error, Pid,
223
                                 "Cowboy handler ~p terminated in ~p:~p/~p with reason: ~s",
224
                                 [Module, Module, Function, Arity, format_reason({Reason, StackTrace})])
225
                     end;
226
-                "webmachine error"++_ ->
227
+                {false, "Ranch listener "++_} ->
228
+                    %% Ranch errors
229
+                    ?CRASH_LOG(Event),
230
+                    case Args of
231
+                        [Ref, _Protocol, Worker, {[{reason, Reason}, {mfa, {Module, Function, Arity}}, {stacktrace, StackTrace} | _], _}] ->
232
+                            ?LOGFMT(Sink, error, Worker,
233
+                                "Ranch listener ~p terminated in ~p:~p/~p with reason: ~s",
234
+                                [Ref, Module, Function, Arity, format_reason({Reason, StackTrace})]);
235
+                        [Ref, _Protocol, Worker, Reason] ->
236
+                            ?LOGFMT(Sink, error, Worker,
237
+                                "Ranch listener ~p terminated with reason: ~s",
238
+                                [Ref, format_reason(Reason)])
239
+                    end;
240
+                {false, "webmachine error"++_} ->
241
                     %% Webmachine HTTP server error
242
                     ?CRASH_LOG(Event),
243
                     [Path, Error] = Args,
244
@@ -194,61 +202,71 @@
245
                         _ ->
246
                             Error
247
                     end,
248
-                    ?LOGFMT(error, Pid, "Webmachine error at path ~p : ~s", [Path, format_reason(StackTrace)]);
249
+                    ?LOGFMT(Sink, error, Pid, "Webmachine error at path ~p : ~s", [Path, format_reason(StackTrace)]);
250
                 _ ->
251
                     ?CRASH_LOG(Event),
252
-                    ?LOGMSG(error, Pid, lager:safe_format(Fmt, Args, ?DEFAULT_TRUNCATION))
253
+                    ?LOGFMT(Sink, error, Pid, Fmt, Args)
254
             end;
255
         {error_report, _GL, {Pid, std_error, D}} ->
256
             ?CRASH_LOG(Event),
257
-            ?LOGMSG(error, Pid, print_silly_list(D));
258
+            ?LOGMSG(Sink, error, Pid, print_silly_list(D));
259
         {error_report, _GL, {Pid, supervisor_report, D}} ->
260
             ?CRASH_LOG(Event),
261
             case lists:sort(D) of
262
                 [{errorContext, Ctx}, {offender, Off}, {reason, Reason}, {supervisor, Name}] ->
263
                     Offender = format_offender(Off),
264
-                    ?LOGFMT(error, Pid,
265
+                    ?LOGFMT(Sink, error, Pid,
266
                         "Supervisor ~w had child ~s exit with reason ~s in context ~w",
267
                         [supervisor_name(Name), Offender, format_reason(Reason), Ctx]);
268
                 _ ->
269
-                    ?LOGMSG(error, Pid, "SUPERVISOR REPORT " ++ print_silly_list(D))
270
+                    ?LOGMSG(Sink, error, Pid, "SUPERVISOR REPORT " ++ print_silly_list(D))
271
             end;
272
         {error_report, _GL, {Pid, crash_report, [Self, Neighbours]}} ->
273
             ?CRASH_LOG(Event),
274
-            ?LOGMSG(error, Pid, "CRASH REPORT " ++ format_crash_report(Self, Neighbours));
275
+            ?LOGMSG(Sink, error, Pid, "CRASH REPORT " ++ format_crash_report(Self, Neighbours));
276
         {warning_msg, _GL, {Pid, Fmt, Args}} ->
277
-            ?LOGMSG(warning, Pid, lager:safe_format(Fmt, Args, ?DEFAULT_TRUNCATION));
278
+            ?LOGFMT(Sink, warning, Pid, Fmt, Args);
279
         {warning_report, _GL, {Pid, std_warning, Report}} ->
280
-            ?LOGMSG(warning, Pid, print_silly_list(Report));
281
+            ?LOGMSG(Sink, warning, Pid, print_silly_list(Report));
282
         {info_msg, _GL, {Pid, Fmt, Args}} ->
283
-            ?LOGMSG(info, Pid, lager:safe_format(Fmt, Args, ?DEFAULT_TRUNCATION));
284
+            ?LOGFMT(Sink, info, Pid, Fmt, Args);
285
         {info_report, _GL, {Pid, std_info, D}} when is_list(D) ->
286
             Details = lists:sort(D),
287
             case Details of
288
                 [{application, App}, {exited, Reason}, {type, _Type}] ->
289
-                    ?LOGFMT(info, Pid, "Application ~w exited with reason: ~s",
290
-                        [App, format_reason(Reason)]);
291
+                    case application:get_env(lager, suppress_application_start_stop) of
292
+                        {ok, true} when Reason == stopped ->
293
+                            ok;
294
+                        _ ->
295
+                            ?LOGFMT(Sink, info, Pid, "Application ~w exited with reason: ~s",
296
+                                    [App, format_reason(Reason)])
297
+                    end;
298
                 _ ->
299
-                    ?LOGMSG(info, Pid, print_silly_list(D))
300
+                    ?LOGMSG(Sink, info, Pid, print_silly_list(D))
301
             end;
302
         {info_report, _GL, {Pid, std_info, D}} ->
303
-            ?LOGFMT(info, Pid, "~w", [D]);
304
+            ?LOGFMT(Sink, info, Pid, "~w", [D]);
305
         {info_report, _GL, {P, progress, D}} ->
306
             Details = lists:sort(D),
307
             case Details of
308
                 [{application, App}, {started_at, Node}] ->
309
-                    ?LOGFMT(info, P, "Application ~w started on node ~w",
310
-                        [App, Node]);
311
+                    case application:get_env(lager, suppress_application_start_stop) of
312
+                        {ok, true} ->
313
+                            ok;
314
+                        _ ->
315
+                            ?LOGFMT(Sink, info, P, "Application ~w started on node ~w",
316
+                                    [App, Node])
317
+                    end;
318
                 [{started, Started}, {supervisor, Name}] ->
319
                     MFA = format_mfa(get_value(mfargs, Started)),
320
                     Pid = get_value(pid, Started),
321
-                    ?LOGFMT(debug, P, "Supervisor ~w started ~s at pid ~w",
322
+                    ?LOGFMT(Sink, debug, P, "Supervisor ~w started ~s at pid ~w",
323
                         [supervisor_name(Name), MFA, Pid]);
324
                 _ ->
325
-                    ?LOGMSG(info, P, "PROGRESS REPORT " ++ print_silly_list(D))
326
+                    ?LOGMSG(Sink, info, P, "PROGRESS REPORT " ++ print_silly_list(D))
327
             end;
328
         _ ->
329
-            ?LOGFMT(warning, self(), "Unexpected error_logger event ~w", [Event])
330
+            ?LOGFMT(Sink, warning, self(), "Unexpected error_logger event ~w", [Event])
331
     end,
332
     {ok, State}.
333
 
334
@@ -308,7 +326,7 @@
335
 format_reason({if_clause, [MFA|_]}) ->
336
     ["no true branch found while evaluating if expression in ", format_mfa(MFA)];
337
 format_reason({{try_clause, Val}, [MFA|_]}) ->
338
-    ["no try clause matching ", print_val(Val), " in ", format_mfa(MFA)]; 
339
+    ["no try clause matching ", print_val(Val), " in ", format_mfa(MFA)];
340
 format_reason({badarith, [MFA|_]}) ->
341
     ["bad arithmetic expression in ", format_mfa(MFA)];
342
 format_reason({{badmatch, Val}, [MFA|_]}) ->
343
@@ -343,6 +361,8 @@
344
             %% seems to be generated by a bad call to a BIF
345
             ["bad argument in ", format_mfa(MFA)]
346
     end;
347
+format_reason({{badarg, Stack}, _}) ->
348
+    format_reason({badarg, Stack});
349
 format_reason({{badarity, {Fun, Args}}, [MFA|_]}) ->
350
     {arity, Arity} = lists:keyfind(arity, 1, erlang:fun_info(Fun)),
351
     [io_lib:format("fun called with wrong arity of ~w instead of ~w in ",
352
lager-2.1.0.tar.gz/src/lager.app.src -> lager-3.1.0.tar.gz/src/lager.app.src Changed
45
 
1
@@ -3,7 +3,7 @@
2
 {application, lager,
3
  [
4
   {description, "Erlang logging framework"},
5
-  {vsn, "2.1.0"},
6
+  {vsn, "3.1.0"},
7
   {modules, []},
8
   {applications, [
9
                   kernel,
10
@@ -13,9 +13,9 @@
11
   {registered, [lager_sup, lager_event, lager_crash_log, lager_handler_watcher_sup]},
12
   {mod, {lager_app, []}},
13
   {env, [
14
-            %% Note: application:start(lager) overwrites previously defined environment variables 
15
+            %% Note: application:start(lager) overwrites previously defined environment variables
16
             %%       thus declaration of default handlers is done at lager_app.erl
17
-  
18
+
19
             %% What colors to use with what log levels
20
             {colored, false},
21
             {colors, [
22
@@ -43,14 +43,18 @@
23
             %% Number of rotated crash logs to keep, 0 means keep only the
24
             %% current one - default is 0
25
             {crash_log_count, 5},
26
-            %% Whether to redirect error_logger messages into lager - defaults to true
27
+            %% Whether to redirect error_logger messages into the default lager_event sink - defaults to true
28
             {error_logger_redirect, true},
29
             %% How many messages per second to allow from error_logger before we start dropping them
30
             {error_logger_hwm, 50},
31
-            %% How big the gen_event mailbox can get before it is switched into sync mode
32
+            %% How big the gen_event mailbox can get before it is
33
+            %% switched into sync mode.  This value only applies to
34
+            %% the default sink; extra sinks can supply their own.
35
             {async_threshold, 20},
36
-            %% Switch back to async mode, when gen_event mailbox size decrease from `async_threshold'
37
-            %% to async_threshold - async_threshold_window
38
+            %% Switch back to async mode, when gen_event mailbox size
39
+            %% decrease from `async_threshold' to async_threshold -
40
+            %% async_threshold_window. This value only applies to the
41
+            %% default sink; extra sinks can supply their own.
42
             {async_threshold_window, 5}
43
         ]}
44
  ]}.
45
lager-2.1.0.tar.gz/src/lager.erl -> lager-3.1.0.tar.gz/src/lager.erl Changed
621
 
1
@@ -21,17 +21,21 @@
2
 -include("lager.hrl").
3
 
4
 -define(LAGER_MD_KEY, '__lager_metadata').
5
+-define(TRACE_SINK, '__trace_sink').
6
+-define(ROTATE_TIMEOUT, 100000).
7
 
8
 %% API
9
 -export([start/0,
10
-        log/3, log/4,
11
+        log/3, log/4, log/5,
12
+        log_unsafe/4,
13
         md/0, md/1,
14
+        rotate_handler/1, rotate_handler/2, rotate_sink/1, rotate_all/0,
15
         trace/2, trace/3, trace_file/2, trace_file/3, trace_file/4, trace_console/1, trace_console/2,
16
-        clear_all_traces/0, stop_trace/1, status/0, 
17
-        get_loglevel/1, set_loglevel/2, set_loglevel/3, get_loglevels/0,
18
-        update_loglevel_config/0, posix_error/1,
19
-        safe_format/3, safe_format_chop/3, dispatch_log/5, dispatch_log/9, 
20
-        do_log/9, pr/2]).
21
+        list_all_sinks/0, clear_all_traces/0, stop_trace/1, stop_trace/3, status/0,
22
+        get_loglevel/1, get_loglevel/2, set_loglevel/2, set_loglevel/3, set_loglevel/4, get_loglevels/1,
23
+        update_loglevel_config/1, posix_error/1, set_loghwm/2, set_loghwm/3, set_loghwm/4,
24
+        safe_format/3, safe_format_chop/3, unsafe_format/2, dispatch_log/5, dispatch_log/7, dispatch_log/9,
25
+        do_log/9, do_log/10, do_log_unsafe/10, pr/2, pr/3, pr_stacktrace/1, pr_stacktrace/2]).
26
 
27
 -type log_level() :: debug | info | notice | warning | error | critical | alert | emergency.
28
 -type log_level_number() :: 0..7.
29
@@ -81,51 +85,85 @@
30
 md(_) ->
31
     erlang:error(badarg).
32
 
33
--spec dispatch_log(log_level(), list(), string(), list() | none, pos_integer()) ->  ok | {error, lager_not_running}.
34
+
35
+-spec dispatch_log(atom(), log_level(), list(), string(), list() | none, pos_integer(), safe | unsafe) ->  ok | {error, lager_not_running} | {error, {sink_not_configured, atom()}}.
36
 %% this is the same check that the parse transform bakes into the module at compile time
37
-dispatch_log(Severity, Metadata, Format, Args, Size) when is_atom(Severity)->
38
+%% see lager_transform (lines 173-216)
39
+dispatch_log(Sink, Severity, Metadata, Format, Args, Size, Safety) when is_atom(Severity)->
40
     SeverityAsInt=lager_util:level_to_num(Severity),
41
-    case {whereis(lager_event), lager_config:get(loglevel, {?LOG_NONE, []})} of
42
-        {undefined, _} ->
43
-            {error, lager_not_running};
44
-        {Pid, {Level, Traces}} when (Level band SeverityAsInt) /= 0 orelse Traces /= [] ->
45
-            do_log(Severity, Metadata, Format, Args, Size, SeverityAsInt, Level, Traces, Pid);
46
-        _ ->
47
-            ok
48
+    case {whereis(Sink), whereis(?DEFAULT_SINK), lager_config:get({Sink, loglevel}, {?LOG_NONE, []})} of
49
+         {undefined, undefined, _} -> {error, lager_not_running};
50
+         {undefined, _LagerEventPid0, _} -> {error, {sink_not_configured, Sink}};
51
+         {SinkPid, _LagerEventPid1, {Level, Traces}} when Safety =:= safe andalso ( (Level band SeverityAsInt) /= 0 orelse Traces /= [] ) ->
52
+            do_log(Severity, Metadata, Format, Args, Size, SeverityAsInt, Level, Traces, Sink, SinkPid);
53
+         {SinkPid, _LagerEventPid1, {Level, Traces}} when Safety =:= unsafe andalso ( (Level band SeverityAsInt) /= 0 orelse Traces /= [] ) ->
54
+            do_log_unsafe(Severity, Metadata, Format, Args, Size, SeverityAsInt, Level, Traces, Sink, SinkPid);
55
+         _ -> ok
56
     end.
57
 
58
 %% @private Should only be called externally from code generated from the parse transform
59
-do_log(Severity, Metadata, Format, Args, Size, SeverityAsInt, LevelThreshold, TraceFilters, Pid) when is_atom(Severity) ->
60
-    Destinations = case TraceFilters of
61
+do_log(Severity, Metadata, Format, Args, Size, SeverityAsInt, LevelThreshold, TraceFilters, Sink, SinkPid) when is_atom(Severity) ->
62
+    FormatFun = fun() -> safe_format_chop(Format, Args, Size) end,
63
+    do_log_impl(Severity, Metadata, Format, Args, SeverityAsInt, LevelThreshold, TraceFilters, Sink, SinkPid, FormatFun).
64
+
65
+do_log_impl(Severity, Metadata, Format, Args, SeverityAsInt, LevelThreshold, TraceFilters, Sink, SinkPid, FormatFun) ->
66
+    {Destinations, TraceSinkPid} = case TraceFilters of
67
         [] ->
68
-            [];
69
+            {[], undefined};
70
         _ ->
71
-            lager_util:check_traces(Metadata,SeverityAsInt,TraceFilters,[])
72
+            {lager_util:check_traces(Metadata,SeverityAsInt,TraceFilters,[]), whereis(?TRACE_SINK)}
73
     end,
74
     case (LevelThreshold band SeverityAsInt) /= 0 orelse Destinations /= [] of
75
         true ->
76
             Msg = case Args of
77
                 A when is_list(A) ->
78
-                    safe_format_chop(Format,Args,Size);
79
+                    FormatFun();
80
                 _ ->
81
                     Format
82
             end,
83
             LagerMsg = lager_msg:new(Msg,
84
                 Severity, Metadata, Destinations),
85
-            case lager_config:get(async, false) of
86
+            case lager_config:get({Sink, async}, false) of
87
                 true ->
88
-                    gen_event:notify(Pid, {log, LagerMsg});
89
+                    gen_event:notify(SinkPid, {log, LagerMsg});
90
                 false ->
91
-                    gen_event:sync_notify(Pid, {log, LagerMsg})
92
+                    gen_event:sync_notify(SinkPid, {log, LagerMsg})
93
+            end,
94
+            case TraceSinkPid /= undefined of
95
+                true ->
96
+                    gen_event:notify(TraceSinkPid, {log, LagerMsg});
97
+                false ->
98
+                    ok
99
             end;
100
         false ->
101
             ok
102
     end.
103
 
104
+%% @private Should only be called externally from code generated from the parse transform
105
+%% Specifically, it would be level ++ `_unsafe' as in `info_unsafe'.
106
+do_log_unsafe(Severity, Metadata, Format, Args, _Size, SeverityAsInt, LevelThreshold, TraceFilters, Sink, SinkPid) when is_atom(Severity) ->
107
+    FormatFun = fun() -> unsafe_format(Format, Args) end,
108
+    do_log_impl(Severity, Metadata, Format, Args, SeverityAsInt, LevelThreshold, TraceFilters, Sink, SinkPid, FormatFun).
109
+
110
+
111
 %% backwards compatible with beams compiled with lager 1.x
112
 dispatch_log(Severity, _Module, _Function, _Line, _Pid, Metadata, Format, Args, Size) ->
113
     dispatch_log(Severity, Metadata, Format, Args, Size).
114
 
115
+%% backwards compatible with beams compiled with lager 2.x
116
+dispatch_log(Severity, Metadata, Format, Args, Size) ->
117
+    dispatch_log(?DEFAULT_SINK, Severity, Metadata, Format, Args, Size, safe).
118
+
119
+%% backwards compatible with beams compiled with lager 2.x
120
+do_log(Severity, Metadata, Format, Args, Size, SeverityAsInt, LevelThreshold, TraceFilters, SinkPid) ->
121
+    do_log(Severity, Metadata, Format, Args, Size, SeverityAsInt,
122
+           LevelThreshold, TraceFilters, ?DEFAULT_SINK, SinkPid).
123
+
124
+
125
+%% TODO:
126
+%% Consider making log2/4 that takes the Level, Pid and Message params of log/3
127
+%% along with a Sink param??
128
+
129
 %% @doc Manually log a message into lager without using the parse transform.
130
 -spec log(log_level(), pid() | atom() | [tuple(),...], list()) -> ok | {error, lager_not_running}.
131
 log(Level, Pid, Message) when is_pid(Pid); is_atom(Pid) ->
132
@@ -140,6 +178,27 @@
133
 log(Level, Metadata, Format, Args) when is_list(Metadata) ->
134
     dispatch_log(Level, Metadata, Format, Args, ?DEFAULT_TRUNCATION).
135
 
136
+log_unsafe(Level, Metadata, Format, Args) when is_list(Metadata) ->
137
+    dispatch_log(?DEFAULT_SINK, Level, Metadata, Format, Args, ?DEFAULT_TRUNCATION, unsafe).
138
+
139
+
140
+%% @doc Manually log a message into lager without using the parse transform.
141
+-spec log(atom(), log_level(), pid() | atom() | [tuple(),...], string(), list()) -> ok | {error, lager_not_running}.
142
+log(Sink, Level, Pid, Format, Args) when is_pid(Pid); is_atom(Pid) ->
143
+    dispatch_log(Sink, Level, [{pid,Pid}], Format, Args, ?DEFAULT_TRUNCATION, safe);
144
+log(Sink, Level, Metadata, Format, Args) when is_list(Metadata) ->
145
+    dispatch_log(Sink, Level, Metadata, Format, Args, ?DEFAULT_TRUNCATION, safe).
146
+
147
+validate_trace_filters(Filters, Level, Backend) ->
148
+    Sink = proplists:get_value(sink, Filters, ?DEFAULT_SINK),
149
+    {Sink,
150
+     lager_util:validate_trace({
151
+                                 proplists:delete(sink, Filters),
152
+                                 Level,
153
+                                 Backend
154
+                               })
155
+    }.
156
+
157
 trace_file(File, Filter) ->
158
     trace_file(File, Filter, debug, []).
159
 
160
@@ -148,34 +207,43 @@
161
 
162
 trace_file(File, Filter, Options) when is_list(Options) ->
163
     trace_file(File, Filter, debug, Options).
164
-    
165
+
166
 trace_file(File, Filter, Level, Options) ->
167
-    Trace0 = {Filter, Level, {lager_file_backend, File}},
168
-    case lager_util:validate_trace(Trace0) of
169
-        {ok, Trace} ->
170
-            Handlers = gen_event:which_handlers(lager_event),
171
+    FileName = lager_util:expand_path(File),
172
+    case validate_trace_filters(Filter, Level, {lager_file_backend, FileName}) of
173
+        {Sink, {ok, Trace}} ->
174
+            Handlers = lager_config:global_get(handlers, []),
175
             %% check if this file backend is already installed
176
-            Res = case lists:member({lager_file_backend, File}, Handlers) of
177
-               false ->
178
-                     %% install the handler
179
-                    LogFileConfig = lists:keystore(level, 1, lists:keystore(file, 1, Options, {file, File}), {level, none}),
180
-                    supervisor:start_child(lager_handler_watcher_sup,
181
-                        [lager_event, {lager_file_backend, File}, LogFileConfig]);
182
-                _ ->
183
-                    {ok, exists}
184
+            Res = case lists:keyfind({lager_file_backend, FileName}, 1, Handlers) of
185
+                      false ->
186
+                          %% install the handler
187
+                          LogFileConfig =
188
+                              lists:keystore(level, 1,
189
+                                             lists:keystore(file, 1,
190
+                                                            Options,
191
+                                                            {file, FileName}),
192
+                                             {level, none}),
193
+                          HandlerInfo =
194
+                              lager_app:start_handler(Sink, {lager_file_backend, FileName},
195
+                                                      LogFileConfig),
196
+                          lager_config:global_set(handlers, [HandlerInfo|Handlers]),
197
+                          {ok, installed};
198
+                      {_Watcher, _Handler, Sink} ->
199
+                          {ok, exists};
200
+                      {_Watcher, _Handler, _OtherSink} ->
201
+                          {error, file_in_use}
202
             end,
203
             case Res of
204
               {ok, _} ->
205
-                add_trace_to_loglevel_config(Trace),
206
-                {ok, Trace};
207
+                add_trace_to_loglevel_config(Trace, Sink),
208
+                {ok, {{lager_file_backend, FileName}, Filter, Level}};
209
               {error, _} = E ->
210
                 E
211
             end;
212
-        Error ->
213
+        {_Sink, Error} ->
214
             Error
215
     end.
216
 
217
-
218
 trace_console(Filter) ->
219
     trace_console(Filter, debug).
220
 
221
@@ -185,28 +253,53 @@
222
 trace(Backend, Filter) ->
223
     trace(Backend, Filter, debug).
224
 
225
+trace({lager_file_backend, File}, Filter, Level) ->
226
+    trace_file(File, Filter, Level);
227
+
228
 trace(Backend, Filter, Level) ->
229
-    Trace0 = {Filter, Level, Backend},
230
-    case lager_util:validate_trace(Trace0) of
231
-        {ok, Trace} ->
232
-            add_trace_to_loglevel_config(Trace),
233
-            {ok, Trace};
234
-        Error ->
235
+    case validate_trace_filters(Filter, Level, Backend) of
236
+        {Sink, {ok, Trace}} ->
237
+            add_trace_to_loglevel_config(Trace, Sink),
238
+            {ok, {Backend, Filter, Level}};
239
+        {_Sink, Error} ->
240
+            Error
241
+    end.
242
+
243
+stop_trace(Backend, Filter, Level) ->
244
+    case validate_trace_filters(Filter, Level, Backend) of
245
+        {Sink, {ok, Trace}} ->
246
+            stop_trace_int(Trace, Sink);
247
+        {_Sink, Error} ->
248
             Error
249
     end.
250
 
251
-stop_trace({_Filter, _Level, Target} = Trace) ->
252
-    {Level, Traces} = lager_config:get(loglevel),
253
+stop_trace({Backend, Filter, Level}) ->
254
+    stop_trace(Backend, Filter, Level).
255
+
256
+%% Important: validate_trace_filters orders the arguments of
257
+%% trace tuples differently than the way outside callers have
258
+%% the trace tuple.
259
+%%
260
+%% That is to say, outside they are represented as 
261
+%% `{Backend, Filter, Level}'
262
+%%
263
+%% and when they come back from validation, they're
264
+%% `{Filter, Level, Backend}'
265
+stop_trace_int({_Filter, _Level, Backend} = Trace, Sink) ->
266
+    {Level, Traces} = lager_config:get({Sink, loglevel}),
267
     NewTraces =  lists:delete(Trace, Traces),
268
     _ = lager_util:trace_filter([ element(1, T) || T <- NewTraces ]),
269
     %MinLevel = minimum_loglevel(get_loglevels() ++ get_trace_levels(NewTraces)),
270
-    lager_config:set(loglevel, {Level, NewTraces}),
271
-    case get_loglevel(Target) of
272
+    lager_config:set({Sink, loglevel}, {Level, NewTraces}),
273
+    case get_loglevel(Sink, Backend) of
274
         none ->
275
             %% check no other traces point here
276
-            case lists:keyfind(Target, 3, NewTraces) of
277
+            case lists:keyfind(Backend, 3, NewTraces) of
278
                 false ->
279
-                    gen_event:delete_handler(lager_event, Target, []);
280
+                    gen_event:delete_handler(Sink, Backend, []),
281
+                    lager_config:global_set(handlers,
282
+                                            lists:keydelete(Backend, 1,
283
+                                                            lager_config:global_get(handlers)));
284
                 _ ->
285
                     ok
286
             end;
287
@@ -215,37 +308,69 @@
288
     end,
289
     ok.
290
 
291
+list_all_sinks() ->
292
+    sets:to_list(
293
+      lists:foldl(fun({_Watcher, _Handler, Sink}, Set) ->
294
+                          sets:add_element(Sink, Set)
295
+                  end,
296
+                  sets:new(),
297
+                  lager_config:global_get(handlers, []))).
298
+
299
+clear_traces_by_sink(Sinks) ->
300
+    lists:foreach(fun(S) ->
301
+                          {Level, _Traces} =
302
+                              lager_config:get({S, loglevel}),
303
+                          lager_config:set({S, loglevel},
304
+                                           {Level, []})
305
+                  end,
306
+                  Sinks).
307
+
308
 clear_all_traces() ->
309
-    {Level, _Traces} = lager_config:get(loglevel),
310
+    Handlers = lager_config:global_get(handlers, []),
311
+    clear_traces_by_sink(list_all_sinks()),
312
     _ = lager_util:trace_filter(none),
313
-    lager_config:set(loglevel, {Level, []}),
314
-    lists:foreach(fun(Handler) ->
315
-          case get_loglevel(Handler) of
316
-            none ->
317
-              gen_event:delete_handler(lager_event, Handler, []);
318
-            _ ->
319
-              ok
320
-          end
321
-      end, gen_event:which_handlers(lager_event)).
322
+    lager_config:global_set(handlers,
323
+                            lists:filter(
324
+      fun({Handler, _Watcher, Sink}) ->
325
+              case get_loglevel(Sink, Handler) of
326
+                  none ->
327
+                      gen_event:delete_handler(Sink, Handler, []),
328
+                      false;
329
+                  _ ->
330
+                      true
331
+              end
332
+      end, Handlers)).
333
+
334
+find_traces(Sinks) ->
335
+    lists:foldl(fun(S, Acc) ->
336
+                        {_Level, Traces} = lager_config:get({S, loglevel}),
337
+                        Acc ++ lists:map(fun(T) -> {S, T} end, Traces)
338
+                end,
339
+                [],
340
+                Sinks).
341
 
342
 status() ->
343
-    Handlers = gen_event:which_handlers(lager_event),
344
-    TraceCount = case length(element(2, lager_config:get(loglevel))) of
345
+    Handlers = lager_config:global_get(handlers, []),
346
+    Sinks = lists:sort(list_all_sinks()),
347
+    Traces = find_traces(Sinks),
348
+    TraceCount = case length(Traces) of
349
         0 -> 1;
350
         N -> N
351
     end,
352
     Status = ["Lager status:\n",
353
         [begin
354
-                    Level = get_loglevel(Handler),
355
+                    Level = get_loglevel(Sink, Handler),
356
                     case Handler of
357
                         {lager_file_backend, File} ->
358
-                            io_lib:format("File ~s at level ~p\n", [File, Level]);
359
+                            io_lib:format("File ~s (~s) at level ~p\n", [File, Sink, Level]);
360
                         lager_console_backend ->
361
-                            io_lib:format("Console at level ~p\n", [Level]);
362
+                            io_lib:format("Console (~s) at level ~p\n", [Sink, Level]);
363
                         _ ->
364
                             []
365
                     end
366
-            end || Handler <- Handlers],
367
+            end || {Handler, _Watcher, Sink} <- lists:sort(fun({_, _, S1},
368
+                                                               {_, _, S2}) -> S1 =< S2 end,
369
+                                                           Handlers)],
370
         "Active Traces:\n",
371
         [begin
372
                     LevelName = case Level of
373
@@ -257,9 +382,9 @@
374
                         Num ->
375
                             lager_util:num_to_level(Num)
376
                     end,
377
-                    io_lib:format("Tracing messages matching ~p at level ~p to ~p\n",
378
-                        [Filter, LevelName, Destination])
379
-            end || {Filter, Level, Destination} <- element(2, lager_config:get(loglevel))],
380
+                    io_lib:format("Tracing messages matching ~p (sink ~s) at level ~p to ~p\n",
381
+                        [Filter, Sink, LevelName, Destination])
382
+            end || {Sink, {Filter, Level, Destination}} <- Traces],
383
          [
384
          "Tracing Reductions:\n",
385
             case ?DEFAULT_TRACER:info('query') of
386
@@ -269,7 +394,7 @@
387
          ],
388
          [
389
           "Tracing Statistics:\n ",
390
-              [ begin 
391
+              [ begin
392
                     [" ", atom_to_list(Table), ": ",
393
                      integer_to_list(?DEFAULT_TRACER:info(Table) div TraceCount),
394
                      "\n"]
395
@@ -280,21 +405,34 @@
396
 
397
 %% @doc Set the loglevel for a particular backend.
398
 set_loglevel(Handler, Level) when is_atom(Level) ->
399
-    Reply = gen_event:call(lager_event, Handler, {set_loglevel, Level}, infinity),
400
-    update_loglevel_config(),
401
-    Reply.
402
+    set_loglevel(?DEFAULT_SINK, Handler, undefined, Level).
403
 
404
 %% @doc Set the loglevel for a particular backend that has multiple identifiers
405
 %% (eg. the file backend).
406
 set_loglevel(Handler, Ident, Level) when is_atom(Level) ->
407
-    Reply = gen_event:call(lager_event, {Handler, Ident}, {set_loglevel, Level}, infinity),
408
-    update_loglevel_config(),
409
+    set_loglevel(?DEFAULT_SINK, Handler, Ident, Level).
410
+
411
+%% @doc Set the loglevel for a particular sink's backend that potentially has
412
+%% multiple identifiers. (Use `undefined' if it doesn't have any.)
413
+set_loglevel(Sink, Handler, Ident, Level) when is_atom(Level) ->
414
+    HandlerArg = case Ident of
415
+        undefined -> Handler;
416
+        _ -> {Handler, Ident}
417
+    end,
418
+    Reply = gen_event:call(Sink, HandlerArg, {set_loglevel, Level}, infinity),
419
+    update_loglevel_config(Sink),
420
     Reply.
421
 
422
-%% @doc Get the loglevel for a particular backend. In the case that the backend
423
-%% has multiple identifiers, the lowest is returned
424
+
425
+%% @doc Get the loglevel for a particular backend on the default sink. In the case that the backend
426
+%% has multiple identifiers, the lowest is returned.
427
 get_loglevel(Handler) ->
428
-    case gen_event:call(lager_event, Handler, get_loglevel, infinity) of
429
+    get_loglevel(?DEFAULT_SINK, Handler).
430
+
431
+%% @doc Get the loglevel for a particular sink's backend. In the case that the backend
432
+%% has multiple identifiers, the lowest is returned.
433
+get_loglevel(Sink, Handler) ->
434
+    case gen_event:call(Sink, Handler, get_loglevel, infinity) of
435
         {mask, Mask} ->
436
             case lager_util:mask_to_levels(Mask) of
437
                 [] -> none;
438
@@ -316,27 +454,43 @@
439
     safe_format_chop("~p", [Error], ?DEFAULT_TRUNCATION).
440
 
441
 %% @private
442
-get_loglevels() ->
443
-    [gen_event:call(lager_event, Handler, get_loglevel, infinity) ||
444
-        Handler <- gen_event:which_handlers(lager_event)].
445
+get_loglevels(Sink) ->
446
+    [gen_event:call(Sink, Handler, get_loglevel, infinity) ||
447
+        Handler <- gen_event:which_handlers(Sink)].
448
+
449
+%% @doc Set the loghwm for the default sink.
450
+set_loghwm(Handler, Hwm) when is_integer(Hwm) ->
451
+    set_loghwm(?DEFAULT_SINK, Handler, Hwm).
452
+
453
+%% @doc Set the loghwm for a particular backend.
454
+set_loghwm(Sink, Handler, Hwm) when is_integer(Hwm) ->
455
+    gen_event:call(Sink, Handler, {set_loghwm, Hwm}, infinity).
456
+
457
+%% @doc Set the loghwm (log high water mark) for file backends with multiple identifiers
458
+set_loghwm(Sink, Handler, Ident, Hwm) when is_integer(Hwm) ->
459
+    gen_event:call(Sink, {Handler, Ident}, {set_loghwm, Hwm}, infinity).
460
 
461
 %% @private
462
-add_trace_to_loglevel_config(Trace) ->
463
-    {MinLevel, Traces} = lager_config:get(loglevel),
464
+add_trace_to_loglevel_config(Trace, Sink) ->
465
+    {MinLevel, Traces} = lager_config:get({Sink, loglevel}),
466
     case lists:member(Trace, Traces) of
467
         false ->
468
             NewTraces = [Trace|Traces],
469
             _ = lager_util:trace_filter([ element(1, T) || T <- NewTraces]),
470
-            lager_config:set(loglevel, {MinLevel, [Trace|Traces]});
471
+            lager_config:set({Sink, loglevel}, {MinLevel, [Trace|Traces]});
472
         _ ->
473
             ok
474
     end.
475
 
476
 %% @doc recalculate min log level
477
-update_loglevel_config() ->
478
-    {_, Traces} = lager_config:get(loglevel),
479
-    MinLog = minimum_loglevel(get_loglevels()),
480
-    lager_config:set(loglevel, {MinLog, Traces}).
481
+update_loglevel_config(error_logger) ->
482
+    %% Not a sink under our control, part of the Erlang logging
483
+    %% utility that error_logger_lager_h attaches to
484
+    true;
485
+update_loglevel_config(Sink) ->
486
+    {_, Traces} = lager_config:get({Sink, loglevel}, {ignore_me, []}),
487
+    MinLog = minimum_loglevel(get_loglevels(Sink)),
488
+    lager_config:set({Sink, loglevel}, {MinLog, Traces}).
489
 
490
 %% @private
491
 minimum_loglevel(Levels) ->
492
@@ -367,49 +521,118 @@
493
 safe_format_chop(Fmt, Args, Limit) ->
494
     safe_format(Fmt, Args, Limit, [{chomp, true}]).
495
 
496
+%% @private Print the format string `Fmt' with `Args' without a size limit.
497
+%% This is unsafe because the output of this function is unbounded.
498
+%%
499
+%% Log messages with unbounded size will kill your application dead as
500
+%% OTP mechanisms stuggle to cope with them.  So this function is
501
+%% intended <b>only</b> for messages which have a reasonable bounded
502
+%% size before they're formatted.
503
+%%
504
+%% If the format string is invalid or not enough arguments are
505
+%% supplied a 'FORMAT ERROR' message is printed instead with the
506
+%% offending arguments. The caller is NOT crashed.
507
+unsafe_format(Fmt, Args) ->
508
+    try io_lib:format(Fmt, Args)
509
+    catch
510
+        _:_ -> io_lib:format("FORMAT ERROR: ~p ~p", [Fmt, Args])
511
+    end.
512
+
513
 %% @doc Print a record lager found during parse transform
514
 pr(Record, Module) when is_tuple(Record), is_atom(element(1, Record)) ->
515
+    pr(Record, Module, []);
516
+pr(Record, _) ->
517
+    Record.
518
+
519
+%% @doc Print a record lager found during parse transform
520
+pr(Record, Module, Options) when is_tuple(Record), is_atom(element(1, Record)), is_list(Options) ->
521
     try 
522
         case is_record_known(Record, Module) of
523
             false ->
524
                 Record;
525
             {RecordName, RecordFields} ->
526
                 {'$lager_record', RecordName, 
527
-                    zip(RecordFields, tl(tuple_to_list(Record)), Module, [])}
528
+                    zip(RecordFields, tl(tuple_to_list(Record)), Module, Options, [])}
529
         end
530
     catch
531
         error:undef ->
532
             Record
533
     end;
534
-pr(Record, _) ->
535
+pr(Record, _, _) ->
536
     Record.
537
 
538
-zip([FieldName|RecordFields], [FieldValue|Record], Module, ToReturn) ->
539
+zip([FieldName|RecordFields], [FieldValue|Record], Module, Options, ToReturn) ->
540
+    Compress = lists:member(compress, Options),
541
     case   is_tuple(FieldValue) andalso
542
            tuple_size(FieldValue) > 0 andalso
543
            is_atom(element(1, FieldValue)) andalso
544
            is_record_known(FieldValue, Module) of
545
+        false when Compress andalso FieldValue =:= undefined ->
546
+            zip(RecordFields, Record, Module, Options, ToReturn);
547
         false ->
548
-            zip(RecordFields, Record, Module, [{FieldName, FieldValue}|ToReturn]);
549
+            zip(RecordFields, Record, Module, Options, [{FieldName, FieldValue}|ToReturn]);
550
         _Else ->
551
-            F = {FieldName, pr(FieldValue, Module)},
552
-            zip(RecordFields, Record, Module, [F|ToReturn])
553
+            F = {FieldName, pr(FieldValue, Module, Options)},
554
+            zip(RecordFields, Record, Module, Options, [F|ToReturn])
555
     end;
556
-zip([], [], _Module, ToReturn) ->
557
+zip([], [], _Module, _Compress, ToReturn) ->
558
     lists:reverse(ToReturn).
559
 
560
-is_record_known(Record, Module) -> 
561
+is_record_known(Record, Module) ->
562
     Name = element(1, Record),
563
     Attrs = Module:module_info(attributes),
564
     case lists:keyfind(lager_records, 1, Attrs) of
565
         false -> false;
566
-        {lager_records, Records} -> 
567
+        {lager_records, Records} ->
568
             case lists:keyfind(Name, 1, Records) of
569
                 false -> false;
570
-                {Name, RecordFields} -> 
571
+                {Name, RecordFields} ->
572
                     case (tuple_size(Record) - 1) =:= length(RecordFields) of
573
                         false -> false;
574
                         true -> {Name, RecordFields}
575
                     end
576
             end
577
     end.
578
+
579
+
580
+%% @doc Print stacktrace in human readable form
581
+pr_stacktrace(Stacktrace) ->
582
+    Indent = "\n    ",
583
+    lists:foldl(
584
+        fun(Entry, Acc) ->
585
+            Acc ++ Indent ++ error_logger_lager_h:format_mfa(Entry)
586
+        end,
587
+        [],
588
+        lists:reverse(Stacktrace)).
589
+
590
+pr_stacktrace(Stacktrace, {Class, Reason}) ->
591
+    lists:flatten(
592
+        pr_stacktrace(Stacktrace) ++ "\n" ++ io_lib:format("~s:~p", [Class, Reason])).
593
+    
594
+rotate_sink(Sink) ->
595
+    Handlers = lager_config:global_get(handlers),
596
+    RotateHandlers = lists:filtermap(
597
+        fun({Handler,_,S}) when S == Sink -> {true, {Handler, Sink}};
598
+           (_)                            -> false 
599
+        end, 
600
+        Handlers),
601
+    rotate_handlers(RotateHandlers).
602
+
603
+rotate_all() -> 
604
+    rotate_handlers(lists:map(fun({H,_,S}) -> {H, S} end,
605
+                              lager_config:global_get(handlers))).
606
+
607
+
608
+rotate_handlers(Handlers) ->
609
+    [ rotate_handler(Handler, Sink) || {Handler, Sink} <- Handlers ].
610
+
611
+
612
+rotate_handler(Handler) ->
613
+    Handlers = lager_config:global_get(handlers),
614
+    case lists:keyfind(Handler, 1, Handlers) of
615
+        {Handler, _, Sink} -> rotate_handler(Handler, Sink);
616
+        false              -> ok
617
+    end.
618
+
619
+rotate_handler(Handler, Sink) ->
620
+    gen_event:call(Sink, Handler, rotate, ?ROTATE_TIMEOUT).
621
lager-2.1.0.tar.gz/src/lager_app.erl -> lager-3.1.0.tar.gz/src/lager_app.erl Changed
318
 
1
@@ -27,93 +27,204 @@
2
 -endif.
3
 -export([start/0,
4
          start/2,
5
+         start_handler/3,
6
          stop/1]).
7
 
8
+-define(FILENAMES, '__lager_file_backend_filenames').
9
+-define(THROTTLE, lager_backend_throttle).
10
+-define(DEFAULT_HANDLER_CONF,
11
+        [{lager_console_backend, info},
12
+         {lager_file_backend,
13
+          [{file, "log/error.log"}, {level, error},
14
+           {size, 10485760}, {date, "$D0"}, {count, 5}]
15
+         },
16
+         {lager_file_backend,
17
+          [{file, "log/console.log"}, {level, info},
18
+           {size, 10485760}, {date, "$D0"}, {count, 5}]
19
+         }
20
+        ]).
21
+
22
 start() ->
23
     application:start(lager).
24
 
25
-start(_StartType, _StartArgs) ->
26
-    {ok, Pid} = lager_sup:start_link(),
27
+start_throttle(Sink, Threshold, Window) ->
28
+    _ = supervisor:start_child(lager_handler_watcher_sup,
29
+                               [Sink, ?THROTTLE, [Threshold, Window]]),
30
+    ok.
31
 
32
+determine_async_behavior(_Sink, {ok, undefined}, _Window) ->
33
+    ok;
34
+determine_async_behavior(_Sink, undefined, _Window) ->
35
+    ok;
36
+determine_async_behavior(_Sink, {ok, Threshold}, _Window) when not is_integer(Threshold) orelse Threshold < 0 ->
37
+    error_logger:error_msg("Invalid value for 'async_threshold': ~p~n",
38
+                           [Threshold]),
39
+    throw({error, bad_config});
40
+determine_async_behavior(Sink, {ok, Threshold}, undefined) ->
41
+    start_throttle(Sink, Threshold, erlang:trunc(Threshold * 0.2));
42
+determine_async_behavior(_Sink, {ok, Threshold}, {ok, Window}) when not is_integer(Window) orelse Window > Threshold orelse Window < 0 ->
43
+    error_logger:error_msg(
44
+      "Invalid value for 'async_threshold_window': ~p~n", [Window]),
45
+    throw({error, bad_config});
46
+determine_async_behavior(Sink, {ok, Threshold}, {ok, Window}) ->
47
+    start_throttle(Sink, Threshold, Window).
48
 
49
-    case application:get_env(lager, async_threshold) of
50
-        undefined ->
51
-            ok;
52
-        {ok, undefined} ->
53
-            undefined;
54
-        {ok, Threshold} when is_integer(Threshold), Threshold >= 0 ->
55
-            DefWindow = erlang:trunc(Threshold * 0.2), % maybe 0?
56
-            ThresholdWindow =
57
-                case application:get_env(lager, async_threshold_window) of
58
+start_handlers(_Sink, undefined) ->
59
+    ok;
60
+start_handlers(_Sink, Handlers) when not is_list(Handlers) ->
61
+    error_logger:error_msg(
62
+      "Invalid value for 'handlers' (must be list): ~p~n", [Handlers]),
63
+    throw({error, bad_config});
64
+start_handlers(Sink, Handlers) ->
65
+    %% handlers failing to start are handled in the handler_watcher
66
+    lager_config:global_set(handlers,
67
+                            lager_config:global_get(handlers, []) ++
68
+                            lists:map(fun({Module, Config}) ->
69
+                                              check_handler_config(Module, Config),
70
+                                              start_handler(Sink, Module, Config);
71
+                                          (_) ->
72
+                                              throw({error, bad_config})
73
+                                      end,
74
+                                      expand_handlers(Handlers))),
75
+    ok.
76
+
77
+start_handler(Sink, Module, Config) ->
78
+    {ok, Watcher} = supervisor:start_child(lager_handler_watcher_sup,
79
+                                           [Sink, Module, Config]),
80
+    {Module, Watcher, Sink}.
81
+
82
+check_handler_config({lager_file_backend, F}, Config) when is_list(Config) ->
83
+    Fs = case get(?FILENAMES) of
84
+        undefined -> ordsets:new();
85
+        X -> X
86
+    end,
87
+    case ordsets:is_element(F, Fs) of
88
+        true ->
89
+            error_logger:error_msg(
90
+              "Cannot have same file (~p) in multiple file backends~n", [F]),
91
+            throw({error, bad_config});
92
+        false ->
93
+            put(?FILENAMES,
94
+                ordsets:add_element(F, Fs))
95
+    end,
96
+    ok;
97
+check_handler_config(_Handler, Config) when is_list(Config) orelse is_atom(Config) ->
98
+    ok;
99
+check_handler_config(Handler, _BadConfig) ->
100
+    throw({error, {bad_config, Handler}}).
101
+
102
+clean_up_config_checks() ->
103
+    erase(?FILENAMES).
104
+
105
+interpret_hwm(undefined) ->
106
+    undefined;
107
+interpret_hwm({ok, undefined}) ->
108
+    undefined;
109
+interpret_hwm({ok, HWM}) when not is_integer(HWM) orelse HWM < 0 ->
110
+    _ = lager:log(warning, self(), "Invalid error_logger high water mark: ~p, disabling", [HWM]),
111
+    undefined;
112
+interpret_hwm({ok, HWM}) ->
113
+    HWM.
114
+
115
+start_error_logger_handler({ok, false}, _HWM, _Whitelist) ->
116
+    [];
117
+start_error_logger_handler(_, HWM, undefined) ->
118
+    start_error_logger_handler(ignore_me, HWM, {ok, []});
119
+start_error_logger_handler(_, HWM, {ok, WhiteList}) ->
120
+    GlStrategy = case application:get_env(lager, error_logger_groupleader_strategy) of
121
                     undefined ->
122
-                        DefWindow;
123
-                    {ok, Window} when is_integer(Window), Window < Threshold, Window >= 0 ->
124
-                        Window;
125
-                    {ok, BadWindow} ->
126
+                        handle;
127
+                    {ok, GlStrategy0} when 
128
+                            GlStrategy0 =:= handle;
129
+                            GlStrategy0 =:= ignore;
130
+                            GlStrategy0 =:= mirror ->
131
+                        GlStrategy0;
132
+                    {ok, BadGlStrategy} ->
133
                         error_logger:error_msg(
134
-                          "Invalid value for 'async_threshold_window': ~p~n", [BadWindow]),
135
+                          "Invalid value for 'error_logger_groupleader_strategy': ~p~n",
136
+                          [BadGlStrategy]),
137
                         throw({error, bad_config})
138
                 end,
139
-            _ = supervisor:start_child(lager_handler_watcher_sup,
140
-                                       [lager_event, lager_backend_throttle, [Threshold, ThresholdWindow]]),
141
-            ok;
142
-        {ok, BadThreshold} ->
143
-            error_logger:error_msg("Invalid value for 'async_threshold': ~p~n", [BadThreshold]),
144
-            throw({error, bad_config})
145
-    end,
146
 
147
-    Handlers = case application:get_env(lager, handlers) of
148
-        undefined ->
149
-            [{lager_console_backend, info},
150
-             {lager_file_backend, [{file, "log/error.log"},   {level, error}, {size, 10485760}, {date, "$D0"}, {count, 5}]},
151
-             {lager_file_backend, [{file, "log/console.log"}, {level, info}, {size, 10485760}, {date, "$D0"}, {count, 5}]}];
152
-        {ok, Val} ->
153
-            Val
154
-    end,
155
+    case supervisor:start_child(lager_handler_watcher_sup, [error_logger, error_logger_lager_h, [HWM, GlStrategy]]) of
156
+        {ok, _} ->
157
+            [begin error_logger:delete_report_handler(X), X end ||
158
+                X <- gen_event:which_handlers(error_logger) -- [error_logger_lager_h | WhiteList]];
159
+        {error, _} ->
160
+            []
161
+    end.
162
 
163
-    %% handlers failing to start are handled in the handler_watcher
164
-    _ = [supervisor:start_child(lager_handler_watcher_sup, [lager_event, Module, Config]) ||
165
-        {Module, Config} <- expand_handlers(Handlers)],
166
+%% `determine_async_behavior/3' is called with the results from either
167
+%% `application:get_env/2' and `proplists:get_value/2'. Since
168
+%% `application:get_env/2' wraps a successful retrieval in an `{ok,
169
+%% Value}' tuple, do the same for the result from
170
+%% `proplists:get_value/2'.
171
+wrap_proplist_value(undefined) ->
172
+    undefined;
173
+wrap_proplist_value(Value) ->
174
+    {ok, Value}.
175
 
176
-    ok = add_configured_traces(),
177
+configure_sink(Sink, SinkDef) ->
178
+    lager_config:new_sink(Sink),
179
+    ChildId = lager_util:make_internal_sink_name(Sink),
180
+    _ = supervisor:start_child(lager_sup,
181
+                               {ChildId,
182
+                                {gen_event, start_link,
183
+                                 [{local, Sink}]},
184
+                                permanent, 5000, worker, dynamic}),
185
+    determine_async_behavior(Sink,
186
+                             wrap_proplist_value(
187
+                               proplists:get_value(async_threshold, SinkDef)),
188
+                             wrap_proplist_value(
189
+                               proplists:get_value(async_threshold_window, SinkDef))
190
+                            ),
191
+    start_handlers(Sink,
192
+                   proplists:get_value(handlers, SinkDef, [])),
193
 
194
-    %% mask the messages we have no use for
195
-    lager:update_loglevel_config(),
196
-
197
-    HighWaterMark = case application:get_env(lager, error_logger_hwm) of
198
-        {ok, undefined} ->
199
-            undefined;
200
-        {ok, HwmVal} when is_integer(HwmVal), HwmVal > 0 ->
201
-            HwmVal;
202
-        {ok, BadVal} ->
203
-            _ = lager:log(warning, self(), "Invalid error_logger high water mark: ~p, disabling", [BadVal]),
204
-            undefined;
205
-        undefined ->
206
-            undefined
207
-    end,
208
+    lager:update_loglevel_config(Sink).
209
 
210
-    SavedHandlers =
211
-        case application:get_env(lager, error_logger_redirect) of
212
-            {ok, false} ->
213
-                [];
214
-            _ ->
215
-                WhiteList = case application:get_env(lager, error_logger_whitelist) of
216
-                    undefined ->
217
-                        [];
218
-                    {ok, WhiteList0} ->
219
-                        WhiteList0
220
-                end,
221
 
222
-                case supervisor:start_child(lager_handler_watcher_sup, [error_logger, error_logger_lager_h, [HighWaterMark]]) of
223
-                    {ok, _} ->
224
-                        [begin error_logger:delete_report_handler(X), X end ||
225
-                            X <- gen_event:which_handlers(error_logger) -- [error_logger_lager_h | WhiteList]];
226
-                    {error, _} ->
227
-                        []
228
-                end
229
-        end,
230
+configure_extra_sinks(Sinks) ->
231
+    lists:foreach(fun({Sink, Proplist}) -> configure_sink(Sink, Proplist) end,
232
+                  Sinks).
233
+
234
+%% R15 doesn't know about application:get_env/3
235
+get_env(Application, Key, Default) ->
236
+    get_env_default(application:get_env(Application, Key),
237
+                    Default).
238
+
239
+get_env_default(undefined, Default) ->
240
+    Default;
241
+get_env_default({ok, Value}, _Default) ->
242
+    Value.
243
+
244
+start(_StartType, _StartArgs) ->
245
+    {ok, Pid} = lager_sup:start_link(),
246
+
247
+    %% Handle the default sink.
248
+    determine_async_behavior(?DEFAULT_SINK,
249
+                             application:get_env(lager, async_threshold),
250
+                             application:get_env(lager, async_threshold_window)),
251
+    start_handlers(?DEFAULT_SINK,
252
+                   get_env(lager, handlers, ?DEFAULT_HANDLER_CONF)),
253
+
254
+
255
+    lager:update_loglevel_config(?DEFAULT_SINK),
256
+
257
+    SavedHandlers = start_error_logger_handler(
258
+                      application:get_env(lager, error_logger_redirect),
259
+                      interpret_hwm(application:get_env(lager, error_logger_hwm)),
260
+                      application:get_env(lager, error_logger_whitelist)
261
+                     ),
262
 
263
-    _ = lager_util:trace_filter(none), 
264
+    _ = lager_util:trace_filter(none),
265
+
266
+    %% Now handle extra sinks
267
+    configure_extra_sinks(get_env(lager, extra_sinks, [])),
268
+
269
+    ok = add_configured_traces(),
270
+
271
+    clean_up_config_checks(),
272
 
273
     {ok, Pid, SavedHandlers}.
274
 
275
@@ -153,9 +264,9 @@
276
 
277
 maybe_make_handler_id(Mod, Config) ->
278
     %% Allow the backend to generate a gen_event handler id, if it wants to.
279
-    %% We don't use erlang:function_exported here because that requires the module 
280
+    %% We don't use erlang:function_exported here because that requires the module
281
     %% already be loaded, which is unlikely at this phase of startup. Using code:load
282
-    %% caused undesireable side-effects with generating code-coverage reports.
283
+    %% caused undesirable side-effects with generating code-coverage reports.
284
     try Mod:config_to_id(Config) of
285
         Id ->
286
             {Id, Config}
287
@@ -229,4 +340,30 @@
288
             )
289
         }
290
     ].
291
+
292
+check_handler_config_test_() ->
293
+    Good = expand_handlers(?DEFAULT_HANDLER_CONF),
294
+    Bad  = expand_handlers([{lager_console_backend, info},
295
+            {lager_file_backend, [{file, "same_file.log"}]},
296
+            {lager_file_backend, [{file, "same_file.log"}, {level, info}]}]),
297
+    AlsoBad = [{lager_logstash_backend,
298
+                                    {level, info},
299
+                                    {output, {udp, "localhost", 5000}},
300
+                                    {format, json},
301
+                                    {json_encoder, jiffy}}],
302
+    BadToo = [{fail, {fail}}],
303
+    [
304
+        {"lager_file_backend_good",
305
+         ?_assertEqual([ok, ok, ok], [ check_handler_config(M,C) || {M,C} <- Good ])
306
+        },
307
+        {"lager_file_backend_bad",
308
+         ?_assertThrow({error, bad_config}, [ check_handler_config(M,C) || {M,C} <- Bad ])
309
+        },
310
+        {"Invalid config dies",
311
+         ?_assertThrow({error, bad_config}, start_handlers(foo, AlsoBad))
312
+        },
313
+        {"Invalid config dies",
314
+         ?_assertThrow({error, {bad_config, _}}, start_handlers(foo, BadToo))
315
+        }
316
+    ].
317
 -endif.
318
lager-2.1.0.tar.gz/src/lager_backend_throttle.erl -> lager-3.1.0.tar.gz/src/lager_backend_throttle.erl Changed
65
 
1
@@ -29,15 +29,27 @@
2
 -export([init/1, handle_call/2, handle_event/2, handle_info/2, terminate/2,
3
         code_change/3]).
4
 
5
+%%
6
+%% Allow test code to verify that we're doing the needful.
7
+-ifdef(TEST).
8
+-define(ETS_TABLE, async_threshold_test).
9
+-define(TOGGLE_SYNC(), test_increment(sync_toggled)).
10
+-define(TOGGLE_ASYNC(), test_increment(async_toggled)).
11
+-else.
12
+-define(TOGGLE_SYNC(), true).
13
+-define(TOGGLE_ASYNC(), true).
14
+-endif.
15
+
16
 -record(state, {
17
+        sink :: atom(),
18
         hwm :: non_neg_integer(),
19
         window_min :: non_neg_integer(),
20
         async = true :: boolean()
21
     }).
22
 
23
-init([Hwm, Window]) ->
24
-    lager_config:set(async, true),
25
-    {ok, #state{hwm=Hwm, window_min=Hwm - Window}}.
26
+init([{sink, Sink}, Hwm, Window]) ->
27
+    lager_config:set({Sink, async}, true),
28
+    {ok, #state{sink=Sink, hwm=Hwm, window_min=Hwm - Window}}.
29
 
30
 
31
 handle_call(get_loglevel, State) ->
32
@@ -52,11 +64,13 @@
33
     case {Len > State#state.hwm, Len < State#state.window_min, State#state.async} of
34
         {true, _, true} ->
35
             %% need to flip to sync mode
36
-            lager_config:set(async, false),
37
+            ?TOGGLE_SYNC(),
38
+            lager_config:set({State#state.sink, async}, false),
39
             {ok, State#state{async=false}};
40
         {_, true, false} ->
41
             %% need to flip to async mode
42
-            lager_config:set(async, true),
43
+            ?TOGGLE_ASYNC(),
44
+            lager_config:set({State#state.sink, async}, true),
45
             {ok, State#state{async=true}};
46
         _ ->
47
             %% nothing needs to change
48
@@ -76,3 +90,16 @@
49
 code_change(_OldVsn, State, _Extra) ->
50
     {ok, State}.
51
 
52
+-ifdef(TEST).
53
+test_get(Key) ->
54
+    get_default(ets:lookup(?ETS_TABLE, Key)).
55
+
56
+test_increment(Key) ->
57
+    ets:insert(?ETS_TABLE,
58
+               {Key, test_get(Key) + 1}).
59
+
60
+get_default([]) ->
61
+    0;
62
+get_default([{_Key, Value}]) ->
63
+    Value.
64
+-endif.
65
lager-2.1.0.tar.gz/src/lager_common_test_backend.erl -> lager-3.1.0.tar.gz/src/lager_common_test_backend.erl Changed
20
 
1
@@ -40,9 +40,15 @@
2
 
3
 bounce(Level) ->
4
     _ = application:stop(lager),
5
-    lager:start(),
6
-    gen_event:add_handler(lager_event, lager_common_test_backend, [Level, false]),
7
-    %lager:set_loglevel(lager_common_test_backend, Level),
8
+    application:set_env(lager, suppress_application_start_stop, true),
9
+    application:set_env(lager, handlers,
10
+                        [
11
+                         {lager_common_test_backend, [Level, false]}
12
+                        ]),
13
+    ok = lager:start(),
14
+    %% we care more about getting all of our messages here than being
15
+    %% careful with the amount of memory that we're using.
16
+    error_logger_lager_h:set_high_water(100000),
17
     ok.
18
 
19
 -spec(init(integer()|atom()|[term()]) -> {ok, #state{}} | {error, atom()}).
20
lager-2.1.0.tar.gz/src/lager_config.erl -> lager-3.1.0.tar.gz/src/lager_config.erl Changed
83
 
1
@@ -20,9 +20,16 @@
2
 
3
 -include("lager.hrl").
4
 
5
--export([new/0, get/1, get/2, set/2]).
6
+-export([new/0, new_sink/1, get/1, get/2, set/2,
7
+         global_get/1, global_get/2, global_set/2]).
8
 
9
 -define(TBL, lager_config).
10
+-define(GLOBAL, '_global').
11
+
12
+%% For multiple sinks, the key is now the registered event name and the old key
13
+%% as a tuple.
14
+%%
15
+%% {{lager_event, loglevel}, Value} instead of {loglevel, Value}
16
 
17
 new() ->
18
     %% set up the ETS configuration table
19
@@ -33,32 +40,48 @@
20
         error:badarg ->
21
             ?INT_LOG(warning, "Table ~p already exists", [?TBL])
22
     end,
23
+    new_sink(?DEFAULT_SINK),
24
+    %% Need to be able to find the `lager_handler_watcher' for all handlers
25
+    ets:insert_new(?TBL, {{?GLOBAL, handlers}, []}),
26
+    ok.
27
+
28
+new_sink(Sink) ->
29
     %% use insert_new here so that if we're in an appup we don't mess anything up
30
     %%
31
     %% until lager is completely started, allow all messages to go through
32
-    ets:insert_new(?TBL, {loglevel, {element(2, lager_util:config_to_mask(debug)), []}}),
33
-    ok.
34
+    ets:insert_new(?TBL, {{Sink, loglevel}, {element(2, lager_util:config_to_mask(debug)), []}}).
35
+
36
+global_get(Key) ->
37
+    global_get(Key, undefined).
38
+
39
+global_get(Key, Default) ->
40
+    get({?GLOBAL, Key}, Default).
41
 
42
+global_set(Key, Value) ->
43
+    set({?GLOBAL, Key}, Value).
44
 
45
+
46
+get({_Sink, _Key}=FullKey) ->
47
+    get(FullKey, undefined);
48
 get(Key) ->
49
-    case ets:lookup(?TBL, Key) of
50
-        [] ->
51
-            undefined;
52
-        [{Key, Res}] ->
53
-            Res
54
-    end.
55
+    get({?DEFAULT_SINK, Key}, undefined).
56
 
57
-get(Key, Default) ->
58
-    try ?MODULE:get(Key) of
59
-        undefined ->
60
+get({Sink, Key}, Default) ->
61
+    try
62
+    case ets:lookup(?TBL, {Sink, Key}) of
63
+        [] ->
64
             Default;
65
-        Res ->
66
+        [{{Sink, Key}, Res}] ->
67
             Res
68
+    end
69
     catch
70
         _:_ ->
71
             Default
72
-    end.
73
+    end;
74
+get(Key, Default) ->
75
+    get({?DEFAULT_SINK, Key}, Default).
76
 
77
+set({Sink, Key}, Value) ->
78
+    ets:insert(?TBL, {{Sink, Key}, Value});
79
 set(Key, Value) ->
80
-    ets:insert(?TBL, {Key, Value}).
81
-
82
+    set({?DEFAULT_SINK, Key}, Value).
83
lager-2.1.0.tar.gz/src/lager_console_backend.erl -> lager-3.1.0.tar.gz/src/lager_console_backend.erl Changed
81
 
1
@@ -38,6 +38,8 @@
2
 -define(TERSE_FORMAT,[time, " ", color, "[", severity,"] ", message]).
3
 
4
 %% @private
5
+init([Level]) when is_atom(Level) ->
6
+    init(Level);
7
 init([Level, true]) -> % for backwards compatibility
8
     init([Level,{lager_default_formatter,[{eol, eol()}]}]);
9
 init([Level,false]) -> % for backwards compatibility
10
@@ -75,7 +77,6 @@
11
 init(Level) ->
12
     init([Level,{lager_default_formatter,?TERSE_FORMAT ++ [eol()]}]).
13
 
14
-
15
 %% @private
16
 handle_call(get_loglevel, #state{level=Level} = State) ->
17
     {ok, Level, State};
18
@@ -186,7 +187,7 @@
19
                         register(user, Pid),
20
                         erlang:group_leader(Pid, whereis(lager_event)),
21
                         gen_event:add_handler(lager_event, lager_console_backend, info),
22
-                        lager_config:set(loglevel, {element(2, lager_util:config_to_mask(info)), []}),
23
+                        lager_config:set({lager_event, loglevel}, {element(2, lager_util:config_to_mask(info)), []}),
24
                         lager:log(info, self(), "Test message"),
25
                         receive
26
                             {io_request, From, ReplyAs, {put_chars, unicode, Msg}} ->
27
@@ -206,7 +207,7 @@
28
                         register(user, Pid),
29
                         erlang:group_leader(Pid, whereis(lager_event)),
30
                         gen_event:add_handler(lager_event, lager_console_backend, [info, true]),
31
-                        lager_config:set(loglevel, {element(2, lager_util:config_to_mask(info)), []}),
32
+                        lager_config:set({lager_event, loglevel}, {element(2, lager_util:config_to_mask(info)), []}),
33
                         lager:info("Test message"),
34
                         PidStr = pid_to_list(self()),
35
                         receive
36
@@ -228,7 +229,7 @@
37
                         gen_event:add_handler(lager_event, lager_console_backend,
38
                           [info, {lager_default_formatter, [date,"#",time,"#",severity,"#",node,"#",pid,"#",
39
                                                             module,"#",function,"#",file,"#",line,"#",message,"\r\n"]}]),
40
-                        lager_config:set(loglevel, {?INFO, []}),
41
+                        lager_config:set({lager_event, loglevel}, {?INFO, []}),
42
                         lager:info("Test message"),
43
                         PidStr = pid_to_list(self()),
44
                         NodeStr = atom_to_list(node()),
45
@@ -251,7 +252,7 @@
46
                         register(user, Pid),
47
                         gen_event:add_handler(lager_event, lager_console_backend, info),
48
                         erlang:group_leader(Pid, whereis(lager_event)),
49
-                        lager_config:set(loglevel, {element(2, lager_util:config_to_mask(info)), []}),
50
+                        lager_config:set({lager_event, loglevel}, {element(2, lager_util:config_to_mask(info)), []}),
51
                         lager:debug("Test message"),
52
                         receive
53
                             {io_request, From, ReplyAs, {put_chars, unicode, _Msg}} ->
54
@@ -280,7 +281,7 @@
55
                         unregister(user),
56
                         register(user, Pid),
57
                         gen_event:add_handler(lager_event, lager_console_backend, info),
58
-                        lager_config:set(loglevel, {element(2, lager_util:config_to_mask(info)), []}),
59
+                        lager_config:set({lager_event, loglevel}, {element(2, lager_util:config_to_mask(info)), []}),
60
                         erlang:group_leader(Pid, whereis(lager_event)),
61
                         lager:debug("Test message"),
62
                         receive
63
@@ -319,7 +320,7 @@
64
                         unregister(user),
65
                         register(user, Pid),
66
                         gen_event:add_handler(lager_event, lager_console_backend, info),
67
-                        lager_config:set(loglevel, {element(2, lager_util:config_to_mask(info)), []}),
68
+                        lager_config:set({lager_event, loglevel}, {element(2, lager_util:config_to_mask(info)), []}),
69
                         lager:set_loglevel(lager_console_backend, '!=info'),
70
                         erlang:group_leader(Pid, whereis(lager_event)),
71
                         lager:debug("Test message"),
72
@@ -350,7 +351,7 @@
73
                         unregister(user),
74
                         register(user, Pid),
75
                         gen_event:add_handler(lager_event, lager_console_backend, info),
76
-                        lager_config:set(loglevel, {element(2, lager_util:config_to_mask(info)), []}),
77
+                        lager_config:set({lager_event, loglevel}, {element(2, lager_util:config_to_mask(info)), []}),
78
                         lager:set_loglevel(lager_console_backend, '=debug'),
79
                         erlang:group_leader(Pid, whereis(lager_event)),
80
                         lager:debug("Test message"),
81
lager-2.1.0.tar.gz/src/lager_crash_log.erl -> lager-3.1.0.tar.gz/src/lager_crash_log.erl Changed
11
 
1
@@ -66,7 +66,8 @@
2
             Date, Count], []).
3
 
4
 %% @private
5
-init([Filename, MaxBytes, Size, Date, Count]) ->
6
+init([RelFilename, MaxBytes, Size, Date, Count]) ->
7
+    Filename = lager_util:expand_path(RelFilename),
8
     case lager_util:open_logfile(Filename, false) of
9
         {ok, {FD, Inode, _}} ->
10
             schedule_rotation(Date),
11
lager-2.1.0.tar.gz/src/lager_default_formatter.erl -> lager-3.1.0.tar.gz/src/lager_default_formatter.erl Changed
272
 
1
@@ -40,7 +40,7 @@
2
 %% or refer to other properties, if desired. You can also use a {atom, semi-iolist(), semi-iolist()} formatter, which
3
 %% acts like a ternary operator's true/false branches.
4
 %%
5
-%% The metadata properties date,time, message, and severity will always exist.  
6
+%% The metadata properties date,time, message, severity, and sev will always exist.  
7
 %% The properties pid, file, line, module, and function will always exist if the parser transform is used.
8
 %%
9
 %% Example:
10
@@ -51,7 +51,7 @@
11
 %%
12
 %%    `[{pid,"Unknown Pid"}]' -> "?.?.?" if pid is in the metadata, "Unknown Pid" if not.
13
 %%
14
-%%    `[{pid, ["My pid is ", pid], "Unknown Pid"}]' -> if pid is in the metada print "My pid is ?.?.?", otherwise print "Unknown Pid"
15
+%%    `[{pid, ["My pid is ", pid], ["Unknown Pid"]}]' -> if pid is in the metada print "My pid is ?.?.?", otherwise print "Unknown Pid"
16
 %% @end
17
 -spec format(lager_msg:lager_msg(),list(),list()) -> any().
18
 format(Msg,[], Colors) ->
19
@@ -86,6 +86,18 @@
20
     T;
21
 output(severity,Msg) ->
22
     atom_to_list(lager_msg:severity(Msg));
23
+output(blank,_Msg) ->
24
+    output({blank," "},_Msg);
25
+output({blank,Fill},_Msg) ->
26
+    Fill;
27
+output(sev,Msg) ->
28
+    %% Write brief acronym for the severity level (e.g. debug -> $D)
29
+    [lager_util:level_to_chr(lager_msg:severity(Msg))];
30
+output(metadata, Msg) ->
31
+    output({metadata, "=", " "}, Msg);
32
+output({metadata, IntSep, FieldSep}, Msg) ->
33
+    MD = lists:keysort(1, lager_msg:metadata(Msg)),
34
+    string:join([io_lib:format("~s~s~p", [K, IntSep, V]) || {K, V} <- MD], FieldSep);
35
 output(Prop,Msg) when is_atom(Prop) ->
36
     Metadata = lager_msg:metadata(Msg),
37
     make_printable(get_metadata(Prop,Metadata,<<"Undefined">>));
38
@@ -101,8 +113,47 @@
39
         _ ->
40
             [ output(V, Msg) || V <- Present]
41
     end;
42
+output({Prop, Present, Absent, Width}, Msg) when is_atom(Prop) ->
43
+    %% sort of like a poor man's ternary operator
44
+    Metadata = lager_msg:metadata(Msg),
45
+    case get_metadata(Prop, Metadata) of
46
+        undefined ->
47
+            [ output(V, Msg, Width) || V <- Absent];
48
+        _ ->
49
+            [ output(V, Msg, Width) || V <- Present]
50
+    end;
51
 output(Other,_) -> make_printable(Other).
52
 
53
+output(message, Msg, _Width) -> lager_msg:message(Msg);
54
+output(date,Msg, _Width) ->
55
+    {D, _T} = lager_msg:datetime(Msg),
56
+    D;
57
+output(time, Msg, _Width) ->
58
+    {_D, T} = lager_msg:datetime(Msg),
59
+    T;
60
+output(severity, Msg, Width) ->
61
+    make_printable(atom_to_list(lager_msg:severity(Msg)), Width);
62
+output(sev,Msg, _Width) ->
63
+    %% Write brief acronym for the severity level (e.g. debug -> $D)
64
+    [lager_util:level_to_chr(lager_msg:severity(Msg))];
65
+output(blank,_Msg, _Width) ->
66
+    output({blank, " "},_Msg, _Width);
67
+output({blank, Fill},_Msg, _Width) ->
68
+    Fill;
69
+output(metadata, Msg, _Width) ->
70
+    output({metadata, "=", " "}, Msg, _Width);
71
+output({metadata, IntSep, FieldSep}, Msg, _Width) ->
72
+    MD = lists:keysort(1, lager_msg:metadata(Msg)),
73
+    [string:join([io_lib:format("~s~s~p", [K, IntSep, V]) || {K, V} <- MD], FieldSep)];
74
+
75
+output(Prop, Msg, Width) when is_atom(Prop) ->
76
+    Metadata = lager_msg:metadata(Msg),
77
+    make_printable(get_metadata(Prop,Metadata,<<"Undefined">>), Width);
78
+output({Prop,Default},Msg, Width) when is_atom(Prop) ->
79
+    Metadata = lager_msg:metadata(Msg),
80
+    make_printable(get_metadata(Prop,Metadata,output(Default,Msg)), Width);
81
+output(Other,_, Width) -> make_printable(Other, Width).
82
+
83
 output_color(_Msg,[]) -> [];
84
 output_color(Msg,Colors) ->
85
     Level = lager_msg:severity(Msg),
86
@@ -117,6 +168,21 @@
87
 make_printable(L) when is_list(L) orelse is_binary(L) -> L; 
88
 make_printable(Other) -> io_lib:format("~p",[Other]).
89
 
90
+make_printable(A,W) when is_integer(W)-> string:left(make_printable(A),W);
91
+make_printable(A,{Align,W}) when is_integer(W) ->
92
+    case Align of
93
+        left ->
94
+            string:left(make_printable(A),W);
95
+        centre ->
96
+            string:centre(make_printable(A),W);
97
+        right ->
98
+            string:right(make_printable(A),W);
99
+        _ ->
100
+            string:left(make_printable(A),W)
101
+    end;
102
+
103
+make_printable(A,_W) -> make_printable(A).
104
+
105
 get_metadata(Key, Metadata) ->
106
     get_metadata(Key, Metadata, undefined).
107
 
108
@@ -164,7 +230,7 @@
109
                         [date, " ", time," [",severity,"] ",pid, " ", message, "\n"]
110
                     )))
111
         },
112
-        {"Non existant metadata can default to string",
113
+        {"Non existent metadata can default to string",
114
             ?_assertEqual(iolist_to_binary([Date, " ", Time, " [error] Fallback Message\n"]),
115
                 iolist_to_binary(format(lager_msg:new("Message",
116
                             Now,
117
@@ -174,7 +240,7 @@
118
                         [date, " ", time," [",severity,"] ",{does_not_exist,"Fallback"}, " ", message, "\n"]
119
                     )))
120
         },
121
-        {"Non existant metadata can default to other metadata",
122
+        {"Non existent metadata can default to other metadata",
123
             ?_assertEqual(iolist_to_binary([Date, " ", Time, " [error] Fallback Message\n"]),
124
                 iolist_to_binary(format(lager_msg:new("Message",
125
                             Now,
126
@@ -183,7 +249,145 @@
127
                             []),
128
                         [date, " ", time," [",severity,"] ",{does_not_exist,pid}, " ", message, "\n"]
129
                     )))
130
+        },
131
+        {"Non existent metadata can default to a string2",
132
+            ?_assertEqual(iolist_to_binary(["Unknown Pid"]),
133
+                iolist_to_binary(format(lager_msg:new("Message",
134
+                            Now,
135
+                            error,
136
+                            [],
137
+                            []),
138
+                        [{pid, ["My pid is ", pid], ["Unknown Pid"]}]
139
+                    )))
140
+        },
141
+        {"Metadata can have extra formatting",
142
+            ?_assertEqual(iolist_to_binary(["My pid is hello"]),
143
+                iolist_to_binary(format(lager_msg:new("Message",
144
+                            Now,
145
+                            error,
146
+                            [{pid, hello}],
147
+                            []),
148
+                        [{pid, ["My pid is ", pid], ["Unknown Pid"]}]
149
+                    )))
150
+        },
151
+        {"Metadata can have extra formatting1",
152
+            ?_assertEqual(iolist_to_binary(["servername"]),
153
+                iolist_to_binary(format(lager_msg:new("Message",
154
+                            Now,
155
+                            error,
156
+                            [{pid, hello}, {server, servername}],
157
+                            []),
158
+                        [{server,{pid, ["(", pid, ")"], ["(Unknown Server)"]}}]
159
+                    )))
160
+        },
161
+        {"Metadata can have extra formatting2",
162
+            ?_assertEqual(iolist_to_binary(["(hello)"]),
163
+                iolist_to_binary(format(lager_msg:new("Message",
164
+                            Now,
165
+                            error,
166
+                            [{pid, hello}],
167
+                            []),
168
+                        [{server,{pid, ["(", pid, ")"], ["(Unknown Server)"]}}]
169
+                    )))
170
+        },
171
+        {"Metadata can have extra formatting3",
172
+            ?_assertEqual(iolist_to_binary(["(Unknown Server)"]),
173
+                iolist_to_binary(format(lager_msg:new("Message",
174
+                            Now,
175
+                            error,
176
+                            [],
177
+                            []),
178
+                        [{server,{pid, ["(", pid, ")"], ["(Unknown Server)"]}}]
179
+                    )))
180
+        },
181
+        {"Metadata can be printed in its enterity",
182
+            ?_assertEqual(iolist_to_binary(["bar=2 baz=3 foo=1"]),
183
+                iolist_to_binary(format(lager_msg:new("Message",
184
+                            Now,
185
+                            error,
186
+                            [{foo, 1}, {bar, 2}, {baz, 3}],
187
+                            []),
188
+                        [metadata]
189
+                    )))
190
+        },
191
+        {"Metadata can be printed in its enterity with custom seperators",
192
+            ?_assertEqual(iolist_to_binary(["bar->2, baz->3, foo->1"]),
193
+                iolist_to_binary(format(lager_msg:new("Message",
194
+                            Now,
195
+                            error,
196
+                            [{foo, 1}, {bar, 2}, {baz, 3}],
197
+                            []),
198
+                        [{metadata, "->", ", "}]
199
+                    )))
200
+        },
201
+        {"Metadata can have extra formatting with width 1",
202
+            ?_assertEqual(iolist_to_binary(["(hello     )(hello     )(hello)(hello)(hello)"]),
203
+                iolist_to_binary(format(lager_msg:new("Message",
204
+                    Now,
205
+                    error,
206
+                    [{pid, hello}],
207
+                    []),
208
+                    ["(",{pid, [pid], "", 10},")",
209
+                        "(",{pid, [pid], "", {bad_align,10}},")",
210
+                        "(",{pid, [pid], "", bad10},")",
211
+                        "(",{pid, [pid], "", {right,bad20}},")",
212
+                        "(",{pid, [pid], "", {bad_align,bad20}},")"]
213
+                )))
214
+        },
215
+        {"Metadata can have extra formatting with width 2",
216
+            ?_assertEqual(iolist_to_binary(["(hello     )"]),
217
+                iolist_to_binary(format(lager_msg:new("Message",
218
+                    Now,
219
+                    error,
220
+                    [{pid, hello}],
221
+                    []),
222
+                    ["(",{pid, [pid], "", {left,10}},")"]
223
+                )))
224
+        },
225
+        {"Metadata can have extra formatting with width 3",
226
+            ?_assertEqual(iolist_to_binary(["(     hello)"]),
227
+                iolist_to_binary(format(lager_msg:new("Message",
228
+                    Now,
229
+                    error,
230
+                    [{pid, hello}],
231
+                    []),
232
+                    ["(",{pid, [pid], "", {right,10}},")"]
233
+                )))
234
+        },
235
+        {"Metadata can have extra formatting with width 4",
236
+            ?_assertEqual(iolist_to_binary(["(  hello   )"]),
237
+                iolist_to_binary(format(lager_msg:new("Message",
238
+                    Now,
239
+                    error,
240
+                    [{pid, hello}],
241
+                    []),
242
+                    ["(",{pid, [pid], "", {centre,10}},")"]
243
+                )))
244
+        },
245
+        {"Metadata can have extra formatting with width 5",
246
+            ?_assertEqual(iolist_to_binary(["error     |hello     ! (  hello   )"]),
247
+                iolist_to_binary(format(lager_msg:new("Message",
248
+                    Now,
249
+                    error,
250
+                    [{pid, hello}],
251
+                    []),
252
+                    [{x,"",[severity,{blank,"|"},pid], 10},"!",blank,"(",{pid, [pid], "", {centre,10}},")"]
253
+                )))
254
+        },
255
+        {"Metadata can have extra formatting with width 6",
256
+            ?_assertEqual(iolist_to_binary([Time,Date," bar=2 baz=3 foo=1 pid=hello EMessage"]),
257
+                iolist_to_binary(format(lager_msg:new("Message",
258
+                    Now,
259
+                    error,
260
+                    [{pid, hello},{foo, 1}, {bar, 2}, {baz, 3}],
261
+                    []),
262
+                    [{x,"",[time]}, {x,"",[date],20},blank,{x,"",[metadata],30},blank,{x,"",[sev],10},message, {message,message,"", {right,20}}]
263
+                )))
264
         }
265
+
266
+
267
+
268
+
269
     ].
270
 
271
 -endif.
272
lager-2.1.0.tar.gz/src/lager_file_backend.erl -> lager-3.1.0.tar.gz/src/lager_file_backend.erl Changed
263
 
1
@@ -63,6 +63,7 @@
2
         size = 0 :: integer(),
3
         date :: undefined | string(),
4
         count = 10 :: integer(),
5
+        shaper :: lager_shaper(),
6
         formatter :: atom(),
7
         formatter_config :: any(),
8
         sync_on :: {'mask', integer()},
9
@@ -74,26 +75,27 @@
10
 
11
 -type option() :: {file, string()} | {level, lager:log_level()} |
12
                   {size, non_neg_integer()} | {date, string()} |
13
-                  {count, non_neg_integer()} | {sync_interval, non_neg_integer()} |
14
+                  {count, non_neg_integer()} | {high_water_mark, non_neg_integer()} |
15
+                  {sync_interval, non_neg_integer()} |
16
                   {sync_size, non_neg_integer()} | {sync_on, lager:log_level()} |
17
                   {check_interval, non_neg_integer()} | {formatter, atom()} |
18
                   {formatter_config, term()}.
19
 
20
 -spec init([option(),...]) -> {ok, #state{}} | {error, bad_config}.
21
 init({FileName, LogLevel}) when is_list(FileName), is_atom(LogLevel) ->
22
-    %% backwards compatability hack
23
+    %% backwards compatibility hack
24
     init([{file, FileName}, {level, LogLevel}]);
25
 init({FileName, LogLevel, Size, Date, Count}) when is_list(FileName), is_atom(LogLevel) ->
26
-    %% backwards compatability hack
27
+    %% backwards compatibility hack
28
     init([{file, FileName}, {level, LogLevel}, {size, Size}, {date, Date}, {count, Count}]);
29
 init([{FileName, LogLevel, Size, Date, Count}, {Formatter,FormatterConfig}]) when is_list(FileName), is_atom(LogLevel), is_atom(Formatter) ->
30
-    %% backwards compatability hack
31
+    %% backwards compatibility hack
32
     init([{file, FileName}, {level, LogLevel}, {size, Size}, {date, Date}, {count, Count}, {formatter, Formatter}, {formatter_config, FormatterConfig}]);
33
 init([LogFile,{Formatter}]) ->
34
-    %% backwards compatability hack
35
+    %% backwards compatibility hack
36
     init([LogFile,{Formatter,[]}]);
37
 init([{FileName, LogLevel}, {Formatter,FormatterConfig}]) when is_list(FileName), is_atom(LogLevel), is_atom(Formatter) ->
38
-    %% backwards compatability hack
39
+    %% backwards compatibility hack
40
     init([{file, FileName}, {level, LogLevel}, {formatter, Formatter}, {formatter_config, FormatterConfig}]);
41
 init(LogFileConfig) when is_list(LogFileConfig) ->
42
     case validate_logfile_proplist(LogFileConfig) of
43
@@ -102,10 +104,12 @@
44
             {error, {fatal, bad_config}};
45
         Config ->
46
             %% probabably a better way to do this, but whatever
47
-            [Name, Level, Date, Size, Count, SyncInterval, SyncSize, SyncOn, CheckInterval, Formatter, FormatterConfig] =
48
-              [proplists:get_value(Key, Config) || Key <- [file, level, date, size, count, sync_interval, sync_size, sync_on, check_interval, formatter, formatter_config]],
49
+            [RelName, Level, Date, Size, Count, HighWaterMark, SyncInterval, SyncSize, SyncOn, CheckInterval, Formatter, FormatterConfig] =
50
+              [proplists:get_value(Key, Config) || Key <- [file, level, date, size, count, high_water_mark, sync_interval, sync_size, sync_on, check_interval, formatter, formatter_config]],
51
+            Name = lager_util:expand_path(RelName),
52
             schedule_rotation(Name, Date),
53
-            State0 = #state{name=Name, level=Level, size=Size, date=Date, count=Count, formatter=Formatter,
54
+            Shaper = #lager_shaper{hwm=HighWaterMark},
55
+            State0 = #state{name=Name, level=Level, size=Size, date=Date, count=Count, shaper=Shaper, formatter=Formatter,
56
                 formatter_config=FormatterConfig, sync_on=SyncOn, sync_interval=SyncInterval, sync_size=SyncSize,
57
                 check_interval=CheckInterval},
58
             State = case lager_util:open_logfile(Name, {SyncSize, SyncInterval}) of
59
@@ -129,15 +133,45 @@
60
     end;
61
 handle_call(get_loglevel, #state{level=Level} = State) ->
62
     {ok, Level, State};
63
+handle_call({set_loghwm, Hwm}, #state{shaper=Shaper, name=Name} = State) ->
64
+    case validate_logfile_proplist([{file, Name}, {high_water_mark, Hwm}]) of
65
+        false ->
66
+            {ok, {error, bad_log_hwm}, State};
67
+        _ ->
68
+            NewShaper = Shaper#lager_shaper{hwm=Hwm},
69
+            ?INT_LOG(notice, "Changed loghwm of ~s to ~p", [Name, Hwm]),
70
+            {ok, {last_loghwm, Shaper#lager_shaper.hwm}, State#state{shaper=NewShaper}}
71
+    end;
72
+handle_call(rotate, State = #state{name=File}) ->
73
+    {ok, NewState} = handle_info({rotate, File}, State),
74
+    {ok, ok, NewState};
75
 handle_call(_Request, State) ->
76
     {ok, ok, State}.
77
 
78
 %% @private
79
 handle_event({log, Message},
80
-    #state{name=Name, level=L,formatter=Formatter,formatter_config=FormatConfig} = State) ->
81
+    #state{name=Name, level=L, shaper=Shaper, formatter=Formatter,formatter_config=FormatConfig} = State) ->
82
     case lager_util:is_loggable(Message,L,{lager_file_backend, Name}) of
83
         true ->
84
-            {ok,write(State, lager_msg:timestamp(Message), lager_msg:severity_as_int(Message), Formatter:format(Message,FormatConfig)) };
85
+            case lager_util:check_hwm(Shaper) of
86
+                {true, Drop, #lager_shaper{hwm=Hwm} = NewShaper} ->
87
+                    NewState = case Drop > 0 of
88
+                        true ->
89
+                            Report = io_lib:format(
90
+                                "lager_file_backend dropped ~p messages in the last second that exceeded the limit of ~p messages/sec", 
91
+                                [Drop, Hwm]),
92
+                            ReportMsg = lager_msg:new(Report, warning, [], []),
93
+                            write(State, lager_msg:timestamp(ReportMsg),
94
+                                lager_msg:severity_as_int(ReportMsg), Formatter:format(ReportMsg, FormatConfig));
95
+                        false ->
96
+                            State
97
+                    end,
98
+                    {ok,write(NewState#state{shaper=NewShaper},
99
+                        lager_msg:timestamp(Message), lager_msg:severity_as_int(Message),
100
+                        Formatter:format(Message,FormatConfig))};
101
+                {false, _, NewShaper} ->
102
+                    {ok, State#state{shaper=NewShaper}}
103
+            end;
104
         false ->
105
             {ok, State}
106
     end;
107
@@ -147,23 +181,23 @@
108
 %% @private
109
 handle_info({rotate, File}, #state{name=File,count=Count,date=Date} = State) ->
110
     _ = lager_util:rotate_logfile(File, Count),
111
+    State1 = close_file(State),
112
     schedule_rotation(File, Date),
113
-    {ok, State};
114
+    {ok, State1};
115
 handle_info(_Info, State) ->
116
     {ok, State}.
117
 
118
 %% @private
119
-terminate(_Reason, #state{fd=FD}) ->
120
-    %% flush and close any file handles
121
-    _ = file:datasync(FD),
122
-    _ = file:close(FD),
123
+terminate(_Reason, State) ->
124
+    %% leaving this function call unmatched makes dialyzer cranky
125
+    _ = close_file(State),
126
     ok.
127
 
128
 %% @private
129
 code_change(_OldVsn, State, _Extra) ->
130
     {ok, State}.
131
 
132
-%% @private convert the config into a gen_event handler ID
133
+%% Convert the config into a gen_event handler ID
134
 config_to_id({Name,_Severity}) when is_list(Name) ->
135
     {?MODULE, Name};
136
 config_to_id({Name,_Severity,_Size,_Rotation,_Count}) ->
137
@@ -236,7 +270,7 @@
138
                     Flap
139
             end,
140
             State#state{flap=Flap2};
141
-        _ -> 
142
+        _ ->
143
             State
144
     end.
145
 
146
@@ -300,6 +334,13 @@
147
         _ ->
148
             throw({bad_config, "Invalid rotation count", Count})
149
     end;
150
+validate_logfile_proplist([{high_water_mark, HighWaterMark}|Tail], Acc) ->
151
+    case HighWaterMark of
152
+        Hwm when is_integer(Hwm), Hwm >= 0 ->
153
+            validate_logfile_proplist(Tail, [{high_water_mark, Hwm}|Acc]);
154
+        _ ->
155
+            throw({bad_config, "Invalid high water mark", HighWaterMark})
156
+    end;
157
 validate_logfile_proplist([{date, Date}|Tail], Acc) ->
158
     case lager_util:parse_rotation_date_spec(Date) of
159
         {ok, Spec} ->
160
@@ -363,6 +404,14 @@
161
     erlang:send_after(lager_util:calculate_next_rotation(Date) * 1000, self(), {rotate, Name}),
162
     ok.
163
 
164
+close_file(#state{fd=undefined} = State) ->
165
+    State;
166
+close_file(#state{fd=FD} = State) ->
167
+    %% Flush and close any file handles.
168
+    _ = file:datasync(FD),
169
+    _ = file:close(FD),
170
+    State#state{fd=undefined}.
171
+
172
 -ifdef(TEST).
173
 
174
 get_loglevel_test() ->
175
@@ -591,6 +640,16 @@
176
                         ?assert(filelib:is_regular("test.log.0"))
177
                 end
178
             },
179
+            {"rotation call should work",
180
+                fun() ->
181
+                        gen_event:add_handler(lager_event, {lager_file_backend, "test.log"}, [{file, "test.log"}, {level, info}, {check_interval, 1000}]),
182
+                        lager:log(error, self(), "Test message1"),
183
+                        lager:log(error, self(), "Test message1"),
184
+                        gen_event:call(lager_event, {lager_file_backend, "test.log"}, rotate, infinity),
185
+                        lager:log(error, self(), "Test message1"),
186
+                        ?assert(filelib:is_regular("test.log.0")) 
187
+                end
188
+            },
189
             {"sync_on option should work",
190
                 fun() ->
191
                         gen_event:add_handler(lager_event, lager_file_backend, [{file, "test.log"}, {level, info}, {sync_on, "=info"}, {check_interval, 5000}, {sync_interval, 5000}]),
192
@@ -665,8 +724,8 @@
193
                             {"test.log", critical}),
194
                         lager:error("Test message"),
195
                         ?assertEqual({ok, <<>>}, file:read_file("test.log")),
196
-                        {Level, _} = lager_config:get(loglevel),
197
-                        lager_config:set(loglevel, {Level, [{[{module,
198
+                        {Level, _} = lager_config:get({lager_event, loglevel}),
199
+                        lager_config:set({lager_event, loglevel}, {Level, [{[{module,
200
                                                 ?MODULE}], ?DEBUG,
201
                                         {lager_file_backend, "test.log"}}]}),
202
                         lager:error("Test message"),
203
@@ -683,8 +742,8 @@
204
                         {ok, Bin1} = file:read_file("test.log"),
205
                         ?assertMatch([_, _, "[critical]", _, "Test message\n"], re:split(Bin1, " ", [{return, list}, {parts, 5}])),
206
                         ok = file:delete("test.log"),
207
-                        {Level, _} = lager_config:get(loglevel),
208
-                        lager_config:set(loglevel, {Level, [{[{module,
209
+                        {Level, _} = lager_config:get({lager_event, loglevel}),
210
+                        lager_config:set({lager_event, loglevel}, {Level, [{[{module,
211
                                                 ?MODULE}], ?DEBUG,
212
                                         {lager_file_backend, "test.log"}}]}),
213
                         lager:critical("Test message"),
214
@@ -707,12 +766,31 @@
215
                         ?assertMatch([_, _, "[error]", _, "Test message\n"], re:split(Bin3, " ", [{return, list}, {parts, 5}]))
216
                 end
217
             },
218
+            {"tracing to a dedicated file should work even if root_log is set",
219
+                fun() ->
220
+                        {ok, P} = file:get_cwd(),
221
+                        file:delete(P ++ "/test_root_log/foo.log"),
222
+                        application:set_env(lager, log_root, P++"/test_root_log"),
223
+                        {ok, _} = lager:trace_file("foo.log", [{module, ?MODULE}]),
224
+                        lager:error("Test message"),
225
+                        %% not elegible for trace
226
+                        lager:log(error, self(), "Test message"),
227
+                        {ok, Bin3} = file:read_file(P++"/test_root_log/foo.log"),
228
+                        application:unset_env(lager, log_root),
229
+                        ?assertMatch([_, _, "[error]", _, "Test message\n"], re:split(Bin3, " ", [{return, list}, {parts, 5}]))
230
+                end
231
+            },
232
             {"tracing with options should work",
233
                 fun() ->
234
                         file:delete("foo.log"),
235
-                        {ok, _} = lager:trace_file("foo.log", [{module, ?MODULE}], [{size, 20}, {check_interval, 1}]), 
236
+                        {ok, _} = lager:trace_file("foo.log", [{module, ?MODULE}], [{size, 20}, {check_interval, 1}]),
237
                         lager:error("Test message"),
238
                         ?assertNot(filelib:is_regular("foo.log.0")),
239
+                        %% rotation is sensitive to intervals between
240
+                        %% writes so we sleep to exceed the 1
241
+                        %% millisecond interval specified by
242
+                        %% check_interval above
243
+                        timer:sleep(2),
244
                         lager:error("Test message"),
245
                         timer:sleep(10),
246
                         ?assert(filelib:is_regular("foo.log.0"))
247
@@ -768,6 +846,10 @@
248
             ?_assertEqual(false,
249
                 validate_logfile_proplist([{file, "test.log"}, {count, infinity}]))
250
         },
251
+        {"bad high water mark",
252
+            ?_assertEqual(false,
253
+                validate_logfile_proplist([{file, "test.log"}, {high_water_mark, infinity}]))
254
+        },
255
         {"bad date",
256
             ?_assertEqual(false,
257
                 validate_logfile_proplist([{file, "test.log"}, {date, "midnight"}]))
258
@@ -808,4 +890,3 @@
259
 
260
 
261
 -endif.
262
-
263
lager-2.1.0.tar.gz/src/lager_handler_watcher.erl -> lager-3.1.0.tar.gz/src/lager_handler_watcher.erl Changed
92
 
1
@@ -38,18 +38,18 @@
2
 -record(state, {
3
         module :: atom(),
4
         config :: any(),
5
-        event :: pid() | atom()
6
+        sink :: pid() | atom()
7
     }).
8
 
9
-start_link(Event, Module, Config) ->
10
-    gen_server:start_link(?MODULE, [Event, Module, Config], []).
11
+start_link(Sink, Module, Config) ->
12
+    gen_server:start_link(?MODULE, [Sink, Module, Config], []).
13
 
14
-start(Event, Module, Config) ->
15
-    gen_server:start(?MODULE, [Event, Module, Config], []).
16
+start(Sink, Module, Config) ->
17
+    gen_server:start(?MODULE, [Sink, Module, Config], []).
18
 
19
-init([Event, Module, Config]) ->
20
-    install_handler(Event, Module, Config),
21
-    {ok, #state{event=Event, module=Module, config=Config}}.
22
+init([Sink, Module, Config]) ->
23
+    install_handler(Sink, Module, Config),
24
+    {ok, #state{sink=Sink, module=Module, config=Config}}.
25
 
26
 handle_call(_Call, _From, State) ->
27
     {reply, ok, State}.
28
@@ -62,18 +62,18 @@
29
 handle_info({gen_event_EXIT, Module, shutdown}, #state{module=Module} = State) ->
30
     {stop, normal, State};
31
 handle_info({gen_event_EXIT, Module, Reason}, #state{module=Module,
32
-        config=Config, event=Event} = State) ->
33
+        config=Config, sink=Sink} = State) ->
34
     case lager:log(error, self(), "Lager event handler ~p exited with reason ~s",
35
         [Module, error_logger_lager_h:format_reason(Reason)]) of
36
       ok ->
37
-        install_handler(Event, Module, Config);
38
+        install_handler(Sink, Module, Config);
39
       {error, _} ->
40
         %% lager is not working, so installing a handler won't work
41
         ok
42
     end,
43
     {noreply, State};
44
-handle_info(reinstall_handler, #state{module=Module, config=Config, event=Event} = State) ->
45
-    install_handler(Event, Module, Config),
46
+handle_info(reinstall_handler, #state{module=Module, config=Config, sink=Sink} = State) ->
47
+    install_handler(Sink, Module, Config),
48
     {noreply, State};
49
 handle_info(stop, State) ->
50
     {stop, normal, State};
51
@@ -87,23 +87,33 @@
52
     {ok, State}.
53
 
54
 %% internal
55
-
56
-install_handler(Event, Module, Config) ->
57
-    case gen_event:add_sup_handler(Event, Module, Config) of
58
+install_handler(Sink, lager_backend_throttle, Config) ->
59
+    %% The lager_backend_throttle needs to know to which sink it is
60
+    %% attached, hence this admittedly ugly workaround. Handlers are
61
+    %% sensitive to the structure of the configuration sent to `init',
62
+    %% sadly, so it's not trivial to add a configuration item to be
63
+    %% ignored to backends without breaking 3rd party handlers.
64
+    install_handler2(Sink, lager_backend_throttle, [{sink, Sink}|Config]);
65
+install_handler(Sink, Module, Config) ->
66
+    install_handler2(Sink, Module, Config).
67
+
68
+%% private
69
+install_handler2(Sink, Module, Config) ->
70
+    case gen_event:add_sup_handler(Sink, Module, Config) of
71
         ok ->
72
-            ?INT_LOG(debug, "Lager installed handler ~p into ~p", [Module, Event]),
73
-            lager:update_loglevel_config(),
74
+            ?INT_LOG(debug, "Lager installed handler ~p into ~p", [Module, Sink]),
75
+            lager:update_loglevel_config(Sink),
76
             ok;
77
         {error, {fatal, Reason}} ->
78
             ?INT_LOG(error, "Lager fatally failed to install handler ~p into"
79
-                " ~p, NOT retrying: ~p", [Module, Event, Reason]),
80
+                " ~p, NOT retrying: ~p", [Module, Sink, Reason]),
81
             %% tell ourselves to stop
82
             self() ! stop,
83
             ok;
84
         Error ->
85
             %% try to reinstall it later
86
             ?INT_LOG(error, "Lager failed to install handler ~p into"
87
-               " ~p, retrying later : ~p", [Module, Event, Error]),
88
+               " ~p, retrying later : ~p", [Module, Sink, Error]),
89
             erlang:send_after(5000, self(), reinstall_handler),
90
             ok
91
     end.
92
lager-2.1.0.tar.gz/src/lager_stdlib.erl -> lager-3.1.0.tar.gz/src/lager_stdlib.erl Changed
10
 
1
@@ -39,7 +39,7 @@
2
 string_p(Term) ->
3
     string_p1(Term).
4
 
5
-string_p1([H|T]) when is_integer(H), H >= $\s, H < 255 ->
6
+string_p1([H|T]) when is_integer(H), H >= $\s, H < 256 ->
7
     string_p1(T);
8
 string_p1([$\n|T]) -> string_p1(T);
9
 string_p1([$\r|T]) -> string_p1(T);
10
lager-2.1.0.tar.gz/src/lager_sup.erl -> lager-3.1.0.tar.gz/src/lager_sup.erl Changed
17
 
1
@@ -34,9 +34,14 @@
2
 init([]) ->
3
     %% set up the config, is safe even during relups
4
     lager_config:new(),
5
+    %% TODO:
6
+    %% Always start lager_event as the default and make sure that 
7
+    %% other gen_event stuff can start up as needed 
8
+    %%
9
+    %% Maybe a new API to handle the sink and its policy?
10
     Children = [
11
         {lager, {gen_event, start_link, [{local, lager_event}]},
12
-            permanent, 5000, worker, [dynamic]},
13
+            permanent, 5000, worker, dynamic},
14
         {lager_handler_watcher_sup, {lager_handler_watcher_sup, start_link, []},
15
             permanent, 5000, supervisor, [lager_handler_watcher_sup]}],
16
 
17
lager-2.1.0.tar.gz/src/lager_transform.erl -> lager-3.1.0.tar.gz/src/lager_transform.erl Changed
302
 
1
@@ -31,8 +31,10 @@
2
 parse_transform(AST, Options) ->
3
     TruncSize = proplists:get_value(lager_truncation_size, Options, ?DEFAULT_TRUNCATION),
4
     Enable = proplists:get_value(lager_print_records_flag, Options, true),
5
+    Sinks = [lager] ++ proplists:get_value(lager_extra_sinks, Options, []),
6
     put(print_records_flag, Enable),
7
     put(truncation_size, TruncSize),
8
+    put(sinks, Sinks),
9
     erlang:put(records, []),
10
     %% .app file should either be in the outdir, or the same dir as the source file
11
     guess_application(proplists:get_value(outdir, Options), hd(AST)),
12
@@ -75,138 +77,163 @@
13
 walk_body(Acc, []) ->
14
     lists:reverse(Acc);
15
 walk_body(Acc, [H|T]) ->
16
-    walk_body([transform_statement(H)|Acc], T).
17
+    walk_body([transform_statement(H, get(sinks))|Acc], T).
18
 
19
-transform_statement({call, Line, {remote, _Line1, {atom, _Line2, lager},
20
-            {atom, _Line3, Severity}}, Arguments0} = Stmt) ->
21
-    case lists:member(Severity, ?LEVELS) of
22
+transform_statement({call, Line, {remote, _Line1, {atom, _Line2, Module},
23
+                                  {atom, _Line3, Function}}, Arguments0} = Stmt,
24
+                    Sinks) ->
25
+    case lists:member(Module, Sinks) of
26
         true ->
27
-            SeverityAsInt=lager_util:level_to_num(Severity),
28
-            DefaultAttrs0 = {cons, Line, {tuple, Line, [
29
-                        {atom, Line, module}, {atom, Line, get(module)}]},
30
-                    {cons, Line, {tuple, Line, [
31
-                                {atom, Line, function}, {atom, Line, get(function)}]},
32
-                        {cons, Line, {tuple, Line, [
33
-                                    {atom, Line, line},
34
-                                    {integer, Line, Line}]},
35
-                        {cons, Line, {tuple, Line, [
36
-                                    {atom, Line, pid},
37
-                                    {call, Line, {atom, Line, pid_to_list}, [
38
-                                            {call, Line, {atom, Line ,self}, []}]}]},
39
-                        {cons, Line, {tuple, Line, [
40
-                                    {atom, Line, node},
41
-                                    {call, Line, {atom, Line, node}, []}]},
42
-                        %% get the metadata with lager:md(), this will always return a list so we can use it as the tail here
43
-                        {call, Line, {remote, Line, {atom, Line, lager}, {atom, Line, md}}, []}}}}}},
44
-                            %{nil, Line}}}}}}},
45
-            DefaultAttrs = case erlang:get(application) of
46
-                undefined ->
47
-                    DefaultAttrs0;
48
-                App ->
49
-                    %% stick the application in the attribute list
50
-                    concat_lists({cons, Line, {tuple, Line, [
51
-                                    {atom, Line, application},
52
-                                    {atom, Line, App}]},
53
-                            {nil, Line}}, DefaultAttrs0)
54
-            end,
55
-            {Traces, Message, Arguments} = case Arguments0 of
56
-                [Format] ->
57
-                    {DefaultAttrs, Format, {atom, Line, none}};
58
-                [Arg1, Arg2] ->
59
-                    %% some ambiguity here, figure out if these arguments are
60
-                    %% [Format, Args] or [Attr, Format].
61
-                    %% The trace attributes will be a list of tuples, so check
62
-                    %% for that.
63
-                    case {element(1, Arg1), Arg1} of
64
-                        {_, {cons, _, {tuple, _, _}, _}} ->
65
-                            {concat_lists(Arg1, DefaultAttrs),
66
-                                Arg2, {atom, Line, none}};
67
-                        {Type, _} when Type == var;
68
-                                       Type == lc;
69
-                                       Type == call;
70
-                                       Type == record_field ->
71
-                            %% crap, its not a literal. look at the second
72
-                            %% argument to see if it is a string
73
-                            case Arg2 of
74
-                                {string, _, _} ->
75
-                                    {concat_lists(Arg1, DefaultAttrs),
76
-                                        Arg2, {atom, Line, none}};
77
-                                _ ->
78
-                                    %% not a string, going to have to guess
79
-                                    %% it's the argument list
80
-                                    {DefaultAttrs, Arg1, Arg2}
81
-                            end;
82
-                        _ ->
83
-                            {DefaultAttrs, Arg1, Arg2}
84
-                    end;
85
-                [Attrs, Format, Args] ->
86
-                    {concat_lists(Attrs, DefaultAttrs), Format, Args}
87
-            end,
88
-            %% Generate some unique variable names so we don't accidentaly export from case clauses.
89
-            %% Note that these are not actual atoms, but the AST treats variable names as atoms.
90
-            LevelVar = make_varname("__Level", Line),
91
-            TracesVar = make_varname("__Traces", Line),
92
-            PidVar = make_varname("__Pid", Line),
93
-            %% Wrap the call to lager_dispatch log in a case that will avoid doing any work if this message is not elegible for logging
94
-            %% case  {whereis(lager_event(lager_event), lager_config:get(loglevel, {?LOG_NONE, []})} of
95
-            {'case', Line,
96
-               {tuple, Line,
97
-                   [{call, Line, {atom, Line, whereis}, [{atom, Line, lager_event}]},
98
-                    {call, Line, {remote, Line, {atom, Line, lager_config}, {atom, Line, get}}, [{atom, Line, loglevel}, {tuple, Line, [{integer, Line, 0},{nil, Line}]}]}]},
99
-                [
100
-                    %% {undefined, _} -> {error, lager_not_running}
101
-                    {clause, Line,
102
-                        [{tuple, Line, [{atom, Line, undefined}, {var, Line, '_'}]}],
103
-                        [],
104
-                        %% trick the linter into avoiding a 'term constructed by not used' error:
105
-                        %% (fun() -> {error, lager_not_running} end)();
106
-                        [{call, Line, {'fun', Line, {clauses, [{clause, Line, [],[], [{tuple, Line, [{atom, Line, error},{atom, Line, lager_not_running}]}]}]}}, []}]},
107
-                    %% If we care about the loglevel, or there's any traces installed, we have do more checking
108
-                    %% {Level, Traces} when (Level band SeverityAsInt) /= 0 orelse Traces /= [] ->
109
-                    {clause, Line,
110
-                        [{tuple, Line, [{var, Line, PidVar}, {tuple, Line, [{var, Line, LevelVar}, {var, Line, TracesVar}]}]}],
111
-                        [[{op, Line, 'orelse',
112
-                          {op, Line, '/=', {op, Line, 'band', {var, Line, LevelVar}, {integer, Line, SeverityAsInt}}, {integer, Line, 0}},
113
-                          {op, Line, '/=', {var, Line, TracesVar}, {nil, Line}}}]],
114
-                        [
115
-                            %% do the call to lager:dispatch_log
116
-                            {call, Line, {remote, Line, {atom, Line, lager}, {atom, Line, do_log}},
117
-                                [
118
-                                    {atom,Line,Severity},
119
-                                    Traces,
120
-                                    Message,
121
-                                    Arguments,
122
-                                    {integer, Line, get(truncation_size)},
123
-                                    {integer, Line, SeverityAsInt},
124
-                                    {var, Line, LevelVar},
125
-                                    {var, Line, TracesVar},
126
-                                    {var, Line, PidVar}
127
-                                ]
128
-                            }
129
-                        ]},
130
-                    %% otherwise, do nothing
131
-                    %% _ -> ok
132
-                {clause, Line, [{var, Line, '_'}],[],[{atom, Line, ok}]}
133
-            ]};
134
+            case lists:member(Function, ?LEVELS) of
135
+                true ->
136
+                    SinkName = lager_util:make_internal_sink_name(Module),
137
+                    do_transform(Line, SinkName, Function, Arguments0);
138
+                false ->
139
+                    case lists:keyfind(Function, 1, ?LEVELS_UNSAFE) of
140
+                        {Function, Severity} ->
141
+                            SinkName = lager_util:make_internal_sink_name(Module),
142
+                            do_transform(Line, SinkName, Severity, Arguments0, unsafe);
143
+                        false ->
144
+                            Stmt
145
+                    end
146
+            end;
147
         false ->
148
-            Stmt
149
+            list_to_tuple(transform_statement(tuple_to_list(Stmt), Sinks))
150
     end;
151
-transform_statement({call, Line, {remote, Line1, {atom, Line2, boston_lager},
152
-            {atom, Line3, Severity}}, Arguments}) ->
153
-        NewArgs = case Arguments of
154
-          [{string, L, Msg}] -> [{string, L, re:replace(Msg, "r", "h", [{return, list}, global])}];
155
-          [{string, L, Format}, Args] -> [{string, L, re:replace(Format, "r", "h", [{return, list}, global])}, Args];
156
-          Other -> Other
157
-        end,
158
-        transform_statement({call, Line, {remote, Line1, {atom, Line2, lager},
159
-              {atom, Line3, Severity}}, NewArgs});
160
-transform_statement(Stmt) when is_tuple(Stmt) ->
161
-    list_to_tuple(transform_statement(tuple_to_list(Stmt)));
162
-transform_statement(Stmt) when is_list(Stmt) ->
163
-    [transform_statement(S) || S <- Stmt];
164
-transform_statement(Stmt) ->
165
+transform_statement(Stmt, Sinks) when is_tuple(Stmt) ->
166
+    list_to_tuple(transform_statement(tuple_to_list(Stmt), Sinks));
167
+transform_statement(Stmt, Sinks) when is_list(Stmt) ->
168
+    [transform_statement(S, Sinks) || S <- Stmt];
169
+transform_statement(Stmt, _Sinks) ->
170
     Stmt.
171
 
172
+do_transform(Line, SinkName, Severity, Arguments0) ->
173
+    do_transform(Line, SinkName, Severity, Arguments0, safe).
174
+
175
+do_transform(Line, SinkName, Severity, Arguments0, Safety) ->
176
+    SeverityAsInt=lager_util:level_to_num(Severity),
177
+    DefaultAttrs0 = {cons, Line, {tuple, Line, [
178
+                                                {atom, Line, module}, {atom, Line, get(module)}]},
179
+                     {cons, Line, {tuple, Line, [
180
+                                                 {atom, Line, function}, {atom, Line, get(function)}]},
181
+                      {cons, Line, {tuple, Line, [
182
+                                                  {atom, Line, line},
183
+                                                  {integer, Line, Line}]},
184
+                       {cons, Line, {tuple, Line, [
185
+                                                   {atom, Line, pid},
186
+                                                   {call, Line, {atom, Line, pid_to_list}, [
187
+                                                                                            {call, Line, {atom, Line ,self}, []}]}]},
188
+                        {cons, Line, {tuple, Line, [
189
+                                                    {atom, Line, node},
190
+                                                    {call, Line, {atom, Line, node}, []}]},
191
+                         %% get the metadata with lager:md(), this will always return a list so we can use it as the tail here
192
+                         {call, Line, {remote, Line, {atom, Line, lager}, {atom, Line, md}}, []}}}}}},
193
+                                                %{nil, Line}}}}}}},
194
+    DefaultAttrs = case erlang:get(application) of
195
+                       undefined ->
196
+                           DefaultAttrs0;
197
+                       App ->
198
+                           %% stick the application in the attribute list
199
+                           concat_lists({cons, Line, {tuple, Line, [
200
+                                                                    {atom, Line, application},
201
+                                                                    {atom, Line, App}]},
202
+                                         {nil, Line}}, DefaultAttrs0)
203
+                   end,
204
+    {Meta, Message, Arguments} = case Arguments0 of
205
+                                       [Format] ->
206
+                                           {DefaultAttrs, Format, {atom, Line, none}};
207
+                                       [Arg1, Arg2] ->
208
+                                           %% some ambiguity here, figure out if these arguments are
209
+                                           %% [Format, Args] or [Attr, Format].
210
+                                           %% The trace attributes will be a list of tuples, so check
211
+                                           %% for that.
212
+                                           case {element(1, Arg1), Arg1} of
213
+                                               {_, {cons, _, {tuple, _, _}, _}} ->
214
+                                                   {concat_lists(Arg1, DefaultAttrs),
215
+                                                    Arg2, {atom, Line, none}};
216
+                                               {Type, _} when Type == var;
217
+                                                              Type == lc;
218
+                                                              Type == call;
219
+                                                              Type == record_field ->
220
+                                                   %% crap, its not a literal. look at the second
221
+                                                   %% argument to see if it is a string
222
+                                                   case Arg2 of
223
+                                                       {string, _, _} ->
224
+                                                           {concat_lists(Arg1, DefaultAttrs),
225
+                                                            Arg2, {atom, Line, none}};
226
+                                                       _ ->
227
+                                                           %% not a string, going to have to guess
228
+                                                           %% it's the argument list
229
+                                                           {DefaultAttrs, Arg1, Arg2}
230
+                                                   end;
231
+                                               _ ->
232
+                                                   {DefaultAttrs, Arg1, Arg2}
233
+                                           end;
234
+                                       [Attrs, Format, Args] ->
235
+                                           {concat_lists(Attrs, DefaultAttrs), Format, Args}
236
+                                   end,
237
+    %% Generate some unique variable names so we don't accidentally export from case clauses.
238
+    %% Note that these are not actual atoms, but the AST treats variable names as atoms.
239
+    LevelVar = make_varname("__Level", Line),
240
+    TracesVar = make_varname("__Traces", Line),
241
+    PidVar = make_varname("__Pid", Line),
242
+    LogFun = case Safety of
243
+                 safe ->
244
+                     do_log;
245
+                 unsafe ->
246
+                     do_log_unsafe
247
+             end,
248
+    %% Wrap the call to lager:dispatch_log/6 in case that will avoid doing any work if this message is not elegible for logging
249
+    %% See lager.erl (lines 89-100) for lager:dispatch_log/6
250
+    %% case {whereis(Sink), whereis(?DEFAULT_SINK), lager_config:get({Sink, loglevel}, {?LOG_NONE, []})} of
251
+    {'case',Line,
252
+         {tuple,Line,
253
+                [{call,Line,{atom,Line,whereis},[{atom,Line,SinkName}]},
254
+                 {call,Line,{atom,Line,whereis},[{atom,Line,?DEFAULT_SINK}]}, 
255
+                 {call,Line,
256
+                       {remote,Line,{atom,Line,lager_config},{atom,Line,get}},
257
+                       [{tuple,Line,[{atom,Line,SinkName},{atom,Line,loglevel}]},
258
+                        {tuple,Line,[{integer,Line,0},{nil,Line}]}]}]},
259
+         %% {undefined, undefined, _} -> {error, lager_not_running};
260
+         [{clause,Line,
261
+                  [{tuple,Line,
262
+                          [{atom,Line,undefined},{atom,Line,undefined},{var,Line,'_'}]}],
263
+                  [],
264
+                  %% trick the linter into avoiding a 'term constructed but not used' error:
265
+                  %% (fun() -> {error, lager_not_running} end)()
266
+                  [{call, Line, {'fun', Line, {clauses, [{clause, Line, [],[], [{tuple, Line, [{atom, Line, error},{atom, Line, lager_not_running}]}]}]}}, []}]
267
+          },
268
+          %% {undefined, _, _} -> {error, {sink_not_configured, Sink}};
269
+          {clause,Line,
270
+                  [{tuple,Line,
271
+                          [{atom,Line,undefined},{var,Line,'_'},{var,Line,'_'}]}],
272
+                  [],
273
+                  %% same trick as above to avoid linter error
274
+                  [{call, Line, {'fun', Line, {clauses, [{clause, Line, [],[], [{tuple,Line, [{atom,Line,error}, {tuple,Line,[{atom,Line,sink_not_configured},{atom,Line,SinkName}]}]}]}]}}, []}] 
275
+          },
276
+          %% {SinkPid, _, {Level, Traces}} when ... -> lager:do_log/9;
277
+          {clause,Line,
278
+                  [{tuple,Line,
279
+                          [{var,Line,PidVar},
280
+                           {var,Line,'_'},
281
+                           {tuple,Line,[{var,Line,LevelVar},{var,Line,TracesVar}]}]}],
282
+                  [[{op, Line, 'orelse',
283
+                    {op, Line, '/=', {op, Line, 'band', {var, Line, LevelVar}, {integer, Line, SeverityAsInt}}, {integer, Line, 0}},
284
+                    {op, Line, '/=', {var, Line, TracesVar}, {nil, Line}}}]],
285
+                  [{call,Line,{remote, Line, {atom, Line, lager}, {atom, Line, LogFun}},
286
+                         [{atom,Line,Severity},
287
+                          Meta,
288
+                          Message,
289
+                          Arguments,
290
+                          {integer, Line, get(truncation_size)},
291
+                          {integer, Line, SeverityAsInt},
292
+                          {var, Line, LevelVar},
293
+                          {var, Line, TracesVar},
294
+                          {atom, Line, SinkName},
295
+                          {var, Line, PidVar}]}]},
296
+          %% _ -> ok
297
+          {clause,Line,[{var,Line,'_'}],[],[{atom,Line,ok}]}]}.
298
+
299
 make_varname(Prefix, Line) ->
300
     list_to_atom(Prefix ++ atom_to_list(get(module)) ++ integer_to_list(Line)).
301
 
302
lager-2.1.0.tar.gz/src/lager_trunc_io.erl -> lager-3.1.0.tar.gz/src/lager_trunc_io.erl Changed
148
 
1
@@ -3,12 +3,12 @@
2
 %% compliance with the License. You should have received a copy of the
3
 %% Erlang Public License along with your Erlang distribution. If not, it can be
4
 %% retrieved via the world wide web at http://www.erlang.org/.
5
-%% 
6
+%%
7
 %% Software distributed under the License is distributed on an "AS IS"
8
 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
9
 %% the License for the specific language governing rights and limitations
10
 %% under the License.
11
-%% 
12
+%%
13
 %% The Initial Developer of the Original Code is Corelatus AB.
14
 %% Portions created by Corelatus are Copyright 2003, Corelatus
15
 %% AB. All Rights Reserved.''
16
@@ -32,7 +32,7 @@
17
 
18
 -module(lager_trunc_io).
19
 -author('matthias@corelatus.se').
20
-%% And thanks to Chris Newcombe for a bug fix 
21
+%% And thanks to Chris Newcombe for a bug fix
22
 -export([format/3, format/4, print/2, print/3, fprint/2, fprint/3, safe/2]). % interface functions
23
 -version("$Id: trunc_io.erl,v 1.11 2009-02-23 12:01:06 matthias Exp $").
24
 
25
@@ -76,11 +76,11 @@
26
 %% @doc Returns an flattened list containing the ASCII representation of the given
27
 %% term.
28
 -spec fprint(term(), pos_integer(), options()) -> string().
29
-fprint(T, Max, Options) -> 
30
+fprint(T, Max, Options) ->
31
     {L, _} = print(T, Max, prepare_options(Options, #print_options{})),
32
     lists:flatten(L).
33
 
34
-%% @doc Same as print, but never crashes. 
35
+%% @doc Same as print, but never crashes.
36
 %%
37
 %% This is a tradeoff. Print might conceivably crash if it's asked to
38
 %% print something it doesn't understand, for example some new data
39
@@ -88,7 +88,7 @@
40
 %% to io_lib to format the term, but then the formatting is
41
 %% depth-limited instead of length limited, so you might run out
42
 %% memory printing it. Out of the frying pan and into the fire.
43
-%% 
44
+%%
45
 -spec safe(term(), pos_integer()) -> {string(), pos_integer()} | {string()}.
46
 safe(What, Len) ->
47
     case catch print(What, Len) of
48
@@ -114,8 +114,8 @@
49
 print(_, _, #print_options{depth=0}) -> {"...", 3};
50
 
51
 
52
-%% @doc We assume atoms, floats, funs, integers, PIDs, ports and refs never need 
53
-%% to be truncated. This isn't strictly true, someone could make an 
54
+%% @doc We assume atoms, floats, funs, integers, PIDs, ports and refs never need
55
+%% to be truncated. This isn't strictly true, someone could make an
56
 %% arbitrarily long bignum. Let's assume that won't happen unless someone
57
 %% is being malicious.
58
 %%
59
@@ -214,15 +214,15 @@
60
     SizeStr = integer_to_list(Size),
61
     {[ValueStr, $:, SizeStr], length(ValueStr) + length(SizeStr) +1};
62
 print(BitString, Max, Options) when is_bitstring(BitString) ->
63
-    case byte_size(BitString) > Max of
64
+    BL = case byte_size(BitString) > Max of
65
         true ->
66
-            BL = binary_to_list(BitString, 1, Max);
67
+            binary_to_list(BitString, 1, Max);
68
         _ ->
69
             R = erlang:bitstring_to_list(BitString),
70
             {Bytes, [Bits]} = lists:splitwith(fun erlang:is_integer/1, R),
71
             %% tag the trailing bits with a special tuple we catch when
72
             %% list_body calls print again
73
-            BL = Bytes ++ [{inline_bitstring, Bits}]
74
+            Bytes ++ [{inline_bitstring, Bits}]
75
     end,
76
     {X, Len0} = list_body(BL, Max - 4, dec_depth(Options), true),
77
     {["<<", X, ">>"], Len0 + 4};
78
@@ -265,7 +265,7 @@
79
     {RC, Len} = record_fields(Fields, Max - length(Leader) + 1, dec_depth(Options)),
80
     {[Leader, RC, "}"], Len + length(Leader) + 1};
81
 
82
-print(Tuple, Max, Options) when is_tuple(Tuple) -> 
83
+print(Tuple, Max, Options) when is_tuple(Tuple) ->
84
     {TC, Len} = tuple_contents(Tuple, Max-2, Options),
85
     {[${, TC, $}], Len + 2};
86
 
87
@@ -307,7 +307,7 @@
88
         false -> $|
89
     end,
90
     {[List ++ [Sep | "..."]], Len + 4};
91
-list_body([H|T], Max, Options, Tuple) -> 
92
+list_body([H|T], Max, Options, Tuple) ->
93
     {List, Len} = print(H, Max, Options),
94
     {Final, FLen} = list_bodyc(T, Max - Len, Options, Tuple),
95
     {[List|Final], FLen + Len};
96
@@ -319,7 +319,7 @@
97
 list_bodyc(_, Max, _Options, _Tuple) when Max < 5 -> {",...", 4};
98
 list_bodyc(_, _Max, #print_options{depth=1}, true) -> {",...", 4};
99
 list_bodyc(_, _Max, #print_options{depth=1}, false) -> {"|...", 4};
100
-list_bodyc([H|T], Max, #print_options{depth=Depth} = Options, Tuple) -> 
101
+list_bodyc([H|T], Max, #print_options{depth=Depth} = Options, Tuple) ->
102
     {List, Len} = print(H, Max, dec_depth(Options)),
103
     {Final, FLen} = list_bodyc(T, Max - Len - 1, dec_depth(Options), Tuple),
104
     Sep = case Depth == 1 andalso not Tuple of
105
@@ -553,7 +553,7 @@
106
     test(M,F),
107
     perf(M,F,Reps-1);
108
 perf(_,_,_) ->
109
-    done.    
110
+    done.
111
 
112
 %% Performance test. Needs a particularly large term I saved as a binary...
113
 -spec perf1() -> {non_neg_integer(), non_neg_integer()}.
114
@@ -570,7 +570,7 @@
115
     ?assertEqual("[\"foo\",98,97,114]", lists:flatten(format("~p", [["foo", $b, $a, $r]], 50))),
116
     ?assertEqual("[\"foo\",98,97,114]", lists:flatten(format("~P", [["foo", $b, $a, $r], 10], 50))),
117
     ?assertEqual("[[102,111,111],98,97,114]", lists:flatten(format("~w", [["foo", $b, $a, $r]], 50))),
118
-    
119
+
120
     %% complex ones
121
     ?assertEqual("    foobar", lists:flatten(format("~10s", [["foo", $b, $a, $r]], 50))),
122
     ?assertEqual("f", lists:flatten(format("~1s", [["foo", $b, $a, $r]], 50))),
123
@@ -836,7 +836,7 @@
124
     ?assertEqual("[1|...]", lists:flatten(format("~P", [[1, 2, 3], 2], 50))),
125
     ?assertEqual("[1,2|...]", lists:flatten(format("~P", [[1, 2, 3], 3], 50))),
126
     ?assertEqual("[1,2,3]", lists:flatten(format("~P", [[1, 2, 3], 4], 50))),
127
-    
128
+
129
     ?assertEqual("{1,...}", lists:flatten(format("~P", [{1, 2, 3}, 2], 50))),
130
     ?assertEqual("{1,2,...}", lists:flatten(format("~P", [{1, 2, 3}, 3], 50))),
131
     ?assertEqual("{1,2,3}", lists:flatten(format("~P", [{1, 2, 3}, 4], 50))),
132
@@ -845,13 +845,13 @@
133
     ?assertEqual("[1,2|...]", lists:flatten(format("~P", [[1, 2, <<3>>], 3], 50))),
134
     ?assertEqual("[1,2,<<...>>]", lists:flatten(format("~P", [[1, 2, <<3>>], 4], 50))),
135
     ?assertEqual("[1,2,<<3>>]", lists:flatten(format("~P", [[1, 2, <<3>>], 5], 50))),
136
-    
137
+
138
     ?assertEqual("<<...>>", lists:flatten(format("~P", [<<0, 0, 0, 0>>, 1], 50))),
139
     ?assertEqual("<<0,...>>", lists:flatten(format("~P", [<<0, 0, 0, 0>>, 2], 50))),
140
     ?assertEqual("<<0,0,...>>", lists:flatten(format("~P", [<<0, 0, 0, 0>>, 3], 50))),
141
     ?assertEqual("<<0,0,0,...>>", lists:flatten(format("~P", [<<0, 0, 0, 0>>, 4], 50))),
142
     ?assertEqual("<<0,0,0,0>>", lists:flatten(format("~P", [<<0, 0, 0, 0>>, 5], 50))),
143
-    
144
+
145
     %% this is a seriously weird edge case
146
     ?assertEqual("<<\"   \"...>>", lists:flatten(format("~P", [<<32, 32, 32, 0>>, 2], 50))),
147
     ?assertEqual("<<\"   \"...>>", lists:flatten(format("~P", [<<32, 32, 32, 0>>, 3], 50))),
148
lager-2.1.0.tar.gz/src/lager_util.erl -> lager-3.1.0.tar.gz/src/lager_util.erl Changed
167
 
1
@@ -18,11 +18,12 @@
2
 
3
 -include_lib("kernel/include/file.hrl").
4
 
5
--export([levels/0, level_to_num/1, num_to_level/1, config_to_mask/1, config_to_levels/1, mask_to_levels/1,
6
+-export([levels/0, level_to_num/1, level_to_chr/1,
7
+        num_to_level/1, config_to_mask/1, config_to_levels/1, mask_to_levels/1,
8
         open_logfile/2, ensure_logfile/4, rotate_logfile/2, format_time/0, format_time/1,
9
         localtime_ms/0, localtime_ms/1, maybe_utc/1, parse_rotation_date_spec/1,
10
         calculate_next_rotation/1, validate_trace/1, check_traces/4, is_loggable/3,
11
-        trace_filter/1, trace_filter/2]).
12
+        trace_filter/1, trace_filter/2, expand_path/1, check_hwm/1, make_internal_sink_name/1]).
13
 
14
 -ifdef(TEST).
15
 -include_lib("eunit/include/eunit.hrl").
16
@@ -33,25 +34,35 @@
17
 levels() ->
18
     [debug, info, notice, warning, error, critical, alert, emergency, none].
19
 
20
-level_to_num(debug)     -> ?DEBUG;
21
-level_to_num(info)      -> ?INFO;
22
-level_to_num(notice)    -> ?NOTICE;
23
-level_to_num(warning)   -> ?WARNING;
24
-level_to_num(error)     -> ?ERROR;
25
-level_to_num(critical)  -> ?CRITICAL;
26
-level_to_num(alert)     -> ?ALERT;
27
-level_to_num(emergency) -> ?EMERGENCY;
28
-level_to_num(none)      -> ?LOG_NONE.
29
-
30
-num_to_level(?DEBUG) -> debug;
31
-num_to_level(?INFO) -> info;
32
-num_to_level(?NOTICE) -> notice;
33
-num_to_level(?WARNING) -> warning;
34
-num_to_level(?ERROR) -> error;
35
-num_to_level(?CRITICAL) -> critical;
36
-num_to_level(?ALERT) -> alert;
37
+level_to_num(debug)      -> ?DEBUG;
38
+level_to_num(info)       -> ?INFO;
39
+level_to_num(notice)     -> ?NOTICE;
40
+level_to_num(warning)    -> ?WARNING;
41
+level_to_num(error)      -> ?ERROR;
42
+level_to_num(critical)   -> ?CRITICAL;
43
+level_to_num(alert)      -> ?ALERT;
44
+level_to_num(emergency)  -> ?EMERGENCY;
45
+level_to_num(none)       -> ?LOG_NONE.
46
+
47
+level_to_chr(debug)      -> $D;
48
+level_to_chr(info)       -> $I;
49
+level_to_chr(notice)     -> $N;
50
+level_to_chr(warning)    -> $W;
51
+level_to_chr(error)      -> $E;
52
+level_to_chr(critical)   -> $C;
53
+level_to_chr(alert)      -> $A;
54
+level_to_chr(emergency)  -> $M;
55
+level_to_chr(none)       -> $ .
56
+
57
+num_to_level(?DEBUG)     -> debug;
58
+num_to_level(?INFO)      -> info;
59
+num_to_level(?NOTICE)    -> notice;
60
+num_to_level(?WARNING)   -> warning;
61
+num_to_level(?ERROR)     -> error;
62
+num_to_level(?CRITICAL)  -> critical;
63
+num_to_level(?ALERT)     -> alert;
64
 num_to_level(?EMERGENCY) -> emergency;
65
-num_to_level(?LOG_NONE) -> none.
66
+num_to_level(?LOG_NONE)  -> none.
67
 
68
 -spec config_to_mask(atom()|string()) -> {'mask', integer()}.
69
 config_to_mask(Conf) ->
70
@@ -467,6 +478,67 @@
71
 i3l(I) when I < 100 -> [$0 | i2l(I)];
72
 i3l(I)              -> integer_to_list(I).
73
 
74
+%% When log_root option is provided, get the real path to a file
75
+expand_path(RelPath) ->
76
+    case application:get_env(lager, log_root) of
77
+        {ok, LogRoot} when is_list(LogRoot) -> % Join relative path
78
+            %% check if the given RelPath contains LogRoot, if so, do not add
79
+            %% it again; see gh #304
80
+            case string:str(filename:dirname(RelPath), LogRoot) of
81
+                X when X > 0 ->
82
+                    RelPath;
83
+                _Zero ->
84
+                    filename:join(LogRoot, RelPath)
85
+            end;
86
+        undefined -> % No log_root given, keep relative path
87
+            RelPath
88
+    end.
89
+
90
+%% Log rate limit, i.e. high water mark for incoming messages
91
+
92
+check_hwm(Shaper = #lager_shaper{hwm = undefined}) ->
93
+    {true, 0, Shaper};
94
+check_hwm(Shaper = #lager_shaper{mps = Mps, hwm = Hwm}) when Mps < Hwm ->
95
+    %% haven't hit high water mark yet, just log it
96
+    {true, 0, Shaper#lager_shaper{mps=Mps+1}};
97
+check_hwm(Shaper = #lager_shaper{lasttime = Last, dropped = Drop}) ->
98
+    %% are we still in the same second?
99
+    {M, S, _} = Now = os:timestamp(),
100
+    case Last of
101
+        {M, S, _} ->
102
+            %% still in same second, but have exceeded the high water mark
103
+            NewDrops = discard_messages(Now, 0),
104
+            {false, 0, Shaper#lager_shaper{dropped=Drop+NewDrops}};
105
+        _ ->
106
+            %% different second, reset all counters and allow it
107
+            {true, Drop, Shaper#lager_shaper{dropped = 0, mps=1, lasttime = Now}}
108
+    end.
109
+
110
+discard_messages(Second, Count) ->
111
+    {M, S, _} = os:timestamp(),
112
+    case Second of
113
+        {M, S, _} ->
114
+            receive
115
+                %% we only discard gen_event notifications, because
116
+                %% otherwise we might discard gen_event internal
117
+                %% messages, such as trapped EXITs
118
+                {notify, _Event} ->
119
+                    discard_messages(Second, Count+1)
120
+            after 0 ->
121
+                    Count
122
+            end;
123
+        _ ->
124
+            Count
125
+    end.
126
+
127
+%% @private Build an atom for the gen_event process based on a sink name.
128
+%% For historical reasons, the default gen_event process for lager itself is named
129
+%% `lager_event'. For all other sinks, it is SinkName++`_lager_event'
130
+make_internal_sink_name(lager) ->
131
+    ?DEFAULT_SINK;
132
+make_internal_sink_name(Sink) ->
133
+    list_to_atom(atom_to_list(Sink) ++ "_lager_event").
134
+
135
 -ifdef(TEST).
136
 
137
 parse_test() ->
138
@@ -707,4 +779,28 @@
139
     ?assertEqual([debug, notice, error], mask_to_levels(?DEBUG bor ?NOTICE bor ?ERROR)),
140
     ok.
141
 
142
+expand_path_test() ->
143
+    OldRootVal = application:get_env(lager, log_root),
144
+
145
+    ok = application:unset_env(lager, log_root),
146
+    ?assertEqual("/foo/bar", expand_path("/foo/bar")),
147
+    ?assertEqual("foo/bar", expand_path("foo/bar")),
148
+
149
+    ok = application:set_env(lager, log_root, "log/dir"),
150
+    ?assertEqual("/foo/bar", expand_path("/foo/bar")), % Absolute path should not be changed
151
+    ?assertEqual("log/dir/foo/bar", expand_path("foo/bar")),
152
+    ?assertEqual("log/dir/foo/bar", expand_path("log/dir/foo/bar")), %% gh #304
153
+
154
+    case OldRootVal of
155
+        undefined -> application:unset_env(lager, log_root);
156
+        {ok, Root} -> application:set_env(lager, log_root, Root)
157
+    end,
158
+    ok.
159
+
160
+sink_name_test_() ->
161
+    [
162
+        ?_assertEqual(lager_event, make_internal_sink_name(lager)),
163
+        ?_assertEqual(audit_lager_event, make_internal_sink_name(audit))
164
+    ].
165
+
166
 -endif.
167
lager-3.1.0.tar.gz/test/compress_pr_record_test.erl Added
18
 
1
@@ -0,0 +1,16 @@
2
+-module(compress_pr_record_test).
3
+
4
+-compile([{parse_transform, lager_transform}]).
5
+
6
+-record(a, {field1, field2, foo, bar, baz, zyu, zix}).
7
+
8
+-ifdef(TEST).
9
+-include_lib("eunit/include/eunit.hrl").
10
+-endif.
11
+
12
+nested_record_test() ->
13
+    A = #a{field1 = "Notice me senpai"},
14
+    Pr_A = lager:pr(A, ?MODULE),
15
+    Pr_A_Comp = lager:pr(A, ?MODULE, [compress]),
16
+    ?assertMatch({'$lager_record', a, [{field1, "Notice me senpai"}, {field2, undefined} | _]}, Pr_A),
17
+    ?assertEqual({'$lager_record', a, [{field1, "Notice me senpai"}]}, Pr_A_Comp).
18
lager-2.1.0.tar.gz/test/crash.erl -> lager-3.1.0.tar.gz/test/crash.erl Changed
12
 
1
@@ -10,8 +10,8 @@
2
 -export([start/0]).
3
 
4
 -record(state, {
5
-        host,
6
-        port
7
+        host :: term(),
8
+        port :: term()
9
     }).
10
 
11
 start() ->
12
lager-3.1.0.tar.gz/test/lager_rotate.erl Added
140
 
1
@@ -0,0 +1,138 @@
2
+-module(lager_rotate).
3
+
4
+-compile(export_all).
5
+
6
+-ifdef(TEST).
7
+-include_lib("eunit/include/eunit.hrl").
8
+-endif.
9
+
10
+
11
+rotate_test_() ->
12
+    {foreach,
13
+        fun() ->
14
+                file:write_file("test1.log", ""),
15
+                file:write_file("test2.log", ""),
16
+                file:write_file("test3.log", ""),
17
+                file:delete("test1.log.0"),
18
+                file:delete("test2.log.0"),
19
+                file:delete("test3.log.0"),
20
+                error_logger:tty(false),
21
+                application:load(lager),
22
+                application:set_env(lager, handlers, 
23
+                    [{lager_file_backend, [{file, "test1.log"}, {level, info}]},
24
+                     {lager_file_backend, [{file, "test2.log"}, {level, info}]}]),
25
+                application:set_env(lager, extra_sinks,
26
+                    [{sink_event, 
27
+                        [{handlers, 
28
+                            [{lager_file_backend, [{file, "test3.log"}, {level, info}]}]}
29
+                        ]}]),
30
+                application:set_env(lager, error_logger_redirect, false),
31
+                application:set_env(lager, async_threshold, undefined),
32
+                lager:start()
33
+        end,
34
+        fun(_) ->
35
+                file:delete("test1.log"),
36
+                file:delete("test2.log"),
37
+                file:delete("test3.log"),
38
+                file:delete("test1.log.0"),
39
+                file:delete("test2.log.0"),
40
+                file:delete("test3.log.0"),
41
+                application:stop(lager),
42
+                application:stop(goldrush),
43
+                error_logger:tty(true)
44
+        end,
45
+        [{"Rotate single file",
46
+            fun() ->
47
+                lager:log(error, self(), "Test message 1"),
48
+                lager:log(sink_event, error, self(), "Sink test message 1", []),
49
+                lager:rotate_handler({lager_file_backend, "test1.log"}),
50
+                timer:sleep(1000),
51
+                true = filelib:is_regular("test1.log.0"),
52
+                lager:log(error, self(), "Test message 2"),
53
+                lager:log(sink_event, error, self(), "Sink test message 2", []),
54
+                
55
+                {ok, File1} = file:read_file("test1.log"),
56
+                {ok, File2} = file:read_file("test2.log"),
57
+                {ok, SinkFile} = file:read_file("test3.log"),
58
+                {ok, File1Old} = file:read_file("test1.log.0"),
59
+
60
+                have_no_log(File1, <<"Test message 1">>),
61
+                have_log(File1, <<"Test message 2">>),
62
+
63
+                have_log(File2, <<"Test message 1">>),
64
+                have_log(File2, <<"Test message 2">>),
65
+
66
+                have_log(File1Old, <<"Test message 1">>),
67
+                have_no_log(File1Old, <<"Test message 2">>),
68
+
69
+                have_log(SinkFile, <<"Sink test message 1">>),
70
+                have_log(SinkFile, <<"Sink test message 2">>)
71
+            end},
72
+         {"Rotate sink",
73
+            fun() ->
74
+                lager:log(error, self(), "Test message 1"),
75
+                lager:log(sink_event, error, self(), "Sink test message 1", []),
76
+                lager:rotate_sink(sink_event),
77
+                timer:sleep(1000),
78
+                true = filelib:is_regular("test3.log.0"),
79
+                lager:log(error, self(), "Test message 2"),
80
+                lager:log(sink_event, error, self(), "Sink test message 2", []),
81
+                {ok, File1} = file:read_file("test1.log"),
82
+                {ok, File2} = file:read_file("test2.log"),
83
+                {ok, SinkFile} = file:read_file("test3.log"),
84
+                {ok, SinkFileOld} = file:read_file("test3.log.0"),
85
+
86
+                have_log(File1, <<"Test message 1">>),
87
+                have_log(File1, <<"Test message 2">>),
88
+
89
+                have_log(File2, <<"Test message 1">>),
90
+                have_log(File2, <<"Test message 2">>),
91
+
92
+                have_log(SinkFileOld, <<"Sink test message 1">>),
93
+                have_no_log(SinkFileOld, <<"Sink test message 2">>),
94
+
95
+                have_no_log(SinkFile, <<"Sink test message 1">>),
96
+                have_log(SinkFile, <<"Sink test message 2">>)
97
+            end},
98
+         {"Rotate all",
99
+            fun() ->
100
+                lager:log(error, self(), "Test message 1"),
101
+                lager:log(sink_event, error, self(), "Sink test message 1", []),
102
+                lager:rotate_all(),
103
+                timer:sleep(1000),
104
+                true = filelib:is_regular("test3.log.0"),
105
+                lager:log(error, self(), "Test message 2"),
106
+                lager:log(sink_event, error, self(), "Sink test message 2", []),
107
+                {ok, File1} = file:read_file("test1.log"),
108
+                {ok, File2} = file:read_file("test2.log"),
109
+                {ok, SinkFile} = file:read_file("test3.log"),
110
+                {ok, File1Old} = file:read_file("test1.log.0"),
111
+                {ok, File2Old} = file:read_file("test2.log.0"),
112
+                {ok, SinkFileOld} = file:read_file("test3.log.0"),
113
+
114
+                have_no_log(File1, <<"Test message 1">>),
115
+                have_log(File1, <<"Test message 2">>),
116
+
117
+                have_no_log(File2, <<"Test message 1">>),
118
+                have_log(File2, <<"Test message 2">>),
119
+
120
+                have_no_log(SinkFile, <<"Sink test message 1">>),
121
+                have_log(SinkFile, <<"Sink test message 2">>),
122
+
123
+                have_log(SinkFileOld, <<"Sink test message 1">>),
124
+                have_no_log(SinkFileOld, <<"Sink test message 2">>),
125
+
126
+                have_log(File1Old, <<"Test message 1">>),
127
+                have_no_log(File1Old, <<"Test message 2">>),
128
+
129
+                have_log(File2Old, <<"Test message 1">>),
130
+                have_no_log(File2Old, <<"Test message 2">>)
131
+
132
+            end}]}.
133
+
134
+have_log(Data, Log) ->
135
+    {_,_} = binary:match(Data, Log).
136
+
137
+have_no_log(Data, Log) ->
138
+    nomatch = binary:match(Data, Log).
139
+
140
lager-2.1.0.tar.gz/test/lager_test_backend.erl -> lager-3.1.0.tar.gz/test/lager_test_backend.erl Changed
1257
 
1
@@ -1,4 +1,6 @@
2
-%% Copyright (c) 2011-2012 Basho Technologies, Inc.  All Rights Reserved.
3
+%% -------------------------------------------------------------------
4
+%%
5
+%% Copyright (c) 2011-2015 Basho Technologies, Inc.
6
 %%
7
 %% This file is provided to you under the Apache License,
8
 %% Version 2.0 (the "License"); you may not use this file
9
@@ -13,6 +15,8 @@
10
 %% KIND, either express or implied.  See the License for the
11
 %% specific language governing permissions and limitations
12
 %% under the License.
13
+%%
14
+%% -------------------------------------------------------------------
15
 
16
 -module(lager_test_backend).
17
 
18
@@ -23,12 +27,15 @@
19
 -export([init/1, handle_call/2, handle_event/2, handle_info/2, terminate/2,
20
         code_change/3]).
21
 
22
--record(state, {level, buffer, ignored}).
23
--record(test, {attrs, format, args}).
24
--compile([{parse_transform, lager_transform}]).
25
+-define(TEST_SINK_NAME, '__lager_test_sink').              %% <-- used by parse transform
26
+-define(TEST_SINK_EVENT, '__lager_test_sink_lager_event'). %% <-- used by lager API calls and internals for gen_event
27
+
28
+-record(state, {level :: list(), buffer :: list(), ignored :: term()}).
29
+-compile({parse_transform, lager_transform}).
30
 
31
 -ifdef(TEST).
32
 -include_lib("eunit/include/eunit.hrl").
33
+-record(test, {attrs :: list(), format :: list(), args :: list()}).
34
 -export([pop/0, count/0, count_ignored/0, flush/0, print_state/0]).
35
 -endif.
36
 
37
@@ -89,28 +96,63 @@
38
 -ifdef(TEST).
39
 
40
 pop() ->
41
-    gen_event:call(lager_event, ?MODULE, pop).
42
+    pop(lager_event).
43
 
44
 count() ->
45
-    gen_event:call(lager_event, ?MODULE, count).
46
+    count(lager_event).
47
 
48
 count_ignored() ->
49
-    gen_event:call(lager_event, ?MODULE, count_ignored).
50
+    count_ignored(lager_event).
51
 
52
 flush() ->
53
-    gen_event:call(lager_event, ?MODULE, flush).
54
+    flush(lager_event).
55
 
56
 print_state() ->
57
-    gen_event:call(lager_event, ?MODULE, print_state).
58
+    print_state(lager_event).
59
 
60
 print_bad_state() ->
61
-    gen_event:call(lager_event, ?MODULE, print_bad_state).
62
+    print_bad_state(lager_event).
63
+
64
+pop(Sink) ->
65
+    gen_event:call(Sink, ?MODULE, pop).
66
+
67
+count(Sink) ->
68
+    gen_event:call(Sink, ?MODULE, count).
69
+
70
+count_ignored(Sink) ->
71
+    gen_event:call(Sink, ?MODULE, count_ignored).
72
+
73
+flush(Sink) ->
74
+    gen_event:call(Sink, ?MODULE, flush).
75
+
76
+print_state(Sink) ->
77
+    gen_event:call(Sink, ?MODULE, print_state).
78
+
79
+print_bad_state(Sink) ->
80
+    gen_event:call(Sink, ?MODULE, print_bad_state).
81
+
82
 
83
 has_line_numbers() ->
84
     %% are we R15 or greater
85
-    Rel = erlang:system_info(otp_release),
86
-    {match, [Major]} = re:run(Rel, "(?|(^R(\\d+)[A|B](|0(\\d)))|(^(\\d+)$))", [{capture, [2], list}]),
87
-    list_to_integer(Major) >= 15.
88
+    % this gets called a LOT - cache the answer
89
+    case erlang:get({?MODULE, has_line_numbers}) of
90
+        undefined ->
91
+            R = otp_version() >= 15,
92
+            erlang:put({?MODULE, has_line_numbers}, R),
93
+            R;
94
+        Bool ->
95
+            Bool
96
+    end.
97
+
98
+otp_version() ->
99
+    otp_version(erlang:system_info(otp_release)).
100
+
101
+otp_version([$R | Rel]) ->
102
+    {Ver, _} = string:to_integer(Rel),
103
+    Ver;
104
+otp_version(Rel) ->
105
+    {Ver, _} = string:to_integer(Rel),
106
+    Ver.
107
 
108
 not_running_test() ->
109
     ?assertEqual({error, lager_not_running}, lager:log(info, self(), "not running")).
110
@@ -126,6 +168,11 @@
111
                         ?assertEqual(0, count())
112
                 end
113
             },
114
+            {"test sink not running",
115
+                fun() ->
116
+                         ?assertEqual({error, {sink_not_configured, test}}, lager:log(test, info, self(), "~p", "not running"))
117
+                end
118
+            },
119
             {"logging works",
120
                 fun() ->
121
                         lager:warning("test message"),
122
@@ -136,6 +183,16 @@
123
                         ok
124
                 end
125
             },
126
+            {"unsafe logging works",
127
+                fun() ->
128
+                        lager:warning_unsafe("test message"),
129
+                        ?assertEqual(1, count()),
130
+                        {Level, _Time, Message, _Metadata}  = pop(),
131
+                        ?assertMatch(Level, lager_util:level_to_num(warning)),
132
+                        ?assertEqual("test message", Message),
133
+                        ok
134
+                end
135
+            },
136
             {"logging with arguments works",
137
                 fun() ->
138
                         lager:warning("test message ~p", [self()]),
139
@@ -146,6 +203,16 @@
140
                         ok
141
                 end
142
             },
143
+            {"unsafe logging with args works",
144
+                fun() ->
145
+                        lager:warning("test message ~p", [self()]),
146
+                        ?assertEqual(1, count()),
147
+                        {Level, _Time, Message,_Metadata}  = pop(),
148
+                        ?assertMatch(Level, lager_util:level_to_num(warning)),
149
+                        ?assertEqual(lists:flatten(io_lib:format("test message ~p", [self()])), lists:flatten(Message)),
150
+                        ok
151
+                end
152
+            },
153
             {"logging works from inside a begin/end block",
154
                 fun() ->
155
                         ?assertEqual(0, count()),
156
@@ -469,6 +536,39 @@
157
                         ok
158
                 end
159
             },
160
+            {"stopped trace stops and removes its event handler - default sink (gh#267)",
161
+                fun() ->
162
+                        Sink = ?DEFAULT_SINK,
163
+                        StartHandlers = gen_event:which_handlers(Sink),
164
+                        {_, T0} = lager_config:get({Sink, loglevel}),
165
+                        StartGlobal = lager_config:global_get(handlers),
166
+                        ?assertEqual([], T0),
167
+                        {ok, TestTrace1} = lager:trace_file("/tmp/test", [{a,b}]),
168
+                        MidHandlers = gen_event:which_handlers(Sink),
169
+                        {ok, TestTrace2} = lager:trace_file("/tmp/test", [{c,d}]),
170
+                        MidHandlers = gen_event:which_handlers(Sink),
171
+                        ?assertEqual(length(StartHandlers)+1, length(MidHandlers)),
172
+                        MidGlobal = lager_config:global_get(handlers),
173
+                        ?assertEqual(length(StartGlobal)+1, length(MidGlobal)),
174
+                        {_, T1} = lager_config:get({Sink, loglevel}),
175
+                        ?assertEqual(2, length(T1)),
176
+                        ok = lager:stop_trace(TestTrace1),
177
+                        {_, T2} = lager_config:get({Sink, loglevel}),
178
+                        ?assertEqual(1, length(T2)),
179
+                        ?assertEqual(length(StartHandlers)+1, length(
180
+                                                                gen_event:which_handlers(Sink))),
181
+
182
+                        ?assertEqual(length(StartGlobal)+1, length(lager_config:global_get(handlers))),
183
+                        ok = lager:stop_trace(TestTrace2),
184
+                        EndHandlers = gen_event:which_handlers(?DEFAULT_SINK),
185
+                        EndGlobal = lager_config:global_get(handlers),
186
+                        {_, T3} = lager_config:get({Sink, loglevel}),
187
+                        ?assertEqual([], T3),
188
+                        ?assertEqual(StartHandlers, EndHandlers),
189
+                        ?assertEqual(StartGlobal, EndGlobal),
190
+                        ok
191
+                end
192
+            },
193
             {"record printing works",
194
                 fun() ->
195
                         print_state(),
196
@@ -545,6 +645,13 @@
197
                         ok
198
                 end
199
             },
200
+            {"unsafe messages really are not truncated",
201
+                fun() ->
202
+                        lager:info_unsafe("doom, doom has come upon you all ~p", [string:copies("doom", 1500)]),
203
+                        {_, _, Msg,_Metadata} = pop(),
204
+                        ?assert(length(lists:flatten(Msg)) == 6035)
205
+                end
206
+            },
207
             {"can't store invalid metadata",
208
                 fun() ->
209
                         ?assertEqual(ok, lager:md([{platypus, gravid}, {sloth, hirsute}, {duck, erroneous}])),
210
@@ -556,6 +663,132 @@
211
         ]
212
     }.
213
 
214
+extra_sinks_test_() ->
215
+    {foreach,
216
+        fun setup_sink/0,
217
+        fun cleanup/1,
218
+        [
219
+            {"observe that there is nothing up my sleeve",
220
+                fun() ->
221
+                        ?assertEqual(undefined, pop(?TEST_SINK_EVENT)),
222
+                        ?assertEqual(0, count(?TEST_SINK_EVENT))
223
+                end
224
+            },
225
+            {"logging works",
226
+                fun() ->
227
+                        ?TEST_SINK_NAME:warning("test message"),
228
+                        ?assertEqual(1, count(?TEST_SINK_EVENT)),
229
+                        {Level, _Time, Message, _Metadata}  = pop(?TEST_SINK_EVENT),
230
+                        ?assertMatch(Level, lager_util:level_to_num(warning)),
231
+                        ?assertEqual("test message", Message),
232
+                        ok
233
+                end
234
+            },
235
+            {"logging with arguments works",
236
+                fun() ->
237
+                        ?TEST_SINK_NAME:warning("test message ~p", [self()]),
238
+                        ?assertEqual(1, count(?TEST_SINK_EVENT)),
239
+                        {Level, _Time, Message,_Metadata}  = pop(?TEST_SINK_EVENT),
240
+                        ?assertMatch(Level, lager_util:level_to_num(warning)),
241
+                        ?assertEqual(lists:flatten(io_lib:format("test message ~p", [self()])), lists:flatten(Message)),
242
+                        ok
243
+                end
244
+            },
245
+            {"variables inplace of literals in logging statements work",
246
+                fun() ->
247
+                        ?assertEqual(0, count(?TEST_SINK_EVENT)),
248
+                        Attr = [{a, alpha}, {b, beta}],
249
+                        Fmt = "format ~p",
250
+                        Args = [world],
251
+                        ?TEST_SINK_NAME:info(Attr, "hello"),
252
+                        ?TEST_SINK_NAME:info(Attr, "hello ~p", [world]),
253
+                        ?TEST_SINK_NAME:info(Fmt, [world]),
254
+                        ?TEST_SINK_NAME:info("hello ~p", Args),
255
+                        ?TEST_SINK_NAME:info(Attr, "hello ~p", Args),
256
+                        ?TEST_SINK_NAME:info([{d, delta}, {g, gamma}], Fmt, Args),
257
+                        ?assertEqual(6, count(?TEST_SINK_EVENT)),
258
+                        {_Level, _Time, Message, Metadata}  = pop(?TEST_SINK_EVENT),
259
+                        ?assertMatch([{a, alpha}, {b, beta}|_], Metadata),
260
+                        ?assertEqual("hello", lists:flatten(Message)),
261
+                        {_Level, _Time2, Message2, _Metadata2}  = pop(?TEST_SINK_EVENT),
262
+                        ?assertEqual("hello world", lists:flatten(Message2)),
263
+                        {_Level, _Time3, Message3, _Metadata3}  = pop(?TEST_SINK_EVENT),
264
+                        ?assertEqual("format world", lists:flatten(Message3)),
265
+                        {_Level, _Time4, Message4, _Metadata4}  = pop(?TEST_SINK_EVENT),
266
+                        ?assertEqual("hello world", lists:flatten(Message4)),
267
+                        {_Level, _Time5, Message5, _Metadata5}  = pop(?TEST_SINK_EVENT),
268
+                        ?assertEqual("hello world", lists:flatten(Message5)),
269
+                        {_Level, _Time6, Message6, Metadata6}  = pop(?TEST_SINK_EVENT),
270
+                        ?assertMatch([{d, delta}, {g, gamma}|_], Metadata6),
271
+                        ?assertEqual("format world", lists:flatten(Message6)),
272
+                        ok
273
+                end
274
+            },
275
+            {"stopped trace stops and removes its event handler - test sink (gh#267)",
276
+                fun() ->
277
+                        Sink = ?TEST_SINK_EVENT,
278
+                        StartHandlers = gen_event:which_handlers(Sink),
279
+                        {_, T0} = lager_config:get({Sink, loglevel}),
280
+                        StartGlobal = lager_config:global_get(handlers),
281
+                        ?assertEqual([], T0),
282
+                        {ok, TestTrace1} = lager:trace_file("/tmp/test", [{sink, Sink}, {a,b}]),
283
+                        MidHandlers = gen_event:which_handlers(Sink),
284
+                        {ok, TestTrace2} = lager:trace_file("/tmp/test", [{sink, Sink}, {c,d}]),
285
+                        MidHandlers = gen_event:which_handlers(Sink),
286
+                        ?assertEqual(length(StartHandlers)+1, length(MidHandlers)),
287
+                        MidGlobal = lager_config:global_get(handlers),
288
+                        ?assertEqual(length(StartGlobal)+1, length(MidGlobal)),
289
+                        {_, T1} = lager_config:get({Sink, loglevel}),
290
+                        ?assertEqual(2, length(T1)),
291
+                        ok = lager:stop_trace(TestTrace1),
292
+                        {_, T2} = lager_config:get({Sink, loglevel}),
293
+                        ?assertEqual(1, length(T2)),
294
+                        ?assertEqual(length(StartHandlers)+1, length(
295
+                                                                gen_event:which_handlers(Sink))),
296
+
297
+                        ?assertEqual(length(StartGlobal)+1, length(lager_config:global_get(handlers))),
298
+                        ok = lager:stop_trace(TestTrace2),
299
+                        EndHandlers = gen_event:which_handlers(Sink),
300
+                        EndGlobal = lager_config:global_get(handlers),
301
+                        {_, T3} = lager_config:get({Sink, loglevel}),
302
+                        ?assertEqual([], T3),
303
+                        ?assertEqual(StartHandlers, EndHandlers),
304
+                        ?assertEqual(StartGlobal, EndGlobal),
305
+                        ok
306
+                end
307
+            },
308
+            {"log messages below the threshold are ignored",
309
+                fun() ->
310
+                        ?assertEqual(0, count(?TEST_SINK_EVENT)),
311
+                        ?TEST_SINK_NAME:debug("this message will be ignored"),
312
+                        ?assertEqual(0, count(?TEST_SINK_EVENT)),
313
+                        ?assertEqual(0, count_ignored(?TEST_SINK_EVENT)),
314
+                        lager_config:set({?TEST_SINK_EVENT, loglevel}, {element(2, lager_util:config_to_mask(debug)), []}),
315
+                        ?TEST_SINK_NAME:debug("this message should be ignored"),
316
+                        ?assertEqual(0, count(?TEST_SINK_EVENT)),
317
+                        ?assertEqual(1, count_ignored(?TEST_SINK_EVENT)),
318
+                        lager:set_loglevel(?TEST_SINK_EVENT, ?MODULE, undefined, debug),
319
+                        ?assertEqual({?DEBUG bor ?INFO bor ?NOTICE bor ?WARNING bor ?ERROR bor ?CRITICAL bor ?ALERT bor ?EMERGENCY, []}, lager_config:get({?TEST_SINK_EVENT, loglevel})),
320
+                        ?TEST_SINK_NAME:debug("this message should be logged"),
321
+                        ?assertEqual(1, count(?TEST_SINK_EVENT)),
322
+                        ?assertEqual(1, count_ignored(?TEST_SINK_EVENT)),
323
+                        ?assertEqual(debug, lager:get_loglevel(?TEST_SINK_EVENT, ?MODULE)),
324
+                        ok
325
+                end
326
+            }
327
+    ]
328
+    }.
329
+
330
+setup_sink() ->
331
+    error_logger:tty(false),
332
+    application:load(lager),
333
+    application:set_env(lager, handlers, []),
334
+    application:set_env(lager, error_logger_redirect, false),
335
+    application:set_env(lager, extra_sinks, [{?TEST_SINK_EVENT, [{handlers, [{?MODULE, info}]}]}]),
336
+    lager:start(),
337
+    gen_event:call(lager_event, ?MODULE, flush),
338
+    gen_event:call(?TEST_SINK_EVENT, ?MODULE, flush).
339
+
340
 setup() ->
341
     error_logger:tty(false),
342
     application:load(lager),
343
@@ -579,107 +812,171 @@
344
 test_body(Expected, Actual) ->
345
     case has_line_numbers() of
346
         true ->
347
-            FileLine = string:substr(Actual, length(Expected)+1),
348
-            Body = string:substr(Actual, 1, length(Expected)),
349
+            ExLen = length(Expected),
350
+            {Body, Rest} = case length(Actual) > ExLen of
351
+                true ->
352
+                    {string:substr(Actual, 1, ExLen),
353
+                        string:substr(Actual, (ExLen + 1))};
354
+                _ ->
355
+                    {Actual, []}
356
+            end,
357
             ?assertEqual(Expected, Body),
358
-            case string:substr(FileLine, 1, 6) of
359
+            % OTP-17 (and maybe later releases) may tack on additional info
360
+            % about the failure, so if Actual starts with Expected (already
361
+            % confirmed by having gotten past assertEqual above) and ends
362
+            % with " line NNN" we can ignore what's in-between. By extension,
363
+            % since there may not be line information appended at all, any
364
+            % text we DO find is reportable, but not a test failure.
365
+            case Rest of
366
                 [] ->
367
-                    %% sometimes there's no line information...
368
-                    ?assert(true);
369
-                " line " ->
370
-                    ?assert(true);
371
-                Other ->
372
-                    ?debugFmt("unexpected trailing data ~p", [Other]),
373
-                    ?assert(false)
374
+                    ok;
375
+                _ ->
376
+                    % isolate the extra data and report it if it's not just
377
+                    % a line number indicator
378
+                    case re:run(Rest, "^.*( line \\d+)$", [{capture, [1]}]) of
379
+                        nomatch ->
380
+                            ?debugFmt(
381
+                                "Trailing data \"~s\" following \"~s\"",
382
+                                [Rest, Expected]);
383
+                        {match, [{0, _}]} ->
384
+                            % the whole sting is " line NNN"
385
+                            ok;
386
+                        {match, [{Off, _}]} ->
387
+                            ?debugFmt(
388
+                                "Trailing data \"~s\" following \"~s\"",
389
+                                [string:substr(Rest, 1, Off), Expected])
390
+                    end
391
             end;
392
-        false ->
393
+        _ ->
394
             ?assertEqual(Expected, Actual)
395
     end.
396
 
397
+error_logger_redirect_crash_setup() ->
398
+    error_logger:tty(false),
399
+    application:load(lager),
400
+    application:set_env(lager, error_logger_redirect, true),
401
+    application:set_env(lager, handlers, [{?MODULE, error}]),
402
+    lager:start(),
403
+    crash:start(),
404
+    lager_event.
405
+
406
+error_logger_redirect_crash_setup_sink() ->
407
+    error_logger:tty(false),
408
+    application:load(lager),
409
+    application:set_env(lager, error_logger_redirect, true),
410
+    application:unset_env(lager, handlers),
411
+    application:set_env(lager, extra_sinks, [
412
+        {error_logger_lager_event, [
413
+            {handlers, [{?MODULE, error}]}]}]),
414
+    lager:start(),
415
+    crash:start(),
416
+    error_logger_lager_event.
417
+
418
+error_logger_redirect_crash_cleanup(_Sink) ->
419
+    application:stop(lager),
420
+    application:stop(goldrush),
421
+    application:unset_env(lager, extra_sinks),
422
+    case whereis(crash) of
423
+        undefined -> ok;
424
+        Pid -> exit(Pid, kill)
425
+    end,
426
+    error_logger:tty(true).
427
 
428
 error_logger_redirect_crash_test_() ->
429
-    TestBody=fun(Name,CrashReason,Expected) -> {Name,
430
+    TestBody=fun(Name,CrashReason,Expected) -> 
431
+        fun(Sink) ->
432
+            {Name,
433
                 fun() ->
434
                         Pid = whereis(crash),
435
                         crash(CrashReason),
436
-                        {Level, _, Msg,Metadata} = pop(),
437
+                        {Level, _, Msg,Metadata} = pop(Sink),
438
                         test_body(Expected, lists:flatten(Msg)),
439
                         ?assertEqual(Pid,proplists:get_value(pid,Metadata)),
440
                         ?assertEqual(lager_util:level_to_num(error),Level)
441
                 end
442
-            } 
443
+            }
444
+        end
445
     end,
446
-    {foreach,
447
-        fun() ->
448
-                error_logger:tty(false),
449
-                application:load(lager),
450
-                application:set_env(lager, error_logger_redirect, true),
451
-                application:set_env(lager, handlers, [{?MODULE, error}]),
452
-                lager:start(),
453
-                crash:start()
454
-        end,
455
-
456
-        fun(_) ->
457
-                application:stop(lager),
458
-                application:stop(goldrush),
459
-                case whereis(crash) of
460
-                    undefined -> ok;
461
-                    Pid -> exit(Pid, kill)
462
-                end,
463
-                error_logger:tty(true)
464
-        end,
465
-        [
466
+    Tests = [
467
+        fun(Sink) -> 
468
             {"again, there is nothing up my sleeve",
469
                 fun() ->
470
-                        ?assertEqual(undefined, pop()),
471
-                        ?assertEqual(0, count())
472
+                        ?assertEqual(undefined, pop(Sink)),
473
+                        ?assertEqual(0, count(Sink))
474
                 end
475
-            },
476
+            }
477
+        end,
478
 
479
-            TestBody("bad return value",bad_return,"gen_server crash terminated with reason: bad return value: bleh"),
480
-            TestBody("bad return value with string",bad_return_string,"gen_server crash terminated with reason: bad return value: {tuple,{tuple,\"string\"}}"),
481
-            TestBody("bad return uncaught throw",throw,"gen_server crash terminated with reason: bad return value: a_ball"),
482
-            TestBody("case clause",case_clause,"gen_server crash terminated with reason: no case clause matching {} in crash:handle_call/3"),
483
-            TestBody("case clause string",case_clause_string,"gen_server crash terminated with reason: no case clause matching \"crash\" in crash:handle_call/3"),
484
-            TestBody("function clause",function_clause,"gen_server crash terminated with reason: no function clause matching crash:function({})"),
485
-            TestBody("if clause",if_clause,"gen_server crash terminated with reason: no true branch found while evaluating if expression in crash:handle_call/3"),
486
-            TestBody("try clause",try_clause,"gen_server crash terminated with reason: no try clause matching [] in crash:handle_call/3"),
487
-            TestBody("undefined function",undef,"gen_server crash terminated with reason: call to undefined function crash:booger/0 from crash:handle_call/3"),
488
-            TestBody("bad math",badarith,"gen_server crash terminated with reason: bad arithmetic expression in crash:handle_call/3"),
489
-            TestBody("bad match",badmatch,"gen_server crash terminated with reason: no match of right hand value {} in crash:handle_call/3"),
490
-            TestBody("bad arity",badarity,"gen_server crash terminated with reason: fun called with wrong arity of 1 instead of 3 in crash:handle_call/3"),
491
-            TestBody("bad arg1",badarg1,"gen_server crash terminated with reason: bad argument in crash:handle_call/3"),
492
-            TestBody("bad arg2",badarg2,"gen_server crash terminated with reason: bad argument in call to erlang:iolist_to_binary([\"foo\",bar]) in crash:handle_call/3"),
493
-            TestBody("bad record",badrecord,"gen_server crash terminated with reason: bad record state in crash:handle_call/3"),
494
-            TestBody("noproc",noproc,"gen_server crash terminated with reason: no such process or port in call to gen_event:call(foo, bar, baz)"),
495
-            TestBody("badfun",badfun,"gen_server crash terminated with reason: bad function booger in crash:handle_call/3")
496
-        ]
497
-    }.
498
+        TestBody("bad return value",bad_return,"gen_server crash terminated with reason: bad return value: bleh"),
499
+        TestBody("bad return value with string",bad_return_string,"gen_server crash terminated with reason: bad return value: {tuple,{tuple,\"string\"}}"),
500
+        TestBody("bad return uncaught throw",throw,"gen_server crash terminated with reason: bad return value: a_ball"),
501
+        TestBody("case clause",case_clause,"gen_server crash terminated with reason: no case clause matching {} in crash:handle_call/3"),
502
+        TestBody("case clause string",case_clause_string,"gen_server crash terminated with reason: no case clause matching \"crash\" in crash:handle_call/3"),
503
+        TestBody("function clause",function_clause,"gen_server crash terminated with reason: no function clause matching crash:function({})"),
504
+        TestBody("if clause",if_clause,"gen_server crash terminated with reason: no true branch found while evaluating if expression in crash:handle_call/3"),
505
+        TestBody("try clause",try_clause,"gen_server crash terminated with reason: no try clause matching [] in crash:handle_call/3"),
506
+        TestBody("undefined function",undef,"gen_server crash terminated with reason: call to undefined function crash:booger/0 from crash:handle_call/3"),
507
+        TestBody("bad math",badarith,"gen_server crash terminated with reason: bad arithmetic expression in crash:handle_call/3"),
508
+        TestBody("bad match",badmatch,"gen_server crash terminated with reason: no match of right hand value {} in crash:handle_call/3"),
509
+        TestBody("bad arity",badarity,"gen_server crash terminated with reason: fun called with wrong arity of 1 instead of 3 in crash:handle_call/3"),
510
+        TestBody("bad arg1",badarg1,"gen_server crash terminated with reason: bad argument in crash:handle_call/3"),
511
+        TestBody("bad arg2",badarg2,"gen_server crash terminated with reason: bad argument in call to erlang:iolist_to_binary([\"foo\",bar]) in crash:handle_call/3"),
512
+        TestBody("bad record",badrecord,"gen_server crash terminated with reason: bad record state in crash:handle_call/3"),
513
+        TestBody("noproc",noproc,"gen_server crash terminated with reason: no such process or port in call to gen_event:call(foo, bar, baz)"),
514
+        TestBody("badfun",badfun,"gen_server crash terminated with reason: bad function booger in crash:handle_call/3")
515
+    ],
516
+    {"Error logger redirect crash", [
517
+        {"Redirect to default sink", 
518
+            {foreach,
519
+            fun error_logger_redirect_crash_setup/0,
520
+            fun error_logger_redirect_crash_cleanup/1,
521
+            Tests}},
522
+        {"Redirect to error_logger_lager_event sink",
523
+            {foreach,
524
+            fun error_logger_redirect_crash_setup_sink/0,
525
+            fun error_logger_redirect_crash_cleanup/1,
526
+            Tests}}
527
+    ]}.
528
 
529
-error_logger_redirect_test_() ->
530
-    {foreach,
531
-        fun() ->
532
-                error_logger:tty(false),
533
-                application:load(lager),
534
-                application:set_env(lager, error_logger_redirect, true),
535
-                application:set_env(lager, handlers, [{?MODULE, info}]),
536
-                lager:start(),
537
-                lager:log(error, self(), "flush flush"),
538
-                timer:sleep(100),
539
-                gen_event:call(lager_event, ?MODULE, flush)
540
-        end,
541
 
542
-        fun(_) ->
543
-                application:stop(lager),
544
-                application:stop(goldrush),
545
-                error_logger:tty(true)
546
-        end,
547
-        [
548
+error_logger_redirect_setup() ->
549
+    error_logger:tty(false),
550
+    application:load(lager),
551
+    application:set_env(lager, error_logger_redirect, true),
552
+    application:set_env(lager, handlers, [{?MODULE, info}]),
553
+    lager:start(),
554
+    lager:log(error, self(), "flush flush"),
555
+    timer:sleep(100),
556
+    gen_event:call(lager_event, ?MODULE, flush),
557
+    lager_event.
558
+
559
+error_logger_redirect_setup_sink() ->
560
+    error_logger:tty(false),
561
+    application:load(lager),
562
+    application:set_env(lager, error_logger_redirect, true),
563
+    application:unset_env(lager, handlers),
564
+    application:set_env(lager, extra_sinks, [
565
+        {error_logger_lager_event, [
566
+            {handlers, [{?MODULE, info}]}]}]),
567
+    lager:start(),
568
+    lager:log(error_logger_lager_event, error, self(), "flush flush", []),
569
+    timer:sleep(100),
570
+    gen_event:call(error_logger_lager_event, ?MODULE, flush),
571
+    error_logger_lager_event.
572
+
573
+error_logger_redirect_cleanup(_) ->
574
+    application:stop(lager),
575
+    application:stop(goldrush),
576
+    application:unset_env(lager, extra_sinks),
577
+    error_logger:tty(true).
578
+
579
+error_logger_redirect_test_() ->
580
+    Tests = [
581
             {"error reports are printed",
582
-                fun() ->
583
+                fun(Sink) ->
584
                         sync_error_logger:error_report([{this, is}, a, {silly, format}]),
585
                         _ = gen_event:which_handlers(error_logger),
586
-                        {Level, _, Msg,Metadata} = pop(),
587
+                        {Level, _, Msg,Metadata} = pop(Sink),
588
                         ?assertEqual(lager_util:level_to_num(error),Level),
589
                         ?assertEqual(self(),proplists:get_value(pid,Metadata)),
590
                         Expected = "this: is, a, silly: format",
591
@@ -688,10 +985,10 @@
592
                 end
593
             },
594
             {"string error reports are printed",
595
-                fun() ->
596
+                fun(Sink) ->
597
                         sync_error_logger:error_report("this is less silly"),
598
                         _ = gen_event:which_handlers(error_logger),
599
-                        {Level, _, Msg,Metadata} = pop(),
600
+                        {Level, _, Msg,Metadata} = pop(Sink),
601
                         ?assertEqual(lager_util:level_to_num(error),Level),
602
                         ?assertEqual(self(),proplists:get_value(pid,Metadata)),
603
                         Expected = "this is less silly",
604
@@ -699,29 +996,40 @@
605
                 end
606
             },
607
             {"error messages are printed",
608
-                fun() ->
609
+                fun(Sink) ->
610
                         sync_error_logger:error_msg("doom, doom has come upon you all"),
611
                         _ = gen_event:which_handlers(error_logger),
612
-                        {Level, _, Msg,Metadata} = pop(),
613
+                        {Level, _, Msg,Metadata} = pop(Sink),
614
                         ?assertEqual(lager_util:level_to_num(error),Level),
615
                         ?assertEqual(self(),proplists:get_value(pid,Metadata)),
616
                         Expected = "doom, doom has come upon you all",
617
                         ?assertEqual(Expected, lists:flatten(Msg))
618
                 end
619
             },
620
+            {"error messages with unicode characters in Args are printed",
621
+                fun(Sink) ->
622
+                        sync_error_logger:error_msg("~ts", ["Привет!"]),
623
+                        _ = gen_event:which_handlers(error_logger),
624
+                        {Level, _, Msg,Metadata} = pop(Sink),
625
+                        ?assertEqual(lager_util:level_to_num(error),Level),
626
+                        ?assertEqual(self(),proplists:get_value(pid,Metadata)),
627
+                        ?assertEqual("Привет!", lists:flatten(Msg))
628
+                end
629
+            },
630
             {"error messages are truncated at 4096 characters",
631
-                fun() ->
632
+                fun(Sink) ->
633
                         sync_error_logger:error_msg("doom, doom has come upon you all ~p", [string:copies("doom", 10000)]),
634
                         _ = gen_event:which_handlers(error_logger),
635
-                        {_, _, Msg,_Metadata} = pop(),
636
+                        {_, _, Msg,_Metadata} = pop(Sink),
637
                         ?assert(length(lists:flatten(Msg)) < 5100)
638
                 end
639
             },
640
+
641
             {"info reports are printed",
642
-                fun() ->
643
+                fun(Sink) ->
644
                         sync_error_logger:info_report([{this, is}, a, {silly, format}]),
645
                         _ = gen_event:which_handlers(error_logger),
646
-                        {Level, _, Msg,Metadata} = pop(),
647
+                        {Level, _, Msg,Metadata} = pop(Sink),
648
                         ?assertEqual(lager_util:level_to_num(info),Level),
649
                         ?assertEqual(self(),proplists:get_value(pid,Metadata)),
650
                         Expected = "this: is, a, silly: format",
651
@@ -729,144 +1037,165 @@
652
                 end
653
             },
654
             {"info reports are truncated at 4096 characters",
655
-                fun() ->
656
+                fun(Sink) ->
657
                         sync_error_logger:info_report([[{this, is}, a, {silly, format}] || _ <- lists:seq(0, 600)]),
658
                         _ = gen_event:which_handlers(error_logger),
659
-                        {Level, _, Msg,Metadata} = pop(),
660
+                        {Level, _, Msg,Metadata} = pop(Sink),
661
                         ?assertEqual(lager_util:level_to_num(info),Level),
662
                         ?assertEqual(self(),proplists:get_value(pid,Metadata)),
663
                         ?assert(length(lists:flatten(Msg)) < 5000)
664
                 end
665
             },
666
             {"single term info reports are printed",
667
-                fun() ->
668
+                fun(Sink) ->
669
                         sync_error_logger:info_report({foolish, bees}),
670
                         _ = gen_event:which_handlers(error_logger),
671
-                        {Level, _, Msg,Metadata} = pop(),
672
+                        {Level, _, Msg,Metadata} = pop(Sink),
673
                         ?assertEqual(lager_util:level_to_num(info),Level),
674
                         ?assertEqual(self(),proplists:get_value(pid,Metadata)),
675
                         ?assertEqual("{foolish,bees}", lists:flatten(Msg))
676
                 end
677
             },
678
             {"single term error reports are printed",
679
-                fun() ->
680
+                fun(Sink) ->
681
                         sync_error_logger:error_report({foolish, bees}),
682
                         _ = gen_event:which_handlers(error_logger),
683
-                        {Level, _, Msg,Metadata} = pop(),
684
+                        {Level, _, Msg,Metadata} = pop(Sink),
685
                         ?assertEqual(lager_util:level_to_num(error),Level),
686
                         ?assertEqual(self(),proplists:get_value(pid,Metadata)),
687
                         ?assertEqual("{foolish,bees}", lists:flatten(Msg))
688
                 end
689
             },
690
             {"string info reports are printed",
691
-                fun() ->
692
+                fun(Sink) ->
693
                         sync_error_logger:info_report("this is less silly"),
694
                         _ = gen_event:which_handlers(error_logger),
695
-                        {Level, _, Msg,Metadata} = pop(),
696
+                        {Level, _, Msg,Metadata} = pop(Sink),
697
                         ?assertEqual(lager_util:level_to_num(info),Level),
698
                         ?assertEqual(self(),proplists:get_value(pid,Metadata)),
699
                         ?assertEqual("this is less silly", lists:flatten(Msg))
700
                 end
701
             },
702
             {"string info reports are truncated at 4096 characters",
703
-                fun() ->
704
+                fun(Sink) ->
705
                         sync_error_logger:info_report(string:copies("this is less silly", 1000)),
706
                         _ = gen_event:which_handlers(error_logger),
707
-                        {Level, _, Msg,Metadata} = pop(),
708
+                        {Level, _, Msg,Metadata} = pop(Sink),
709
                         ?assertEqual(lager_util:level_to_num(info),Level),
710
                         ?assertEqual(self(),proplists:get_value(pid,Metadata)),
711
                         ?assert(length(lists:flatten(Msg)) < 5100)
712
                 end
713
             },
714
             {"strings in a mixed report are printed as strings",
715
-                fun() ->
716
+                fun(Sink) ->
717
                         sync_error_logger:info_report(["this is less silly", {than, "this"}]),
718
                         _ = gen_event:which_handlers(error_logger),
719
-                        {Level, _, Msg,Metadata} = pop(),
720
+                        {Level, _, Msg,Metadata} = pop(Sink),
721
                         ?assertEqual(lager_util:level_to_num(info),Level),
722
                         ?assertEqual(self(),proplists:get_value(pid,Metadata)),
723
                         ?assertEqual("\"this is less silly\", than: \"this\"", lists:flatten(Msg))
724
                 end
725
             },
726
             {"info messages are printed",
727
-                fun() ->
728
+                fun(Sink) ->
729
                         sync_error_logger:info_msg("doom, doom has come upon you all"),
730
                         _ = gen_event:which_handlers(error_logger),
731
-                        {Level, _, Msg,Metadata} = pop(),
732
+                        {Level, _, Msg,Metadata} = pop(Sink),
733
                         ?assertEqual(lager_util:level_to_num(info),Level),
734
                         ?assertEqual(self(),proplists:get_value(pid,Metadata)),
735
                         ?assertEqual("doom, doom has come upon you all", lists:flatten(Msg))
736
                 end
737
             },
738
             {"info messages are truncated at 4096 characters",
739
-                fun() ->
740
+                fun(Sink) ->
741
                         sync_error_logger:info_msg("doom, doom has come upon you all ~p", [string:copies("doom", 10000)]),
742
                         _ = gen_event:which_handlers(error_logger),
743
-                        {Level, _, Msg,Metadata} = pop(),
744
+                        {Level, _, Msg,Metadata} = pop(Sink),
745
                         ?assertEqual(lager_util:level_to_num(info),Level),
746
                         ?assertEqual(self(),proplists:get_value(pid,Metadata)),
747
                         ?assert(length(lists:flatten(Msg)) < 5100)
748
                 end
749
             },
750
+            {"info messages with unicode characters in Args are printed",
751
+                fun(Sink) ->
752
+                        sync_error_logger:info_msg("~ts", ["Привет!"]),
753
+                        _ = gen_event:which_handlers(error_logger),
754
+                        {Level, _, Msg,Metadata} = pop(Sink),
755
+                        ?assertEqual(lager_util:level_to_num(info),Level),
756
+                        ?assertEqual(self(),proplists:get_value(pid,Metadata)),
757
+                        ?assertEqual("Привет!", lists:flatten(Msg))
758
+                end
759
+            },
760
+            {"warning messages with unicode characters in Args are printed",
761
+                fun(Sink) ->
762
+                        sync_error_logger:warning_msg("~ts", ["Привет!"]),
763
+                        Map = error_logger:warning_map(),
764
+                        _ = gen_event:which_handlers(error_logger),
765
+                        {Level, _, Msg,Metadata} = pop(Sink),
766
+                        ?assertEqual(lager_util:level_to_num(Map),Level),
767
+                        ?assertEqual(self(),proplists:get_value(pid,Metadata)),
768
+                        ?assertEqual("Привет!", lists:flatten(Msg))
769
+                end
770
+            },
771
 
772
             {"warning messages are printed at the correct level",
773
-                fun() ->
774
+                fun(Sink) ->
775
                         sync_error_logger:warning_msg("doom, doom has come upon you all"),
776
                         Map = error_logger:warning_map(),
777
                         _ = gen_event:which_handlers(error_logger),
778
-                        {Level, _, Msg,Metadata} = pop(),
779
+                        {Level, _, Msg,Metadata} = pop(Sink),
780
                         ?assertEqual(lager_util:level_to_num(Map),Level),
781
                         ?assertEqual(self(),proplists:get_value(pid,Metadata)),
782
                         ?assertEqual("doom, doom has come upon you all", lists:flatten(Msg))
783
                 end
784
             },
785
             {"warning reports are printed at the correct level",
786
-                fun() ->
787
+                fun(Sink) ->
788
                         sync_error_logger:warning_report([{i, like}, pie]),
789
                         Map = error_logger:warning_map(),
790
                         _ = gen_event:which_handlers(error_logger),
791
-                        {Level, _, Msg,Metadata} = pop(),
792
+                        {Level, _, Msg,Metadata} = pop(Sink),
793
                         ?assertEqual(lager_util:level_to_num(Map),Level),
794
                         ?assertEqual(self(),proplists:get_value(pid,Metadata)),
795
                         ?assertEqual("i: like, pie", lists:flatten(Msg))
796
                 end
797
             },
798
             {"single term warning reports are printed at the correct level",
799
-                fun() ->
800
+                fun(Sink) ->
801
                         sync_error_logger:warning_report({foolish, bees}),
802
                         Map = error_logger:warning_map(),
803
                         _ = gen_event:which_handlers(error_logger),
804
-                        {Level, _, Msg,Metadata} = pop(),
805
+                        {Level, _, Msg,Metadata} = pop(Sink),
806
                         ?assertEqual(lager_util:level_to_num(Map),Level),
807
                         ?assertEqual(self(),proplists:get_value(pid,Metadata)),
808
                         ?assertEqual("{foolish,bees}", lists:flatten(Msg))
809
                 end
810
             },
811
             {"application stop reports",
812
-                fun() ->
813
+                fun(Sink) ->
814
                         sync_error_logger:info_report([{application, foo}, {exited, quittin_time}, {type, lazy}]),
815
                         _ = gen_event:which_handlers(error_logger),
816
-                        {Level, _, Msg,Metadata} = pop(),
817
+                        {Level, _, Msg,Metadata} = pop(Sink),
818
                         ?assertEqual(lager_util:level_to_num(info),Level),
819
                         ?assertEqual(self(),proplists:get_value(pid,Metadata)),
820
                         ?assertEqual("Application foo exited with reason: quittin_time", lists:flatten(Msg))
821
                 end
822
             },
823
             {"supervisor reports",
824
-                fun() ->
825
+                fun(Sink) ->
826
                         sync_error_logger:error_report(supervisor_report, [{errorContext, france}, {offender, [{name, mini_steve}, {mfargs, {a, b, [c]}}, {pid, bleh}]}, {reason, fired}, {supervisor, {local, steve}}]),
827
                         _ = gen_event:which_handlers(error_logger),
828
-                        {Level, _, Msg,Metadata} = pop(),
829
+                        {Level, _, Msg,Metadata} = pop(Sink),
830
                         ?assertEqual(lager_util:level_to_num(error),Level),
831
                         ?assertEqual(self(),proplists:get_value(pid,Metadata)),
832
                         ?assertEqual("Supervisor steve had child mini_steve started with a:b(c) at bleh exit with reason fired in context france", lists:flatten(Msg))
833
                 end
834
             },
835
             {"supervisor reports with real error",
836
-                fun() ->
837
+                fun(Sink) ->
838
                         sync_error_logger:error_report(supervisor_report, [{errorContext, france}, {offender, [{name, mini_steve}, {mfargs, {a, b, [c]}}, {pid, bleh}]}, {reason, {function_clause,[{crash,handle_info,[foo]}]}}, {supervisor, {local, steve}}]),
839
                         _ = gen_event:which_handlers(error_logger),
840
-                        {Level, _, Msg,Metadata} = pop(),
841
+                        {Level, _, Msg,Metadata} = pop(Sink),
842
                         ?assertEqual(lager_util:level_to_num(error),Level),
843
                         ?assertEqual(self(),proplists:get_value(pid,Metadata)),
844
                         ?assertEqual("Supervisor steve had child mini_steve started with a:b(c) at bleh exit with reason no function clause matching crash:handle_info(foo) in context france", lists:flatten(Msg))
845
@@ -874,10 +1203,10 @@
846
             },
847
 
848
             {"supervisor reports with real error and pid",
849
-                fun() ->
850
+                fun(Sink) ->
851
                         sync_error_logger:error_report(supervisor_report, [{errorContext, france}, {offender, [{name, mini_steve}, {mfargs, {a, b, [c]}}, {pid, bleh}]}, {reason, {function_clause,[{crash,handle_info,[foo]}]}}, {supervisor, somepid}]),
852
                         _ = gen_event:which_handlers(error_logger),
853
-                        {Level, _, Msg,Metadata} = pop(),
854
+                        {Level, _, Msg,Metadata} = pop(Sink),
855
                         ?assertEqual(lager_util:level_to_num(error),Level),
856
                         ?assertEqual(self(),proplists:get_value(pid,Metadata)),
857
                         ?assertEqual("Supervisor somepid had child mini_steve started with a:b(c) at bleh exit with reason no function clause matching crash:handle_info(foo) in context france", lists:flatten(Msg))
858
@@ -885,20 +1214,20 @@
859
             },
860
 
861
             {"supervisor_bridge reports",
862
-                fun() ->
863
+                fun(Sink) ->
864
                         sync_error_logger:error_report(supervisor_report, [{errorContext, france}, {offender, [{mod, mini_steve}, {pid, bleh}]}, {reason, fired}, {supervisor, {local, steve}}]),
865
                         _ = gen_event:which_handlers(error_logger),
866
-                        {Level, _, Msg,Metadata} = pop(),
867
+                        {Level, _, Msg,Metadata} = pop(Sink),
868
                         ?assertEqual(lager_util:level_to_num(error),Level),
869
                         ?assertEqual(self(),proplists:get_value(pid,Metadata)),
870
                         ?assertEqual("Supervisor steve had child at module mini_steve at bleh exit with reason fired in context france", lists:flatten(Msg))
871
                 end
872
             },
873
             {"application progress report",
874
-                fun() ->
875
+                fun(Sink) ->
876
                         sync_error_logger:info_report(progress, [{application, foo}, {started_at, node()}]),
877
                         _ = gen_event:which_handlers(error_logger),
878
-                        {Level, _, Msg,Metadata} = pop(),
879
+                        {Level, _, Msg,Metadata} = pop(Sink),
880
                         ?assertEqual(lager_util:level_to_num(info),Level),
881
                         ?assertEqual(self(),proplists:get_value(pid,Metadata)),
882
                         Expected = lists:flatten(io_lib:format("Application foo started on node ~w", [node()])),
883
@@ -906,34 +1235,34 @@
884
                 end
885
             },
886
             {"supervisor progress report",
887
-                fun() ->
888
-                        lager:set_loglevel(?MODULE, debug),
889
-                        ?assertEqual({?DEBUG bor ?INFO bor ?NOTICE bor ?WARNING bor ?ERROR bor ?CRITICAL bor ?ALERT bor ?EMERGENCY, []}, lager_config:get(loglevel)),
890
+                fun(Sink) ->
891
+                        lager:set_loglevel(Sink, ?MODULE, undefined, debug),
892
+                        ?assertEqual({?DEBUG bor ?INFO bor ?NOTICE bor ?WARNING bor ?ERROR bor ?CRITICAL bor ?ALERT bor ?EMERGENCY, []}, lager_config:get({Sink, loglevel})),
893
                         sync_error_logger:info_report(progress, [{supervisor, {local, foo}}, {started, [{mfargs, {foo, bar, 1}}, {pid, baz}]}]),
894
                         _ = gen_event:which_handlers(error_logger),
895
-                        {Level, _, Msg,Metadata} = pop(),
896
+                        {Level, _, Msg,Metadata} = pop(Sink),
897
                         ?assertEqual(lager_util:level_to_num(debug),Level),
898
                         ?assertEqual(self(),proplists:get_value(pid,Metadata)),
899
                         ?assertEqual("Supervisor foo started foo:bar/1 at pid baz", lists:flatten(Msg))
900
                 end
901
             },
902
             {"supervisor progress report with pid",
903
-                fun() ->
904
-                        lager:set_loglevel(?MODULE, debug),
905
-                        ?assertEqual({?DEBUG bor ?INFO bor ?NOTICE bor ?WARNING bor ?ERROR bor ?CRITICAL bor ?ALERT bor ?EMERGENCY, []}, lager_config:get(loglevel)),
906
+                fun(Sink) ->
907
+                        lager:set_loglevel(Sink, ?MODULE, undefined, debug),
908
+                        ?assertEqual({?DEBUG bor ?INFO bor ?NOTICE bor ?WARNING bor ?ERROR bor ?CRITICAL bor ?ALERT bor ?EMERGENCY, []}, lager_config:get({Sink, loglevel})),
909
                         sync_error_logger:info_report(progress, [{supervisor, somepid}, {started, [{mfargs, {foo, bar, 1}}, {pid, baz}]}]),
910
                         _ = gen_event:which_handlers(error_logger),
911
-                        {Level, _, Msg,Metadata} = pop(),
912
+                        {Level, _, Msg,Metadata} = pop(Sink),
913
                         ?assertEqual(lager_util:level_to_num(debug),Level),
914
                         ?assertEqual(self(),proplists:get_value(pid,Metadata)),
915
                         ?assertEqual("Supervisor somepid started foo:bar/1 at pid baz", lists:flatten(Msg))
916
                 end
917
             },
918
             {"crash report for emfile",
919
-                fun() ->
920
+                fun(Sink) ->
921
                         sync_error_logger:error_report(crash_report, [[{pid, self()}, {registered_name, []}, {error_info, {error, emfile, [{stack, trace, 1}]}}], []]),
922
                         _ = gen_event:which_handlers(error_logger),
923
-                        {Level, _, Msg,Metadata} = pop(),
924
+                        {Level, _, Msg,Metadata} = pop(Sink),
925
                         ?assertEqual(lager_util:level_to_num(error),Level),
926
                         ?assertEqual(self(),proplists:get_value(pid,Metadata)),
927
                         Expected = lists:flatten(io_lib:format("CRASH REPORT Process ~w with 0 neighbours crashed with reason: maximum number of file descriptors exhausted, check ulimit -n", [self()])),
928
@@ -941,10 +1270,10 @@
929
                 end
930
             },
931
             {"crash report for system process limit",
932
-                fun() ->
933
+                fun(Sink) ->
934
                         sync_error_logger:error_report(crash_report, [[{pid, self()}, {registered_name, []}, {error_info, {error, system_limit, [{erlang, spawn, 1}]}}], []]),
935
                         _ = gen_event:which_handlers(error_logger),
936
-                        {Level, _, Msg,Metadata} = pop(),
937
+                        {Level, _, Msg,Metadata} = pop(Sink),
938
                         ?assertEqual(lager_util:level_to_num(error),Level),
939
                         ?assertEqual(self(),proplists:get_value(pid,Metadata)),
940
                         Expected = lists:flatten(io_lib:format("CRASH REPORT Process ~w with 0 neighbours crashed with reason: system limit: maximum number of processes exceeded", [self()])),
941
@@ -952,10 +1281,10 @@
942
                 end
943
             },
944
             {"crash report for system process limit2",
945
-                fun() ->
946
+                fun(Sink) ->
947
                         sync_error_logger:error_report(crash_report, [[{pid, self()}, {registered_name, []}, {error_info, {error, system_limit, [{erlang, spawn_opt, 1}]}}], []]),
948
                         _ = gen_event:which_handlers(error_logger),
949
-                        {Level, _, Msg,Metadata} = pop(),
950
+                        {Level, _, Msg,Metadata} = pop(Sink),
951
                         ?assertEqual(lager_util:level_to_num(error),Level),
952
                         ?assertEqual(self(),proplists:get_value(pid,Metadata)),
953
                         Expected = lists:flatten(io_lib:format("CRASH REPORT Process ~w with 0 neighbours crashed with reason: system limit: maximum number of processes exceeded", [self()])),
954
@@ -963,10 +1292,10 @@
955
                 end
956
             },
957
             {"crash report for system port limit",
958
-                fun() ->
959
+                fun(Sink) ->
960
                         sync_error_logger:error_report(crash_report, [[{pid, self()}, {registered_name, []}, {error_info, {error, system_limit, [{erlang, open_port, 1}]}}], []]),
961
                         _ = gen_event:which_handlers(error_logger),
962
-                        {Level, _, Msg,Metadata} = pop(),
963
+                        {Level, _, Msg,Metadata} = pop(Sink),
964
                         ?assertEqual(lager_util:level_to_num(error),Level),
965
                         ?assertEqual(self(),proplists:get_value(pid,Metadata)),
966
                         Expected = lists:flatten(io_lib:format("CRASH REPORT Process ~w with 0 neighbours crashed with reason: system limit: maximum number of ports exceeded", [self()])),
967
@@ -974,10 +1303,10 @@
968
                 end
969
             },
970
             {"crash report for system port limit",
971
-                fun() ->
972
+                fun(Sink) ->
973
                         sync_error_logger:error_report(crash_report, [[{pid, self()}, {registered_name, []}, {error_info, {error, system_limit, [{erlang, list_to_atom, 1}]}}], []]),
974
                         _ = gen_event:which_handlers(error_logger),
975
-                        {Level, _, Msg,Metadata} = pop(),
976
+                        {Level, _, Msg,Metadata} = pop(Sink),
977
                         ?assertEqual(lager_util:level_to_num(error),Level),
978
                         ?assertEqual(self(),proplists:get_value(pid,Metadata)),
979
                         Expected = lists:flatten(io_lib:format("CRASH REPORT Process ~w with 0 neighbours crashed with reason: system limit: tried to create an atom larger than 255, or maximum atom count exceeded", [self()])),
980
@@ -985,10 +1314,10 @@
981
                 end
982
             },
983
             {"crash report for system ets table limit",
984
-                fun() ->
985
+                fun(Sink) ->
986
                         sync_error_logger:error_report(crash_report, [[{pid, self()}, {registered_name, test}, {error_info, {error, system_limit, [{ets,new,[segment_offsets,[ordered_set,public]]},{mi_segment,open_write,1},{mi_buffer_converter,handle_cast,2},{gen_server,handle_msg,5},{proc_lib,init_p_do_apply,3}]}}], []]),
987
                         _ = gen_event:which_handlers(error_logger),
988
-                        {Level, _, Msg,Metadata} = pop(),
989
+                        {Level, _, Msg,Metadata} = pop(Sink),
990
                         ?assertEqual(lager_util:level_to_num(error),Level),
991
                         ?assertEqual(self(),proplists:get_value(pid,Metadata)),
992
                         Expected = lists:flatten(io_lib:format("CRASH REPORT Process ~w with 0 neighbours crashed with reason: system limit: maximum number of ETS tables exceeded", [test])),
993
@@ -996,75 +1325,75 @@
994
                 end
995
             },
996
             {"crash report for unknown system limit should be truncated at 500 characters",
997
-                fun() ->
998
+                fun(Sink) ->
999
                         sync_error_logger:error_report(crash_report, [[{pid, self()}, {error_info, {error, system_limit, [{wtf,boom,[string:copies("aaaa", 4096)]}]}}], []]),
1000
                         _ = gen_event:which_handlers(error_logger),
1001
-                        {_, _, Msg,_Metadata} = pop(),
1002
+                        {_, _, Msg,_Metadata} = pop(Sink),
1003
                         ?assert(length(lists:flatten(Msg)) > 550),
1004
                         ?assert(length(lists:flatten(Msg)) < 600)
1005
                 end
1006
             },
1007
-            {"crash reports for 'special processes' should be handled right - function_clause", 
1008
-                fun() ->
1009
+            {"crash reports for 'special processes' should be handled right - function_clause",
1010
+                fun(Sink) ->
1011
                         {ok, Pid} = special_process:start(),
1012
                         unlink(Pid),
1013
                         Pid ! function_clause,
1014
                         timer:sleep(500),
1015
                         _ = gen_event:which_handlers(error_logger),
1016
-                        {_, _, Msg, _Metadata} = pop(),
1017
+                        {_, _, Msg, _Metadata} = pop(Sink),
1018
                         Expected = lists:flatten(io_lib:format("CRASH REPORT Process ~p with 0 neighbours crashed with reason: no function clause matching special_process:foo(bar)",
1019
                                 [Pid])),
1020
                         test_body(Expected, lists:flatten(Msg))
1021
                 end
1022
             },
1023
-            {"crash reports for 'special processes' should be handled right - case_clause", 
1024
-                fun() ->
1025
+            {"crash reports for 'special processes' should be handled right - case_clause",
1026
+                fun(Sink) ->
1027
                         {ok, Pid} = special_process:start(),
1028
                         unlink(Pid),
1029
                         Pid ! {case_clause, wtf},
1030
                         timer:sleep(500),
1031
                         _ = gen_event:which_handlers(error_logger),
1032
-                        {_, _, Msg, _Metadata} = pop(),
1033
+                        {_, _, Msg, _Metadata} = pop(Sink),
1034
                         Expected = lists:flatten(io_lib:format("CRASH REPORT Process ~p with 0 neighbours crashed with reason: no case clause matching wtf in special_process:loop/0",
1035
                                 [Pid])),
1036
                         test_body(Expected, lists:flatten(Msg))
1037
                 end
1038
             },
1039
-            {"crash reports for 'special processes' should be handled right - exit", 
1040
-                fun() ->
1041
+            {"crash reports for 'special processes' should be handled right - exit",
1042
+                fun(Sink) ->
1043
                         {ok, Pid} = special_process:start(),
1044
                         unlink(Pid),
1045
                         Pid ! exit,
1046
                         timer:sleep(500),
1047
                         _ = gen_event:which_handlers(error_logger),
1048
-                        {_, _, Msg, _Metadata} = pop(),
1049
+                        {_, _, Msg, _Metadata} = pop(Sink),
1050
                         Expected = lists:flatten(io_lib:format("CRASH REPORT Process ~p with 0 neighbours exited with reason: byebye in special_process:loop/0",
1051
                                 [Pid])),
1052
                         test_body(Expected, lists:flatten(Msg))
1053
                 end
1054
             },
1055
-            {"crash reports for 'special processes' should be handled right - error", 
1056
-                fun() ->
1057
+            {"crash reports for 'special processes' should be handled right - error",
1058
+                fun(Sink) ->
1059
                         {ok, Pid} = special_process:start(),
1060
                         unlink(Pid),
1061
                         Pid ! error,
1062
                         timer:sleep(500),
1063
                         _ = gen_event:which_handlers(error_logger),
1064
-                        {_, _, Msg, _Metadata} = pop(),
1065
+                        {_, _, Msg, _Metadata} = pop(Sink),
1066
                         Expected = lists:flatten(io_lib:format("CRASH REPORT Process ~p with 0 neighbours crashed with reason: mybad in special_process:loop/0",
1067
                                 [Pid])),
1068
                         test_body(Expected, lists:flatten(Msg))
1069
                 end
1070
             },
1071
             {"webmachine error reports",
1072
-                fun() ->
1073
+                fun(Sink) ->
1074
                         Path = "/cgi-bin/phpmyadmin",
1075
                         Reason = {error,{error,{badmatch,{error,timeout}},
1076
                                         [{myapp,dostuff,2,[{file,"src/myapp.erl"},{line,123}]},
1077
                                          {webmachine_resource,resource_call,3,[{file,"src/webmachine_resource.erl"},{line,169}]}]}},
1078
                         sync_error_logger:error_msg("webmachine error: path=~p~n~p~n", [Path, Reason]),
1079
                         _ = gen_event:which_handlers(error_logger),
1080
-                        {Level, _, Msg,Metadata} = pop(),
1081
+                        {Level, _, Msg,Metadata} = pop(Sink),
1082
                         ?assertEqual(lager_util:level_to_num(error),Level),
1083
                         ?assertEqual(self(),proplists:get_value(pid,Metadata)),
1084
                         ?assertEqual("Webmachine error at path \"/cgi-bin/phpmyadmin\" : no match of right hand value {error,timeout} in myapp:dostuff/2 line 123", lists:flatten(Msg))
1085
@@ -1072,7 +1401,7 @@
1086
                 end
1087
             },
1088
             {"Cowboy error reports, 8 arg version",
1089
-             fun() ->
1090
+             fun(Sink) ->
1091
                         Stack = [{my_handler,init, 3,[{file,"src/my_handler.erl"},{line,123}]},
1092
                                  {cowboy_handler,handler_init,4,[{file,"src/cowboy_handler.erl"},{line,169}]}],
1093
 
1094
@@ -1085,14 +1414,14 @@
1095
                             [my_handler, init, 3, error, {badmatch, {error, timeout}}, [],
1096
                              "Request", Stack]),
1097
                         _ = gen_event:which_handlers(error_logger),
1098
-                        {Level, _, Msg,Metadata} = pop(),
1099
+                        {Level, _, Msg,Metadata} = pop(Sink),
1100
                         ?assertEqual(lager_util:level_to_num(error),Level),
1101
                         ?assertEqual(self(),proplists:get_value(pid,Metadata)),
1102
                         ?assertEqual("Cowboy handler my_handler terminated in my_handler:init/3 with reason: no match of right hand value {error,timeout} in my_handler:init/3 line 123", lists:flatten(Msg))
1103
                 end
1104
             },
1105
             {"Cowboy error reports, 10 arg version",
1106
-             fun() ->
1107
+             fun(Sink) ->
1108
                         Stack = [{my_handler,somecallback, 3,[{file,"src/my_handler.erl"},{line,123}]},
1109
                                  {cowboy_handler,handler_init,4,[{file,"src/cowboy_handler.erl"},{line,169}]}],
1110
                         sync_error_logger:error_msg(
1111
@@ -1104,61 +1433,85 @@
1112
                              {}, "Request", Stack]),
1113
 
1114
                         _ = gen_event:which_handlers(error_logger),
1115
-                        {Level, _, Msg,Metadata} = pop(),
1116
+                        {Level, _, Msg,Metadata} = pop(Sink),
1117
                         ?assertEqual(lager_util:level_to_num(error),Level),
1118
                         ?assertEqual(self(),proplists:get_value(pid,Metadata)),
1119
                         ?assertEqual("Cowboy handler my_handler terminated in my_handler:somecallback/3 with reason: no match of right hand value {error,timeout} in my_handler:somecallback/3 line 123", lists:flatten(Msg))
1120
                 end
1121
             },
1122
             {"Cowboy error reports, 5 arg version",
1123
-             fun() ->
1124
+             fun(Sink) ->
1125
                         sync_error_logger:error_msg(
1126
                             "** Cowboy handler ~p terminating; "
1127
                             "function ~p/~p was not exported~n"
1128
                             "** Request was ~p~n** State was ~p~n~n",
1129
                             [my_handler, to_json, 2, "Request", {}]),
1130
                         _ = gen_event:which_handlers(error_logger),
1131
-                        {Level, _, Msg,Metadata} = pop(),
1132
+                        {Level, _, Msg,Metadata} = pop(Sink),
1133
                         ?assertEqual(lager_util:level_to_num(error),Level),
1134
                         ?assertEqual(self(),proplists:get_value(pid,Metadata)),
1135
                         ?assertEqual("Cowboy handler my_handler terminated with reason: call to undefined function my_handler:to_json/2", lists:flatten(Msg))
1136
                 end
1137
             },
1138
             {"messages should not be generated if they don't satisfy the threshold",
1139
-                fun() ->
1140
-                        lager:set_loglevel(?MODULE, error),
1141
-                        ?assertEqual({?ERROR bor ?CRITICAL bor ?ALERT bor ?EMERGENCY, []}, lager_config:get(loglevel)),
1142
+                fun(Sink) ->
1143
+                        lager:set_loglevel(Sink, ?MODULE, undefined, error),
1144
+                        ?assertEqual({?ERROR bor ?CRITICAL bor ?ALERT bor ?EMERGENCY, []}, lager_config:get({Sink, loglevel})),
1145
                         sync_error_logger:info_report([hello, world]),
1146
                         _ = gen_event:which_handlers(error_logger),
1147
-                        ?assertEqual(0, count()),
1148
-                        ?assertEqual(0, count_ignored()),
1149
-                        lager:set_loglevel(?MODULE, info),
1150
-                        ?assertEqual({?INFO bor ?NOTICE bor ?WARNING bor ?ERROR bor ?CRITICAL bor ?ALERT bor ?EMERGENCY, []}, lager_config:get(loglevel)),
1151
+                        ?assertEqual(0, count(Sink)),
1152
+                        ?assertEqual(0, count_ignored(Sink)),
1153
+                        lager:set_loglevel(Sink, ?MODULE, undefined, info),
1154
+                        ?assertEqual({?INFO bor ?NOTICE bor ?WARNING bor ?ERROR bor ?CRITICAL bor ?ALERT bor ?EMERGENCY, []}, lager_config:get({Sink, loglevel})),
1155
                         sync_error_logger:info_report([hello, world]),
1156
                         _ = gen_event:which_handlers(error_logger),
1157
-                        ?assertEqual(1, count()),
1158
-                        ?assertEqual(0, count_ignored()),
1159
-                        lager:set_loglevel(?MODULE, error),
1160
-                        ?assertEqual({?ERROR bor ?CRITICAL bor ?ALERT bor ?EMERGENCY, []}, lager_config:get(loglevel)),
1161
-                        lager_config:set(loglevel, {element(2, lager_util:config_to_mask(debug)), []}),
1162
+                        ?assertEqual(1, count(Sink)),
1163
+                        ?assertEqual(0, count_ignored(Sink)),
1164
+                        lager:set_loglevel(Sink, ?MODULE, undefined, error),
1165
+                        ?assertEqual({?ERROR bor ?CRITICAL bor ?ALERT bor ?EMERGENCY, []}, lager_config:get({Sink, loglevel})),
1166
+                        lager_config:set({Sink, loglevel}, {element(2, lager_util:config_to_mask(debug)), []}),
1167
                         sync_error_logger:info_report([hello, world]),
1168
                         _ = gen_event:which_handlers(error_logger),
1169
-                        ?assertEqual(1, count()),
1170
-                        ?assertEqual(1, count_ignored())
1171
+                        ?assertEqual(1, count(Sink)),
1172
+                        ?assertEqual(1, count_ignored(Sink))
1173
                 end
1174
             }
1175
-        ]
1176
-    }.
1177
+        ],
1178
+    SinkTests = lists:map(
1179
+        fun({Name, F}) ->
1180
+            fun(Sink) -> {Name, fun() -> F(Sink) end} end
1181
+        end,
1182
+        Tests),
1183
+    {"Error logger redirect", [
1184
+        {"Redirect to default sink", 
1185
+            {foreach,
1186
+            fun error_logger_redirect_setup/0,
1187
+            fun error_logger_redirect_cleanup/1,
1188
+            SinkTests}},
1189
+        {"Redirect to error_logger_lager_event sink",
1190
+            {foreach,
1191
+            fun error_logger_redirect_setup_sink/0,
1192
+            fun error_logger_redirect_cleanup/1,
1193
+            SinkTests}}
1194
+    ]}.
1195
 
1196
 safe_format_test() ->
1197
     ?assertEqual("foo bar", lists:flatten(lager:safe_format("~p ~p", [foo, bar], 1024))),
1198
     ?assertEqual("FORMAT ERROR: \"~p ~p ~p\" [foo,bar]", lists:flatten(lager:safe_format("~p ~p ~p", [foo, bar], 1024))),
1199
     ok.
1200
 
1201
+unsafe_format_test() ->
1202
+    ?assertEqual("foo bar", lists:flatten(lager:unsafe_format("~p ~p", [foo, bar]))),
1203
+    ?assertEqual("FORMAT ERROR: \"~p ~p ~p\" [foo,bar]", lists:flatten(lager:unsafe_format("~p ~p ~p", [foo, bar]))),
1204
+    ok.
1205
+
1206
 async_threshold_test_() ->
1207
     {foreach,
1208
         fun() ->
1209
                 error_logger:tty(false),
1210
+                ets:new(async_threshold_test, [set, named_table, public]),
1211
+                ets:insert_new(async_threshold_test, {sync_toggled, 0}),
1212
+                ets:insert_new(async_threshold_test, {async_toggled, 0}),
1213
                 application:load(lager),
1214
                 application:set_env(lager, error_logger_redirect, false),
1215
                 application:set_env(lager, async_threshold, 2),
1216
@@ -1170,6 +1523,7 @@
1217
                 application:unset_env(lager, async_threshold),
1218
                 application:stop(lager),
1219
                 application:stop(goldrush),
1220
+                ets:delete(async_threshold_test),
1221
                 error_logger:tty(true)
1222
         end,
1223
         [
1224
@@ -1184,11 +1538,22 @@
1225
                         %% serialize on mailbox
1226
                         _ = gen_event:which_handlers(lager_event),
1227
                         timer:sleep(500),
1228
-                        %% there should be a ton of outstanding messages now, so async is false
1229
-                        ?assertEqual(false, lager_config:get(async)),
1230
-                        %% wait for all the workers to return, meaning that all the messages have been logged (since we're in sync mode)
1231
+
1232
+                        %% By now the flood of messages will have
1233
+                        %% forced the backend throttle to turn off
1234
+                        %% async mode, but it's possible all
1235
+                        %% outstanding requests have been processed,
1236
+                        %% so checking the current status (sync or
1237
+                        %% async) is an exercise in race control.
1238
+
1239
+                        %% Instead, we'll see whether the backend
1240
+                        %% throttle has toggled into sync mode at any
1241
+                        %% point in the past
1242
+                        ?assertMatch([{sync_toggled, N}] when N > 0,
1243
+                                                              ets:lookup(async_threshold_test, sync_toggled)),
1244
+                        %% wait for all the workers to return, meaning that all the messages have been logged (since we're definitely in sync mode at the end of the run)
1245
                         collect_workers(Workers),
1246
-                        %% serialize ont  the mailbox again
1247
+                        %% serialize on the mailbox again
1248
                         _ = gen_event:which_handlers(lager_event),
1249
                         %% just in case...
1250
                         timer:sleep(1000),
1251
@@ -1268,5 +1633,3 @@
1252
     }.
1253
 
1254
 -endif.
1255
-
1256
-
1257
lager-2.1.0.tar.gz/test/pr_nested_record_test.erl -> lager-3.1.0.tar.gz/test/pr_nested_record_test.erl Changed
17
 
1
@@ -2,13 +2,11 @@
2
 
3
 -compile([{parse_transform, lager_transform}]).
4
 
5
--record(a, {field1, field2}).
6
--record(b, {field1, field2}).
7
+-record(a, {field1 :: term(), field2 :: term()}).
8
+-record(b, {field1 :: term() , field2 :: term()}).
9
 
10
 
11
--ifdef(TEST).
12
 -include_lib("eunit/include/eunit.hrl").
13
--endif.
14
 
15
 nested_record_test() ->
16
     A = #a{field1 = x, field2 = y}, 
17
lager-3.1.0.tar.gz/test/pr_stacktrace_test.erl Added
58
 
1
@@ -0,0 +1,55 @@
2
+-module(pr_stacktrace_test).
3
+
4
+-compile([{parse_transform, lager_transform}]).
5
+
6
+-include_lib("eunit/include/eunit.hrl").
7
+
8
+make_throw() ->
9
+    throw({test, exception}).
10
+
11
+bad_arity() ->
12
+    lists:concat([], []).
13
+
14
+bad_arg() ->
15
+    integer_to_list(1.0).
16
+
17
+pr_stacktrace_throw_test() ->
18
+    Result = try
19
+        make_throw()
20
+    catch
21
+        Class:Reason ->
22
+            lager:pr_stacktrace(erlang:get_stacktrace(), {Class, Reason})
23
+    end,
24
+ExpectedPart = "
25
+    pr_stacktrace_test:pr_stacktrace_throw_test/0 line 18
26
+    pr_stacktrace_test:make_throw/0 line 8
27
+throw:{test,exception}",
28
+    ?assertNotEqual(0, string:str(Result, ExpectedPart)).
29
+
30
+
31
+pr_stacktrace_bad_arg_test() ->
32
+    Result = try
33
+        bad_arg()
34
+    catch
35
+        Class:Reason ->
36
+            lager:pr_stacktrace(erlang:get_stacktrace(), {Class, Reason})
37
+    end,
38
+ExpectedPart = "
39
+    pr_stacktrace_test:pr_stacktrace_bad_arg_test/0 line 32
40
+    pr_stacktrace_test:bad_arg/0 line 14
41
+error:badarg",
42
+    ?assertNotEqual(0, string:str(Result, ExpectedPart)).
43
+
44
+
45
+pr_stacktrace_bad_arity_test() ->
46
+    Result = try
47
+        bad_arity()
48
+    catch
49
+        Class:Reason ->
50
+            lager:pr_stacktrace(erlang:get_stacktrace(), {Class, Reason})
51
+    end,
52
+ExpectedPart = "
53
+    pr_stacktrace_test:pr_stacktrace_bad_arity_test/0 line 46
54
+    lists:concat([], [])
55
+error:undef",
56
+    ?assertNotEqual(0, string:str(Result, ExpectedPart)).
57
\ No newline at end of file
58
lager-2.1.0.tar.gz/test/trunc_io_eqc.erl -> lager-3.1.0.tar.gz/test/trunc_io_eqc.erl Changed
10
 
1
@@ -91,7 +91,7 @@
2
 
3
 %% Generates a printable string
4
 gen_print_str() ->
5
-    ?LET(Xs, list(char()), [X || X <- Xs, io_lib:printable_list([X]), X /= $~, X < 255]).
6
+    ?LET(Xs, list(char()), [X || X <- Xs, io_lib:printable_list([X]), X /= $~, X < 256]).
7
 
8
 gen_print_bin() ->
9
     ?LET(Xs, gen_print_str(), list_to_binary(Xs)).
10
lager-3.1.0.tar.gz/test/zzzz_gh280_crash.erl Added
35
 
1
@@ -0,0 +1,33 @@
2
+%% @doc This test is named zzzz_gh280_crash because it has to be run first and tests are run in 
3
+%% reverse alphabetical order.
4
+%%
5
+%% The problem we are attempting to detect here is when log_mf_h is installed as a handler for error_logger
6
+%% and lager starts up to replace the current handlers with its own.  This causes a start up crash because
7
+%% OTP error logging modules do not have any notion of a lager-style log level.
8
+-module(zzzz_gh280_crash).
9
+-compile(export_all).
10
+
11
+-include_lib("eunit/include/eunit.hrl").
12
+
13
+gh280_crash_test() ->
14
+    application:stop(lager),
15
+    application:stop(goldrush),
16
+
17
+    error_logger:tty(false),
18
+    %% see https://github.com/erlang/otp/blob/maint/lib/stdlib/src/log_mf_h.erl#L81
19
+    %% for an explanation of the init arguments to log_mf_h
20
+    ok = gen_event:add_sup_handler(error_logger, log_mf_h, log_mf_h:init("/tmp", 10000, 5)),
21
+    lager:start(),
22
+    Result = receive
23
+        {gen_event_EXIT,log_mf_h,normal} -> 
24
+            true;
25
+        {gen_event_EXIT,Handler,Reason} ->
26
+            {Handler,Reason};
27
+        X -> 
28
+            X
29
+    after 1000 ->
30
+        timeout
31
+    end,
32
+    ?assert(Result),
33
+    application:stop(lager),
34
+    application:stop(goldrush).
35
Refresh
erlang-lager
Refresh


Request History
Jeroen van Meeuwen's avatar

vanmeeuwen created request over 8 years ago

Submit erlang-lager


Jeroen van Meeuwen's avatar

vanmeeuwen accepted review over 8 years ago

Accept


Jeroen van Meeuwen's avatar

vanmeeuwen accepted review over 8 years ago

Accept


Jeroen van Meeuwen's avatar

vanmeeuwen approved review over 8 years ago

Accept


Jeroen van Meeuwen's avatar

vanmeeuwen accepted request over 8 years ago

Accept