267 lines
7.8 KiB
Python
267 lines
7.8 KiB
Python
# Licensed to the Apache Software Foundation (ASF) under one
|
|
# or more contributor license agreements. See the NOTICE file
|
|
# distributed with this work for additional information
|
|
# regarding copyright ownership. The ASF licenses this file
|
|
# to you under the Apache License, Version 2.0 (the
|
|
# "License"); you may not use this file except in compliance
|
|
# with the License. You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing,
|
|
# software distributed under the License is distributed on an
|
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
# KIND, either express or implied. See the License for the
|
|
# specific language governing permissions and limitations
|
|
# under the License.
|
|
"""Logging interface in MetaSchedule"""
|
|
|
|
import logging
|
|
import logging.config
|
|
import os
|
|
import os.path as osp
|
|
from collections.abc import Callable
|
|
from logging import Logger
|
|
from typing import Any
|
|
|
|
|
|
def get_logger(name: str) -> Logger:
|
|
"""Create or get a logger by its name. This is essentially a wrapper of python's native logger.
|
|
|
|
Parameters
|
|
----------
|
|
name : str
|
|
The name of the logger.
|
|
|
|
Returns
|
|
-------
|
|
logger : Logger
|
|
The logger instance.
|
|
"""
|
|
return logging.getLogger(name)
|
|
|
|
|
|
def get_logging_func(logger: Logger) -> Callable[[int, str, int, str], None] | None:
|
|
"""Get the logging function.
|
|
|
|
Parameters
|
|
----------
|
|
logger : Logger
|
|
The logger instance.
|
|
Returns
|
|
-------
|
|
result : Optional[Callable]
|
|
The function to do the specified level of logging.
|
|
"""
|
|
if logger is None:
|
|
return None
|
|
|
|
level2log = {
|
|
logging.DEBUG: logger.debug,
|
|
logging.INFO: logger.info,
|
|
logging.WARNING: logger.warning,
|
|
logging.ERROR: logger.error,
|
|
# logging.FATAL not included
|
|
}
|
|
|
|
def logging_func(level: int, filename: str, lineo: int, msg: str):
|
|
if level < 0: # clear the output in notebook / console
|
|
from IPython.display import ( # type: ignore # pylint: disable=import-outside-toplevel
|
|
clear_output,
|
|
)
|
|
|
|
clear_output(wait=True)
|
|
else:
|
|
level2log[level](f"[{os.path.basename(filename)}:{lineo}] " + msg)
|
|
|
|
return logging_func
|
|
|
|
|
|
def create_loggers(
|
|
log_dir: str,
|
|
params: list[dict[str, Any]],
|
|
logger_config: dict[str, Any] | None = None,
|
|
disable_existing_loggers: bool = False,
|
|
):
|
|
"""Create loggers from configuration"""
|
|
if logger_config is None:
|
|
config = {}
|
|
else:
|
|
config = logger_config
|
|
|
|
config.setdefault("loggers", {})
|
|
config.setdefault("handlers", {})
|
|
config.setdefault("formatters", {})
|
|
|
|
global_logger_name = "tvm.s_tir.meta_schedule"
|
|
global_logger = logging.getLogger(global_logger_name)
|
|
if global_logger.level is logging.NOTSET:
|
|
global_logger.setLevel(logging.DEBUG)
|
|
console_logging_level = logging._levelToName[ # pylint: disable=protected-access
|
|
global_logger.level
|
|
]
|
|
|
|
config["loggers"].setdefault(
|
|
global_logger_name,
|
|
{
|
|
"level": logging.DEBUG,
|
|
"handlers": [handler.get_name() for handler in global_logger.handlers]
|
|
+ [global_logger_name + ".console", global_logger_name + ".file"],
|
|
"propagate": False,
|
|
},
|
|
)
|
|
config["loggers"].setdefault(
|
|
"{logger_name}",
|
|
{
|
|
"level": "DEBUG",
|
|
"handlers": [
|
|
"{logger_name}.file",
|
|
],
|
|
"propagate": False,
|
|
},
|
|
)
|
|
config["handlers"].setdefault(
|
|
global_logger_name + ".console",
|
|
{
|
|
"class": "logging.StreamHandler",
|
|
"stream": "ext://sys.stdout",
|
|
"formatter": "tvm.s_tir.meta_schedule.standard_formatter",
|
|
"level": console_logging_level,
|
|
},
|
|
)
|
|
config["handlers"].setdefault(
|
|
global_logger_name + ".file",
|
|
{
|
|
"class": "logging.FileHandler",
|
|
"filename": "{log_dir}/" + __name__ + ".task_scheduler.log",
|
|
"mode": "a",
|
|
"level": "DEBUG",
|
|
"formatter": "tvm.s_tir.meta_schedule.standard_formatter",
|
|
},
|
|
)
|
|
config["handlers"].setdefault(
|
|
"{logger_name}.file",
|
|
{
|
|
"class": "logging.FileHandler",
|
|
"filename": "{log_dir}/{logger_name}.log",
|
|
"mode": "a",
|
|
"level": "DEBUG",
|
|
"formatter": "tvm.s_tir.meta_schedule.standard_formatter",
|
|
},
|
|
)
|
|
config["formatters"].setdefault(
|
|
"tvm.s_tir.meta_schedule.standard_formatter",
|
|
{
|
|
"format": "%(asctime)s [%(levelname)s] %(message)s",
|
|
"datefmt": "%Y-%m-%d %H:%M:%S",
|
|
},
|
|
)
|
|
|
|
# set up dictConfig loggers
|
|
p_config = {"version": 1, "disable_existing_loggers": disable_existing_loggers}
|
|
for k, v in config.items():
|
|
if k in ["formatters", "handlers", "loggers"]:
|
|
p_config[k] = _batch_parameterize_config(v, params) # type: ignore
|
|
else:
|
|
p_config[k] = v
|
|
logging.config.dictConfig(p_config)
|
|
|
|
# check global logger
|
|
if global_logger.level not in [logging.DEBUG, logging.INFO]:
|
|
global_logger.warning(
|
|
"Logging level set to %s, please set to logging.INFO"
|
|
" or logging.DEBUG to view full log.",
|
|
logging._levelToName[global_logger.level], # pylint: disable=protected-access
|
|
)
|
|
global_logger.info("Logging directory: %s", log_dir)
|
|
|
|
|
|
def _batch_parameterize_config(
|
|
config: dict[str, Any],
|
|
params: list[dict[str, str]],
|
|
) -> dict[str, Any]:
|
|
"""Parameterize the given configuration with multiple parameters sets.
|
|
|
|
Parameters
|
|
----------
|
|
config : Dict[str, Any]
|
|
The given config dict.
|
|
Params : List[Dict[str, str]]
|
|
List of the given multiple parameters sets.
|
|
|
|
Returns
|
|
-------
|
|
result : Dict[str, Any]
|
|
The parameterized configuration.
|
|
"""
|
|
results = {}
|
|
for name, cfg in config.items():
|
|
for p in params:
|
|
p_name = name.format(**p)
|
|
if p_name not in results:
|
|
p_cfg = _parameterize_config(cfg, p)
|
|
results[p_name] = p_cfg
|
|
return results
|
|
|
|
|
|
def _parameterize_config(
|
|
config: dict[str, Any],
|
|
params: dict[str, str],
|
|
) -> dict[str, Any]:
|
|
"""Parameterize the given configuration.
|
|
|
|
Parameters
|
|
----------
|
|
config : Dict[str, Any]
|
|
The given config dict.
|
|
Params : Dict[str, str]
|
|
The given parameters.
|
|
|
|
Returns
|
|
-------
|
|
result : Dict[str, Any]
|
|
The parameterized configuration.
|
|
"""
|
|
result = {}
|
|
for k, v in config.items():
|
|
if isinstance(k, str):
|
|
k = k.format(**params)
|
|
if isinstance(v, str):
|
|
v = v.format(**params)
|
|
elif isinstance(v, dict):
|
|
v = _parameterize_config(v, params)
|
|
elif isinstance(v, list):
|
|
v = [t.format(**params) for t in v]
|
|
result[k] = v
|
|
return result
|
|
|
|
|
|
def get_loggers_from_work_dir(
|
|
work_dir: str,
|
|
task_names: list[str],
|
|
) -> list[Logger]:
|
|
"""Create loggers from work directory
|
|
|
|
Parameters
|
|
----------
|
|
work_dir : str
|
|
The work directory.
|
|
task_names : List[str]
|
|
The list of task names.
|
|
|
|
Returns
|
|
-------
|
|
loggers : List[Logger]
|
|
The list of loggers.
|
|
"""
|
|
log_dir = osp.join(work_dir, "logs")
|
|
os.makedirs(log_dir, exist_ok=True)
|
|
pattern = __name__ + ".task_{i:0" + f"{len(str(len(task_names) - 1))}" + "d}_{name}"
|
|
# Very long names may need be clipped to prevent os errors, we use the first 100 characters.
|
|
loggers = [pattern.format(i=i, name=name[:100]) for i, name in enumerate(task_names)]
|
|
create_loggers(
|
|
log_dir=log_dir,
|
|
params=[{"log_dir": log_dir, "logger_name": logger} for logger in loggers],
|
|
)
|
|
return [get_logger(logger) for logger in loggers]
|