63 lines
2.8 KiB
Python
63 lines
2.8 KiB
Python
|
|
# -*- coding: utf-8 -*-
|
||
|
|
# 에디터 로그(Editor.log)를 기준으로 오류를 센다. 콘솔 조회(get_console_logs)는 엔진이 직접 찍는 오류(BatchDrawCommand 등)를 놓쳤다.
|
||
|
|
# python logscan.py mark 지금 로그 크기를 적어 둔다
|
||
|
|
# python logscan.py scan [이름] 적어 둔 자리부터 끝까지에서 오류 머리줄을 센다
|
||
|
|
import io, os, re, sys, collections
|
||
|
|
|
||
|
|
LOG = r"C:\Users\sw\AppData\Local\Unity\Editor\Editor.log"
|
||
|
|
MARK = os.path.join(os.path.dirname(os.path.abspath(__file__)), "logscan.mark")
|
||
|
|
|
||
|
|
# 도구가 내는 메시지(게임 오류 아님)
|
||
|
|
IGNORE = ("Failed to handle /api/exec request",)
|
||
|
|
|
||
|
|
def read_from(offset):
|
||
|
|
with open(LOG, "rb") as f:
|
||
|
|
f.seek(offset)
|
||
|
|
return f.read().decode("utf-8", errors="replace")
|
||
|
|
|
||
|
|
def main():
|
||
|
|
cmd = sys.argv[1] if len(sys.argv) > 1 else "scan"
|
||
|
|
if cmd == "mark":
|
||
|
|
size = os.path.getsize(LOG)
|
||
|
|
io.open(MARK, "w").write(str(size))
|
||
|
|
print("mark", size)
|
||
|
|
return
|
||
|
|
offset = int(io.open(MARK).read().strip()) if os.path.exists(MARK) else 0
|
||
|
|
size = os.path.getsize(LOG)
|
||
|
|
if offset > size:
|
||
|
|
offset = 0 # 에디터가 다시 떠서 로그가 새로 시작됨
|
||
|
|
text = read_from(offset)
|
||
|
|
lines = text.split("\n")
|
||
|
|
heads = collections.Counter()
|
||
|
|
unloads = 0
|
||
|
|
for i, l in enumerate(lines):
|
||
|
|
l = l.rstrip("\r")
|
||
|
|
if l.startswith("Unloading ") and "unused Assets" in l:
|
||
|
|
unloads += 1
|
||
|
|
head = None
|
||
|
|
if l.startswith("A BatchDrawCommand was submitted"):
|
||
|
|
head = "A BatchDrawCommand was submitted with an invalid Batch, Mesh, or Material ID"
|
||
|
|
elif l.startswith(("UnityEngine.Debug:LogError", "UnityEngine.Debug:LogException", "UnityEngine.Debug:LogAssertion", "UnityEngine.Debug:LogErrorFormat")):
|
||
|
|
j = i
|
||
|
|
while j > 0 and not lines[j].startswith("UnityEngine.Debug:ExtractStackTraceNoAlloc"):
|
||
|
|
j -= 1
|
||
|
|
head = lines[j - 1].strip()[:200] if j > 0 else "?"
|
||
|
|
elif re.match(r"^[A-Za-z_\.]*Exception: ", l) or re.match(r"^[A-Za-z_\.]+Exception$", l.strip()):
|
||
|
|
head = l.strip()[:200]
|
||
|
|
elif l.startswith(("Assertion failed", "Some objects were not cleaned up", "Can't destroy", "Destroying GameObjects immediately")):
|
||
|
|
head = l.strip()[:200]
|
||
|
|
elif "error CS" in l:
|
||
|
|
head = l.strip()[:200]
|
||
|
|
if head is None:
|
||
|
|
continue
|
||
|
|
if any(x in head for x in IGNORE):
|
||
|
|
continue
|
||
|
|
heads[re.sub(r"\d+", "#", head)] += 1
|
||
|
|
name = sys.argv[2] if len(sys.argv) > 2 else ""
|
||
|
|
total = sum(heads.values())
|
||
|
|
print("LOGSCAN %s bytes=%d lines=%d unloadUnused=%d errors=%d" % (name, size - offset, len(lines), unloads, total))
|
||
|
|
for h, c in heads.most_common(20):
|
||
|
|
print(" x%d %s" % (c, h))
|
||
|
|
|
||
|
|
main()
|