Repository: microsoft/nni
File: nni/runtime/log.py (Python)

Code before:
    def __init__(self):
        fmt = '[%(asctime)s] (%(threadName)s:%(name)s) %(message)s'
        datefmt = '%Y-%m-%d %H:%M:%S'
        super().__init__(fmt, datefmt)

    def formatMessage(self, record: LogRecord) -> str:
        if '${' in record.message:
            message = string.Template(record.message).safe_substitute(_colorful)
        else:
            message = record.message

        if record.levelno >= logging.ERROR:
            level = colorama.Fore.RED + 'ERROR: '
        elif record.levelno >= logging.WARNING:
            level = colorama.Fore.YELLOW + 'WARNING: '
        elif record.levelno >= logging.INFO:
            level = colorama.Fore.GREEN
        else:
            level = colorama.Fore.BLUE

        content = level + message + colorama.Style.RESET_ALL
        if record.levelno >= logging.INFO:
            return f'[{record.asctime}] {content}'
        elif record.threadName == 'MainThread':
            return f'[{record.asctime}] {record.name} {content}'
        else:
            return f'[{record.asctime}] {record.threadName}:{record.name} {content}'

class _LogFileFormatter(Formatter):
    def __init__(self):
        fmt = '[%(asctime)s] (%(threadName)s:%(name)s) %(message)s'
        datefmt = '%Y-%m-%d %H:%M:%S'
        super().__init__(fmt, datefmt)

    def formatMessage(self, record: LogRecord) -> str:
        if '${' in record.message:
            message = string.Template(record.message).safe_substitute(_colorless)
        else:
            message = record.message

        if record.threadName == 'MainThread':
            return f'[{record.asctime}] {record.levelname} ({record.name}) {message}'
        else:
            return f'[{record.asctime}] {record.levelname} ({record.threadName}:{record.name}) {message}'


## legacy part ##

handlers = {}

log_format = '[%(asctime)s] %(levelname)s (%(name)s/%(threadName)s) %(message)s'

Review comment (bug):
what if `log_file` or `level` mismatch with current existed handler? or why not raise error here?

Code after:

def silence_stdout() -> None:
    """
    Stop NNI from printing to stdout.

    By default NNI prints log messages of ``INFO`` and higher levels to console.
    Use this function if you want a clean stdout, or if you want to handle logs by yourself.
    """
    handler = _handlers.pop('_stdout_', None)
    if handler is not None:
        _root_logger.removeHandler(handler)

def start_experiment_logging(experiment_id: str, log_file: Path, level: str) -> None:
    """
    Register the log file handler for an experiment's ``experiment.log``.

    This function should be invoked on starting experiment.
    We don't want to create the experiment folder if the user does not launch it.

    If there are multiple experiments running concurrently,
    log messages will be written to all running experiments' log files.

    It is safe to call it multiple times.
    """
    if experiment_id in _handlers:
        return

    handler = FileHandler(log_file, encoding='utf_8')
    handler.setFormatter(_LogFileFormatter())
    handler.setLevel(level.upper())

    _handlers[experiment_id] = handler
    _root_logger.addHandler(handler)

def stop_experiment_logging(experiment_id: str) -> None:
    """
    Unregister an experiment's ``experiment.log`` handler.
    """
    handler = _handlers.pop(experiment_id, None)
    if handler is not None:
        _root_logger.removeHandler(handler)

def enable_global_logging(enable: bool = True) -> None:
    """
    Let NNI to handle all logs. Useful for debugging.

    By default only NNI's logs are printed to stdout and saved to ``nni-experiments`` log files.
    The function will extend these settings to all modules' logs.

    Use ``enable_global_logging(False)`` to reverse it.
    The log level of root logger will not be reversed though.