Codebase list logbook / 71f7ae4
New upstream version 1.4.2 Downstreamer 5 years ago
5 changed file(s) with 125 addition(s) and 50 deletion(s). Raw diff Collapse all Expand all
00 Logbook Changelog
11 =================
2
3 Not yet released
4
5 - Use correct record delimiters (null for UNIX, newline for network) in SyslogHandler (thanks Jonathan Kamens)
6 - Try to reconnect to SyslogHandler TCP sockets when they are disconnected (thanks Jonathan Kamens)
7 - Use RFC 5424 format for networking logging in SyslogHandler (thanks Jonathan Kamens)
28
39 Here you can see the full list of changes between each Logbook release.
410
497497
498498 if (not suppression_count and
499499 len(self._record_limits) >= self.max_record_cache):
500 cache_items = self._record_limits.items()
501 cache_items.sort()
500 cache_items = sorted(self._record_limits.items())
502501 del cache_items[:int(self._record_limits)
503502 * self.record_cache_prune]
504503 self._record_limits = dict(cache_items)
15341533 def __init__(self, application_name=None, address=None,
15351534 facility='user', socktype=socket.SOCK_DGRAM,
15361535 level=NOTSET, format_string=None, filter=None,
1537 bubble=False):
1536 bubble=False, record_delimiter=None):
15381537 Handler.__init__(self, level, filter, bubble)
15391538 StringFormatterHandlerMixin.__init__(self, format_string)
15401539 self.application_name = application_name
15451544 else:
15461545 address = '/dev/log'
15471546
1548 self.address = address
1547 self.remote_address = self.address = address
15491548 self.facility = facility
15501549 self.socktype = socktype
15511550
15521551 if isinstance(address, string_types):
15531552 self._connect_unixsocket()
1553 self.enveloper = self.unix_envelope
1554 default_delimiter = u'\x00'
15541555 else:
15551556 self._connect_netsocket()
1557 self.enveloper = self.net_envelope
1558 default_delimiter = u'\n'
1559
1560 self.record_delimiter = default_delimiter \
1561 if record_delimiter is None else record_delimiter
1562
1563 self.connection_exception = getattr(
1564 __builtins__, 'BrokenPipeError', socket.error)
15561565
15571566 def _connect_unixsocket(self):
15581567 self.unixsocket = True
15681577 self.unixsocket = False
15691578 self.socket = socket.socket(socket.AF_INET, self.socktype)
15701579 if self.socktype == socket.SOCK_STREAM:
1571 self.socket.connect(self.address)
1580 self.socket.connect(self.remote_address)
15721581 self.address = self.socket.getsockname()
15731582
15741583 def encode_priority(self, record):
15771586 self.LOG_WARNING)
15781587 return (facility << 3) | priority
15791588
1589 def wrap_segments(self, record, before):
1590 msg = self.format(record)
1591 segments = [segment for segment in msg.split(self.record_delimiter)]
1592 return (before + segment + self.record_delimiter
1593 for segment in segments)
1594
1595 def unix_envelope(self, record):
1596 before = u'<{}>{}'.format(
1597 self.encode_priority(record),
1598 self.application_name + ':' if self.application_name else '')
1599 return self.wrap_segments(record, before)
1600
1601 def net_envelope(self, record):
1602 # Gross but effective
1603 try:
1604 format_string = self.format_string
1605 application_name = self.application_name
1606 if not application_name and record.channel and \
1607 '{record.channel}: ' in format_string:
1608 self.format_string = format_string.replace(
1609 '{record.channel}: ', '')
1610 self.application_name = record.channel
1611 # RFC 5424: <PRIVAL>version timestamp hostname app-name procid
1612 # msgid structured-data message
1613 before = u'<{}>1 {}Z {} {} {} - - '.format(
1614 self.encode_priority(record),
1615 record.time.isoformat(),
1616 socket.gethostname(),
1617 self.application_name if self.application_name else '-',
1618 record.process)
1619 return self.wrap_segments(record, before)
1620 finally:
1621 self.format_string = format_string
1622 self.application_name = application_name
1623
15801624 def emit(self, record):
1581 prefix = u('')
1582 if self.application_name is not None:
1583 prefix = self.application_name + u(':')
1584 self.send_to_socket((u('<%d>%s%s\x00') % (
1585 self.encode_priority(record),
1586 prefix,
1587 self.format(record)
1588 )).encode('utf-8'))
1625 for segment in self.enveloper(record):
1626 self.send_to_socket(segment.encode('utf-8'))
15891627
15901628 def send_to_socket(self, data):
15911629 if self.unixsocket:
15981636 # the flags are no longer optional on Python 3
15991637 self.socket.sendto(data, 0, self.address)
16001638 else:
1601 self.socket.sendall(data)
1639 try:
1640 self.socket.sendall(data)
1641 except self.connection_exception:
1642 self._connect_netsocket()
1643 self.socket.send(data)
16021644
16031645 def close(self):
16041646 self.socket.close()
3232 with redirected_logging(set_root_logger_level):
3333 logger.debug('This is from the old system')
3434 logger.info('This is from the old system')
35 logger.warn('This is from the old %s', 'system')
35 logger.warning('This is from the old %s', 'system')
3636 logger.error('This is from the old system')
3737 logger.critical('This is from the old system')
3838 logger.error('This is a %(what)s %(where)s', {'what': 'mapping', 'where': 'test'})
3939 header, data = mail.split('\n\n', 1)
4040 if 'Content-Transfer-Encoding: base64' in header:
4141 data = base64.b64decode(data).decode('utf-8')
42 assert re.search('Message type:\s+ERROR', data)
43 assert re.search('Location:.*%s' %
42 assert re.search(r'Message type:\s+ERROR', data)
43 assert re.search(r'Location:.*%s' %
4444 re.escape(__file_without_pyc__), data)
45 assert re.search('Module:\s+%s' % __name__, data)
46 assert re.search('Function:\s+test_mail_handler', data)
45 assert re.search(r'Module:\s+%s' % __name__, data)
46 assert re.search(r'Function:\s+test_mail_handler', data)
4747 body = u('Viva la Espa\xf1a')
4848 if sys.version_info < (3, 0):
4949 body = body.encode('utf-8')
7171 body, rest = pieces
7272 rest = rest.replace('\r', '')
7373
74 assert re.search('Message type:\s+ERROR', body)
75 assert re.search('Module:\s+%s' % __name__, body)
76 assert re.search('Function:\s+test_mail_handler_batching', body)
74 assert re.search(r'Message type:\s+ERROR', body)
75 assert re.search(r'Module:\s+%s' % __name__, body)
76 assert re.search(r'Function:\s+test_mail_handler_batching', body)
7777
7878 related = rest.strip().split('\n\n')
7979 assert len(related) == 2
80 assert re.search('Message type:\s+WARNING', related[0])
81 assert re.search('Message type:\s+DEBUG', related[1])
80 assert re.search(r'Message type:\s+WARNING', related[0])
81 assert re.search(r'Message type:\s+DEBUG', related[1])
8282
8383 assert 'And this triggers it again' in mail_handler.mails[1][2]
8484
100100 body, rest = pieces
101101 rest = rest.replace('\r', '')
102102
103 assert re.search('Message type:\\s+ERROR', body)
104 assert re.search('Module:\s+' + __name__, body)
105 assert re.search('Function:\s+test_group_handler_mail_combo', body)
103 assert re.search(r'Message type:\s+ERROR', body)
104 assert re.search(r'Module:\s+' + __name__, body)
105 assert re.search(r'Function:\s+test_group_handler_mail_combo', body)
106106
107107 related = rest.strip().split('\n\n')
108108 assert len(related) == 2
109 assert re.search('Message type:\s+WARNING', related[0])
110 assert re.search('Message type:\s+DEBUG', related[1])
109 assert re.search(r'Message type:\s+WARNING', related[0])
110 assert re.search(r'Message type:\s+DEBUG', related[1])
111111
112112
113113 def test_mail_handler_arguments():
00 import os
1 import re
12 import socket
23 from contextlib import closing
34
67
78 import pytest
89
10 unix_socket = "/tmp/__unixsock_logbook.test"
911
10 def test_syslog_handler(logger, activation_strategy, unix_sock_path):
11 to_test = [
12 (socket.AF_INET, ('127.0.0.1', 0)),
13 ]
14 if hasattr(socket, 'AF_UNIX'):
15 to_test.append((socket.AF_UNIX, unix_sock_path))
16 for sock_family, address in to_test:
17 with closing(socket.socket(sock_family, socket.SOCK_DGRAM)) as inc:
18 inc.bind(address)
19 inc.settimeout(1)
20 for app_name in [None, 'Testing']:
21 handler = logbook.SyslogHandler(app_name, inc.getsockname())
22 with activation_strategy(handler):
23 logger.warn('Syslog is weird')
24 try:
12 to_test = [
13 (socket.AF_INET, socket.SOCK_DGRAM, ('127.0.0.1', 0)),
14 (socket.AF_INET, socket.SOCK_STREAM, ('127.0.0.1', 0)),
15 ]
16 if hasattr(socket, 'AF_UNIX'):
17 to_test.append((socket.AF_UNIX, socket.SOCK_DGRAM, unix_socket))
18
19 @pytest.mark.usefixtures("unix_sock_path")
20 @pytest.mark.parametrize("sock_family,socktype,address", to_test)
21 def test_syslog_handler(logger, activation_strategy,
22 sock_family, socktype, address):
23 delimiter = {socket.AF_UNIX: '\x00',
24 socket.AF_INET: '\n'}[sock_family]
25 with closing(socket.socket(sock_family, socktype)) as inc:
26 inc.bind(address)
27 if socktype == socket.SOCK_STREAM:
28 inc.listen(0)
29 inc.settimeout(1)
30 for app_name in [None, 'Testing']:
31 if sock_family == socket.AF_UNIX:
32 expected = (r'^<12>%stestlogger: Syslog is weird%s$' %
33 (app_name + ':' if app_name else '',
34 delimiter))
35 else:
36 expected = (r'^<12>1 \d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(\.\d+)?Z %s %s %d '
37 '- - %sSyslog is weird%s$' %
38 (socket.gethostname(),
39 app_name if app_name else 'testlogger',
40 os.getpid(), 'testlogger: ' if app_name else '',
41 delimiter))
42
43 handler = logbook.SyslogHandler(app_name, inc.getsockname(),
44 socktype=socktype)
45 with activation_strategy(handler):
46 logger.warn('Syslog is weird')
47 try:
48 if socktype == socket.SOCK_STREAM:
49 with closing(inc.accept()[0]) as inc2:
50 rv = inc2.recv(1024)
51 else:
2552 rv = inc.recvfrom(1024)[0]
26 except socket.error:
27 assert False, 'got timeout on socket'
28 assert rv == (
29 u('<12>%stestlogger: Syslog is weird\x00') %
30 ((app_name and (app_name + u(':'))) or u(''))).encode('utf-8')
53 except socket.error:
54 assert False, 'got timeout on socket'
55 rv = rv.decode('utf-8')
56 assert re.match(expected, rv), \
57 'expected {}, got {}'.format(expected, rv)
3158
3259
3360 @pytest.fixture
3461 def unix_sock_path(request):
35 returned = "/tmp/__unixsock_logbook.test"
62 returned = unix_socket
3663
3764 @request.addfinalizer
3865 def cleanup():