Add a syslog writer
COUCHDB-3067
diff --git a/src/couch_log_writer_syslog.erl b/src/couch_log_writer_syslog.erl
new file mode 100644
index 0000000..738d162
--- /dev/null
+++ b/src/couch_log_writer_syslog.erl
@@ -0,0 +1,155 @@
+% 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).
+-behavior(couch_log_writer).
+
+
+-export([
+ init/0,
+ terminate/2,
+ write/2
+]).
+
+
+-include("couch_log.hrl").
+
+
+-record(st, {
+ socket,
+ host,
+ port,
+ hostname,
+ os_pid,
+ appid,
+ facility
+}).
+
+
+-define(SYSLOG_VERSION, 1).
+
+
+-ifdef(TEST).
+-compile(export_all).
+-endif.
+
+
+init() ->
+ {ok, Socket} = gen_udp:open(0),
+
+ SysLogHost = config:get("log", "syslog_host"),
+ Host = case inet:getaddr(SysLogHost, inet) of
+ {ok, Address} when SysLogHost /= undefined ->
+ Address;
+ _ ->
+ undefined
+ end,
+
+ {ok, #st{
+ socket = Socket,
+ host = Host,
+ port = config:get_integer("log", "syslog_port", 514),
+ hostname = net_adm:localhost(),
+ os_pid = os:getpid(),
+ appid = config:get("log", "syslog_appid", "couchdb"),
+ facility = get_facility(config:get("log", "syslog_facility", "local2"))
+ }}.
+
+
+terminate(_Reason, St) ->
+ gen_udp:close(St#st.socket).
+
+
+write(Entry, St) ->
+ #log_entry{
+ level = Level,
+ pid = Pid,
+ msg = Msg,
+ msg_id = MsgId,
+ time_stamp = TimeStamp
+ } = Entry,
+ Fmt = "<~B>~B ~s ~s ~s ~p ~s - ",
+ Args = [
+ St#st.facility bor get_level(Level),
+ ?SYSLOG_VERSION,
+ TimeStamp,
+ St#st.hostname,
+ St#st.appid,
+ Pid,
+ MsgId
+ ],
+ Pre = io_lib:format(Fmt, Args),
+ ok = send(St, [Pre, Msg, $\n]),
+ {ok, St}.
+
+
+send(#st{host=undefined}, Packet) ->
+ io:format(standard_error, "~s", [Packet]);
+
+send(St, Packet) ->
+ #st{
+ socket = Socket,
+ host = Host,
+ port = Port
+ } = St,
+ gen_udp:send(Socket, Host, Port, Packet).
+
+
+get_facility(Name) ->
+ FacId = case Name of
+ "kern" -> 0; % Kernel messages
+ "user" -> 1; % Random user-level messages
+ "mail" -> 2; % Mail system
+ "daemon" -> 3; % System daemons
+ "auth" -> 4; % Security/Authorization messages
+ "syslog" -> 5; % Internal Syslog messages
+ "lpr" -> 6; % Line printer subsystem
+ "news" -> 7; % Network news subsystems
+ "uucp" -> 8; % UUCP subsystem
+ "clock" -> 9; % Clock daemon
+ "authpriv" -> 10; % Security/Authorization messages
+ "ftp" -> 11; % FTP daemon
+ "ntp" -> 12; % NTP subsystem
+ "audit" -> 13; % Log audit
+ "alert" -> 14; % Log alert
+ "cron" -> 15; % Scheduling daemon
+ "local0" -> 16; % Local use 0
+ "local1" -> 17; % Local use 1
+ "local2" -> 18; % Local use 2
+ "local3" -> 19; % Local use 3
+ "local4" -> 20; % Local use 4
+ "local5" -> 21; % Local use 5
+ "local6" -> 22; % Local use 6
+ "local7" -> 23; % Local use 7
+ _ ->
+ try list_to_integer(Name) of
+ N when N >= 0, N =< 23 -> N;
+ _ -> 23
+ catch _:_ ->
+ 23
+ end
+ end,
+ FacId bsl 3.
+
+
+get_level(Name) when is_atom(Name) ->
+ case Name of
+ debug -> 7;
+ info -> 6;
+ notice -> 5;
+ warning -> 4;
+ error -> 3;
+ critical -> 2;
+ alert -> 1;
+ emergency -> 0;
+ _ -> 3
+ end.