Skip to content

Commit a2695be

Browse files
miss-islingtonvsajipambv
authored
[3.10] Improve logging documentation with example and additional cookbook re… (GH-93644) (GH-93648)
(cherry picked from commit e974b3e) Co-authored-by: Vinay Sajip <vinay_sajip@yahoo.co.uk> Co-authored-by: Łukasz Langa <lukasz@langa.pl>
1 parent 5b2ad48 commit a2695be

File tree

3 files changed

+101
-2
lines changed

3 files changed

+101
-2
lines changed

Doc/howto/logging-cookbook.rst

+89
Original file line numberDiff line numberDiff line change
@@ -3002,6 +3002,95 @@ refer to the comments in the code snippet for more detailed information.
30023002
if __name__=='__main__':
30033003
main()
30043004
3005+
Logging to syslog with RFC5424 support
3006+
--------------------------------------
3007+
3008+
Although :rfc:`5424` dates from 2009, most syslog servers are configured by detault to
3009+
use the older :rfc:`3164`, which hails from 2001. When ``logging`` was added to Python
3010+
in 2003, it supported the earlier (and only existing) protocol at the time. Since
3011+
RFC5424 came out, as there has not been widespread deployment of it in syslog
3012+
servers, the :class:`~logging.handlers.SysLogHandler` functionality has not been
3013+
updated.
3014+
3015+
RFC 5424 contains some useful features such as support for structured data, and if you
3016+
need to be able to log to a syslog server with support for it, you can do so with a
3017+
subclassed handler which looks something like this::
3018+
3019+
import datetime
3020+
import logging.handlers
3021+
import re
3022+
import socket
3023+
import time
3024+
3025+
class SysLogHandler5424(logging.handlers.SysLogHandler):
3026+
3027+
tz_offset = re.compile(r'([+-]\d{2})(\d{2})$')
3028+
escaped = re.compile(r'([\]"\\])')
3029+
3030+
def __init__(self, *args, **kwargs):
3031+
self.msgid = kwargs.pop('msgid', None)
3032+
self.appname = kwargs.pop('appname', None)
3033+
super().__init__(*args, **kwargs)
3034+
3035+
def format(self, record):
3036+
version = 1
3037+
asctime = datetime.datetime.fromtimestamp(record.created).isoformat()
3038+
m = self.tz_offset.match(time.strftime('%z'))
3039+
has_offset = False
3040+
if m and time.timezone:
3041+
hrs, mins = m.groups()
3042+
if int(hrs) or int(mins):
3043+
has_offset = True
3044+
if not has_offset:
3045+
asctime += 'Z'
3046+
else:
3047+
asctime += f'{hrs}:{mins}'
3048+
try:
3049+
hostname = socket.gethostname()
3050+
except Exception:
3051+
hostname = '-'
3052+
appname = self.appname or '-'
3053+
procid = record.process
3054+
msgid = '-'
3055+
msg = super().format(record)
3056+
sdata = '-'
3057+
if hasattr(record, 'structured_data'):
3058+
sd = record.structured_data
3059+
# This should be a dict where the keys are SD-ID and the value is a
3060+
# dict mapping PARAM-NAME to PARAM-VALUE (refer to the RFC for what these
3061+
# mean)
3062+
# There's no error checking here - it's purely for illustration, and you
3063+
# can adapt this code for use in production environments
3064+
parts = []
3065+
3066+
def replacer(m):
3067+
g = m.groups()
3068+
return '\\' + g[0]
3069+
3070+
for sdid, dv in sd.items():
3071+
part = f'[{sdid}'
3072+
for k, v in dv.items():
3073+
s = str(v)
3074+
s = self.escaped.sub(replacer, s)
3075+
part += f' {k}="{s}"'
3076+
part += ']'
3077+
parts.append(part)
3078+
sdata = ''.join(parts)
3079+
return f'{version} {asctime} {hostname} {appname} {procid} {msgid} {sdata} {msg}'
3080+
3081+
You'll need to be familiar with RFC 5424 to fully understand the above code, and it
3082+
may be that you have slightly different needs (e.g. for how you pass structural data
3083+
to the log). Nevertheless, the above should be adaptable to your speciric needs. With
3084+
the above handler, you'd pass structured data using something like this::
3085+
3086+
sd = {
3087+
'foo@12345': {'bar': 'baz', 'baz': 'bozz', 'fizz': r'buzz'},
3088+
'foo@54321': {'rab': 'baz', 'zab': 'bozz', 'zzif': r'buzz'}
3089+
}
3090+
extra = {'structured_data': sd}
3091+
i = 1
3092+
logger.debug('Message %d', i, extra=extra)
3093+
30053094

30063095
.. patterns-to-avoid:
30073096

Doc/library/logging.rst

+10-2
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,17 @@ is that all Python modules can participate in logging, so your application log
3030
can include your own messages integrated with messages from third-party
3131
modules.
3232

33+
The simplest example:
34+
35+
.. code-block:: none
36+
37+
>>> import logging
38+
>>> logging.warning('Watch out!')
39+
WARNING:root:Watch out!
40+
3341
The module provides a lot of functionality and flexibility. If you are
34-
unfamiliar with logging, the best way to get to grips with it is to see the
35-
tutorials (see the links on the right).
42+
unfamiliar with logging, the best way to get to grips with it is to view the
43+
tutorials (**see the links above and on the right**).
3644

3745
The basic classes defined by the module, together with their functions, are
3846
listed below.

Doc/tools/susp-ignored.csv

+2
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,8 @@ library/ipaddress,,::,2001:db00::0/ffff:ff00::
171171
library/itertools,,:step,elements from seq[start:stop:step]
172172
library/itertools,,::,kernel = tuple(kernel)[::-1]
173173
library/itertools,,:stop,elements from seq[start:stop:step]
174+
library/logging,,:root,WARNING:root:Watch out!
175+
library/logging,,:Watch,WARNING:root:Watch out!
174176
library/logging.handlers,,:port,host:port
175177
library/mmap,,:i2,obj[i1:i2]
176178
library/multiprocessing,,`,# Add more tasks using `put()`

0 commit comments

Comments
 (0)