diff options
Diffstat (limited to 'repo_trace.py')
| -rw-r--r-- | repo_trace.py | 110 |
1 files changed, 4 insertions, 106 deletions
diff --git a/repo_trace.py b/repo_trace.py index 0ff3b694..7be0c045 100644 --- a/repo_trace.py +++ b/repo_trace.py | |||
| @@ -15,128 +15,26 @@ | |||
| 15 | """Logic for tracing repo interactions. | 15 | """Logic for tracing repo interactions. |
| 16 | 16 | ||
| 17 | Activated via `repo --trace ...` or `REPO_TRACE=1 repo ...`. | 17 | Activated via `repo --trace ...` or `REPO_TRACE=1 repo ...`. |
| 18 | |||
| 19 | Temporary: Tracing is always on. Set `REPO_TRACE=0` to turn off. | ||
| 20 | To also include trace outputs in stderr do `repo --trace_to_stderr ...` | ||
| 21 | """ | 18 | """ |
| 22 | 19 | ||
| 23 | import sys | 20 | import sys |
| 24 | import os | 21 | import os |
| 25 | import tempfile | ||
| 26 | import time | ||
| 27 | from contextlib import ContextDecorator | ||
| 28 | 22 | ||
| 29 | # Env var to implicitly turn on tracing. | 23 | # Env var to implicitly turn on tracing. |
| 30 | REPO_TRACE = 'REPO_TRACE' | 24 | REPO_TRACE = 'REPO_TRACE' |
| 31 | 25 | ||
| 32 | # Temporarily set tracing to always on unless user expicitly sets to 0. | 26 | _TRACE = os.environ.get(REPO_TRACE) == '1' |
| 33 | _TRACE = os.environ.get(REPO_TRACE) != '0' | ||
| 34 | |||
| 35 | _TRACE_TO_STDERR = False | ||
| 36 | |||
| 37 | _TRACE_FILE = None | ||
| 38 | |||
| 39 | _TRACE_FILE_NAME = 'TRACE_FILE' | ||
| 40 | |||
| 41 | _MAX_SIZE = 5 # in mb | ||
| 42 | |||
| 43 | _NEW_COMMAND_SEP = '+++++++++++++++NEW COMMAND+++++++++++++++++++' | ||
| 44 | |||
| 45 | |||
| 46 | def IsStraceToStderr(): | ||
| 47 | return _TRACE_TO_STDERR | ||
| 48 | 27 | ||
| 49 | 28 | ||
| 50 | def IsTrace(): | 29 | def IsTrace(): |
| 51 | return _TRACE | 30 | return _TRACE |
| 52 | 31 | ||
| 53 | 32 | ||
| 54 | def SetTraceToStderr(): | ||
| 55 | global _TRACE_TO_STDERR | ||
| 56 | _TRACE_TO_STDERR = True | ||
| 57 | |||
| 58 | |||
| 59 | def SetTrace(): | 33 | def SetTrace(): |
| 60 | global _TRACE | 34 | global _TRACE |
| 61 | _TRACE = True | 35 | _TRACE = True |
| 62 | 36 | ||
| 63 | 37 | ||
| 64 | def _SetTraceFile(): | 38 | def Trace(fmt, *args): |
| 65 | global _TRACE_FILE | 39 | if IsTrace(): |
| 66 | _TRACE_FILE = _GetTraceFile() | 40 | print(fmt % args, file=sys.stderr) |
| 67 | |||
| 68 | |||
| 69 | class Trace(ContextDecorator): | ||
| 70 | |||
| 71 | def _time(self): | ||
| 72 | """Generate nanoseconds of time in a py3.6 safe way""" | ||
| 73 | return int(time.time()*1e+9) | ||
| 74 | |||
| 75 | def __init__(self, fmt, *args, first_trace=False): | ||
| 76 | if not IsTrace(): | ||
| 77 | return | ||
| 78 | self._trace_msg = fmt % args | ||
| 79 | |||
| 80 | if not _TRACE_FILE: | ||
| 81 | _SetTraceFile() | ||
| 82 | |||
| 83 | if first_trace: | ||
| 84 | _ClearOldTraces() | ||
| 85 | self._trace_msg = '%s %s' % (_NEW_COMMAND_SEP, self._trace_msg) | ||
| 86 | |||
| 87 | |||
| 88 | def __enter__(self): | ||
| 89 | if not IsTrace(): | ||
| 90 | return self | ||
| 91 | |||
| 92 | print_msg = f"PID: {os.getpid()} START: {self._time()} :" + self._trace_msg + '\n' | ||
| 93 | |||
| 94 | with open(_TRACE_FILE, 'a') as f: | ||
| 95 | print(print_msg, file=f) | ||
| 96 | |||
| 97 | if _TRACE_TO_STDERR: | ||
| 98 | print(print_msg, file=sys.stderr) | ||
| 99 | |||
| 100 | return self | ||
| 101 | |||
| 102 | def __exit__(self, *exc): | ||
| 103 | if not IsTrace(): | ||
| 104 | return False | ||
| 105 | |||
| 106 | print_msg = f"PID: {os.getpid()} END: {self._time()} :" + self._trace_msg + '\n' | ||
| 107 | |||
| 108 | with open(_TRACE_FILE, 'a') as f: | ||
| 109 | print(print_msg, file=f) | ||
| 110 | |||
| 111 | if _TRACE_TO_STDERR: | ||
| 112 | print(print_msg, file=sys.stderr) | ||
| 113 | |||
| 114 | return False | ||
| 115 | |||
| 116 | |||
| 117 | def _GetTraceFile(): | ||
| 118 | """Get the trace file or create one.""" | ||
| 119 | # TODO: refactor to pass repodir to Trace. | ||
| 120 | repo_dir = os.path.dirname(os.path.dirname(__file__)) | ||
| 121 | trace_file = os.path.join(repo_dir, _TRACE_FILE_NAME) | ||
| 122 | print('Trace outputs in %s' % trace_file) | ||
| 123 | return trace_file | ||
| 124 | |||
| 125 | def _ClearOldTraces(): | ||
| 126 | """Clear traces from old commands if trace file is too big. | ||
| 127 | |||
| 128 | Note: If the trace file contains output from two `repo` | ||
| 129 | commands that were running at the same time, this | ||
| 130 | will not work precisely. | ||
| 131 | """ | ||
| 132 | if os.path.isfile(_TRACE_FILE): | ||
| 133 | while os.path.getsize(_TRACE_FILE)/(1024*1024) > _MAX_SIZE: | ||
| 134 | temp = tempfile.NamedTemporaryFile(mode='w', delete=False) | ||
| 135 | with open(_TRACE_FILE, 'r', errors='ignore') as fin: | ||
| 136 | trace_lines = fin.readlines() | ||
| 137 | for i , l in enumerate(trace_lines): | ||
| 138 | if 'END:' in l and _NEW_COMMAND_SEP in l: | ||
| 139 | temp.writelines(trace_lines[i+1:]) | ||
| 140 | break | ||
| 141 | temp.close() | ||
| 142 | os.replace(temp.name, _TRACE_FILE) | ||
