You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by da...@apache.org on 2016/07/22 09:49:43 UTC
[4/5] couch-log commit: updated refs/heads/master to da0e489
Add test suite for couch_log
COUCHDB-3067
Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/commit/16a46572
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/tree/16a46572
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/diff/16a46572
Branch: refs/heads/master
Commit: 16a46572b92bf0b53b3aa94bc209cae0ba2257c1
Parents: c572aae
Author: Paul J. Davis <pa...@gmail.com>
Authored: Wed Jul 20 12:53:50 2016 -0500
Committer: Paul J. Davis <pa...@gmail.com>
Committed: Fri Jul 22 04:39:08 2016 -0500
----------------------------------------------------------------------
include/couch_log.hrl | 3 +
rebar.config | 2 +
test/couch_log_config_listener_test.erl | 56 ++
test/couch_log_config_test.erl | 110 ++++
test/couch_log_error_logger_h_test.erl | 45 ++
test/couch_log_formatter_test.erl | 768 +++++++++++++++++++++++++++
test/couch_log_monitor_test.erl | 67 +++
test/couch_log_server_test.erl | 118 ++++
test/couch_log_test.erl | 85 +++
test/couch_log_test_util.erl | 153 ++++++
test/couch_log_util_test.erl | 55 ++
test/couch_log_writer_ets.erl | 49 ++
test/couch_log_writer_file_test.erl | 161 ++++++
test/couch_log_writer_stderr_test.erl | 58 ++
test/couch_log_writer_syslog_test.erl | 122 +++++
test/couch_log_writer_test.erl | 54 ++
16 files changed, 1906 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/blob/16a46572/include/couch_log.hrl
----------------------------------------------------------------------
diff --git a/include/couch_log.hrl b/include/couch_log.hrl
index a472c0c..fa544a8 100644
--- a/include/couch_log.hrl
+++ b/include/couch_log.hrl
@@ -17,3 +17,6 @@
msg_id,
time_stamp
}).
+
+
+-define(COUCH_LOG_TEST_TABLE, couch_log_test_table).
http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/blob/16a46572/rebar.config
----------------------------------------------------------------------
diff --git a/rebar.config b/rebar.config
new file mode 100644
index 0000000..e0d1844
--- /dev/null
+++ b/rebar.config
@@ -0,0 +1,2 @@
+{cover_enabled, true}.
+{cover_print_enabled, true}.
http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/blob/16a46572/test/couch_log_config_listener_test.erl
----------------------------------------------------------------------
diff --git a/test/couch_log_config_listener_test.erl b/test/couch_log_config_listener_test.erl
new file mode 100644
index 0000000..9a8e16d
--- /dev/null
+++ b/test/couch_log_config_listener_test.erl
@@ -0,0 +1,56 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+% http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_log_config_listener_test).
+
+
+-include_lib("couch_log/include/couch_log.hrl").
+-include_lib("eunit/include/eunit.hrl").
+
+
+-define(HANDLER, {config_listener, couch_log_config_listener}).
+
+
+couch_log_config_test_() ->
+ {setup,
+ fun couch_log_test_util:start/0,
+ fun couch_log_test_util:stop/1,
+ [
+ fun check_restart_listener/0,
+ fun check_ignore_non_log/0
+ ]
+ }.
+
+
+check_restart_listener() ->
+ Handlers1 = gen_event:which_handlers(config_event),
+ ?assert(lists:member(?HANDLER, Handlers1)),
+
+ gen_event:delete_handler(config_event, ?HANDLER, testing),
+
+ Handlers2 = gen_event:which_handlers(config_event),
+ ?assert(not lists:member(?HANDLER, Handlers2)),
+
+ timer:sleep(1000),
+
+ Handlers3 = gen_event:which_handlers(config_event),
+ ?assert(lists:member(?HANDLER, Handlers3)).
+
+
+check_ignore_non_log() ->
+ Run = fun() ->
+ couch_log_test_util:with_config_listener(fun() ->
+ config:set("foo", "bar", "baz"),
+ couch_log_test_util:wait_for_config()
+ end)
+ end,
+ ?assertError(config_change_timeout, Run()).
http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/blob/16a46572/test/couch_log_config_test.erl
----------------------------------------------------------------------
diff --git a/test/couch_log_config_test.erl b/test/couch_log_config_test.erl
new file mode 100644
index 0000000..c4677f3
--- /dev/null
+++ b/test/couch_log_config_test.erl
@@ -0,0 +1,110 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+% http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_log_config_test).
+
+
+-include_lib("couch_log/include/couch_log.hrl").
+-include_lib("eunit/include/eunit.hrl").
+
+
+couch_log_config_test_() ->
+ {setup,
+ fun couch_log_test_util:start/0,
+ fun couch_log_test_util:stop/1,
+ [
+ fun check_level/0,
+ fun check_max_message_size/0,
+ fun check_bad_level/0,
+ fun check_bad_max_message_size/0
+ ]
+ }.
+
+
+check_level() ->
+ % Default level is info
+ ?assertEqual(info, couch_log_config:get(level)),
+ ?assertEqual(2, couch_log_config:get(level_int)),
+
+ couch_log_test_util:with_config_listener(fun() ->
+ config:set("log", "level", "emerg"),
+ couch_log_test_util:wait_for_config(),
+ ?assertEqual(emergency, couch_log_config:get(level)),
+ ?assertEqual(8, couch_log_config:get(level_int)),
+
+ config:set("log", "level", "debug"),
+ couch_log_test_util:wait_for_config(),
+ ?assertEqual(debug, couch_log_config:get(level)),
+ ?assertEqual(1, couch_log_config:get(level_int)),
+
+ config:delete("log", "level"),
+ couch_log_test_util:wait_for_config(),
+ ?assertEqual(info, couch_log_config:get(level)),
+ ?assertEqual(2, couch_log_config:get(level_int))
+ end).
+
+
+check_max_message_size() ->
+ % Default is 16000
+ ?assertEqual(16000, couch_log_config:get(max_message_size)),
+
+ couch_log_test_util:with_config_listener(fun() ->
+ config:set("log", "max_message_size", "1024"),
+ couch_log_test_util:wait_for_config(),
+ ?assertEqual(1024, couch_log_config:get(max_message_size)),
+
+ config:delete("log", "max_message_size"),
+ couch_log_test_util:wait_for_config(),
+ ?assertEqual(16000, couch_log_config:get(max_message_size))
+ end).
+
+
+check_bad_level() ->
+ % Default level is info
+ ?assertEqual(info, couch_log_config:get(level)),
+ ?assertEqual(2, couch_log_config:get(level_int)),
+
+ couch_log_test_util:with_config_listener(fun() ->
+ config:set("log", "level", "debug"),
+ couch_log_test_util:wait_for_config(),
+ ?assertEqual(debug, couch_log_config:get(level)),
+ ?assertEqual(1, couch_log_config:get(level_int)),
+
+ config:set("log", "level", "this is not a valid level name"),
+ couch_log_test_util:wait_for_config(),
+ ?assertEqual(info, couch_log_config:get(level)),
+ ?assertEqual(2, couch_log_config:get(level_int)),
+
+ config:delete("log", "level"),
+ couch_log_test_util:wait_for_config(),
+ ?assertEqual(info, couch_log_config:get(level)),
+ ?assertEqual(2, couch_log_config:get(level_int))
+ end).
+
+
+check_bad_max_message_size() ->
+ % Default level is 16000
+ ?assertEqual(16000, couch_log_config:get(max_message_size)),
+
+ couch_log_test_util:with_config_listener(fun() ->
+ config:set("log", "max_message_size", "1024"),
+ couch_log_test_util:wait_for_config(),
+ ?assertEqual(1024, couch_log_config:get(max_message_size)),
+
+ config:set("log", "max_message_size", "this is not a valid size"),
+ couch_log_test_util:wait_for_config(),
+ ?assertEqual(16000, couch_log_config:get(max_message_size)),
+
+ config:delete("log", "max_message_size"),
+ couch_log_test_util:wait_for_config(),
+ ?assertEqual(16000, couch_log_config:get(max_message_size))
+ end).
http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/blob/16a46572/test/couch_log_error_logger_h_test.erl
----------------------------------------------------------------------
diff --git a/test/couch_log_error_logger_h_test.erl b/test/couch_log_error_logger_h_test.erl
new file mode 100644
index 0000000..b78598f
--- /dev/null
+++ b/test/couch_log_error_logger_h_test.erl
@@ -0,0 +1,45 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+% http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_log_error_logger_h_test).
+
+
+-include_lib("eunit/include/eunit.hrl").
+
+
+-define(HANDLER, couch_log_error_logger_h).
+
+
+couch_log_error_logger_h_test_() ->
+ {setup,
+ fun couch_log_test_util:start/0,
+ fun couch_log_test_util:stop/1,
+ [
+ fun handler_ignores_unknown_messages/0,
+ fun coverage_test/0
+ ]
+ }.
+
+
+handler_ignores_unknown_messages() ->
+ Handlers1 = gen_event:which_handlers(error_logger),
+ ?assert(lists:member(?HANDLER, Handlers1)),
+ ?assertEqual(ignored, gen_event:call(error_logger, ?HANDLER, foo)),
+
+ error_logger ! this_is_a_message,
+ Handlers2 = gen_event:which_handlers(error_logger),
+ ?assert(lists:member(?HANDLER, Handlers2)).
+
+
+coverage_test() ->
+ Resp = couch_log_error_logger_h:code_change(foo, bazinga, baz),
+ ?assertEqual({ok, bazinga}, Resp).
http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/blob/16a46572/test/couch_log_formatter_test.erl
----------------------------------------------------------------------
diff --git a/test/couch_log_formatter_test.erl b/test/couch_log_formatter_test.erl
new file mode 100644
index 0000000..1e8457b
--- /dev/null
+++ b/test/couch_log_formatter_test.erl
@@ -0,0 +1,768 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+% http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_log_formatter_test).
+
+
+-include("couch_log.hrl").
+-include_lib("eunit/include/eunit.hrl").
+
+
+truncate_fmt_test() ->
+ Msg = [0 || _ <- lists:seq(1, 1048576)],
+ Entry = couch_log_formatter:format(info, self(), "~w", [Msg]),
+ ?assert(length(Entry#log_entry.msg) =< 16000).
+
+
+truncate_test() ->
+ Msg = [0 || _ <- lists:seq(1, 1048576)],
+ Entry = couch_log_formatter:format(info, self(), Msg),
+ ?assert(length(Entry#log_entry.msg) =< 16000).
+
+
+gen_server_error_test() ->
+ Pid = self(),
+ Event = {
+ error,
+ erlang:group_leader(),
+ {
+ Pid,
+ "** Generic server and some stuff",
+ [a_gen_server, {foo, bar}, server_state, some_reason]
+ }
+ },
+ ?assertMatch(
+ #log_entry{
+ level = error,
+ pid = Pid
+ },
+ do_format(Event)
+ ),
+ do_matches(do_format(Event), [
+ "gen_server a_gen_server terminated",
+ "with reason: some_reason",
+ "last msg: {foo,bar}",
+ "state: server_state"
+ ]).
+
+
+gen_fsm_error_test() ->
+ Pid = self(),
+ Event = {
+ error,
+ erlang:group_leader(),
+ {
+ Pid,
+ "** State machine did a thing",
+ [a_gen_fsm, {ohai,there}, state_name, curr_state, barf]
+ }
+ },
+ ?assertMatch(
+ #log_entry{
+ level = error,
+ pid = Pid
+ },
+ do_format(Event)
+ ),
+ do_matches(do_format(Event), [
+ "gen_fsm a_gen_fsm in state state_name",
+ "with reason: barf",
+ "last msg: {ohai,there}",
+ "state: curr_state"
+ ]).
+
+
+gen_event_error_test() ->
+ Pid = self(),
+ Event = {
+ error,
+ erlang:group_leader(),
+ {
+ Pid,
+ "** gen_event handler did a thing",
+ [
+ handler_id,
+ a_gen_event,
+ {ohai,there},
+ curr_state,
+ barf
+ ]
+ }
+ },
+ ?assertMatch(
+ #log_entry{
+ level = error,
+ pid = Pid
+ },
+ do_format(Event)
+ ),
+ do_matches(do_format(Event), [
+ "gen_event handler_id installed in a_gen_event",
+ "reason: barf",
+ "last msg: {ohai,there}",
+ "state: curr_state"
+ ]).
+
+
+normal_error_test() ->
+ Pid = self(),
+ Event = {
+ error,
+ erlang:group_leader(),
+ {
+ Pid,
+ "format thing: ~w ~w",
+ [
+ first_arg,
+ second_arg
+ ]
+ }
+ },
+ ?assertMatch(
+ #log_entry{
+ level = error,
+ pid = Pid,
+ msg = "format thing: first_arg second_arg"
+ },
+ do_format(Event)
+ ).
+
+
+error_report_std_error_test() ->
+ Pid = self(),
+ Event = {
+ error_report,
+ erlang:group_leader(),
+ {
+ Pid,
+ std_error,
+ [foo, {bar, baz}]
+ }
+ },
+ ?assertMatch(
+ #log_entry{
+ level = error,
+ pid = Pid,
+ msg = "foo, bar: baz"
+ },
+ do_format(Event)
+ ).
+
+
+supervisor_report_test() ->
+ Pid = self(),
+ % A standard supervisor report
+ Event1 = {
+ error_report,
+ erlang:group_leader(),
+ {
+ Pid,
+ supervisor_report,
+ [
+ {supervisor, sup_name},
+ {offender, [
+ {id, sup_child},
+ {pid, list_to_pid("<0.1.0>")},
+ {mfargs, {some_mod, some_fun, 3}}
+ ]},
+ {reason, a_reason},
+ {errorContext, some_context}
+ ]
+ }
+ },
+ ?assertMatch(
+ #log_entry{
+ level = error,
+ pid = Pid
+ },
+ do_format(Event1)
+ ),
+ do_matches(do_format(Event1), [
+ "Supervisor sup_name",
+ "had child sup_child started with some_mod:some_fun/3 at <0.1.0> exit",
+ "with reason a_reason",
+ "in context some_context"
+ ]),
+ % Slightly older using name instead of id
+ % in the offender blob.
+ Event2 = {
+ error_report,
+ erlang:group_leader(),
+ {
+ Pid,
+ supervisor_report,
+ [
+ {supervisor, sup_name},
+ {offender, [
+ {name, sup_child},
+ {pid, list_to_pid("<0.1.0>")},
+ {mfargs, {some_mod, some_fun, 3}}
+ ]},
+ {reason, a_reason},
+ {errorContext, some_context}
+ ]
+ }
+ },
+ ?assertMatch(
+ #log_entry{
+ level = error,
+ pid = Pid
+ },
+ do_format(Event2)
+ ),
+ do_matches(do_format(Event2), [
+ "Supervisor sup_name",
+ "had child sup_child started with some_mod:some_fun/3 at <0.1.0> exit",
+ "with reason a_reason",
+ "in context some_context"
+ ]),
+ % A supervisor_bridge
+ Event3 = {
+ error_report,
+ erlang:group_leader(),
+ {
+ Pid,
+ supervisor_report,
+ [
+ {supervisor, sup_name},
+ {offender, [
+ {mod, bridge_mod},
+ {pid, list_to_pid("<0.1.0>")}
+ ]},
+ {reason, a_reason},
+ {errorContext, some_context}
+ ]
+ }
+ },
+ ?assertMatch(
+ #log_entry{
+ level = error,
+ pid = Pid
+ },
+ do_format(Event3)
+ ),
+ do_matches(do_format(Event3), [
+ "Supervisor sup_name",
+ "had child at module bridge_mod at <0.1.0> exit",
+ "with reason a_reason",
+ "in context some_context"
+ ]),
+ % Any other supervisor report
+ Event4 = {
+ error_report,
+ erlang:group_leader(),
+ {
+ Pid,
+ supervisor_report,
+ [foo, {a, thing}, bang]
+ }
+ },
+ ?assertMatch(
+ #log_entry{
+ level = error,
+ pid = Pid,
+ msg = "SUPERVISOR REPORT foo, a: thing, bang"
+ },
+ do_format(Event4)
+ ).
+
+
+crash_report_test() ->
+ Pid = self(),
+ % A standard crash report
+ Event1 = {
+ error_report,
+ erlang:group_leader(),
+ {
+ Pid,
+ crash_report,
+ [
+ [
+ {pid, list_to_pid("<0.2.0>")},
+ {error_info, {
+ exit,
+ undef,
+ [{mod_name, fun_name, [a, b]}]
+ }}
+ ],
+ [list_to_pid("<0.3.0>"), list_to_pid("<0.4.0>")]
+ ]
+ }
+ },
+ ?assertMatch(
+ #log_entry{
+ level = error,
+ pid = Pid
+ },
+ do_format(Event1)
+ ),
+ do_matches(do_format(Event1), [
+ "Process <0.2.0>",
+ "with 2 neighbors",
+ "exited",
+ "reason: call to undefined function mod_name:fun_name\\(a, b\\)"
+ ]),
+ % A registered process crash report
+ Event2 = {
+ error_report,
+ erlang:group_leader(),
+ {
+ Pid,
+ crash_report,
+ [
+ [
+ {pid, list_to_pid("<0.2.0>")},
+ {registered_name, couch_log_server},
+ {error_info, {
+ exit,
+ undef,
+ [{mod_name, fun_name, [a, b]}]
+ }}
+ ],
+ [list_to_pid("<0.3.0>"), list_to_pid("<0.4.0>")]
+ ]
+ }
+ },
+ do_matches(do_format(Event2), [
+ "Process couch_log_server \\(<0.2.0>\\)"
+ ]),
+ % A non-exit crash report
+ Event3 = {
+ error_report,
+ erlang:group_leader(),
+ {
+ Pid,
+ crash_report,
+ [
+ [
+ {pid, list_to_pid("<0.2.0>")},
+ {registered_name, couch_log_server},
+ {error_info, {
+ killed,
+ undef,
+ [{mod_name, fun_name, [a, b]}]
+ }}
+ ],
+ [list_to_pid("<0.3.0>"), list_to_pid("<0.4.0>")]
+ ]
+ }
+ },
+ do_matches(do_format(Event3), [
+ "crashed"
+ ]),
+ % A extra report info
+ Event4 = {
+ error_report,
+ erlang:group_leader(),
+ {
+ Pid,
+ crash_report,
+ [
+ [
+ {pid, list_to_pid("<0.2.0>")},
+ {error_info, {
+ killed,
+ undef,
+ [{mod_name, fun_name, [a, b]}]
+ }},
+ {another, entry},
+ yep
+ ],
+ [list_to_pid("<0.3.0>"), list_to_pid("<0.4.0>")]
+ ]
+ }
+ },
+ do_matches(do_format(Event4), [
+ "; another: entry, yep"
+ ]).
+
+
+warning_report_test() ->
+ Pid = self(),
+ % A warning message
+ Event1 = {
+ warning_msg,
+ erlang:group_leader(),
+ {
+ Pid,
+ "a ~s string ~w",
+ ["format", 7]
+ }
+ },
+ ?assertMatch(
+ #log_entry{
+ level = warning,
+ pid = Pid,
+ msg = "a format string 7"
+ },
+ do_format(Event1)
+ ),
+ % A warning report
+ Event2 = {
+ warning_report,
+ erlang:group_leader(),
+ {
+ Pid,
+ std_warning,
+ [list, 'of', {things, indeed}]
+ }
+ },
+ ?assertMatch(
+ #log_entry{
+ level = warning,
+ pid = Pid,
+ msg = "list, of, things: indeed"
+ },
+ do_format(Event2)
+ ).
+
+
+info_report_test() ->
+ Pid = self(),
+ % An info message
+ Event1 = {
+ info_msg,
+ erlang:group_leader(),
+ {
+ Pid,
+ "an info ~s string ~w",
+ ["format", 7]
+ }
+ },
+ ?assertMatch(
+ #log_entry{
+ level = info,
+ pid = Pid,
+ msg = "an info format string 7"
+ },
+ do_format(Event1)
+ ),
+ % Application exit info
+ Event2 = {
+ info_report,
+ erlang:group_leader(),
+ {
+ Pid,
+ std_info,
+ [
+ {type, no_idea},
+ {application, couch_log},
+ {exited, red_sox_are_on}
+ ]
+ }
+ },
+ ?assertMatch(
+ #log_entry{
+ level = info,
+ pid = Pid,
+ msg = "Application couch_log exited with reason: red_sox_are_on"
+ },
+ do_format(Event2)
+ ),
+ % Any other std_info message
+ Event3 = {
+ info_report,
+ erlang:group_leader(),
+ {
+ Pid,
+ std_info,
+ [
+ {type, no_idea},
+ {application, couch_log}
+ ]
+ }
+ },
+ ?assertMatch(
+ #log_entry{
+ level = info,
+ pid = Pid,
+ msg = "type: no_idea, application: couch_log"
+ },
+ do_format(Event3)
+ ),
+ % Non-list other report
+ Event4 = {
+ info_report,
+ erlang:group_leader(),
+ {
+ Pid,
+ std_info,
+ dang
+ }
+ },
+ ?assertMatch(
+ #log_entry{
+ level = info,
+ pid = Pid,
+ msg = "dang"
+ },
+ do_format(Event4)
+ ).
+
+
+progress_report_test() ->
+ Pid = self(),
+ % Application started
+ Event1 = {
+ info_report,
+ erlang:group_leader(),
+ {
+ Pid,
+ progress,
+ [{started_at, 'nonode@nohost'}, {application, app_name}]
+ }
+ },
+ ?assertMatch(
+ #log_entry{
+ level = info,
+ pid = Pid,
+ msg = "Application app_name started on node nonode@nohost"
+ },
+ do_format(Event1)
+ ),
+ % Supervisor started child
+ Event2 = {
+ info_report,
+ erlang:group_leader(),
+ {
+ Pid,
+ progress,
+ [
+ {supervisor, sup_dude},
+ {started, [
+ {mfargs, {mod_name, fun_name, 1}},
+ {pid, list_to_pid("<0.5.0>")}
+ ]}
+ ]
+ }
+ },
+ ?assertMatch(
+ #log_entry{
+ level = debug,
+ pid = Pid,
+ msg = "Supervisor sup_dude started mod_name:fun_name/1"
+ " at pid <0.5.0>"
+ },
+ do_format(Event2)
+ ),
+ % Other progress report
+ Event3 = {
+ info_report,
+ erlang:group_leader(),
+ {
+ Pid,
+ progress,
+ [a, {thing, boop}, here]
+ }
+ },
+ ?assertMatch(
+ #log_entry{
+ level = info,
+ pid = Pid,
+ msg = "PROGRESS REPORT a, thing: boop, here"
+ },
+ do_format(Event3)
+ ).
+
+
+log_unknown_event_test() ->
+ Pid = self(),
+ ?assertMatch(
+ #log_entry{
+ level = warning,
+ pid = Pid,
+ msg = "Unexpected error_logger event an_unknown_event"
+ },
+ do_format(an_unknown_event)
+ ).
+
+
+format_reason_test_() ->
+ Cases = [
+ {
+ {'function not exported', [{a, b, 2}, {c, d, 1}, {e, f, 2}]},
+ "call to unexported function a:b/2 at c:d/1 <= e:f/2"
+ },
+ {
+ {'function not exported', [{a, b, 2, []}, {c, d, 1}, {e, f, 2}]},
+ "call to unexported function a:b/2 at c:d/1 <= e:f/2"
+ },
+ {
+ {undef, [{a, b, 2, []}, {c, d, 1}, {e, f, 2}]},
+ "call to undefined function a:b/2 at c:d/1 <= e:f/2"
+ },
+ {
+ {bad_return, {{a, b, 2}, {'EXIT', killed}}},
+ "bad return value {'EXIT',killed} from a:b/2"
+ },
+ {
+ {bad_return_value, foo},
+ "bad return value foo"
+ },
+ {
+ {{bad_return_value, foo}, {h, i, 0}},
+ "bad return value foo at h:i/0"
+ },
+ {
+ {{badrecord, {foo, 1, 4}}, [{h, i, 0}, {j, k, [a, b]}]},
+ "bad record {foo,1,4} at h:i/0 <= j:k/2"
+ },
+ {
+ {{case_clause, bingo}, [{j, k, 3}, {z, z, 0}]},
+ "no case clause matching bingo at j:k/3 <= z:z/0"
+ },
+ {
+ {function_clause, [{j, k, [a, 2]}, {y, x, 1}]},
+ "no function clause matching j:k(a, 2) at y:x/1"
+ },
+ {
+ {if_clause, [{j, k, [a, 2]}, {y, x, 1}]},
+ "no true branch found while evaluating if expression at j:k/2 <= y:x/1"
+ },
+ {
+ {{try_clause, bango}, [{j, k, [a, 2]}, {y, x, 1}]},
+ "no try clause matching bango at j:k/2 <= y:x/1"
+ },
+ {
+ {badarith, [{j, k, [a, 2]}, {y, x, 1}]},
+ "bad arithmetic expression at j:k/2 <= y:x/1"
+ },
+ {
+ {{badmatch, bongo}, [{j, k, [a, 2]}, {y, x, 1}]},
+ "no match of right hand value bongo at j:k/2 <= y:x/1"
+ },
+ {
+ {emfile, [{j, k, [a, 2]}, {y, x, 1}]},
+ "maximum number of file descriptors exhausted, check ulimit -n; j:k/2 <= y:x/1"
+ },
+ {
+ {system_limit, [{erlang, open_port, []}, {y, x, 1}]},
+ "system limit: maximum number of ports exceeded at y:x/1"
+ },
+ {
+ {system_limit, [{erlang, spawn, []}, {y, x, 1}]},
+ "system limit: maximum number of processes exceeded at y:x/1"
+ },
+ {
+ {system_limit, [{erlang, spawn_opt, []}, {y, x, 1}]},
+ "system limit: maximum number of processes exceeded at y:x/1"
+ },
+ {
+ {system_limit, [{erlang, list_to_atom, ["foo"]}, {y, x, 1}]},
+ "system limit: tried to create an atom larger than 255, or maximum atom count exceeded at y:x/1"
+ },
+ {
+ {system_limit, [{ets, new, []}, {y, x, 1}]},
+ "system limit: maximum number of ETS tables exceeded at y:x/1"
+ },
+ {
+ {system_limit, [{couch_log, totes_logs, []}, {y, x, 1}]},
+ "system limit: couch_log:totes_logs() at y:x/1"
+ },
+ {
+ {badarg, [{j, k, [a, 2]}, {y, x, 1}]},
+ "bad argument in call to j:k(a, 2) at y:x/1"
+ },
+ {
+ {{badarg, [{j, k, [a, 2]}, {y, x, 1}]}, some_ignored_thing},
+ "bad argument in call to j:k(a, 2) at y:x/1"
+ },
+ {
+ {{badarity, {fun erlang:spawn/1, [a, b]}}, [{y, x, 1}]},
+ "function called with wrong arity of 2 instead of 1 at y:x/1"
+ },
+ {
+ {noproc, [{y, x, 1}]},
+ "no such process or port in call to y:x/1"
+ },
+ {
+ {{badfun, 2}, [{y, x, 1}]},
+ "bad function 2 called at y:x/1"
+ },
+ {
+ {a_reason, [{y, x, 1}]},
+ "a_reason at y:x/1"
+ },
+ {
+ {a_reason, [{y, x, 1, [{line, 4}]}]},
+ "a_reason at y:x/1(line:4)"
+ }
+ ],
+ [
+ {Msg, fun() -> ?assertEqual(
+ Msg,
+ lists:flatten(couch_log_formatter:format_reason(Reason))
+ ) end}
+ || {Reason, Msg} <- Cases
+ ].
+
+
+coverage_test() ->
+ % MFA's that aren't
+ ?assertEqual(["foo"], couch_log_formatter:format_mfa(foo)),
+
+ % Traces with line numbers
+ Trace = [{x, y, [a], [{line, 4}]}],
+ ?assertEqual(
+ "x:y/1(line:4)",
+ lists:flatten(couch_log_formatter:format_trace(Trace))
+ ),
+
+ % Excercising print_silly_list
+ ?assertMatch(
+ #log_entry{
+ level = error,
+ msg = "foobar"
+ },
+ do_format({
+ error_report,
+ erlang:group_leader(),
+ {self(), std_error, "foobar"}
+ })
+ ),
+
+ % Excercising print_silly_list
+ ?assertMatch(
+ #log_entry{
+ level = error,
+ msg = "dang"
+ },
+ do_format({
+ error_report,
+ erlang:group_leader(),
+ {self(), std_error, dang}
+ })
+ ).
+
+
+do_format(Event) ->
+ E = couch_log_formatter:format(Event),
+ E#log_entry{
+ msg = lists:flatten(E#log_entry.msg),
+ msg_id = lists:flatten(E#log_entry.msg_id),
+ time_stamp = lists:flatten(E#log_entry.time_stamp)
+ }.
+
+
+do_matches(_, []) ->
+ ok;
+
+do_matches(#log_entry{msg = Msg} = E, [Pattern | RestPatterns]) ->
+ case re:run(Msg, Pattern) of
+ {match, _} ->
+ ok;
+ nomatch ->
+ Err1 = io_lib:format("'~s' does not match '~s'", [Pattern, Msg]),
+ Err2 = lists:flatten(Err1),
+ ?assertEqual(nomatch, Err2)
+ end,
+ do_matches(E, RestPatterns).
http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/blob/16a46572/test/couch_log_monitor_test.erl
----------------------------------------------------------------------
diff --git a/test/couch_log_monitor_test.erl b/test/couch_log_monitor_test.erl
new file mode 100644
index 0000000..eec0085
--- /dev/null
+++ b/test/couch_log_monitor_test.erl
@@ -0,0 +1,67 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+% http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_log_monitor_test).
+
+
+-include_lib("eunit/include/eunit.hrl").
+
+
+-define(HANDLER, couch_log_error_logger_h).
+
+
+couch_log_monitor_test_() ->
+ {setup,
+ fun couch_log_test_util:start/0,
+ fun couch_log_test_util:stop/1,
+ [
+ fun monitor_ignores_unknown_messages/0,
+ fun monitor_restarts_handler/0,
+ fun coverage_test/0
+ ]
+ }.
+
+
+monitor_ignores_unknown_messages() ->
+ Pid1 = get_monitor_pid(),
+
+ ?assertEqual(ignored, gen_server:call(Pid1, do_foo_please)),
+
+ gen_server:cast(Pid1, do_bar_please),
+ Pid1 ! do_baz_please,
+ timer:sleep(250),
+ ?assert(is_process_alive(Pid1)).
+
+
+monitor_restarts_handler() ->
+ Pid1 = get_monitor_pid(),
+ error_logger:delete_report_handler(?HANDLER),
+ timer:sleep(250),
+
+ ?assert(not is_process_alive(Pid1)),
+
+ Pid2 = get_monitor_pid(),
+ ?assert(is_process_alive(Pid2)),
+
+ Handlers = gen_event:which_handlers(error_logger),
+ ?assert(lists:member(?HANDLER, Handlers)).
+
+
+coverage_test() ->
+ Resp = couch_log_monitor:code_change(foo, bazinga, baz),
+ ?assertEqual({ok, bazinga}, Resp).
+
+
+get_monitor_pid() ->
+ Children = supervisor:which_children(couch_log_sup),
+ [MonPid] = [Pid || {couch_log_monitor, Pid, _, _} <- Children, is_pid(Pid)],
+ MonPid.
http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/blob/16a46572/test/couch_log_server_test.erl
----------------------------------------------------------------------
diff --git a/test/couch_log_server_test.erl b/test/couch_log_server_test.erl
new file mode 100644
index 0000000..7af570e
--- /dev/null
+++ b/test/couch_log_server_test.erl
@@ -0,0 +1,118 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+% http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_log_server_test).
+
+
+-include("couch_log.hrl").
+-include_lib("eunit/include/eunit.hrl").
+
+
+couch_log_server_test_() ->
+ {setup,
+ fun couch_log_test_util:start/0,
+ fun couch_log_test_util:stop/1,
+ [
+ fun check_can_reconfigure/0,
+ fun check_can_restart/0,
+ fun check_can_cast_log_entry/0,
+ fun check_logs_ignored_messages/0
+ ]
+ }.
+
+
+check_can_reconfigure() ->
+ couch_log:error("a message", []),
+ ?assertEqual(0, couch_log_test_util:last_log_key()),
+ ?assertEqual(ok, couch_log_server:reconfigure()),
+ ?assertEqual('$end_of_table', couch_log_test_util:last_log_key()),
+
+ couch_log_test_util:with_config_listener(fun() ->
+ couch_log:error("another message", []),
+ ?assertEqual(0, couch_log_test_util:last_log_key()),
+ config:set("log", "some_key", "some_val"),
+ couch_log_test_util:wait_for_config(),
+ ?assertEqual('$end_of_table', couch_log_test_util:last_log_key())
+ end).
+
+
+check_can_restart() ->
+ Pid1 = whereis(couch_log_server),
+ Ref = erlang:monitor(process, Pid1),
+ ?assert(is_process_alive(Pid1)),
+
+ supervisor:terminate_child(couch_log_sup, couch_log_server),
+ supervisor:restart_child(couch_log_sup, couch_log_server),
+
+ receive
+ {'DOWN', Ref, _, _, _} -> ok
+ after 1000 ->
+ erlang:error(timeout_restarting_couch_log_server)
+ end,
+
+ ?assert(not is_process_alive(Pid1)),
+
+ Pid2 = whereis(couch_log_server),
+ ?assertNotEqual(Pid2, Pid1),
+ ?assert(is_process_alive(Pid2)).
+
+
+check_can_cast_log_entry() ->
+ Entry = #log_entry{
+ level = critical,
+ pid = self(),
+ msg = "this will be casted",
+ msg_id = "----",
+ time_stamp = "2016-07-20-almost-my-birthday"
+ },
+ ok = gen_server:cast(couch_log_server, {log, Entry}),
+ timer:sleep(500), % totes gross
+ ?assertEqual(Entry, couch_log_test_util:last_log()).
+
+
+check_logs_ignored_messages() ->
+ gen_server:call(couch_log_server, a_call),
+ ?assertMatch(
+ #log_entry{
+ level = error,
+ pid = couch_log_server,
+ msg = "couch_log_server ignored a_call"
+ },
+ couch_log_test_util:last_log()
+ ),
+
+ gen_server:cast(couch_log_server, a_cast),
+ timer:sleep(500), % yes gross
+ ?assertMatch(
+ #log_entry{
+ level = error,
+ pid = couch_log_server,
+ msg = "couch_log_server ignored a_cast"
+ },
+ couch_log_test_util:last_log()
+ ),
+
+ couch_log_server ! an_info,
+ timer:sleep(500), % still gross
+ ?assertMatch(
+ #log_entry{
+ level = error,
+ pid = couch_log_server,
+ msg = "couch_log_server ignored an_info"
+ },
+ couch_log_test_util:last_log()
+ ).
+
+
+coverage_test() ->
+ Resp = couch_log_server:code_change(foo, bazinga, baz),
+ ?assertEqual({ok, bazinga}, Resp).
http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/blob/16a46572/test/couch_log_test.erl
----------------------------------------------------------------------
diff --git a/test/couch_log_test.erl b/test/couch_log_test.erl
new file mode 100644
index 0000000..1777730
--- /dev/null
+++ b/test/couch_log_test.erl
@@ -0,0 +1,85 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+% http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_log_test).
+
+
+-include_lib("couch_log/include/couch_log.hrl").
+-include_lib("eunit/include/eunit.hrl").
+
+
+couch_log_test_() ->
+ {setup,
+ fun couch_log_test_util:start/0,
+ fun couch_log_test_util:stop/1,
+ gen() ++ [fun check_set_level/0]
+ }.
+
+
+check_set_level() ->
+ couch_log:set_level(crit),
+ ?assertEqual("crit", config:get("log", "level")).
+
+
+levels() ->
+ [
+ debug,
+ info,
+ notice,
+ warning,
+ error,
+ critical,
+ alert,
+ emergency,
+ none
+ ].
+
+
+gen() ->
+ lists:map(fun(L) ->
+ Name = "Test log level: " ++ couch_log_util:level_to_string(L),
+ {Name, fun() -> check_levels(L, levels()) end}
+ end, levels() -- [none]).
+
+
+check_levels(_, []) ->
+ ok;
+
+check_levels(TestLevel, [CfgLevel | RestLevels]) ->
+ TestInt = couch_log_util:level_to_integer(TestLevel),
+ CfgInt = couch_log_util:level_to_integer(CfgLevel),
+ Pid = self(),
+ Msg = new_msg(),
+ LastKey = couch_log_test_util:last_log_key(),
+ couch_log_test_util:with_level(CfgLevel, fun() ->
+ couch_log:TestLevel(Msg, []),
+ case TestInt >= CfgInt of
+ true ->
+ ?assertMatch(
+ #log_entry{
+ level = TestLevel,
+ pid = Pid,
+ msg = Msg
+ },
+ couch_log_test_util:last_log()
+ );
+ false ->
+ ?assertEqual(LastKey, couch_log_test_util:last_log_key())
+ end
+ end),
+ check_levels(TestLevel, RestLevels).
+
+
+new_msg() ->
+ random:seed(os:timestamp()),
+ Bin = list_to_binary([random:uniform(255) || _ <- lists:seq(1, 16)]),
+ couch_util:to_hex(Bin).
http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/blob/16a46572/test/couch_log_test_util.erl
----------------------------------------------------------------------
diff --git a/test/couch_log_test_util.erl b/test/couch_log_test_util.erl
new file mode 100644
index 0000000..2503669
--- /dev/null
+++ b/test/couch_log_test_util.erl
@@ -0,0 +1,153 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+% http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_log_test_util).
+-compile(export_all).
+
+
+-include("couch_log.hrl").
+
+
+start() ->
+ remove_error_loggers(),
+ application:set_env(config, ini_files, config_files()),
+ application:start(config),
+ ignore_common_loggers(),
+ application:start(couch_log).
+
+
+stop(_) ->
+ application:stop(config),
+ application:stop(couch_log).
+
+
+with_level(Name, Fun) ->
+ with_config_listener(fun() ->
+ try
+ LevelStr = couch_log_util:level_to_string(Name),
+ config:set("log", "level", LevelStr, false),
+ wait_for_config(),
+ Fun()
+ after
+ config:delete("log", "level", false)
+ end
+ end).
+
+
+with_config_listener(Fun) ->
+ Listener = self(),
+ try
+ add_listener(Listener),
+ Fun()
+ after
+ rem_listener(Listener)
+ end.
+
+
+wait_for_config() ->
+ receive
+ couch_log_config_change_finished -> ok
+ after 1000 ->
+ erlang:error(config_change_timeout)
+ end.
+
+
+with_meck(Mods, Fun) ->
+ lists:foreach(fun(M) ->
+ case M of
+ {Name, Opts} -> meck:new(Name, Opts);
+ Name -> meck:new(Name)
+ end
+ end, Mods),
+ try
+ Fun()
+ after
+ lists:foreach(fun(M) ->
+ case M of
+ {Name, _} -> meck:unload(Name);
+ Name -> meck:unload(Name)
+ end
+ end, Mods)
+ end.
+
+
+ignore_common_loggers() ->
+ IgnoreSet = [
+ application_controller,
+ config,
+ config_event
+ ],
+ lists:foreach(fun(Proc) ->
+ disable_logs_from(Proc)
+ end, IgnoreSet).
+
+
+disable_logs_from(Pid) when is_pid(Pid) ->
+ Ignored = case application:get_env(couch_log, ignored_pids) of
+ {ok, L} when is_list(L) ->
+ lists:usort([Pid | L]);
+ _E ->
+ [Pid]
+ end,
+ IgnoredAlive = [P || P <- Ignored, is_process_alive(P)],
+ application:set_env(couch_log, ignored_pids, IgnoredAlive);
+
+disable_logs_from(Name) when is_atom(Name) ->
+ case whereis(Name) of
+ P when is_pid(P) ->
+ disable_logs_from(P);
+ undefined ->
+ erlang:error({unknown_pid_name, Name})
+ end.
+
+
+last_log_key() ->
+ ets:last(?COUCH_LOG_TEST_TABLE).
+
+
+last_log() ->
+ [{_, Entry}] = ets:lookup(?COUCH_LOG_TEST_TABLE, last_log_key()),
+ Entry.
+
+
+remove_error_loggers() ->
+ lists:foreach(fun(Handler) ->
+ error_logger:delete_report_handler(Handler)
+ end, gen_event:which_handlers(error_logger)).
+
+
+config_files() ->
+ Path = filename:dirname(code:which(?MODULE)),
+ Name = filename:join(Path, "couch_log_test.ini"),
+ ok = file:write_file(Name, "[log]\nwriter = ets\n"),
+ [Name].
+
+
+add_listener(Listener) ->
+ Listeners = case application:get_env(couch_log, config_listeners) of
+ {ok, L} when is_list(L) ->
+ lists:usort([Listener | L]);
+ _ ->
+ [Listener]
+ end,
+ application:set_env(couch_log, config_listeners, Listeners).
+
+
+rem_listener(Listener) ->
+ Listeners = case application:get_env(couch_lig, config_listeners) of
+ {ok, L} when is_list(L) ->
+ L -- [Listener];
+ _ ->
+ []
+ end,
+ application:set_env(couch_log, config_listeners, Listeners).
+
http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/blob/16a46572/test/couch_log_util_test.erl
----------------------------------------------------------------------
diff --git a/test/couch_log_util_test.erl b/test/couch_log_util_test.erl
new file mode 100644
index 0000000..e97911a
--- /dev/null
+++ b/test/couch_log_util_test.erl
@@ -0,0 +1,55 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+% http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_log_util_test).
+
+
+-include_lib("couch_log/include/couch_log.hrl").
+-include_lib("eunit/include/eunit.hrl").
+
+
+get_message_id_test() ->
+ ?assertEqual("--------", couch_log_util:get_msg_id()),
+ erlang:put(nonce, "deadbeef"),
+ ?assertEqual("deadbeef", couch_log_util:get_msg_id()),
+ erlang:put(nonce, undefined).
+
+
+level_to_atom_test() ->
+ lists:foreach(fun(L) ->
+ ?assert(is_atom(couch_log_util:level_to_atom(L))),
+ ?assert(is_integer(couch_log_util:level_to_integer(L))),
+ ?assert(is_list(couch_log_util:level_to_string(L)))
+ end, levels()).
+
+
+string_p_test() ->
+ ?assertEqual(false, couch_log_util:string_p([])),
+ ?assertEqual(false, couch_log_util:string_p([[false]])),
+ ?assertEqual(true, couch_log_util:string_p([$\n])),
+ ?assertEqual(true, couch_log_util:string_p([$\r])),
+ ?assertEqual(true, couch_log_util:string_p([$\t])),
+ ?assertEqual(true, couch_log_util:string_p([$\v])),
+ ?assertEqual(true, couch_log_util:string_p([$\b])),
+ ?assertEqual(true, couch_log_util:string_p([$\f])),
+ ?assertEqual(true, couch_log_util:string_p([$\e])).
+
+
+levels() ->
+ [
+ 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ "1", "2", "3", "4", "5", "6", "7", "8", "9",
+ debug, info, notice, warning, warn, error, err,
+ critical, crit, alert, emergency, emerg, none,
+ "debug", "info", "notice", "warning", "warn", "error", "err",
+ "critical", "crit", "alert", "emergency", "emerg", "none"
+ ].
http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/blob/16a46572/test/couch_log_writer_ets.erl
----------------------------------------------------------------------
diff --git a/test/couch_log_writer_ets.erl b/test/couch_log_writer_ets.erl
new file mode 100644
index 0000000..d5fd327
--- /dev/null
+++ b/test/couch_log_writer_ets.erl
@@ -0,0 +1,49 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+% http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_log_writer_ets).
+-behaviour(couch_log_writer).
+
+
+-export([
+ init/0,
+ terminate/2,
+ write/2
+]).
+
+
+-include("couch_log.hrl").
+
+
+init() ->
+ ets:new(?COUCH_LOG_TEST_TABLE, [named_table, public, ordered_set]),
+ {ok, 0}.
+
+
+terminate(_, _St) ->
+ ets:delete(?COUCH_LOG_TEST_TABLE),
+ ok.
+
+
+write(Entry0, St) ->
+ Entry = Entry0#log_entry{
+ msg = lists:flatten(Entry0#log_entry.msg),
+ time_stamp = lists:flatten(Entry0#log_entry.time_stamp)
+ },
+ Ignored = application:get_env(couch_log, ignored_pids, []),
+ case lists:member(Entry#log_entry.pid, Ignored) of
+ true ->
+ {ok, St};
+ false ->
+ ets:insert(?COUCH_LOG_TEST_TABLE, {St, Entry}),
+ {ok, St + 1}
+ end.
http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/blob/16a46572/test/couch_log_writer_file_test.erl
----------------------------------------------------------------------
diff --git a/test/couch_log_writer_file_test.erl b/test/couch_log_writer_file_test.erl
new file mode 100644
index 0000000..6d3f3ec
--- /dev/null
+++ b/test/couch_log_writer_file_test.erl
@@ -0,0 +1,161 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+% http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_log_writer_file_test).
+
+
+-include_lib("kernel/include/file.hrl").
+-include_lib("couch_log/include/couch_log.hrl").
+-include_lib("eunit/include/eunit.hrl").
+
+
+-define(WRITER, couch_log_writer_file).
+
+
+couch_log_writer_file_test_() ->
+ {setup,
+ fun couch_log_test_util:start/0,
+ fun couch_log_test_util:stop/1,
+ [
+ fun check_init_terminate/0,
+ fun() ->
+ couch_log_test_util:with_meck(
+ [{filelib, [unstick]}],
+ fun check_ensure_dir_fail/0
+ )
+ end,
+ fun() ->
+ couch_log_test_util:with_meck(
+ [{file, [unstick, passthrough]}],
+ fun check_open_fail/0
+ )
+ end,
+ fun() ->
+ couch_log_test_util:with_meck(
+ [{file, [unstick, passthrough]}],
+ fun check_read_file_info_fail/0
+ )
+ end,
+ fun check_file_write/0,
+ fun check_buffered_file_write/0,
+ fun check_reopen/0
+ ]
+ }.
+
+
+check_init_terminate() ->
+ {ok, St} = ?WRITER:init(),
+ ok = ?WRITER:terminate(stop, St).
+
+
+check_ensure_dir_fail() ->
+ meck:expect(filelib, ensure_dir, 1, {error, eperm}),
+ ?assertEqual({error, eperm}, ?WRITER:init()),
+ ?assert(meck:called(filelib, ensure_dir, 1)),
+ ?assert(meck:validate(filelib)).
+
+
+check_open_fail() ->
+ meck:expect(file, open, 2, {error, enotfound}),
+ ?assertEqual({error, enotfound}, ?WRITER:init()),
+ ?assert(meck:called(file, open, 2)),
+ ?assert(meck:validate(file)).
+
+
+check_read_file_info_fail() ->
+ RFI = fun
+ ("./couch.log") -> {error, enoent};
+ (Path) -> meck:passthrough([Path])
+ end,
+ meck:expect(file, read_file_info, RFI),
+ ?assertEqual({error, enoent}, ?WRITER:init()),
+ ?assert(meck:called(file, read_file_info, 1)),
+ ?assert(meck:validate(file)).
+
+
+check_file_write() ->
+ % Make sure we have an empty log for this test
+ IsFile = filelib:is_file("./couch.log"),
+ if not IsFile -> ok; true ->
+ file:delete("./couch.log")
+ end,
+
+ Entry = #log_entry{
+ level = info,
+ pid = list_to_pid("<0.1.0>"),
+ msg = "stuff",
+ msg_id = "msg_id",
+ time_stamp = "time_stamp"
+ },
+ {ok, St} = ?WRITER:init(),
+ {ok, NewSt} = ?WRITER:write(Entry, St),
+ ok = ?WRITER:terminate(stop, NewSt),
+
+ {ok, Data} = file:read_file("./couch.log"),
+ Expect = <<"[info] time_stamp nonode@nohost <0.1.0> msg_id stuff\n">>,
+ ?assertEqual(Expect, Data).
+
+
+check_buffered_file_write() ->
+ % Make sure we have an empty log for this test
+ IsFile = filelib:is_file("./couch.log"),
+ if not IsFile -> ok; true ->
+ file:delete("./couch.log")
+ end,
+
+ config:set("log", "write_buffer", "1024"),
+ config:set("log", "write_delay", "10"),
+
+ try
+ Entry = #log_entry{
+ level = info,
+ pid = list_to_pid("<0.1.0>"),
+ msg = "stuff",
+ msg_id = "msg_id",
+ time_stamp = "time_stamp"
+ },
+ {ok, St} = ?WRITER:init(),
+ {ok, NewSt} = ?WRITER:write(Entry, St),
+ ok = ?WRITER:terminate(stop, NewSt)
+ after
+ config:delete("log", "write_buffer"),
+ config:delete("log", "write_delay")
+ end,
+
+ {ok, Data} = file:read_file("./couch.log"),
+ Expect = <<"[info] time_stamp nonode@nohost <0.1.0> msg_id stuff\n">>,
+ ?assertEqual(Expect, Data).
+
+
+check_reopen() ->
+ {ok, St1} = clear_clock(?WRITER:init()),
+ {ok, St2} = clear_clock(couch_log_writer_file:maybe_reopen(St1)),
+ ?assertEqual(St1, St2),
+
+ % Delete file
+ file:delete("./couch.log"),
+ {ok, St3} = clear_clock(couch_log_writer_file:maybe_reopen(St2)),
+ ?assert(element(3, St3) /= element(3, St2)),
+
+ % Recreate file
+ file:delete("./couch.log"),
+ file:write_file("./couch.log", ""),
+ {ok, St4} = clear_clock(couch_log_writer_file:maybe_reopen(St3)),
+ ?assert(element(3, St4) /= element(3, St2)).
+
+
+clear_clock({ok, St}) ->
+ {ok, clear_clock(St)};
+
+clear_clock(St) ->
+ {st, Path, Fd, INode, _} = St,
+ {st, Path, Fd, INode, {0, 0, 0}}.
http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/blob/16a46572/test/couch_log_writer_stderr_test.erl
----------------------------------------------------------------------
diff --git a/test/couch_log_writer_stderr_test.erl b/test/couch_log_writer_stderr_test.erl
new file mode 100644
index 0000000..1e99263
--- /dev/null
+++ b/test/couch_log_writer_stderr_test.erl
@@ -0,0 +1,58 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+% http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_log_writer_stderr_test).
+
+
+-include_lib("couch_log/include/couch_log.hrl").
+-include_lib("eunit/include/eunit.hrl").
+
+
+-define(WRITER, couch_log_writer_stderr).
+
+
+couch_log_writer_stderr_test_() ->
+ {setup,
+ fun couch_log_test_util:start/0,
+ fun couch_log_test_util:stop/1,
+ [
+ fun check_init_terminate/0,
+ fun() ->
+ couch_log_test_util:with_meck(
+ [{io, [unstick]}],
+ fun check_write/0
+ )
+ end
+ ]
+ }.
+
+
+check_init_terminate() ->
+ {ok, St} = ?WRITER:init(),
+ ok = ?WRITER:terminate(stop, St).
+
+
+check_write() ->
+ meck:expect(io, format, 3, ok),
+
+ Entry = #log_entry{
+ level = debug,
+ pid = list_to_pid("<0.1.0>"),
+ msg = "stuff",
+ msg_id = "msg_id",
+ time_stamp = "time_stamp"
+ },
+ {ok, St} = ?WRITER:init(),
+ {ok, NewSt} = ?WRITER:write(Entry, St),
+ ok = ?WRITER:terminate(stop, NewSt),
+
+ ?assert(meck:validate(io)).
http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/blob/16a46572/test/couch_log_writer_syslog_test.erl
----------------------------------------------------------------------
diff --git a/test/couch_log_writer_syslog_test.erl b/test/couch_log_writer_syslog_test.erl
new file mode 100644
index 0000000..c32b5c6
--- /dev/null
+++ b/test/couch_log_writer_syslog_test.erl
@@ -0,0 +1,122 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+% http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_log_writer_syslog_test).
+
+
+-include_lib("couch_log/include/couch_log.hrl").
+-include_lib("eunit/include/eunit.hrl").
+
+
+-define(WRITER, couch_log_writer_syslog).
+
+
+couch_log_writer_syslog_test_() ->
+ {setup,
+ fun couch_log_test_util:start/0,
+ fun couch_log_test_util:stop/1,
+ [
+ fun check_init_terminate/0,
+ fun() ->
+ couch_log_test_util:with_meck(
+ [{io, [unstick]}],
+ fun check_stderr_write/0
+ )
+ end,
+ fun() ->
+ couch_log_test_util:with_meck(
+ [{gen_udp, [unstick]}],
+ fun check_udp_send/0
+ )
+ end
+ ]
+ }.
+
+
+check_init_terminate() ->
+ {ok, St} = ?WRITER:init(),
+ ok = ?WRITER:terminate(stop, St).
+
+
+check_stderr_write() ->
+ meck:expect(io, format, 3, ok),
+
+ Entry = #log_entry{
+ level = debug,
+ pid = list_to_pid("<0.1.0>"),
+ msg = "stuff",
+ msg_id = "msg_id",
+ time_stamp = "time_stamp"
+ },
+ {ok, St} = ?WRITER:init(),
+ {ok, NewSt} = ?WRITER:write(Entry, St),
+ ok = ?WRITER:terminate(stop, NewSt),
+
+ ?assert(meck:called(io, format, 3)),
+ ?assert(meck:validate(io)).
+
+
+check_udp_send() ->
+ meck:expect(gen_udp, open, 1, {ok, socket}),
+ meck:expect(gen_udp, send, 4, ok),
+ meck:expect(gen_udp, close, fun(socket) -> ok end),
+
+ config:set("log", "syslog_host", "localhost"),
+ try
+ Entry = #log_entry{
+ level = debug,
+ pid = list_to_pid("<0.1.0>"),
+ msg = "stuff",
+ msg_id = "msg_id",
+ time_stamp = "time_stamp"
+ },
+ {ok, St} = ?WRITER:init(),
+ {ok, NewSt} = ?WRITER:write(Entry, St),
+ ok = ?WRITER:terminate(stop, NewSt)
+ after
+ config:delete("log", "syslog_host")
+ end,
+
+ ?assert(meck:called(gen_udp, open, 1)),
+ ?assert(meck:called(gen_udp, send, 4)),
+ ?assert(meck:called(gen_udp, close, 1)),
+ ?assert(meck:validate(gen_udp)).
+
+
+facility_test() ->
+ Names = [
+ "kern", "user", "mail", "daemon", "auth", "syslog", "lpr",
+ "news", "uucp", "clock", "authpriv", "ftp", "ntp", "audit",
+ "alert", "cron", "local0", "local1", "local2", "local3",
+ "local4", "local5", "local6", "local7"
+ ],
+ lists:foldl(fun(Name, Id) ->
+ IdStr = lists:flatten(io_lib:format("~w", [Id])),
+ ?assertEqual(Id bsl 3, couch_log_writer_syslog:get_facility(Name)),
+ ?assertEqual(Id bsl 3, couch_log_writer_syslog:get_facility(IdStr)),
+ Id + 1
+ end, 0, Names),
+ ?assertEqual(23 bsl 3, couch_log_writer_syslog:get_facility("foo")),
+ ?assertEqual(23 bsl 3, couch_log_writer_syslog:get_facility("-1")),
+ ?assertEqual(23 bsl 3, couch_log_writer_syslog:get_facility("24")).
+
+
+level_test() ->
+ Levels = [
+ emergency, alert, critical, error,
+ warning, notice, info, debug
+ ],
+ lists:foldl(fun(Name, Id) ->
+ ?assertEqual(Id, couch_log_writer_syslog:get_level(Name)),
+ Id + 1
+ end, 0, Levels),
+ ?assertEqual(3, couch_log_writer_syslog:get_level(foo)).
http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/blob/16a46572/test/couch_log_writer_test.erl
----------------------------------------------------------------------
diff --git a/test/couch_log_writer_test.erl b/test/couch_log_writer_test.erl
new file mode 100644
index 0000000..d0bb347
--- /dev/null
+++ b/test/couch_log_writer_test.erl
@@ -0,0 +1,54 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+% http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_log_writer_test).
+
+
+-include_lib("couch_log/include/couch_log.hrl").
+-include_lib("eunit/include/eunit.hrl").
+
+
+couch_log_writer_test_() ->
+ {setup,
+ fun couch_log_test_util:start/0,
+ fun couch_log_test_util:stop/1,
+ [
+ fun check_writer_change/0
+ ]
+ }.
+
+
+check_writer_change() ->
+ % Change to file and back
+ couch_log_test_util:with_config_listener(fun() ->
+ config:set("log", "writer", "file"),
+ couch_log_test_util:wait_for_config(),
+ ?assertEqual(undefined, ets:info(?COUCH_LOG_TEST_TABLE)),
+ ?assert(is_pid(whereis(couch_log_server))),
+
+ config:set("log", "writer", "couch_log_writer_ets"),
+ couch_log_test_util:wait_for_config(),
+ ?assertEqual(0, ets:info(?COUCH_LOG_TEST_TABLE, size))
+ end),
+
+ % Using a bad setting doesn't break things
+ couch_log_test_util:with_config_listener(fun() ->
+ config:set("log", "writer", "hopefully not an atom or module"),
+ couch_log_test_util:wait_for_config(),
+ ?assertEqual(undefined, ets:info(?COUCH_LOG_TEST_TABLE)),
+ ?assert(is_pid(whereis(couch_log_server))),
+
+ config:set("log", "writer", "couch_log_writer_ets"),
+ couch_log_test_util:wait_for_config(),
+ ?assertEqual(0, ets:info(?COUCH_LOG_TEST_TABLE, size))
+ end).
+