|
19 | 19 | import copy |
20 | 20 | import datetime |
21 | 21 | import inspect |
| 22 | +import logging |
22 | 23 | import os |
23 | 24 | import re |
24 | 25 | import sys |
|
49 | 50 | INVALID_EXPRESSION_INDEX = '<N/A>' |
50 | 51 |
|
51 | 52 |
|
| 53 | +def NormalizePath(path): |
| 54 | + """Removes any Python system path prefix from the given path. |
| 55 | +
|
| 56 | + Python keeps almost all paths absolute. This is not what we actually |
| 57 | + want to return. This loops through system paths (directories in which |
| 58 | + Python will load modules). If "path" is relative to one of them, the |
| 59 | + directory prefix is removed. |
| 60 | +
|
| 61 | + Args: |
| 62 | + path: absolute path to normalize (relative paths will not be altered) |
| 63 | +
|
| 64 | + Returns: |
| 65 | + Relative path if "path" is within one of the sys.path directories or |
| 66 | + the input otherwise. |
| 67 | + """ |
| 68 | + path = os.path.normpath(path) |
| 69 | + |
| 70 | + for sys_path in sys.path: |
| 71 | + if not sys_path: |
| 72 | + continue |
| 73 | + |
| 74 | + # Append '/' at the end of the path if it's not there already. |
| 75 | + sys_path = os.path.join(sys_path, '') |
| 76 | + |
| 77 | + if path.startswith(sys_path): |
| 78 | + return path[len(sys_path):] |
| 79 | + |
| 80 | + return path |
| 81 | + |
| 82 | + |
| 83 | +class LineNoFilter(logging.Filter): |
| 84 | + """Enables overriding the path and line number in a logging record. |
| 85 | +
|
| 86 | + The "extra" parameter in logging cannot override existing fields in log |
| 87 | + record, so we can't use it to directly set pathname and lineno. Instead, |
| 88 | + we add this filter to the default logger, and it looks for "cdbg_pathname" |
| 89 | + and "cdbg_lineno", moving them to the pathname and lineno fields accordingly. |
| 90 | + """ |
| 91 | + |
| 92 | + def filter(self, record): |
| 93 | + # This method gets invoked for user-generated logging, so verify that this |
| 94 | + # particular invocation came from our logging code. |
| 95 | + if record.pathname != inspect.currentframe().f_code.co_filename: |
| 96 | + return True |
| 97 | + if hasattr(record, 'cdbg_pathname'): |
| 98 | + record.pathname = record.cdbg_pathname |
| 99 | + del record.cdbg_pathname |
| 100 | + if hasattr(record, 'cdbg_lineno'): |
| 101 | + record.lineno = record.cdbg_lineno |
| 102 | + del record.cdbg_lineno |
| 103 | + return True |
| 104 | + |
| 105 | + |
| 106 | +def SetLogger(logger): |
| 107 | + """Sets the logger object to use for all 'LOG' breakpoint actions.""" |
| 108 | + global log_info_message |
| 109 | + global log_warning_message |
| 110 | + global log_error_message |
| 111 | + log_info_message = logger.info |
| 112 | + log_warning_message = logger.warning |
| 113 | + log_error_message = logger.error |
| 114 | + logger.addFilter(LineNoFilter()) |
| 115 | + |
| 116 | + |
52 | 117 | class CaptureCollector(object): |
53 | 118 | """Captures application state snapshot. |
54 | 119 |
|
@@ -145,7 +210,7 @@ def Collect(self, top_frame): |
145 | 210 | breakpoint_frames.append({ |
146 | 211 | 'function': code.co_name, |
147 | 212 | 'location': { |
148 | | - 'path': CaptureCollector._NormalizePath(code.co_filename), |
| 213 | + 'path': NormalizePath(code.co_filename), |
149 | 214 | 'line': frame.f_lineno}, |
150 | 215 | 'arguments': frame_arguments, |
151 | 216 | 'locals': frame_locals}) |
@@ -403,36 +468,6 @@ def _CaptureRequestLogId(self): |
403 | 468 | self.breakpoint['labels'][ |
404 | 469 | labels.Breakpoint.REQUEST_LOG_ID] = request_log_id |
405 | 470 |
|
406 | | - @staticmethod |
407 | | - def _NormalizePath(path): |
408 | | - """Converts an absolute path to a relative one. |
409 | | -
|
410 | | - Python keeps almost all paths absolute. This is not what we actually |
411 | | - want to return. This loops through system paths (directories in which |
412 | | - Python will load modules). If "path" is relative to one of them, the |
413 | | - directory prefix is removed. |
414 | | -
|
415 | | - Args: |
416 | | - path: absolute path to normalize (relative paths will not be altered) |
417 | | -
|
418 | | - Returns: |
419 | | - Relative path if "path" is within one of the sys.path directories or |
420 | | - the input otherwise. |
421 | | - """ |
422 | | - path = os.path.normpath(path) |
423 | | - |
424 | | - for sys_path in sys.path: |
425 | | - if not sys_path: |
426 | | - continue |
427 | | - |
428 | | - # Append '/' at the end of the path if it's not there already. |
429 | | - sys_path = os.path.join(sys_path, '') |
430 | | - |
431 | | - if path.startswith(sys_path): |
432 | | - return path[len(sys_path):] |
433 | | - |
434 | | - return path |
435 | | - |
436 | 471 |
|
437 | 472 | class LogCollector(object): |
438 | 473 | """Captures minimal application snapshot and logs it to application log. |
@@ -489,7 +524,10 @@ def Log(self, frame): |
489 | 524 | self._definition.get('logMessageFormat', ''), |
490 | 525 | self._EvaluateExpressions(frame)) |
491 | 526 |
|
492 | | - self._log_message('LOGPOINT: ' + message) |
| 527 | + self._log_message('LOGPOINT: ' + message, extra={ |
| 528 | + 'cdbg_pathname': NormalizePath(frame.f_code.co_filename), |
| 529 | + 'cdbg_lineno': frame.f_lineno |
| 530 | + }) |
493 | 531 | return None |
494 | 532 |
|
495 | 533 | def _EvaluateExpressions(self, frame): |
|
0 commit comments