Make couch_log configurable
This allows to set logging backend and level via config API making
logging configuration a bit closer as it was in CouchDB 1.x.
diff --git a/src/couch_log.app.src b/src/couch_log.app.src
new file mode 100644
index 0000000..efcebec
--- /dev/null
+++ b/src/couch_log.app.src
@@ -0,0 +1,26 @@
+% 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.
+
+{application, couch_log, [
+ {description, "CouchDB Log API"},
+ {vsn, git},
+ {modules, [
+ couch_log,
+ couch_log_app,
+ couch_log_config_listener,
+ couch_log_stderr,
+ couch_log_sup
+ ]},
+ {registered, [couch_log_sup]},
+ {applications, [kernel, stdlib, config]},
+ {mod, {couch_log_app, []}}
+]}.
diff --git a/src/couch_log.app.src.script b/src/couch_log.app.src.script
deleted file mode 100644
index aafd368..0000000
--- a/src/couch_log.app.src.script
+++ /dev/null
@@ -1,42 +0,0 @@
-% 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.
-
-CouchConfig = case filelib:is_file(os:getenv("COUCHDB_CONFIG")) of
- true ->
- {ok, Result} = file:consult(os:getenv("COUCHDB_CONFIG")),
- Result;
- false ->
- []
-end.
-
-Backend = case lists:keyfind(couch_log_backend, 1, CouchConfig) of
- {couch_log_backend, Backend0} ->
- Backend0;
- false ->
- couch_log_stderr
-end.
-
-BackendApps = case lists:keyfind(couch_log_backend_apps, 1, CouchConfig) of
- {couch_log_backend_apps, {list, Apps}} ->
- Apps;
- false ->
- []
-end.
-
-{application, couch_log, [
- {description, "CouchDB Log API"},
- {vsn, git},
- {modules, [couch_log]},
- {registered, []},
- {applications, [kernel, stdlib] ++ BackendApps},
- {env, [{backend, Backend}]}
-]}.
diff --git a/src/couch_log.erl b/src/couch_log.erl
index b24e5e1..c07f9a4 100644
--- a/src/couch_log.erl
+++ b/src/couch_log.erl
@@ -30,63 +30,93 @@
-callback emergency(Fmt::string(), Args::list()) -> ok.
-callback set_level(Level::atom()) -> ok.
+-spec level_integer(atom()) -> integer().
+level_integer(debug) -> 1;
+level_integer(info) -> 2;
+level_integer(notice) -> 3;
+level_integer(warning) -> 4;
+level_integer(error) -> 5;
+level_integer(critical) -> 6;
+level_integer(alert) -> 7;
+level_integer(emergency) -> 8;
+level_integer(none) -> 9.
+
+-spec level_to_atom(string() | integer()) -> atom().
+level_to_atom("1") -> debug;
+level_to_atom("debug") -> debug;
+level_to_atom("2") -> info;
+level_to_atom("info") -> info;
+level_to_atom("3") -> notice;
+level_to_atom("notice") -> notice;
+level_to_atom("4") -> warning;
+level_to_atom("warning") -> warning;
+level_to_atom("5") -> error;
+level_to_atom("error") -> error;
+level_to_atom("6") -> critical;
+level_to_atom("critical") -> critical;
+level_to_atom("7") -> alert;
+level_to_atom("alert") -> alert;
+level_to_atom("8") -> emergency;
+level_to_atom("emergency") -> emergency;
+level_to_atom("9") -> none;
+level_to_atom("none") -> none;
+level_to_atom(V) when is_integer(V) -> level_to_atom(integer_to_list(V));
+level_to_atom(V) when is_list(V) -> notice.
-spec debug(string(), list()) -> ok.
-debug(Fmt, Args) ->
- {ok, Backend} = get_backend(),
- catch couch_stats:increment_counter([couch_log, level, debug]),
- Backend:debug(Fmt, Args).
+debug(Fmt, Args) -> log(debug, Fmt, Args).
-spec info(string(), list()) -> ok.
-info(Fmt, Args) ->
- {ok, Backend} = get_backend(),
- catch couch_stats:increment_counter([couch_log, level, info]),
- Backend:info(Fmt, Args).
+info(Fmt, Args) -> log(info, Fmt, Args).
-spec notice(string(), list()) -> ok.
-notice(Fmt, Args) ->
- {ok, Backend} = get_backend(),
- catch couch_stats:increment_counter([couch_log, level, notice]),
- Backend:notice(Fmt, Args).
+notice(Fmt, Args) -> log(notice, Fmt, Args).
-spec warning(string(), list()) -> ok.
-warning(Fmt, Args) ->
- {ok, Backend} = get_backend(),
- catch couch_stats:increment_counter([couch_log, level, warning]),
- Backend:warning(Fmt, Args).
+warning(Fmt, Args) -> log(warning, Fmt, Args).
-spec error(string(), list()) -> ok.
-error(Fmt, Args) ->
- {ok, Backend} = get_backend(),
- catch couch_stats:increment_counter([couch_log, level, 'error']),
- Backend:error(Fmt, Args).
+error(Fmt, Args) -> log(error, Fmt, Args).
-spec critical(string(), list()) -> ok.
-critical(Fmt, Args) ->
- {ok, Backend} = get_backend(),
- catch couch_stats:increment_counter([couch_log, level, critical]),
- Backend:critical(Fmt, Args).
+critical(Fmt, Args) -> log(critical, Fmt, Args).
-spec alert(string(), list()) -> ok.
-alert(Fmt, Args) ->
- {ok, Backend} = get_backend(),
- catch couch_stats:increment_counter([couch_log, level, alert]),
- Backend:alert(Fmt, Args).
+alert(Fmt, Args) -> log(alert, Fmt, Args).
-spec emergency(string(), list()) -> ok.
-emergency(Fmt, Args) ->
- {ok, Backend} = get_backend(),
- catch couch_stats:increment_counter([couch_log, level, emergency]),
- Backend:emergency(Fmt, Args).
+emergency(Fmt, Args) -> log(emergency, Fmt, Args).
--spec set_level(atom()) -> ok.
-set_level(Level) ->
+-spec log(atom(), string(), list()) -> ok.
+log(Level, Fmt, Args) ->
+ case is_active_level(Level) of
+ false -> ok;
+ true ->
+ {ok, Backend} = get_backend(),
+ catch couch_stats:increment_counter([couch_log, level, Level]),
+ apply(Backend, Level, [Fmt, Args])
+ end.
+
+-spec is_active_level(atom()) -> boolean.
+is_active_level(Level) ->
+ CurrentLevel = level_to_atom(config:get("log", "level", "notice")),
+ level_integer(Level) >= level_integer(CurrentLevel).
+
+-spec set_level(atom() | string() | integer()) -> ok.
+set_level(Level) when is_atom(Level) ->
{ok, Backend} = get_backend(),
- Backend:set_level(Level).
+ Backend:set_level(Level);
+set_level(Level) ->
+ set_level(level_to_atom(Level)).
-spec get_backend() -> {ok, atom()}.
get_backend() ->
- application:get_env(?MODULE, backend).
+ BackendName = "couch_log_" ++ config:get("log", "backend", "stderr"),
+ Backend = list_to_existing_atom(BackendName), %% yes, we need crash here
+ case erlang:module_loaded(Backend) of
+ true -> {ok, Backend};
+ false -> {ok, couch_log_stderr}
+ end.
-ifdef(TEST).
@@ -111,13 +141,16 @@
}.
setup() ->
+ ok = meck:new(config),
+ ok = meck:expect(config, get,
+ fun("log", "backend", _) -> "eunit";
+ ("log", "level", _) -> "debug" end),
meck:new([couch_stats, couch_log_eunit], [non_strict]),
meck:expect(couch_stats, increment_counter, 1, ok),
- setup_couch_log_eunit(),
- application:load(?MODULE),
- application:set_env(?MODULE, backend, couch_log_eunit).
+ setup_couch_log_eunit().
cleanup(_) ->
+ meck:unload(config),
meck:unload([couch_stats, couch_log_eunit]).
setup_couch_log_eunit() ->
diff --git a/src/couch_log_app.erl b/src/couch_log_app.erl
new file mode 100644
index 0000000..91a8ecc
--- /dev/null
+++ b/src/couch_log_app.erl
@@ -0,0 +1,24 @@
+% 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_app).
+
+-behaviour(application).
+
+-export([start/2, stop/1]).
+
+
+start(_Type, _StartArgs) ->
+ couch_log_sup:start_link().
+
+stop(_State) ->
+ ok.
diff --git a/src/couch_log_config_listener.erl b/src/couch_log_config_listener.erl
new file mode 100644
index 0000000..6dc7ea6
--- /dev/null
+++ b/src/couch_log_config_listener.erl
@@ -0,0 +1,46 @@
+% 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).
+-vsn(2).
+-behaviour(config_listener).
+
+% public interface
+-export([subscribe/0]).
+
+% config_listener callback
+-export([handle_config_change/5, handle_config_terminate/3]).
+
+subscribe() ->
+ Settings = [
+ {backend, config:get("log", "backend", "stderr")},
+ {level, config:get("log", "level", "notice")}
+ ],
+ ok = config:listen_for_changes(?MODULE, Settings),
+ ok.
+
+handle_config_change("log", "backend", Value, _, Settings) ->
+ {level, Level} = lists:keyfind(level, 1, Settings),
+ couch_log:set_level(Level),
+ {ok, lists:keyreplace(backend, 1, Settings, {backend, Value})};
+handle_config_change("log", "level", Value, _, Settings) ->
+ couch_log:set_level(Value),
+ {ok, lists:keyreplace(level, 1, Settings, {level, Value})};
+handle_config_change(_, _, _, _, Settings) ->
+ {ok, Settings}.
+
+handle_config_terminate(_, stop, _) -> ok;
+handle_config_terminate(_Server, _Reason, State) ->
+ spawn(fun() ->
+ timer:sleep(5000),
+ config:listen_for_changes(?MODULE, State)
+ end).
diff --git a/src/couch_log_sup.erl b/src/couch_log_sup.erl
new file mode 100644
index 0000000..9d69fd0
--- /dev/null
+++ b/src/couch_log_sup.erl
@@ -0,0 +1,27 @@
+% 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_sup).
+
+-behaviour(supervisor).
+
+-export([init/1]).
+-export([start_link/0]).
+
+
+start_link() ->
+ supervisor:start_link({local, ?MODULE}, ?MODULE, []).
+
+
+init([]) ->
+ ok = couch_log_config_listener:subscribe(),
+ {ok, {{one_for_one, 1, 1}, []}}.