comparison doc/admin_guide.txt @ 8446:14c7c07b32d8

feature: add thread local trace_id and trace_reason to logging. Added trace_id to default logging so that all logs for a given request share the same trace_id. This allows correlation of logs across a request. admin_guide.txt, upgrading.txt: add docs update sample configs to include trace_id. rewrite logging docs in admin_guide. Hopefully they are clearer now. clean up some stuff in the logging config file docs. admin.py: add decorators to run_command to enable trace_id. change calls to db.commit() to use run_command to get trace_id. configuration.py: clean up imports. update docstrings, comments and inline docs. add trace_id to default log format. add function for testing decorated with trace_id. add support for dumping stack trace in logging. add check for pytest in sys.modules to enable log propagation when pytest is running. Otherwise tests fail as the caplog logger doesn't see the roundup logs. logcontext.py: new file to handle thread local contextvar mangement. mailgw.py: add decorators for trace_id etc. scripts/roundup_xlmrpc_server.py: add decorators for trace_id etc. fix encoding bug turning bytes into a string. fix command line issue where we can't set encoding. (not sure if changing encoding via command line even works) cgi/client.py decorate two entry points for trace_id etc. cgi/wsgi_handler.py: decorate entry point for trace_id etc. test/test_config.py: add test for trace_id in new log format. test various cases for sinfo and errors in formating msg.
author John Rouillard <rouilj@ieee.org>
date Tue, 16 Sep 2025 22:53:00 -0400
parents 39a6825d10ca
children 1522e0e15903
comparison
equal deleted inserted replaced
8445:4b6e9a0e13ee 8446:14c7c07b32d8
44 installs configuration files, HTML templates, detector code and a new 44 installs configuration files, HTML templates, detector code and a new
45 database. You have complete control over where this stuff goes through 45 database. You have complete control over where this stuff goes through
46 both choosing your "tracker home" and the ``main`` -> ``database`` variable 46 both choosing your "tracker home" and the ``main`` -> ``database`` variable
47 in the tracker's config.ini. 47 in the tracker's config.ini.
48 48
49 .. _rounduplogging:
49 50
50 Configuring Roundup Message Logging 51 Configuring Roundup Message Logging
51 =================================== 52 ===================================
52 53
53 You can control how Roundup logs messages using your tracker's 54 You can control how Roundup logs messages using your tracker's
54 config.ini file. Roundup uses the standard Python (2.3+) logging 55 config.ini file. Roundup uses the standard Python logging
55 implementation. The config file and ``roundup-server`` provide very 56 implementation. The config file and ``roundup-server`` provide
56 basic control over logging. 57 very basic control over logging.
57 58
58 Configuration for "BasicLogging" implementation: 59 ``roundup-server``'s logging is controlled from the command
59 - tracker configuration file specifies the location of a log file 60 line. You can:
60 ``logging`` -> ``filename`` 61
61 - tracker configuration file specifies the level to log to as 62 - specify the location of a log file or
62 ``logging`` -> ``level`` 63 - enable logging using the standard Python logging library under
63 - tracker configuration file lets you disable other loggers 64 the tag/channel ``roundup.http``
64 (e.g. when running under a wsgi framework) with 65
65 ``logging`` -> ``disable_loggers``. 66 Configuration for "BasicLogging" implementation for your tracker
66 - tracker configuration file can set the log format using 67 is done using the settings in the tracker's ``config.ini`` under
67 ``logging`` -> ``format``. See :ref:`logFormat` for more info. 68 the ``logging`` section:
68 - ``roundup-server`` specifies the location of a log file on the command 69
69 line 70 - ``filename`` setting: specifies the location of a log file
70 - ``roundup-server`` enable using the standard python logger with 71 - ``level`` setting: specifies the minimum level to log
71 the tag/channel ``roundup.http`` on the command line 72 - ``disable_loggers`` setting: disable other loggers (e.g. when
72 73 running under a wsgi framework)
73 By supplying a standard log config file in ini or json (dictionary) 74 - ``format`` setting: set the log format template. See
74 format, you get more control over the logs. You can set different 75 :ref:`logFormat` for more info.
75 levels for logs (e.g. roundup.hyperdb can be set to WARNING while 76
76 other Roundup log channels are set to INFO and roundup.mailgw logs at 77 In either case, if logfile is not specified, logging is sent to
77 DEBUG level). You can also send the logs for roundup.mailgw to syslog, 78 sys.stderr. If level is not set, only ERROR or higher priority
78 and other roundup logs go to an automatically rotating log file, or 79 log messages will be reported.
79 are submitted to your log aggregator over https. 80
80 81 You can get more control over logging by using the ``config``
81 Configuration for standard "logging" module: 82 setting in the tracker's ``config.ini``. Using a logging config
82 - tracker configuration file specifies the location of a logging 83 file overrides all the rest of the other logging settings in
83 configuration file as ``logging`` -> ``config``. 84 ``config.ini``. You get more control over the logs by supplying
84 85 a log config file in ini or json (dictionary) format.
85 In both cases, if no logfile is specified then logging will simply be sent 86
86 to sys.stderr with only logging of ERROR messages. 87 Using this, you can set different levels by channel. For example
88 roundup.hyperdb can be set to WARNING while other Roundup log
89 channels are set to INFO and the roundup.mailgw channel logs at
90 the DEBUG level. You can also control the distribution of
91 logs. For example roundup.mailgw logs to syslog, other channels
92 log to an automatically rotating log file, or are submitted to
93 your log aggregator over https.
87 94
88 .. _logFormat: 95 .. _logFormat:
89 96
90 Defining the Log Format 97 Defining the Log Format
91 ----------------------- 98 -----------------------
92 99
93 Starting with Roundup 2.6 you can specify the logging format. In the 100 Starting with Roundup 2.6 you can specify the logging format in
94 ``logging`` -> ``format`` setting of config.ini you can use any of the 101 config.ini. The ``logging`` -> ``format`` setting of config.ini
95 `standard logging LogRecord attributes 102 supports all of the `standard logging LogRecord attributes
96 <https://docs.python.org/3/library/logging.html#logrecord-attributes>`_. 103 <https://docs.python.org/3/library/logging.html#logrecord-attributes>`_
97 However you must double any ``%`` format markers. The default value 104 or Roundup logging attributes. However you must double any ``%``
98 is:: 105 format markers. The default value is::
99 106
100 %%(asctime)s %%(levelname)s %%(message)s 107 %%(asctime)s %%(trace_id)s %%(levelname)s %%(message)s
101 108
102 Standard Logging Setup 109 Roundup Logging Attributes
110 --------------------------
111
112 The `logging package has a number of attributes
113 <https://docs.python.org/3/library/logging.html#logrecord-attributes>`_
114 that can be expanded in the format template. In addition to the
115 ones supplied by Python's logging module, Roundup defines
116 additional attributes:
117
118 trace_id
119 a unique string that is generated for each request. It is
120 unique per thread.
121
122 trace_reason
123 a string describing the reason for the trace/request.
124
125 * the URL for a web triggered (http, rest, xmlrpc) request
126 * the email message id for an email triggered request
127 * the roundup-admin os user and start of command. Only first
128 two words in command are printed so seting a password will
129 not be leaked to the logs.
130
131 sinfo
132 the stack traceback information at the time the log call id
133 made.
134
135 This must be intentionally activated by using the extras
136 parameter. For example calling::
137
138 logging.get_logger('roundup.something').warning(
139 "I am here\n%(sinfo)s", extra={"sinfo": 2})
140
141 in the function confirmid() of the file detectors/reauth.py
142 in your demo tracker will print 2 items on the stack
143 including the log call. It results in the following (5 lines
144 total in the log file)::
145
146 2025-09-14 23:07:58,668 Cm0ZPlBaklLZ3Mm6hAAgoC WARNING I am here
147 File "[...]/roundup/hyperdb.py", line 1924, in fireAuditors
148 audit(self.db, self, nodeid, newvalues)
149 File "demo/detectors/reauth.py", line 7, in confirmid
150 logging.getLogger('roundup.something').warning(
151
152 Note that the output does not include the arguments to
153 ``warning`` because they are on the following line. If you
154 want arguments to the log call included, they have to be on
155 the same line.
156
157 Setting ``sinfo`` to an integer value N includes N lines up
158 the stack ending with the logging call. Setting it to 0
159 includes all the lines in the stack ending with the logging
160 call.
161
162 If the value is less than 0, the stack dump doesn't end at
163 the logging call but continues to the function that
164 generates the stack report. So it includes functions inside
165 the logging module.
166
167 Setting it to a number larger than the stack trace will
168 print the trace down to the log call. So using ``-1000``
169 will print up to 1000 stack frames and start at the function
170 that generates the stack report.
171
172 Setting ``sinfo`` to a non-integer value ``{"sinfo": None}``
173 will produce 5 lines of the stack trace ending at the
174 logging call.
175
176 pct_char
177 produces a single ``%`` sign in the log. The usual way of
178 embedding a percent sign in a formatted string is to double
179 it like: ``%%``. However when the format string is specified
180 in the config.ini file percent signs are manipulated. So
181 ``%%(pct_char)s`` can be used in config.ini to print a
182 percent sign.
183
184 The default logging template is defined in config.ini in the
185 ``logging`` -> ``format`` setting. It includes the ``trace_id``.
186 When searching logs, you can use the trace_id to see all the log
187 messages associated with a request.
188
189 If you want to log from a detector, extension or other code, you
190 can use these tokens in your messages when calling the logging
191 functions. (Note that doubling ``%`` signs is only required when
192 defining the log format in a config file, not when defining a
193 msg.) For example::
194
195 logging.getLogger('roundup.myextension').error('problem with '
196 '%(trace_reason)s')
197
198 will include the url in the message when triggered from the
199 web. This also works with other log methods: ``warning()``,
200 ``debug()``, ....
201
202 Note you must **not** use positional arguments in your
203 message. Using::
204
205 logging.getLogger('roundup.myextension').error(
206 '%s problem with %(trace_reason)s', "a")
207
208 will not properly substitute the argument. You must use mapping
209 key based arguments and define the local values as part of the
210 extra dictionary. For example::
211
212 logging.getLogger('roundup.myextension').error('%(article)s '
213 'problem with %(trace_reason)',
214 extra={"article": some_local_variable})
215
216 Also if you are logging any data supplied by a user, you must not
217 log it directly. If the variable ``url`` contains the url typed in
218 by the user, never use:
219
220 logger.info(url)
221
222 or
223
224 logger.info("Url is %s" % url)
225
226 Use:
227
228 logger.info("Url is %s", url)
229
230 or
231
232 logger.info("Url is %(url)s", extra={"url": url)
233
234 This prevents printf style tokens in ``url`` from being processed
235 where it can raise an exception. This could be used to prevent
236 the log message from being generated.
237
238 More on trace_id
239 ~~~~~~~~~~~~~~~~
240
241 The trace_id provides a unique token (a UUID4 encoded to make it
242 shorter) for each transaction in the database. It is unique to
243 each thread or transaction. A transaction:
244
245 for the web interface is
246 each web, rest or xmlrpc request
247
248 for the email interface is
249 each email request. Using pipe mode will generate one
250 transaction. Using pop/imap etc can generate multiple
251 transactions, one for each email. Logging that occurs prior
252 to processing an email transaction has the default
253 ``not_set`` value for trace_id
254
255 for the roundup-admin interface is
256 each command in the interactive interface or on the command
257 line. Plus one transaction when/if a commit happens on
258 roundup-admin exit.
259
260 When creating scripts written using the roundup package the entry
261 point should use the ``@gen_trace_id`` decorator. For example to
262 decorate the entry point that performs one transaction::
263
264 from roundup.logcontext import gen_trace_id
265
266 # stuff ...
267
268 @gen_trace_id()
269 def main(...):
270 ...
271
272 If your script does multiple processing operations, decorate the entry
273 point for the processing operation::
274
275 from roundup.logcontext import gen_trace_id
276
277 @gen_trace_id()
278 def process_one(thing):
279 ...
280
281 def main():
282 for thing in things:
283 process_one(thing)
284
285
286 Advanced Logging Setup
103 ---------------------- 287 ----------------------
104 288
105 If the settings in config.ini are not sufficient for your logging 289 If the settings in config.ini are not sufficient for your logging
106 requirements, you can specify a full logging configuration in one of 290 requirements, you can specify a full logging configuration in one
107 two formats: 291 of two formats:
108 292
293 * `dictConfig format
294 <https://docs.python.org/3/library/logging.config.html#logging.config.dictConfig>`_
295 using json with comment support
109 * `fileConfig format 296 * `fileConfig format
110 <https://docs.python.org/3/library/logging.config.html#logging.config.fileConfig>`_ 297 <https://docs.python.org/3/library/logging.config.html#logging.config.fileConfig>`_
111 in ini style 298 in ini style
112 * `dictConfig format 299
113 <https://docs.python.org/3/library/logging.config.html#logging.config.dictConfig>`_ 300 The dictConfig format allows more control over configuration
114 using json with comment support 301 including loading your own log handlers and disabling existing
115 302 handlers. If you use the fileConfig format, the ``logging`` ->
116 The dictConfig allows more control over configuration including 303 ``disable_loggers`` flag in the tracker's config is used to
117 loading your own log handlers and disabling existing handlers. If you 304 enable/disable pre-existing loggers as there is no way to do this
118 use the fileConfig format, the ``logging`` -> ``disable_loggers`` flag 305 in the logging config file.
119 in the tracker's config is used to enable/disable pre-existing loggers
120 as there is no way to do this in the logging config file.
121 306
122 .. _`dictLogConfig`: 307 .. _`dictLogConfig`:
123 308
124 dictConfig Based Logging Config 309 dictConfig Based Logging Config
125 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 310 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
126 311
127 dictConfigs are specified in JSON format with support for comments. 312 dictConfigs are specified in JSON format with support for
128 The file name in the tracker's config for the ``logging`` -> ``config`` 313 comments. The file name in the tracker's config for the
129 setting must end with ``.json`` to choose the correct processing. 314 ``logging`` -> ``config`` setting must end with ``.json`` to
315 choose the correct processing.
130 316
131 Comments have to be in one of two forms based on javascript line 317 Comments have to be in one of two forms based on javascript line
132 comments: 318 comments:
133 319
134 1. A ``//`` possibly indented with whitespace on a line is considereda 320 1. A ``//`` possibly indented with whitespace on a line is
135 a comment and is stripped from the file before being passed to the 321 considereda a comment and is stripped from the file before
136 json parser. This is a "line comment". 322 being passed to the json parser. This is a "line comment".
137 323
138 2. A ``//`` with at least three white space characters before it is 324 2. A ``//`` with at least three white space characters before it
139 stripped from the end of the line before begin passed to the json 325 is stripped from the end of the line before being passed to
140 parser. This is an "inline comment". 326 the json parser. This is an "inline comment".
141 327
142 Block style comments are not supported. 328 Block style comments are not supported.
143 329
144 Other than this the file is a standard json file that matches the 330 Other than this the file is a standard json file that matches the
145 `Configuration dictionary schema 331 `Configuration dictionary schema
149 335
150 Example dictConfig Logging Config 336 Example dictConfig Logging Config
151 ................................. 337 .................................
152 338
153 Note that this file is not actually JSON format as it include 339 Note that this file is not actually JSON format as it include
154 comments. However by using javascript style comments, some tools that 340 comments. However by using javascript style comments, some tools
155 expect JSON (editors, linters, formatters) might work with it. A 341 that treat JSON like javascript (editors, linters, formatters)
156 command like ``sed -e 's#^\s*//.*##' -e 's#\s*\s\s\s//.*##' 342 might work with it. A command like::
157 logging.json`` can be used to strip comments for programs that need 343
158 it. 344 sed -e 's#^\s*//.*##' -e 's#\s*\s\s\s//.*##' logging.json
345
346 can be used to strip comments for programs that need it.
159 347
160 The config below works with the `Waitress wsgi server 348 The config below works with the `Waitress wsgi server
161 <https://github.com/Pylons/waitress>`_ configured to use the 349 <https://github.com/Pylons/waitress>`_ configured to use the
162 roundup.wsgi channel. It also controls the `TransLogger middleware 350 roundup.wsgi channel. It also controls the `TransLogger
163 <https://github.com/pasteorg/paste>`_ configured to use 351 middleware <https://github.com/pasteorg/paste>`_ configured to
164 roundup.wsgi.translogger, to produce httpd style combined logs. The 352 use roundup.wsgi.translogger, to produce httpd style combined
165 log file is specified relative to the current working directory not 353 logs. The log file is specified relative to the current working
166 the tracker home. The tracker home is the subdirectory demo under the 354 directory not the tracker home. The tracker home is the
167 current working directory. The commented config is:: 355 subdirectory demo under the current working directory. The
356 commented config is::
168 357
169 { 358 {
170 "version": 1, // only supported version 359 "version": 1, // only supported version
171 "disable_existing_loggers": false, // keep the wsgi loggers 360 "disable_existing_loggers": false, // keep the wsgi loggers
172 361
173 "formatters": { 362 "formatters": {
174 // standard format for Roundup messages 363 // standard format for Roundup messages
175 "standard": { 364 "standard": {
176 "format": "%(asctime)s %(ctx_id)s %(levelname)s %(name)s:%(module)s %(msg)s" 365 "format": "%(asctime)s %(trace_id)s %(levelname)s %(name)s:%(module)s %(msg)s"
177 }, 366 },
178 // used for waitress wsgi server to produce httpd style logs 367 // used for waitress wsgi server to produce httpd style logs
179 "http": { 368 "http": {
180 "format": "%(message)s" 369 "format": "%(message)s %(trace_id)"
370
181 } 371 }
182 }, 372 },
183 "handlers": { 373 "handlers": {
184 // create an access.log style http log file 374 // create an access.log style http log file
185 "access": { 375 "access": {
252 } 442 }
253 443
254 fileConfig Based Logging Config 444 fileConfig Based Logging Config
255 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 445 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
256 446
257 The file config is an older and more limited method of configuring 447 The file config is an older and more limited method of
258 logging. It is described by the `Configuration file format 448 configuring logging. It is described by the `Configuration file
449 format
259 <https://docs.python.org/3/library/logging.config.html#configuration-file-format>`_ 450 <https://docs.python.org/3/library/logging.config.html#configuration-file-format>`_
260 in the Python documentation. The file name in the tracker's config for 451 in the Python documentation. The file name in the tracker's
261 the ``logging`` -> ``config`` setting must end with ``.ini`` to choose 452 config for the ``logging`` -> ``config`` setting must end with
262 the correct processing. 453 ``.ini`` to choose the correct processing.
263 454
264 Example fileConfig LoggingConfig 455 Example fileConfig LoggingConfig
265 ................................ 456 ................................
266 457
267 This is an example .ini used with roundup-server configured to use 458 This is an example .ini used with roundup-server configured to use
341 532
342 [formatters] 533 [formatters]
343 keys=basic,plain 534 keys=basic,plain
344 535
345 [formatter_basic] 536 [formatter_basic]
346 format=%(asctime)s %(process)d %(name)s:%(module)s.%(funcName)s,%(levelname)s: %(message)s 537 format=%(asctime)s %(trace_id)s %(process)d %(name)s:%(module)s.%(funcName)s,%(levelname)s: %(message)s
347 datefmt=%Y-%m-%d %H:%M:%S 538 datefmt=%Y-%m-%d %H:%M:%S
348 539
349 [formatter_plain] 540 [formatter_plain]
350 format=%(process)d %(message)s 541 format=%(process)d %(message)s
351 542

Roundup Issue Tracker: http://roundup-tracker.org/