truncate_net_log.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. #!/usr/bin/env python
  2. # Copyright 2017 The Chromium Authors. All rights reserved.
  3. # Use of this source code is governed by a BSD-style license that can be
  4. # found in the LICENSE file.
  5. import os
  6. import re
  7. import sys
  8. kUsage = '''Usage: truncate_net_log.py INPUT_FILE OUTPUT_FILE TRUNCATED_SIZE
  9. Creates a smaller version of INPUT_FILE (which is a chrome-net-export-log.json
  10. formatted NetLog file) and saves it to OUTPUT_FILE. Note that this works by
  11. reading the file line by line and not fully parsing the JSON, so it must match
  12. the exact format (whitespace and all).
  13. File truncation is done by dropping the oldest events and keeping everything
  14. else.
  15. Parameters:
  16. INPUT_FILE:
  17. Path to net-export JSON file
  18. OUTPUT_FILE:
  19. Path to save truncated file to
  20. TRUNCATED_SIZE:
  21. The desired (approximate) size for the truncated file. May use a suffix to
  22. indicate units. Examples:
  23. 2003 --> 2003 bytes
  24. 100K --> 100 KiB
  25. 8M --> 8 MiB
  26. 1.5m --> 1.5 MiB
  27. '''
  28. def get_file_size(path):
  29. '''Returns the filesize of |path| in bytes'''
  30. return os.stat(path).st_size
  31. def truncate_log_file(in_path, out_path, desired_size):
  32. '''Copies |in_path| to |out_path| such that it is approximately
  33. |desired_size| bytes large. This is accomplished by dropping the oldest
  34. events first. The final file size may not be exactly |desired_size| as only
  35. complete event lines are skipped.'''
  36. orig_size = get_file_size(in_path)
  37. bytes_to_truncate = orig_size - desired_size
  38. # This variable is True if the current line being processed is an Event line.
  39. inside_events = False
  40. with open(out_path, 'w') as out_file:
  41. with open(in_path, 'r') as in_file:
  42. for line in in_file:
  43. # The final line before polledData closes the events array, and hence
  44. # ends in "],". The check for polledData is more for documentation
  45. # sake.
  46. if inside_events and (line.startswith('"polledData": {' or
  47. line.endswith('],\n'))):
  48. inside_events = False
  49. # If this is an event line and need to drop more bytes, go ahead and
  50. # skip the line. Otherwise copy it to the output file.
  51. if inside_events and bytes_to_truncate > 0:
  52. bytes_to_truncate -= len(line)
  53. else:
  54. out_file.write(line)
  55. # All lines after this are events (up until the closing square
  56. # bracket).
  57. if line.startswith('"events": ['):
  58. inside_events = True
  59. sys.stdout.write(
  60. 'Truncated file from %d to %d bytes\n' % (orig_size,
  61. get_file_size(out_path)))
  62. def parse_filesize_str(filesize_str):
  63. '''Parses a string representation of a file size into a byte value, or None
  64. on failure'''
  65. filesize_str = filesize_str.lower()
  66. m = re.match('([0-9\.]+)([km]?)', filesize_str)
  67. if not m:
  68. return None
  69. # Try to parse as decimal (regex above accepts some invalid decimals too).
  70. float_value = 0.0
  71. try:
  72. float_value = float(m.group(1))
  73. except ValueError:
  74. return None
  75. kSuffixValueBytes = {
  76. 'k': 1024,
  77. 'm': 1024 * 1024,
  78. '': 1,
  79. }
  80. suffix = m.group(2)
  81. return int(float_value * kSuffixValueBytes[suffix])
  82. def main():
  83. if len(sys.argv) != 4:
  84. sys.stderr.write('ERROR: Requires 3 command line arguments\n')
  85. sys.stderr.write(kUsage)
  86. sys.exit(1)
  87. in_path = os.path.normpath(sys.argv[1])
  88. out_path = os.path.normpath(sys.argv[2])
  89. if in_path == out_path:
  90. sys.stderr.write('ERROR: OUTPUT_FILE must be different from INPUT_FILE\n')
  91. sys.stderr.write(kUsage)
  92. sys.exit(1)
  93. size_str = sys.argv[3]
  94. size_bytes = parse_filesize_str(size_str)
  95. if size_bytes is None:
  96. sys.stderr.write('ERROR: Could not parse TRUNCATED_SIZE: %s\n' % size_str)
  97. sys.stderr.write(kUsage)
  98. sys.exit(1)
  99. truncate_log_file(in_path, out_path, size_bytes)
  100. if __name__ == '__main__':
  101. main()