summaryrefslogtreecommitdiff
path: root/websockify/sysloghandler.py
blob: 37ee9dd24ce23134777c7000e735734d27a16a4f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import logging.handlers as handlers, socket, os, time


class WebsockifySysLogHandler(handlers.SysLogHandler):
    """
    A handler class that sends proper Syslog-formatted messages,
    as defined by RFC 5424.
    """

    _legacy_head_fmt = '<{pri}>{ident}[{pid}]: '
    _rfc5424_head_fmt = '<{pri}>1 {timestamp} {hostname} {ident} {pid} - - '
    _head_fmt = _rfc5424_head_fmt
    _legacy = False
    _timestamp_fmt = '%Y-%m-%dT%H:%M:%SZ'
    _max_hostname = 255
    _max_ident = 24 #safer for old daemons
    _send_length = False
    _tail = '\n'


    ident = None


    def __init__(self, address=('localhost', handlers.SYSLOG_UDP_PORT),
                 facility=handlers.SysLogHandler.LOG_USER,
                 socktype=None, ident=None, legacy=False):
        """
        Initialize a handler.

        If address is specified as a string, a UNIX socket is used. To log to a
        local syslogd, "WebsockifySysLogHandler(address="/dev/log")" can be
        used. If facility is not specified, LOG_USER is used. If socktype is
        specified as socket.SOCK_DGRAM or socket.SOCK_STREAM, that specific
        socket type will be used. For Unix sockets, you can also specify a
        socktype of None, in which case socket.SOCK_DGRAM will be used, falling
        back to socket.SOCK_STREAM. If ident is specified, this string will be
        used as the application name in all messages sent. Set legacy to True
        to use the old version of the protocol.
        """

        self.ident = ident

        if legacy:
            self._legacy = True
            self._head_fmt = self._legacy_head_fmt

        super().__init__(address, facility, socktype)


    def emit(self, record):
        """
        Emit a record.

        The record is formatted, and then sent to the syslog server. If
        exception information is present, it is NOT sent to the server.
        """

        try:
            # Gather info.
            text = self.format(record).replace(self._tail, ' ')
            if not text: # nothing to log
                return

            pri = self.encodePriority(self.facility,
                                      self.mapPriority(record.levelname))

            timestamp = time.strftime(self._timestamp_fmt, time.gmtime());

            hostname = socket.gethostname()[:self._max_hostname]

            if self.ident:
                ident = self.ident[:self._max_ident]
            else:
                ident = ''

            pid = os.getpid() # shouldn't need truncation

            # Format the header.
            head = {
                'pri': pri,
                'timestamp': timestamp,
                'hostname': hostname,
                'ident': ident,
                'pid': pid,
            }
            msg = self._head_fmt.format(**head).encode('ascii', 'ignore')

            # Encode text as plain ASCII if possible, else use UTF-8 with BOM.
            try:
                msg += text.encode('ascii')
            except UnicodeEncodeError:
                msg += text.encode('utf-8-sig')

            # Add length or tail character, if necessary.
            if self.socktype != socket.SOCK_DGRAM:
                if self._send_length:
                    msg = ('%d ' % len(msg)).encode('ascii') + msg
                else:
                    msg += self._tail.encode('ascii')

            # Send the message.
            if self.unixsocket:
                try:
                    self.socket.send(msg)
                except socket.error:
                    self._connect_unixsocket(self.address)
                    self.socket.send(msg)

            else:
                if self.socktype == socket.SOCK_DGRAM:
                    self.socket.sendto(msg, self.address)
                else:
                    self.socket.sendall(msg)

        except (KeyboardInterrupt, SystemExit):
            raise
        except:
            self.handleError(record)