Mercurial > p > roundup > code
comparison roundup/configuration.py @ 8443:39a6825d10ca
feat: allow admin to set logging format from config.ini
This is prep work for adding a per thread logging variable that can be
used to tie all logs for a single request together.
This uses the same default logging format as before, just moves it to
config.ini.
Also because of configparser, the logging format has to have doubled %
signs. So use:
%%(asctime)s
not '%(asctime)s' as configparser tries to interpolate that string and
asctime is not defined in the configparser's scope. Using %%(asctime)s
is not interpolated by configparser and is passed into Roundup.
| author | John Rouillard <rouilj@ieee.org> |
|---|---|
| date | Mon, 01 Sep 2025 21:54:48 -0400 |
| parents | 66284037142e |
| children | 14c7c07b32d8 |
comparison
equal
deleted
inserted
replaced
| 8442:0bb29d0509c9 | 8443:39a6825d10ca |
|---|---|
| 271 def load_ini(self, config): | 271 def load_ini(self, config): |
| 272 """Load value from ConfigParser object""" | 272 """Load value from ConfigParser object""" |
| 273 try: | 273 try: |
| 274 if config.has_option(self.section, self.setting): | 274 if config.has_option(self.section, self.setting): |
| 275 self.set(config.get(self.section, self.setting)) | 275 self.set(config.get(self.section, self.setting)) |
| 276 except configparser.InterpolationSyntaxError as e: | 276 except (configparser.InterpolationSyntaxError, |
| 277 configparser.InterpolationMissingOptionError) as e: | |
| 277 raise ParsingOptionError( | 278 raise ParsingOptionError( |
| 278 _("Error in %(filepath)s with section [%(section)s] at " | 279 _("Error in %(filepath)s with section [%(section)s] at " |
| 279 "option %(option)s: %(message)s") % { | 280 "option %(option)s: %(message)s") % { |
| 280 "filepath": self.config.filepath, | 281 "filepath": self.config.filepath, |
| 281 "section": e.section, | 282 "section": e.section, |
| 572 if pathlist: | 573 if pathlist: |
| 573 return pathlist | 574 return pathlist |
| 574 else: | 575 else: |
| 575 return None | 576 return None |
| 576 | 577 |
| 578 | |
| 579 class LoggingFormatOption(Option): | |
| 580 """Replace escaped % (as %%) with single %. | |
| 581 | |
| 582 Config file parsing allows variable interpolation using | |
| 583 %(keyname)s. However this is exactly the format that we need | |
| 584 for creating a logging format string. So we tell the user to | |
| 585 quote the string using %%(...). Then we turn %%( -> %( when | |
| 586 retrieved. | |
| 587 """ | |
| 588 | |
| 589 class_description = ("Allowed value: Python logging module named " | |
| 590 "attributes with % sign doubled.") | |
| 591 | |
| 592 def str2value(self, value): | |
| 593 """Check format of unquoted string looking for missing specifiers. | |
| 594 | |
| 595 This does a dirty check to see if a token is missing a | |
| 596 specifier. So "%(ascdate)s %(level) " would fail because of | |
| 597 the 's' missing after 'level)'. But "%(ascdate)s %(level)s" | |
| 598 would pass. | |
| 599 | |
| 600 Note that %(foo)s generates a error from the ini parser | |
| 601 with a less than wonderful message. | |
| 602 """ | |
| 603 unquoted_val = value.replace("%%(", "%(") | |
| 604 | |
| 605 # regexp matches all current logging record object attribute names. | |
| 606 scanned_result = re.sub(r'%\([A-Za-z_]+\)\S','', unquoted_val ) | |
| 607 if scanned_result.find('%(') != -1: | |
| 608 raise OptionValueError( | |
| 609 self, unquoted_val, | |
| 610 "Check that all substitution tokens have a format " | |
| 611 "specifier after the ). Unrecognized use of %%(...) in: " | |
| 612 "%s" % scanned_result) | |
| 613 | |
| 614 return str(unquoted_val) | |
| 615 | |
| 616 def _value2str(self, value): | |
| 617 """Replace %( with %%( to quote the format substitution param. | |
| 618 """ | |
| 619 return value.replace("%(", "%%(") | |
| 577 | 620 |
| 578 class OriginHeadersListOption(Option): | 621 class OriginHeadersListOption(Option): |
| 579 | 622 |
| 580 """List of space seperated origin header values. | 623 """List of space seperated origin header values. |
| 581 """ | 624 """ |
| 1612 "If above 'config' option is set, this option has no effect."), | 1655 "If above 'config' option is set, this option has no effect."), |
| 1613 (Option, "level", "ERROR", | 1656 (Option, "level", "ERROR", |
| 1614 "Minimal severity level of messages written to log file.\n" | 1657 "Minimal severity level of messages written to log file.\n" |
| 1615 "If above 'config' option is set, this option has no effect.\n" | 1658 "If above 'config' option is set, this option has no effect.\n" |
| 1616 "Allowed values: DEBUG, INFO, WARNING, ERROR"), | 1659 "Allowed values: DEBUG, INFO, WARNING, ERROR"), |
| 1660 (LoggingFormatOption, "format", | |
| 1661 "%(asctime)s %(levelname)s %(message)s", | |
| 1662 "Format of the logging messages with all '%' signs\n" | |
| 1663 "doubled so they are not interpreted by the config file."), | |
| 1617 (BooleanOption, "disable_loggers", "no", | 1664 (BooleanOption, "disable_loggers", "no", |
| 1618 "If set to yes, only the loggers configured in this section will\n" | 1665 "If set to yes, only the loggers configured in this section will\n" |
| 1619 "be used. Yes will disable gunicorn's --access-logfile.\n"), | 1666 "be used. Yes will disable gunicorn's --access-logfile.\n"), |
| 1620 )), | 1667 )), |
| 1621 ("mail", ( | 1668 ("mail", ( |
| 2446 # set file & level on the roundup logger | 2493 # set file & level on the roundup logger |
| 2447 logger = logging.getLogger('roundup') | 2494 logger = logging.getLogger('roundup') |
| 2448 hdlr = logging.FileHandler(_file) if _file else \ | 2495 hdlr = logging.FileHandler(_file) if _file else \ |
| 2449 logging.StreamHandler(sys.stdout) | 2496 logging.StreamHandler(sys.stdout) |
| 2450 | 2497 |
| 2451 formatter = logging.Formatter( | 2498 formatter = logging.Formatter(self["LOGGING_FORMAT"]) |
| 2452 '%(asctime)s %(levelname)s %(message)s') | |
| 2453 hdlr.setFormatter(formatter) | 2499 hdlr.setFormatter(formatter) |
| 2454 # no logging API to remove all existing handlers!?! | 2500 # no logging API to remove all existing handlers!?! |
| 2455 for h in logger.handlers: | 2501 for h in logger.handlers: |
| 2456 h.close() | 2502 h.close() |
| 2457 logger.removeHandler(hdlr) | 2503 logger.removeHandler(hdlr) |
