Download Install Tutorial Docs FAQ Tools WikiLicense Team IRC Planet Involvement Shop Book

Changeset 1984

Show
Ignore:
Timestamp:
06/22/08 16:11:54
Author:
fumanchu
Message:

Fix for #824 (_cplogging.LogManager?.access method not handling unicode in login names properly). While I was at it, I made the access log template configurable, moved the log tests from test_core into test_logging, and added a new logtest module.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/cherrypy/_cplogging.py

    r1948 r1984  
    1919    error_log = None 
    2020    access_log = None 
     21    access_log_format = \ 
     22        '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"' 
    2123     
    2224    def __init__(self, appid=None, logger_root="cherrypy"): 
     
    6264     
    6365    def access(self): 
    64         """Write to the access log (in Apache/NCSA Combined Log format).""" 
     66        """Write to the access log (in Apache/NCSA Combined Log format). 
     67         
     68        Like Apache started doing in 2.0.46, non-printable and other special 
     69        characters in %r (and we expand that to all parts) are escaped using 
     70        \\xhh sequences, where hh stands for the hexadecimal representation 
     71        of the raw byte. Exceptions from this rule are " and \\, which are 
     72        escaped by prepending a backslash, and all whitespace characters, 
     73        which are written in their C-style notation (\\n, \\t, etc). 
     74        """ 
    6575        request = cherrypy.request 
    66         inheaders = request.headers 
    6776        remote = request.remote 
    6877        response = cherrypy.response 
    6978        outheaders = response.headers 
    70         tmpl = '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"' 
    71         s = tmpl % {'h': remote.name or remote.ip, 
    72                     'l': '-', 
    73                     'u': getattr(request, "login", None) or "-", 
    74                     't': self.time(), 
    75                     'r': request.request_line, 
    76                     's': response.status.split(" ", 1)[0], 
    77                     'b': outheaders.get('Content-Length', '') or "-", 
    78                     'f': inheaders.get('Referer', ''), 
    79                     'a': inheaders.get('User-Agent', ''), 
    80                     } 
     79        inheaders = request.headers 
     80         
     81        atoms = {'h': remote.name or remote.ip, 
     82                 'l': '-', 
     83                 'u': getattr(request, "login", None) or "-", 
     84                 't': self.time(), 
     85                 'r': request.request_line, 
     86                 's': response.status.split(" ", 1)[0], 
     87                 'b': outheaders.get('Content-Length', '') or "-", 
     88                 'f': inheaders.get('Referer', ''), 
     89                 'a': inheaders.get('User-Agent', ''), 
     90                 } 
     91        for k, v in atoms.items(): 
     92            if isinstance(v, unicode): 
     93                v = v.encode('utf8') 
     94            elif not isinstance(v, str): 
     95                v = str(v) 
     96            # Fortunately, repr(str) escapes unprintable chars, \n, \t, etc 
     97            # and backslash for us. All we have to do is strip the quotes. 
     98            v = repr(v)[1:-1] 
     99            # Escape double-quote. 
     100            atoms[k] = v.replace('"', '\\"') 
     101         
    81102        try: 
    82             self.access_log.log(logging.INFO, s
     103            self.access_log.log(logging.INFO, self.access_log_format % atoms
    83104        except: 
    84105            self(traceback=True) 
  • trunk/cherrypy/test/test.py

    r1908 r1984  
    375375        'test_httpauth', 
    376376        'test_httplib', 
     377        'test_logging', 
    377378        'test_objectmapping', 
    378379        'test_misc_tools', 
  • trunk/cherrypy/test/test_core.py

    r1978 r1984  
    1313 
    1414 
    15 log_file = os.path.join(localDir, "test.log") 
    16 log_access_file = os.path.join(localDir, "access.log") 
    1715favicon_path = os.path.join(os.getcwd(), localDir, "../favicon.ico") 
    1816 
     
    419417     
    420418    cherrypy.config.update({ 
    421         'log.error_file': log_file, 
    422419        'environment': 'test_suite', 
    423420        'server.max_request_body_size': 200, 
     
    425422        }) 
    426423    appconf = { 
    427         '/': {'log.access_file': log_access_file}, 
    428424        '/method': {'request.methods_with_bodies': ("POST", "PUT", "PROPFIND")}, 
    429425        } 
     
    489485        msg = "Illegal response status from server ('error' is non-numeric)." 
    490486        self.assertErrorPage(500, msg) 
    491      
    492     def testLogging(self): 
    493         f = open(log_access_file, "wb") 
    494         f.write("") 
    495         f.close() 
    496         f = open(log_file, "wb") 
    497         f.write("") 
    498         f.close() 
    499          
    500         self.getPage("/flatten/as_string", 
    501                      headers=[('Referer', 'http://www.cherrypy.org/'), 
    502                               ('User-Agent', 'Mozilla/5.0')]) 
    503         self.assertBody('content') 
    504         self.assertStatus(200) 
    505          
    506         self.getPage("/flatten/as_yield") 
    507         self.assertBody('content') 
    508         self.assertStatus(200) 
    509          
    510         data = open(log_access_file, "rb").readlines() 
    511          
    512         host = self.HOST 
    513         if host == '0.0.0.0': 
    514             # INADDR_ANY, which should respond on localhost. 
    515             host = "127.0.0.1" 
    516         if host == '::': 
    517             # IN6ADDR_ANY, which should respond on localhost. 
    518             host = "::1" 
    519         intro = '%s - - [' % host 
    520          
    521         if not data[0].startswith(intro): 
    522             self.fail("%r doesn't start with %r" % (data[0], intro)) 
    523         haslength = False 
    524         for k, v in self.headers: 
    525             if k.lower() == 'content-length': 
    526                 haslength = True 
    527         line = data[-2].strip() 
    528         if haslength: 
    529             if not line.endswith('] "GET %s/flatten/as_string HTTP/1.1" 200 7 ' 
    530                                  '"http://www.cherrypy.org/" "Mozilla/5.0"' 
    531                                  % self.prefix()): 
    532                 self.fail(line) 
    533         else: 
    534             if not line.endswith('] "GET %s/flatten/as_string HTTP/1.1" 200 - ' 
    535                                  '"http://www.cherrypy.org/" "Mozilla/5.0"' 
    536                                  % self.prefix()): 
    537                 self.fail(line) 
    538          
    539         if not data[-1].startswith(intro): 
    540             self.fail("%r doesn't start with %r" % (data[-1], intro)) 
    541         haslength = False 
    542         for k, v in self.headers: 
    543             if k.lower() == 'content-length': 
    544                 haslength = True 
    545         line = data[-1].strip() 
    546         if haslength: 
    547             self.assert_(line.endswith('] "GET %s/flatten/as_yield HTTP/1.1" 200 7 "" ""' 
    548                                           % self.prefix())) 
    549         else: 
    550             self.assert_(line.endswith('] "GET %s/flatten/as_yield HTTP/1.1" 200 - "" ""' 
    551                                           % self.prefix())) 
    552          
    553         ignore = helper.webtest.ignored_exceptions 
    554         ignore.append(ValueError) 
    555         try: 
    556             # Test that tracebacks get written to the error log. 
    557             self.getPage("/error/page_method") 
    558             self.assertInBody("raise ValueError()") 
    559             data = open(log_file, "rb").readlines() 
    560             self.assertEqual(data[0].strip().endswith('HTTP Traceback (most recent call last):'), True) 
    561             self.assertEqual(data[-3].strip().endswith('raise ValueError()'), True) 
    562         finally: 
    563             ignore.pop() 
    564487     
    565488    def testSlashes(self): 

Hosted by WebFaction

Log in as guest/cpguest to create tickets