1% Licensed under the Apache License, Version 2.0 (the "License"); you may not
2% use this file except in compliance with the License. You may obtain a copy of
3% the License at
4%
5%   http://www.apache.org/licenses/LICENSE-2.0
6%
7% Unless required by applicable law or agreed to in writing, software
8% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10% License for the specific language governing permissions and limitations under
11% the License.
12
13-module(couch_log_writer_syslog).
14-behavior(couch_log_writer).
15
16
17-export([
18    init/0,
19    terminate/2,
20    write/2
21]).
22
23
24-include("couch_log.hrl").
25
26
27-record(st, {
28    socket,
29    host,
30    port,
31    hostname,
32    os_pid,
33    appid,
34    facility
35}).
36
37
38-define(SYSLOG_VERSION, 1).
39
40
41-ifdef(TEST).
42-export([
43    get_facility/1,
44    get_level/1
45]).
46-endif.
47
48
49init() ->
50    {ok, Socket} = gen_udp:open(0),
51
52    Host = case config:get("log", "syslog_host") of
53        undefined ->
54            undefined;
55        SysLogHost ->
56            case inet:getaddr(SysLogHost, inet) of
57                {ok, Address} ->
58                    Address;
59                _ ->
60                    undefined
61            end
62        end,
63
64    {ok, #st{
65        socket = Socket,
66        host = Host,
67        port = config:get_integer("log", "syslog_port", 514),
68        hostname = net_adm:localhost(),
69        os_pid = os:getpid(),
70        appid = config:get("log", "syslog_appid", "couchdb"),
71        facility = get_facility(config:get("log", "syslog_facility", "local2"))
72    }}.
73
74
75terminate(_Reason, St) ->
76    gen_udp:close(St#st.socket).
77
78
79write(Entry, St) ->
80    #log_entry{
81        level = Level,
82        pid = Pid,
83        msg = Msg,
84        msg_id = MsgId,
85        time_stamp = TimeStamp
86    } = Entry,
87    Fmt = "<~B>~B ~s ~s ~s ~p ~s - ",
88    Args = [
89        St#st.facility bor get_level(Level),
90        ?SYSLOG_VERSION,
91        TimeStamp,
92        St#st.hostname,
93        St#st.appid,
94        Pid,
95        MsgId
96    ],
97    Pre = io_lib:format(Fmt, Args),
98    ok = send(St, [Pre, Msg, $\n]),
99    {ok, St}.
100
101
102send(#st{host=undefined}, Packet) ->
103    io:format(standard_error, "~s", [Packet]);
104
105send(St, Packet) ->
106    #st{
107        socket = Socket,
108        host = Host,
109        port = Port
110    } = St,
111    gen_udp:send(Socket, Host, Port, Packet).
112
113
114get_facility(Name) ->
115    FacId = case Name of
116        "kern"      ->  0; % Kernel messages
117        "user"      ->  1; % Random user-level messages
118        "mail"      ->  2; % Mail system
119        "daemon"    ->  3; % System daemons
120        "auth"      ->  4; % Security/Authorization messages
121        "syslog"    ->  5; % Internal Syslog messages
122        "lpr"       ->  6; % Line printer subsystem
123        "news"      ->  7; % Network news subsystems
124        "uucp"      ->  8; % UUCP subsystem
125        "clock"     ->  9; % Clock daemon
126        "authpriv"  -> 10; % Security/Authorization messages
127        "ftp"       -> 11; % FTP daemon
128        "ntp"       -> 12; % NTP subsystem
129        "audit"     -> 13; % Log audit
130        "alert"     -> 14; % Log alert
131        "cron"      -> 15; % Scheduling daemon
132        "local0"    -> 16; % Local use 0
133        "local1"    -> 17; % Local use 1
134        "local2"    -> 18; % Local use 2
135        "local3"    -> 19; % Local use 3
136        "local4"    -> 20; % Local use 4
137        "local5"    -> 21; % Local use 5
138        "local6"    -> 22; % Local use 6
139        "local7"    -> 23; % Local use 7
140        _ ->
141            try list_to_integer(Name) of
142                N when N >= 0, N =< 23 -> N;
143                _ -> 23
144            catch _:_ ->
145                23
146            end
147    end,
148    FacId bsl 3.
149
150
151get_level(Name) when is_atom(Name) ->
152    case Name of
153        debug       -> 7;
154        info        -> 6;
155        notice      -> 5;
156        warning     -> 4;
157        error       -> 3;
158        critical    -> 2;
159        alert       -> 1;
160        emergency   -> 0;
161        _           -> 3
162    end.
163