parser.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. # Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. from __future__ import print_function
  15. import collections
  16. import json
  17. import re
  18. import sys
  19. from collections import defaultdict
  20. from logger import tags
  21. import logger.logger as nvl
  22. LogLine = collections.namedtuple('LogLine', [
  23. 'full_string', # the complete line as a string
  24. 'worker', # the worker id
  25. 'token', # the token, i.e. ':::NVLOG'
  26. 'version_str', # the version string, e.g. 'v0.1.0'
  27. 'model', # the model, e.g. 'ncf'
  28. 'timestamp', # seconds as a float, e.g. 1234.567
  29. 'filename', # the which generated the log line, e.g. './convert.py'
  30. 'lineno', # the line in the file which generated the log line, e.g. 119
  31. 'tag', # the string tag
  32. 'value', # the parsed value associated with the tag, or None if no value
  33. 'epoch', # the epoch number of -1 if none
  34. 'iteration', # the interation number of -1 if none
  35. 'scope' # run, epoch, iteration, eval_iteration
  36. ])
  37. def get_dict_value(x):
  38. if isinstance(x, dict):
  39. return x
  40. return {"value": x}
  41. def get_value(x):
  42. if isinstance(x, dict):
  43. if "value" in x:
  44. return x.get("value")
  45. else:
  46. return x
  47. return x
  48. def get_named_value(x, name):
  49. if isinstance(x, dict):
  50. if name in x:
  51. return x.get(name)
  52. else:
  53. return None
  54. return x
  55. class NVLogParser(object):
  56. def __init__(self, token=nvl.NVLOGGER_TOKEN, version=nvl.NVLOGGER_VERSION):
  57. self.epoch = defaultdict(lambda: -1)
  58. self.iteration = defaultdict(lambda: -1)
  59. self.scope = defaultdict(lambda: 0)
  60. self.version = version
  61. self.token = token
  62. self.line_pattern = (
  63. '^'
  64. '([\d]?)' # optional worker id (0)
  65. '(' + token + ')' # mandatory token (1)
  66. 'v([\d]+\.[\d+]\.[\d+])[ ]' # mandatory version (2)
  67. '([A-Za-z0-9_]+)[ ]' # mandatory model (3)
  68. '([\d\.]+)[ ]' # mandatory timestamp (4)
  69. '\(([^: ]+)' # mandatory file (5)
  70. ':(\d+)\)[ ]' # mandatory lineno (6)
  71. '([A-Za-z0-9_]+)[ ]?' # mandatory tag (7)
  72. '(:\s+(.+))?' # optional value (8)
  73. '$'
  74. )
  75. # print(self.line_pattern)
  76. self.line_regex = re.compile(self.line_pattern, re.X)
  77. def string_to_logline(self, string):
  78. m = self.line_regex.match(string)
  79. if m is None:
  80. raise ValueError('does not match regex')
  81. args = [
  82. m.group(0), # full string
  83. ]
  84. # by default
  85. worker = m.group(1)
  86. if worker == "":
  87. worker = "(0)"
  88. args.append(worker)
  89. args.append(m.group(2)) # token
  90. args.append(m.group(3)) # version
  91. args.append(m.group(4)) # model
  92. try:
  93. ts = float(m.group(5)) # parse timestamp
  94. args.append(ts)
  95. except ValueError:
  96. raise ValueError('timestamp format incorrect')
  97. args.append(m.group(6)) # file name
  98. try:
  99. lineno = int(m.group(7)) # may raise error
  100. args.append(lineno)
  101. except ValueError:
  102. raise ValueError('line number format incorrect')
  103. tag = m.group(8)
  104. args.append(tag) # tag
  105. # 9th is ignored
  106. value = m.group(10)
  107. if value is not None:
  108. j = json.loads(value)
  109. args.append(j)
  110. else:
  111. # no Value
  112. args.append(None)
  113. # update processing state
  114. if tag == tags.TRAIN_EPOCH_START or tag == tags.TRAIN_EPOCH:
  115. self.epoch[worker] = get_named_value(value, tags.VALUE_EPOCH)
  116. self.scope[worker] = nvl.EPOCH_SCOPE
  117. self.iteration[worker] = -1
  118. if tag == tags.TRAIN_EPOCH_STOP:
  119. self.scope[worker] = nvl.RUN_SCOPE
  120. if tag == tags.TRAIN_ITER_START:
  121. self.iteration[worker] = get_named_value(value, tags.VALUE_ITERATION)
  122. self.scope[worker] = nvl.TRAIN_ITER_SCOPE
  123. if tag == tags.TRAIN_ITER_STOP:
  124. self.scope[worker] = nvl.EPOCH_SCOPE
  125. if tag == tags.PERF_IT_PER_SEC:
  126. self.scope[worker] = nvl.EPOCH_SCOPE
  127. if tag == tags.PERF_TIME_TO_TRAIN:
  128. self.scope[worker] = nvl.RUN_SCOPE
  129. args.append(self.epoch[worker])
  130. args.append(self.iteration[worker])
  131. args.append(self.scope[worker])
  132. return LogLine(*args)
  133. def parse_generator(self, gen):
  134. worker_loglines = defaultdict(list)
  135. loglines = []
  136. failed = []
  137. # state init for parsing
  138. self.epoch.clear()
  139. self.iteration.clear()
  140. self.scope.clear()
  141. for line in gen:
  142. line = line.strip()
  143. if line.find(self.token) == -1:
  144. continue
  145. try:
  146. ll = self.string_to_logline(line)
  147. worker_loglines[ll.worker].append(ll)
  148. loglines.append(ll)
  149. except ValueError as e:
  150. failed.append((line, str(e)))
  151. return loglines, failed, worker_loglines
  152. def parse_file(self, filename):
  153. with open(filename) as f:
  154. return self.parse_generator(f)
  155. if __name__ == '__main__':
  156. if len(sys.argv) != 2:
  157. print('usage: parser.py FILENAME')
  158. print(' tests parsing on the file.')
  159. sys.exit(1)
  160. filename = sys.argv[1]
  161. parser = NVLogParser()
  162. loglines, errors, worker_loglines = parser.parse_file(filename)
  163. print('Parsed {} log lines with {} errors.'.format(len(loglines), len(errors)))
  164. print('Found workers: {}.'.format(list(worker_loglines.keys())))
  165. if len(errors) > 0:
  166. print('Lines which failed to parse:')
  167. for line, error in errors:
  168. print(' Following line failed: {}'.format(error))
  169. print(line)
  170. if len(loglines) > 0:
  171. print('Lines which where parsed sucessfully:')
  172. for line in loglines:
  173. print(line.full_string, " ---> ", line.epoch, line.iteration, line.scope)