Edgewall Software

Ticket #7286: corrected_enhanced_logging.3.patch

File corrected_enhanced_logging.3.patch, 25.1 KB (added by Pedro Algarvio, aka, s0undt3ch <ufs@…>, 4 years ago)
  • trac/attachment.py

     
    748748        if legacy_action: 
    749749            decision = legacy_action in perm 
    750750            if not decision: 
    751                 self.env.log.debug('LegacyAttachmentPolicy denied %s ' 
    752                                    'access to %s. User needs %s' % 
    753                                    (username, resource, legacy_action)) 
     751                self.log.debug('LegacyAttachmentPolicy denied %s ' 
     752                               'access to %s. User needs %s' % 
     753                               (username, resource, legacy_action)) 
    754754            return decision 
    755755        else: 
    756756            for d in self.delegates: 
  • trac/env.py

     
    22# 
    33# Copyright (C) 2003-2008 Edgewall Software 
    44# Copyright (C) 2003-2007 Jonas Borgström <jonas@edgewall.com> 
     5# Copyright (C) 2008 Pedro Algarvio <ufs@ufsoft.org> 
    56# All rights reserved. 
    67# 
    78# This software is licensed as described in the file COPYING, which 
     
    1415# 
    1516# Author: Jonas Borgström <jonas@edgewall.com> 
    1617 
     18import logging 
    1719import os 
    1820try: 
    1921    import threading 
     
    147149         - $(path)s     the path for the current environment 
    148150         - $(basename)s the last path component of the current environment 
    149151         - $(project)s  the project name 
    150  
    151          Note the usage of `$(...)s` instead of `%(...)s` as the latter form 
    152          would be interpreted by the ConfigParser itself. 
    153  
    154          Example: 
     152         
     153        Note the usage of `$(...)s` instead of `%(...)s` as the latter form 
     154        would be interpreted by the ConfigParser itself. 
     155         
     156        Example: 
    155157         ($(thread)d) Trac[$(basename)s:$(module)s] $(levelname)s: $(message)s 
    156  
    157          (since 0.10.5)""") 
     158         
     159        (since 0.10.5)""") 
     160    log_filters = ListOption('logging', 'log_filters', [], doc= 
     161        """Custom logging handlers. 
     162         
     163        If nothing set, logging will be as it was, nothing is changed. 
     164         
     165        Example usage is: 
     166        log_filters = trac:WARNING, trac.ticket:DEBUG 
     167         
     168        The above would translate to: 
     169            * all messages who's module name starts with `trac` and log level 
     170            is higher than `WARNING` are logged; 
     171            * all messages who's module name starts with `trac.ticket` and log 
     172            level is higher than `DEBUG` are logged; 
     173            * all other messages who's module name does not start with any of 
     174            the above and for which their level is higher than the default 
     175            `log_level` will be logged; 
     176         
     177        This way you can narrow the debugging messages to the modules you 
     178        wish to. The same applies to a plugin you're coding: 
     179        log_filters = trac:ERROR, my.plug.module:DEBUG 
     180         
     181        (since 0.12)""") 
    158182 
    159183    def __init__(self, path, create=False, options=[]): 
    160184        """Initialize the Trac environment. 
     
    201225        environment configuration) and `log` (a logger object).""" 
    202226        component.env = self 
    203227        component.config = self.config 
    204         component.log = self.log 
     228        component.log = logging.getLogger( 
     229            "%s.%s" % (self.path, component.__class__.__module__) 
     230        ) 
    205231 
    206232    def is_component_enabled(self, cls): 
    207233        """Implemented to only allow activation of components that are not 
     
    347373 
    348374    def setup_log(self): 
    349375        """Initialize the logging sub-system.""" 
    350         from trac.log import logger_factory 
     376        from trac.log import setup_logging 
    351377        logtype = self.log_type 
    352378        logfile = self.log_file 
    353379        if logtype == 'file' and not os.path.isabs(logfile): 
     
    358384                     .replace('%(path)s', self.path) \ 
    359385                     .replace('%(basename)s', os.path.basename(self.path)) \ 
    360386                     .replace('%(project)s', self.project_name) 
    361         self.log = logger_factory(logtype, logfile, self.log_level, self.path, 
    362                                   format=format) 
     387        setup_logging(logtype, logfile, self.log_level, self.path, 
     388                      format, self.log_filters) 
     389        self.log = logging.getLogger("%s.%s" % (self.path, __name__)) 
    363390 
    364391    def get_known_users(self, cnx=None): 
    365392        """Generator that yields information about all known users, i.e. users 
     
    551578                env.log.info('Reloading environment due to configuration ' 
    552579                             'change') 
    553580                env.shutdown() 
    554                 if hasattr(env.log, '_trac_handler'): 
    555                     hdlr = env.log._trac_handler 
    556                     env.log.removeHandler(hdlr) 
     581                env_root_logger = logging.getLogger(env.path) 
     582                if hasattr(env_root_logger, '_trac_handler'):                     
     583                    hdlr = env_root_logger._trac_handler 
     584                    env_root_logger.removeHandler(hdlr) 
    557585                    hdlr.close() 
     586                del env_root_logger 
    558587                del env_cache[env_path] 
    559588                env = None 
    560589            if env is None: 
  • trac/mimeview/enscript.py

     
    136136        mimetype = mimetype.split(';', 1)[0] # strip off charset 
    137137        mode = self._types[mimetype][0] 
    138138        cmdline += ' --color -h -q --language=html -p - -E%s' % mode 
    139         self.env.log.debug("Enscript command line: %s" % cmdline) 
     139        self.log.debug("Enscript command line: %s" % cmdline) 
    140140 
    141141        np = NaivePopen(cmdline, content.encode('utf-8'), capturestderr=1) 
    142142        if np.errorlevel or np.err: 
  • trac/mimeview/php.py

     
    8181    def render(self, context, mimetype, content, filename=None, rev=None): 
    8282        # -n to ignore php.ini so we're using default colors 
    8383        cmdline = '%s -sn' % self.path 
    84         self.env.log.debug("PHP command line: %s" % cmdline) 
     84        self.log.debug("PHP command line: %s" % cmdline) 
    8585 
    8686        content = content_to_unicode(self.env, content, mimetype) 
    8787        content = content.encode('utf-8') 
  • trac/ticket/report.py

     
    531531            # The number of tickets is obtained. 
    532532            count_sql = 'SELECT COUNT(*) FROM (' + sql + ') AS tab' 
    533533            cursor.execute(count_sql, args) 
    534             self.env.log.debug("Query SQL(Get num items): " + count_sql) 
     534            self.log.debug("Query SQL(Get num items): " + count_sql) 
    535535            for row in cursor: 
    536536                pass 
    537537            self.num_items = row[0] 
     
    539539            # The column name is obtained. 
    540540            get_col_name_sql = 'SELECT * FROM ( ' + sql + ' ) AS tab LIMIT 1' 
    541541            cursor.execute(get_col_name_sql, args) 
    542             self.env.log.debug("Query SQL(Get col names): " + get_col_name_sql) 
     542            self.log.debug("Query SQL(Get col names): " + get_col_name_sql) 
    543543            cols = get_column_names(cursor) 
    544544 
    545545            sort_col = req.args.get('sort', '') 
    546             self.env.log.debug("Colnum Names %s, Sort column %s" % 
     546            self.log.debug("Colnum Names %s, Sort column %s" % 
    547547                               (str(cols), sort_col)) 
    548548            order_cols = [] 
    549549            if '__group__' in cols: 
     
    567567            sql = " ".join(['SELECT * FROM (', sql, ') AS tab', order_by]) 
    568568            sql =" ".join([sql, 'LIMIT', str(self.limit), 'OFFSET', 
    569569                           str(self.offset)]) 
    570             self.env.log.debug("Query SQL: " + sql) 
     570            self.log.debug("Query SQL: " + sql) 
    571571        cursor.execute(sql, args) 
    572         self.env.log.debug("Query SQL: " + sql) 
     572        self.log.debug("Query SQL: " + sql) 
    573573        # FIXME: fetchall should probably not be used. 
    574574        info = cursor.fetchall() or [] 
    575575        cols = get_column_names(cursor) 
  • trac/ticket/roadmap.py

     
    646646                cursor.execute("UPDATE ticket SET milestone=%s WHERE " 
    647647                               "milestone=%s and status != 'closed'", 
    648648                                (retarget_to, old_name)) 
    649                 self.env.log.info('Tickets associated with milestone %s ' 
    650                                   'retargeted to %s' % (old_name, retarget_to)) 
     649                self.log.info('Tickets associated with milestone %s ' 
     650                              'retargeted to %s' % (old_name, retarget_to)) 
    651651        else: 
    652652            milestone.insert() 
    653653        db.commit() 
  • trac/htdocs/css/admin.css

     
    2121 
    2222#tabcontent { padding: 0.4em 2em; margin-left: 12em; min-height: 300px; } 
    2323#tabcontent h2 { color: #333; margin-top: 0; } 
    24 p.help { color: #666; font-size: 90%; margin: 1em .5em .5em; } 
     24div.help, p.help { color: #666; font-size: 90%; margin: 1em .5em .5em; } 
    2525 
    2626#enumlist tbody td { vertical-align: middle; } 
    2727 
  • trac/db/mysql_backend.py

     
    7070        from trac.db_default import schema 
    7171        for table in schema: 
    7272            for stmt in self.to_sql(table): 
    73                 self.env.log.debug(stmt) 
     73                self.log.debug(stmt) 
    7474                cursor.execute(stmt) 
    7575        cnx.commit() 
    7676 
  • trac/admin/web_ui.py

     
    22# 
    33# Copyright (C) 2005-2008 Edgewall Software 
    44# Copyright (C) 2005 Jonas Borgström <jonas@edgewall.com> 
     5# Copyright (C) 2008 Pedro Algarvio <ufs@ufsoft.org> 
    56# All rights reserved. 
    67# 
    78# This software is licensed as described in the file COPYING, which 
     
    211212        log_level = self.env.log_level 
    212213        log_file = self.env.log_file 
    213214        log_dir = os.path.join(self.env.path, 'log') 
     215        log_filters = self.config.getlist('logging', 'log_filters') 
     216        for idx, filter in enumerate(log_filters): 
     217            filter = filter.split(':') 
     218            if not (len(filter) > 1 and filter[1]): 
     219                log_filters[idx] = "%s:%s" % (filter[0], log_level) 
    214220 
    215221        log_types = [ 
    216222            dict(name='', label=_('None'), selected=False, disabled=False), 
     
    229235 
    230236        if req.method == 'POST': 
    231237            changed = False 
    232  
    233             new_type = req.args.get('log_type') 
    234             if new_type and new_type not in ('stderr', 'file', 'syslog', 
    235                                              'eventlog'): 
    236                 raise TracError( 
    237                     _('Unknown log type %(type)s', type=new_type), 
    238                     _('Invalid log type') 
    239                 ) 
    240             if new_type != log_type: 
    241                 self.config.set('logging', 'log_type', new_type or 'none') 
     238            if 'add_filter' in req.args: 
     239                filter_module_name = req.args.get('filter_modname') 
     240                if not filter_module_name: 
     241                    raise TracError(_("Filter module name must not be empty")) 
     242                filter_log_level = req.args.get('filter_loglevel') 
     243                if filter_log_level and filter_log_level not in log_levels: 
     244                    raise TracError(_('Unknown log level %(level)s', 
     245                                      level=filter_log_level), 
     246                                    _('Invalid log level')) 
     247                for filter in log_filters: 
     248                    if filter.split(':')[0] == filter_module_name or \ 
     249                        filter.split(':')[0].rstrip('.*') == filter_module_name: 
     250                        raise TracError( 
     251                            _("A filter for module '%(module)s' already exists." 
     252                              " Remove that one first.", 
     253                              module=filter_module_name), 
     254                            _("Filter already exists")) 
     255                new_log_filter = "%s:%s" % (filter_module_name.rstrip('.*'), 
     256                                            filter_log_level) 
     257                log_filters.append(new_log_filter) 
     258                self.log.debug("Adding new filter '%s' to log_filters", 
     259                               new_log_filter) 
     260                self.config.set('logging', 'log_filters',', '.join(log_filters)) 
    242261                changed = True 
    243                 log_type = new_type 
    244  
    245             if log_type: 
    246                 new_level = req.args.get('log_level') 
    247                 if new_level and new_level not in log_levels: 
     262            elif 'delete_filters' in req.args: 
     263                selected = req.args.getlist('sel') 
     264                for filter in selected: 
     265                    self.log.debug("Removing filter '%s' from log_filters", 
     266                                   filter) 
     267                    log_filters.pop(log_filters.index(filter)) 
     268                self.config.set('logging', 'log_filters',', '.join(log_filters)) 
     269                changed = True 
     270            else: 
     271                new_type = req.args.get('log_type') 
     272                if new_type and new_type not in ('stderr', 'file', 'syslog', 
     273                                                 'eventlog'): 
    248274                    raise TracError( 
    249                         _('Unknown log level %(level)s', level=new_level), 
    250                         _('Invalid log level')) 
    251                 if new_level and new_level != log_level: 
    252                     self.config.set('logging', 'log_level', new_level) 
     275                        _('Unknown log type %(type)s', type=new_type), 
     276                        _('Invalid log type') 
     277                    ) 
     278                if new_type != log_type: 
     279                    self.config.set('logging', 'log_type', new_type or 'none') 
    253280                    changed = True 
    254                     log_evel = new_level 
    255             else: 
    256                 self.config.remove('logging', 'log_level') 
    257                 changed = True 
     281                    log_type = new_type 
    258282 
    259             if log_type == 'file': 
    260                 new_file = req.args.get('log_file', 'trac.log') 
    261                 if new_file != log_file: 
    262                     self.config.set('logging', 'log_file', new_file or '') 
     283                if log_type: 
     284                    new_level = req.args.get('log_level') 
     285                    if new_level and new_level not in log_levels: 
     286                        raise TracError( 
     287                            _('Unknown log level %(level)s', level=new_level), 
     288                            _('Invalid log level')) 
     289                    if new_level and new_level != log_level: 
     290                        self.config.set('logging', 'log_level', new_level) 
     291                        changed = True 
     292                        log_evel = new_level 
     293                else: 
     294                    self.config.remove('logging', 'log_level') 
    263295                    changed = True 
    264                     log_file = new_file 
    265                 if log_type == 'file' and not log_file: 
    266                     raise TracError(_('You must specify a log file'), 
    267                                     _('Missing field')) 
    268             else: 
    269                 self.config.remove('logging', 'log_file') 
    270                 changed = True 
    271296 
     297                if log_type == 'file': 
     298                    new_file = req.args.get('log_file', 'trac.log') 
     299                    if new_file != log_file: 
     300                        self.config.set('logging', 'log_file', new_file or '') 
     301                        changed = True 
     302                        log_file = new_file 
     303                    if log_type == 'file' and not log_file: 
     304                        raise TracError(_('You must specify a log file'), 
     305                                        _('Missing field')) 
     306                else: 
     307                    self.config.remove('logging', 'log_file') 
     308                    changed = True 
    272309            if changed: 
    273310                self.config.save() 
    274311            req.redirect(req.href.admin(cat, page)) 
     
    276313        data = { 
    277314            'type': log_type, 'types': log_types, 
    278315            'level': log_level, 'levels': log_levels, 
    279             'file': log_file, 'dir': log_dir 
     316            'file': log_file, 'dir': log_dir, 
     317            'filters': log_filters 
    280318        } 
    281319        return 'admin_logging.html', {'log': data} 
    282320 
  • trac/admin/templates/admin_logging.html

     
    5454        </div> 
    5555      </fieldset> 
    5656    </form> 
     57 
     58    <fieldset> 
     59      <legend>Logging Filters</legend> 
     60 
     61      <form class="addnew" id="newlog_filters" name="newlog_filters" method="post"> 
     62        <fieldset> 
     63          <legend>Add New Logging Filter</legend> 
     64          <table> 
     65            <tr class="field"> 
     66              <th><label for="filter_modname">Module:</label></th> 
     67              <td><input type="text" id="filter_modname" name="filter_modname"/></td> 
     68            </tr> 
     69            <tr class="field"> 
     70              <th><label for="filter_loglevel">Log level:</label></th> 
     71              <td> 
     72                <select id="filter_loglevel" name="filter_loglevel"> 
     73                  <option py:for="level in log.levels">$level</option> 
     74                </select> 
     75              </td> 
     76            </tr> 
     77          </table> 
     78        <div class="buttons"> 
     79          <input type="submit" name="add_filter" value="${_('Add Filter')}"/> 
     80        </div> 
     81        </fieldset> 
     82      </form> 
     83 
     84      <p class="help">If nothing set, logging will be as it was, nothing is 
     85      changed.</p> 
     86 
     87      <form class="mod" id="log_filters" name="log_filters" method="post"> 
     88        <div class="field"> 
     89          <table class="listing" id="filters_table"> 
     90            <thead> 
     91              <tr> 
     92                <th class="sel">&nbsp;</th> 
     93                <th>Module</th> 
     94                <th>Log level</th> 
     95              </tr> 
     96            </thead> 
     97            <tbody py:if="log.filters"> 
     98              <tr py:for="mod, level in [f.split(':') for f in log.filters]"> 
     99                <td class="sel"><input type="checkbox" name="sel" value="$mod:$level"/></td> 
     100                <td>$mod</td> 
     101                <td>$level</td> 
     102              </tr> 
     103            </tbody> 
     104            <tbody py:if="not log.filters"> 
     105              <tr><td colspan="3"> 
     106                <center><b>No Filters Available</b></center> 
     107              </td></tr> 
     108            </tbody> 
     109          </table> 
     110        </div> 
     111        <div class="buttons" py:if="log.filters"> 
     112          <input type="submit" name="delete_filters" value="${_('Delete Selected Filters')}"/> 
     113        </div> 
     114 
     115        <div class="help"> 
     116          <p>Example usage is:</p> 
     117<pre> 
     118  [logging] 
     119  log_filters = trac:WARNING, trac.ticket:DEBUG 
     120</pre>   
     121          <p>The above would translate to:</p>   
     122          <ul> 
     123            <li>all messages who's module name starts with <b><tt>trac</tt></b> 
     124            and log level is higher than <b><tt>WARNING</tt></b> are logged;</li> 
     125             
     126            <li>all messages who's module name starts with <b><tt>trac.ticket</tt></b> 
     127            and log level is higher than <b><tt>DEBUG</tt></b> are logged;</li> 
     128   
     129            <li>all other messages who's module name <b>does not</b> start with either 
     130            <b><tt>trac</tt></b> or <b><tt>trac.ticket</tt></b> will be logged if 
     131            their level is higher than the default <b><tt>log_level</tt></b>;</li> 
     132          </ul> 
     133   
     134          <p>This way you can narrow the debugging messages to the modules you wish to.</p> 
     135          <p>The same applies to a plugin you're coding:</p> 
     136<pre> 
     137  [logging] 
     138  log_filters = trac:ERROR, my.plug.module:DEBUG 
     139</pre> 
     140        </div> 
     141      </form> 
     142    </fieldset> 
    57143  </body> 
    58144 
    59145</html> 
  • trac/log.py

     
    33# Copyright (C) 2003-2008 Edgewall Software 
    44# Copyright (C) 2003-2005 Daniel Lundin <daniel@edgewall.com> 
    55# Copyright (C) 2006 Christian Boos <cboos@neuf.fr> 
     6# Copyright (C) 2008 Pedro Algarvio <ufs@ufsoft.org> 
    67# All rights reserved. 
    78# 
    89# This software is licensed as described in the file COPYING, which 
     
    1920import logging.handlers 
    2021import sys 
    2122 
    22 def logger_factory(logtype='syslog', logfile=None, level='WARNING', 
    23                    logid='Trac', format=None): 
    24     logger = logging.getLogger(logid) 
     23from trac.util.compat import sorted 
     24from trac.util.translation import _ 
     25 
     26 
     27def setup_logging(logtype='syslog', logfile=None, level='WARNING', logid='Trac', 
     28                  format=None, filters=()): 
     29    env_root_logger = logging.getLogger(logid) 
     30         
    2531    logtype = logtype.lower() 
    2632    if logtype == 'file': 
    2733        hdlr = logging.FileHandler(logfile) 
    2834    elif logtype in ('winlog', 'eventlog', 'nteventlog'): 
    2935        # Requires win32 extensions 
    30         hdlr = logging.handlers.NTEventLogHandler(logid, 
    31                                                   logtype='Application') 
     36        hdlr = logging.handlers.NTEventLogHandler(logid, logtype='Application') 
    3237    elif logtype in ('syslog', 'unix'): 
    3338        hdlr = logging.handlers.SysLogHandler('/dev/log') 
    3439    elif logtype in ('stderr'): 
     
    4752        datefmt = '%X' 
    4853    level = level.upper() 
    4954    if level in ('DEBUG', 'ALL'): 
    50         logger.setLevel(logging.DEBUG) 
     55        env_root_logger.setLevel(logging.DEBUG) 
    5156    elif level == 'INFO': 
    52         logger.setLevel(logging.INFO) 
     57        env_root_logger.setLevel(logging.INFO) 
    5358    elif level == 'ERROR': 
    54         logger.setLevel(logging.ERROR) 
     59        env_root_logger.setLevel(logging.ERROR) 
    5560    elif level == 'CRITICAL': 
    56         logger.setLevel(logging.CRITICAL) 
     61        env_root_logger.setLevel(logging.CRITICAL) 
    5762    else: 
    58         logger.setLevel(logging.WARNING) 
    59     formatter = logging.Formatter(format, datefmt) 
    60     hdlr.setFormatter(formatter) 
    61     logger.addHandler(hdlr) 
     63        env_root_logger.setLevel(logging.WARNING) 
    6264 
     65    hdlr.setFormatter(TracFormatter(logid, format, datefmt)) 
     66    # Assign handler right away to be able to log filter errors 
     67    env_root_logger.addHandler(hdlr)     
     68     
     69    if filters: 
     70        hdlr.addFilter(TracFilter(filters, level, logid)) 
     71         
    6372    # Remember our handler so that we can remove it later 
    64     logger._trac_handler = hdlr  
     73    env_root_logger._trac_handler = hdlr 
     74 
     75 
     76class TracFilter(logging.Filter): 
     77    def __init__(self, trac_filters=(), default_level='DEBUG', name=''): 
     78        self.qns = [] 
     79        for filter in trac_filters: 
     80            filter = filter.split(':') 
     81            if len(filter) > 1 and filter[1]: 
     82                qn, lvl = filter[0].rstrip('.*'), filter[1] 
     83            else: 
     84                qn, lvl = filter[0].rstrip('.*'), default_level 
     85            path_and_qn = "%s.%s" % (name, qn) 
     86             
     87            if lvl == 'ALL': 
     88                lvl = 'DEBUG' 
     89                 
     90            if lvl not in logging._levelNames: 
     91                logging.getLogger("%s.%s" % (name, __name__)).warning( 
     92                    _("Level '%(level)s' for filter '%(filter)s' not know. " 
     93                      "Ignoring filter.", 
     94                      level=lvl.upper(), filter=':'.join(filter))) 
     95                continue 
     96                             
     97            self.qns.append((path_and_qn, logging.getLevelName(lvl.upper()))) 
     98             
     99        self.qns = sorted(self.qns, key=lambda x: len(x[0]), reverse=True) 
     100         
     101        logging.Filter.__init__(self, name) 
     102 
     103    def filter(self, record): 
     104        for qn, level in self.qns: 
     105            if record.name.startswith("%s." % qn) or record.name == qn: 
     106                # Match trac, trac.web but not tracforge 
     107                if level <= record.levelno: 
     108                    return 1 
     109                return 0 
     110        # No point returning `logging.Filter.filter(self, record)` 
     111        # All logging messages will arrive to the filter with self.name at 
     112        # least equal to environment path, ie, self.name 
     113        return 1 
     114 
     115 
     116class TracFormatter(logging.Formatter): 
     117 
     118    def __init__(self, env_path, fmt, datefmt): 
     119        self.env_path = env_path 
     120        # Calculate strip length at init time, no need to keep calculating it 
     121        self.strip_length = len(env_path)+1 
     122        logging.Formatter.__init__(self, fmt, datefmt) 
    65123 
    66     return logger 
     124    def format(self, record): 
     125        # get full dotted module name and stick that under record.module 
     126        if record.name.startswith(self.env_path): 
     127            record.module = record.name[self.strip_length:] 
     128        return logging.Formatter.format(self, record)