Repository: bitcoin/bitcoin
File: test/functional/combine_logs.py (Python)

Code before:
        colors["node0"] = "\033[0;34m"  # BLUE
        colors["node1"] = "\033[0;32m"  # GREEN
        colors["node2"] = "\033[0;31m"  # RED
        colors["node3"] = "\033[0;33m"  # YELLOW
        colors["reset"] = "\033[0m"  # Reset font color

    log_events = read_logs(testdir)

    if args.html:
        print_logs_html(log_events)
    else:
        print_logs_plain(log_events, colors)
        print_node_warnings(testdir, colors)


def read_logs(tmp_dir):
    """Reads log files.

    Delegates to generator function get_log_events() to provide individual log events
    for each of the input log files."""

    # Find out what the folder is called that holds the debug.log file
    glob = pathlib.Path(tmp_dir).glob('node0/**/debug.log')
    path = next(glob, None)
    if path:
        assert next(glob, None) is None #  more than one debug.log, should never happen
        chain = re.search(r'node0/(.+?)/debug\.log$', path.as_posix()).group(1)  # extract the chain name
    else:
        chain = 'regtest'  # fallback to regtest (should only happen when none exists)

    files = [("test", "%s/test_framework.log" % tmp_dir)]
    for i in itertools.count():
        logfile = "{}/node{}/{}/debug.log".format(tmp_dir, i, chain)
        if not os.path.isfile(logfile):
            break
        files.append(("node%d" % i, logfile))

    return heapq.merge(*[get_log_events(source, f) for source, f in files])


def print_node_warnings(tmp_dir, colors):
    """Print nodes' errors and warnings"""

    warnings = []
    for stream in ['stdout', 'stderr']:
        for i in itertools.count():
            folder = "{}/node{}/{}".format(tmp_dir, i, stream)
            if not os.path.isdir(folder):
                break
            for (_, _, fns) in os.walk(folder):
                for fn in fns:

Review comment (bug):
nit: "should never happen" -> but it just did, so can we either provide more context or remove the statement that was just invalidated?
```suggestion
        assert len(debug_logs) == 1, (
            'Found multiple debug.log files, expected only one:\n\t' + 
            '\n\t'.join(str(f) for f in debug_logs)
        )
```

Code after:
        colors["node0"] = "\033[0;34m"  # BLUE
        colors["node1"] = "\033[0;32m"  # GREEN
        colors["node2"] = "\033[0;31m"  # RED
        colors["node3"] = "\033[0;33m"  # YELLOW
        colors["reset"] = "\033[0m"  # Reset font color

    log_events = read_logs(testdir)

    if args.html:
        print_logs_html(log_events)
    else:
        print_logs_plain(log_events, colors)
        print_node_warnings(testdir, colors)


def read_logs(tmp_dir):
    """Reads log files.

    Delegates to generator function get_log_events() to provide individual log events
    for each of the input log files."""

    # Find out what the folder is called that holds node 0's debug.log file
    debug_logs = list(pathlib.Path(tmp_dir).glob('node0/**/debug.log'))
    if len(debug_logs) > 0:
        assert len(debug_logs) < 2, 'Max one debug.log is supported, ' \
            'found several:\n\t' + '\n\t'.join([str(f) for f in debug_logs])
        path = debug_logs[0]
        chain = re.search(r'node0/(.+?)/debug\.log$', path.as_posix()).group(1)  # extract the chain name
    else:
        chain = 'regtest'  # fallback to regtest (should only happen when none exists)

    files = [("test", "%s/test_framework.log" % tmp_dir)]
    for i in itertools.count():
        logfile = "{}/node{}/{}/debug.log".format(tmp_dir, i, chain)
        if not os.path.isfile(logfile):
            break
        files.append(("node%d" % i, logfile))

    return heapq.merge(*[get_log_events(source, f) for source, f in files])


def print_node_warnings(tmp_dir, colors):
    """Print nodes' errors and warnings"""

    warnings = []
    for stream in ['stdout', 'stderr']:
        for i in itertools.count():
            folder = "{}/node{}/{}".format(tmp_dir, i, stream)
            if not os.path.isdir(folder):
                break
            for (_, _, fns) in os.walk(folder):