Skip to content

Commit 7bfeb8a

Browse files
[3.11] Improve logging documentation with example and additional cookbook re… (GH-93644) (GH-93647)
1 parent 4c41f21 commit 7bfeb8a

File tree

2 files changed

+99
-2
lines changed

2 files changed

+99
-2
lines changed

Doc/howto/logging-cookbook.rst

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

29993088
.. patterns-to-avoid:
30003089

Doc/library/logging.rst

Lines changed: 10 additions & 2 deletions
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.

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy