clean_logs.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. import glob, re, os.path, textwrap
  2. from colorama import Fore, Back, Style
  3. def target_prefix(dir):
  4. if dir.startswith("net"):
  5. return "net"
  6. elif dir.startswith("serial"):
  7. return "serial"
  8. elif dir.startswith("util"):
  9. return "util"
  10. elif dir.startswith("runtime"):
  11. return "runtime"
  12. elif dir.startswith("zk"):
  13. return "zk"
  14. elif dir.startswith("raft"):
  15. return "raft"
  16. elif dir.startswith("sdk/src/crypto"):
  17. return "sdk::crypto"
  18. elif dir.startswith("sdk"):
  19. return "sdk"
  20. elif dir.startswith("contract/dao"):
  21. return "dao"
  22. elif dir.startswith("contract/money"):
  23. return "money"
  24. elif dir.startswith("rpc"):
  25. return "rpc"
  26. elif dir.startswith("system"):
  27. return "system"
  28. elif dir.startswith("dht"):
  29. return "dht"
  30. elif dir.startswith("consensus"):
  31. return "consensus"
  32. elif dir.startswith("zkas"):
  33. return "zkas"
  34. elif dir.startswith("blockchain"):
  35. return "blockchain"
  36. elif dir.startswith("wallet"):
  37. return "wallet"
  38. else:
  39. assert not dir or dir == "tx"
  40. return ""
  41. def target_suffix(prefix, base):
  42. if prefix in ("dao", "money"):
  43. # Just shorten the target to simply "dao" or "money"
  44. # We don't need the fine grained details
  45. return ""
  46. elif base in ("mod.rs", "lib.rs"):
  47. # Just use the module name as the target with these files
  48. return ""
  49. # Otherwise just use the filename as the target suffix
  50. return base.removesuffix(".rs")
  51. def log_target(fname):
  52. dir, base = os.path.dirname(fname), os.path.basename(fname)
  53. prefix = target_prefix(dir)
  54. suffix = target_suffix(prefix, base)
  55. # you don't need :: when the suffix is empty
  56. if not suffix and not prefix:
  57. return ""
  58. if not suffix:
  59. return prefix
  60. if not prefix:
  61. return suffix
  62. return f"{prefix}::{suffix}"
  63. def replace(fname, contents):
  64. target = log_target(fname)
  65. # You can debug like this:
  66. #if target != "net::protocol_seed":
  67. # return None
  68. print(f"Replacing {target}" + " "*(40 - len(target)) + f"[{fname}]")
  69. result = []
  70. lines = contents.split("\n")
  71. i = 0
  72. while i < len(lines):
  73. line = lines[i]
  74. # Line number
  75. ln = lambda: i + 1
  76. # only used for debug output
  77. old_text = None
  78. new_text = None
  79. # This is used as a debug goto
  80. is_modified = False
  81. log_level = None
  82. if "trace!(" in line:
  83. log_level = "trace"
  84. elif "debug!(" in line:
  85. log_level = "debug"
  86. elif "info!(" in line:
  87. log_level = "info"
  88. elif "warn!(" in line:
  89. log_level = "warn"
  90. elif "error!(" in line:
  91. log_level = "error"
  92. if log_level is not None:
  93. # Walk backwards to find the function name
  94. # Range is (i, 0]
  95. function_name = None
  96. for j in range(i - 1, -1, -1):
  97. past_line = lines[j]
  98. if (match := re.search(
  99. r"fn ([\w]+)(<[\w: \+']+>)?\(",
  100. past_line
  101. )):
  102. function_name = match.group(1)
  103. break
  104. assert function_name is not None
  105. local_target = target
  106. if target in (
  107. "consensus::protocol_proposal",
  108. "consensus::protocol_sync",
  109. "consensus::protocol_sync_consensus",
  110. "consensus::protocol_tx",
  111. "runtime::db",
  112. "net::hosts",
  113. "net::protocol_address",
  114. "net::protocol_ping",
  115. "net::protocol_seed",
  116. "net::protocol_version",
  117. "net::p2p",
  118. "net::message_subscriber",
  119. "net::channel",
  120. ):
  121. local_target += f"::{function_name}()"
  122. # No target exists for this file at all. Just ignore these
  123. # We would normally delete any target set for these files
  124. # but so far we have none of them, so just ignore them.
  125. if not target:
  126. print(
  127. " "
  128. + Back.RED + "Skip [no target]:" + Style.RESET_ALL
  129. + f" {line}"
  130. )
  131. # Single lines with a target that's a constant or string
  132. elif re.search(rf'{log_level}!\(target: ([\w]+|"[\w:\-\(\)]+"),', line):
  133. old_text = f"{ln()}: {line}"
  134. line = re.sub(
  135. r'target: ([\w]+|"[\w:\-\(\)]+"),',
  136. f'target: "{local_target}",',
  137. line
  138. )
  139. is_modified = True
  140. new_text = f"{ln()}: {line}"
  141. # Normal single lines with no target set
  142. elif f'{log_level}!("' in line:
  143. old_text = f"{ln()}: {line}"
  144. #print(f" No target: {line}")
  145. line = line.replace(f'{log_level}!(',
  146. f'{log_level}!(target: "{local_target}", ')
  147. is_modified = True
  148. new_text = f"{ln()}: {line}"
  149. # Multiline logs
  150. # We read the next line and check if there's a target set or not
  151. else:
  152. assert re.search(rf"{log_level}!\($", line)
  153. old_text = f"{ln()}: {line}"
  154. new_text = f"{ln()}: {line}"
  155. result.append(line)
  156. i += 1
  157. assert i < len(lines)
  158. line = lines[i]
  159. old_text += f"\n{ln()}: {line}"
  160. # Constants or target strings set
  161. if re.search(r'target: ([\w]+|"[\w:\-\(\)]+"),', line):
  162. line = re.sub(
  163. r'target: ([\w]+|"[\w:\-\(\)]+"),',
  164. f'target: "{local_target}",',
  165. line
  166. )
  167. new_text += f"\n{ln()}: {line}"
  168. # Multi-line logs with no target set
  169. # Insert an extra line with the target
  170. else:
  171. assert re.search('^"', line)
  172. leading_space = lambda line: len(line) - len(line.lstrip())
  173. added_line = (" "*leading_space(line)
  174. + f'target: "{local_target}",')
  175. result.append(added_line)
  176. new_text += f"\n{ln()}: {added_line}\n{ln() + 1}: {line}"
  177. is_modified = True
  178. if is_modified:
  179. assert old_text is not None and new_text is not None
  180. print(
  181. Fore.RED
  182. + textwrap.indent(old_text, " < ")
  183. + Style.RESET_ALL
  184. )
  185. print(
  186. Fore.GREEN
  187. + textwrap.indent(new_text, " > ")
  188. + Style.RESET_ALL
  189. )
  190. print()
  191. result.append(line)
  192. i += 1
  193. return "\n".join(result)
  194. def main():
  195. for fname in glob.glob("**/*.rs", root_dir="src/", recursive=True):
  196. with open(f"src/{fname}", "r") as f:
  197. contents = f.read()
  198. if (contents := replace(fname, contents)) is None:
  199. # Skip this file
  200. continue
  201. # Uncomment this to apply the changes
  202. with open(f"src/{fname}", "w") as f:
  203. f.write(contents)
  204. if __name__ == "__main__":
  205. main()