Mercurial > p > roundup > code
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 |
