diff --git a/cloudinit/cmd/clean.py b/cloudinit/cmd/clean.py index d852ab711cc1..70a1a3bb26ed 100755 --- a/cloudinit/cmd/clean.py +++ b/cloudinit/cmd/clean.py @@ -11,7 +11,7 @@ import os import sys -from cloudinit import settings +from cloudinit import log, settings from cloudinit.distros import uses_systemd from cloudinit.net.netplan import CLOUDINIT_NETPLAN_FILE from cloudinit.stages import Init @@ -19,7 +19,6 @@ from cloudinit.util import ( del_dir, del_file, - error, get_config_logfiles, is_link, write_file, @@ -144,12 +143,12 @@ def remove_artifacts(init, remove_logs, remove_seed=False, remove_config=None): else: del_file(path) except OSError as e: - error("Could not remove {0}: {1}".format(path, str(e))) + log.error("Could not remove {0}: {1}".format(path, str(e))) return 1 try: runparts(settings.CLEAN_RUNPARTS_DIR) except Exception as e: - error( + log.error( f"Failure during run-parts of {settings.CLEAN_RUNPARTS_DIR}: {e}" ) return 1 @@ -176,7 +175,7 @@ def handle_clean_args(name, args): try: subp(cmd, capture=False) except ProcessExecutionError as e: - error( + log.error( 'Could not reboot this system using "{0}": {1}'.format( cmd, str(e) ) diff --git a/cloudinit/cmd/cloud_id.py b/cloudinit/cmd/cloud_id.py index 4a856fe28b55..cd436dde657e 100644 --- a/cloudinit/cmd/cloud_id.py +++ b/cloudinit/cmd/cloud_id.py @@ -10,8 +10,8 @@ from cloudinit.cmd.devel import read_cfg_paths from cloudinit.cmd.status import RunningStatus, get_status_details +from cloudinit.log import error from cloudinit.sources import METADATA_UNKNOWN, canonical_cloud_id -from cloudinit.util import error NAME = "cloud-id" diff --git a/cloudinit/cmd/main.py b/cloudinit/cmd/main.py index 85495818a9d1..e5964883e7e3 100644 --- a/cloudinit/cmd/main.py +++ b/cloudinit/cmd/main.py @@ -115,7 +115,7 @@ def log_ppid(distro, bootstage_name): def welcome(action, msg=None): if not msg: msg = welcome_format(action) - util.multi_log("%s\n" % (msg), console=False, stderr=True, log=LOG) + log.multi_log("%s\n" % (msg), console=False, stderr=True, log=LOG) return msg diff --git a/cloudinit/config/cc_disk_setup.py b/cloudinit/config/cc_disk_setup.py index 0281a7b0b36b..56011d7f7420 100644 --- a/cloudinit/config/cc_disk_setup.py +++ b/cloudinit/config/cc_disk_setup.py @@ -12,7 +12,7 @@ import shlex from pathlib import Path -from cloudinit import subp, util +from cloudinit import log, subp, util from cloudinit.cloud import Cloud from cloudinit.config import Config from cloudinit.config.schema import MetaSchema diff --git a/cloudinit/config/cc_final_message.py b/cloudinit/config/cc_final_message.py index dc263ee2d3d5..e7ac307b2ee9 100644 --- a/cloudinit/config/cc_final_message.py +++ b/cloudinit/config/cc_final_message.py @@ -9,7 +9,7 @@ import logging -from cloudinit import templater, util, version +from cloudinit import log, templater, util, version from cloudinit.cloud import Cloud from cloudinit.config import Config from cloudinit.config.schema import MetaSchema @@ -57,7 +57,7 @@ def handle(name: str, cfg: Config, cloud: Cloud, args: list) -> None: "datasource": str(cloud.datasource), } subs.update(dict([(k.upper(), v) for k, v in subs.items()])) - util.multi_log( + log.multi_log( "%s\n" % (templater.render_string(msg_in, subs)), console=False, stderr=True, diff --git a/cloudinit/config/cc_keys_to_console.py b/cloudinit/config/cc_keys_to_console.py index 3ca2c0882618..08c2a7f68976 100644 --- a/cloudinit/config/cc_keys_to_console.py +++ b/cloudinit/config/cc_keys_to_console.py @@ -11,7 +11,7 @@ import logging import os -from cloudinit import subp, util +from cloudinit import log, subp, util from cloudinit.cloud import Cloud from cloudinit.config import Config from cloudinit.config.schema import MetaSchema @@ -64,7 +64,7 @@ def handle(name: str, cfg: Config, cloud: Cloud, args: list) -> None: try: cmd = [helper_path, ",".join(fp_blacklist), ",".join(key_blacklist)] (stdout, _stderr) = subp.subp(cmd) - util.multi_log("%s\n" % (stdout.strip()), stderr=False, console=True) + log.multi_log("%s\n" % (stdout.strip()), stderr=False, console=True) except Exception: LOG.warning("Writing keys to the system console failed!") raise diff --git a/cloudinit/config/cc_mounts.py b/cloudinit/config/cc_mounts.py index b36d774e58c4..d122f9e5846f 100644 --- a/cloudinit/config/cc_mounts.py +++ b/cloudinit/config/cc_mounts.py @@ -16,7 +16,7 @@ import re from typing import Dict, List, Optional, Tuple, cast -from cloudinit import subp, util +from cloudinit import log, subp, util from cloudinit.cloud import Cloud from cloudinit.config import Config from cloudinit.config.schema import MetaSchema diff --git a/cloudinit/config/cc_resizefs.py b/cloudinit/config/cc_resizefs.py index 101fe438ab54..62220e6af7ac 100644 --- a/cloudinit/config/cc_resizefs.py +++ b/cloudinit/config/cc_resizefs.py @@ -15,7 +15,7 @@ import stat from typing import Optional -from cloudinit import lifecycle, subp, util +from cloudinit import lifecycle, log, subp, util from cloudinit.cloud import Cloud from cloudinit.config import Config from cloudinit.config.schema import MetaSchema diff --git a/cloudinit/config/cc_set_passwords.py b/cloudinit/config/cc_set_passwords.py index 224ae6b85fe2..649a37735af5 100644 --- a/cloudinit/config/cc_set_passwords.py +++ b/cloudinit/config/cc_set_passwords.py @@ -12,7 +12,7 @@ from string import ascii_letters, digits from typing import List -from cloudinit import features, lifecycle, subp, util +from cloudinit import features, lifecycle, log, subp, util from cloudinit.cloud import Cloud from cloudinit.config import Config from cloudinit.config.schema import MetaSchema @@ -220,7 +220,7 @@ def handle(name: str, cfg: Config, cloud: Cloud, args: list) -> None: "Set the following 'random' passwords\n", "\n".join(randlist), ) - util.multi_log( + log.multi_log( "%s\n%s\n" % blurb, stderr=False, fallback_to_stdout=False ) diff --git a/cloudinit/config/cc_ssh_authkey_fingerprints.py b/cloudinit/config/cc_ssh_authkey_fingerprints.py index 8b02789b739d..955c722c0d22 100644 --- a/cloudinit/config/cc_ssh_authkey_fingerprints.py +++ b/cloudinit/config/cc_ssh_authkey_fingerprints.py @@ -9,7 +9,7 @@ import hashlib import logging -from cloudinit import ssh_util, util +from cloudinit import log, ssh_util, util from cloudinit.cloud import Cloud from cloudinit.config import Config from cloudinit.config.schema import MetaSchema @@ -67,7 +67,7 @@ def _pprint_key_entries( "%sno authorized SSH keys fingerprints found for user %s.\n" % (prefix, user) ) - util.multi_log(message, console=True, stderr=False) + log.multi_log(message, console=True, stderr=False) return tbl_fields = [ "Keytype", @@ -97,7 +97,7 @@ def _pprint_key_entries( ] lines.extend(authtbl_lines) for line in lines: - util.multi_log( + log.multi_log( text="%s%s\n" % (prefix, line), stderr=False, console=True ) diff --git a/cloudinit/config/cc_ubuntu_pro.py b/cloudinit/config/cc_ubuntu_pro.py index 9291dde7c879..a93d1bb49a82 100644 --- a/cloudinit/config/cc_ubuntu_pro.py +++ b/cloudinit/config/cc_ubuntu_pro.py @@ -8,7 +8,7 @@ from typing import Any, List from urllib.parse import urlparse -from cloudinit import subp, util +from cloudinit import log, subp, util from cloudinit.cloud import Cloud from cloudinit.config import Config from cloudinit.config.schema import MetaSchema diff --git a/cloudinit/config/schema.py b/cloudinit/config/schema.py index de9547b987ce..27caa0bd3065 100644 --- a/cloudinit/config/schema.py +++ b/cloudinit/config/schema.py @@ -35,10 +35,10 @@ from cloudinit.cmd.devel import read_cfg_paths from cloudinit.handlers import INCLUSION_TYPES_MAP, type_from_starts_with from cloudinit.helpers import Paths +from cloudinit.log import error from cloudinit.sources import DataSourceNotFoundException from cloudinit.temp_utils import mkdtemp from cloudinit.util import ( - error, get_modules_from_dir, load_text_file, load_yaml, diff --git a/cloudinit/distros/networking.py b/cloudinit/distros/networking.py index e864463874aa..474c711820c8 100644 --- a/cloudinit/distros/networking.py +++ b/cloudinit/distros/networking.py @@ -2,7 +2,7 @@ import logging import os -from cloudinit import net, subp, util +from cloudinit import log, net, subp, util from cloudinit.distros.parsers import ifconfig from cloudinit.net.netops.iproute2 import Iproute2 diff --git a/cloudinit/distros/package_management/apt.py b/cloudinit/distros/package_management/apt.py index a2c8b579e7d8..fe25056fea74 100644 --- a/cloudinit/distros/package_management/apt.py +++ b/cloudinit/distros/package_management/apt.py @@ -7,7 +7,7 @@ import time from typing import Any, Iterable, List, Mapping, Optional, Sequence, cast -from cloudinit import helpers, subp, util +from cloudinit import helpers, log, subp, util from cloudinit.distros.package_management.package_manager import ( PackageManager, UninstalledPackages, diff --git a/cloudinit/log/__init__.py b/cloudinit/log/__init__.py new file mode 100644 index 000000000000..8e75875bd376 --- /dev/null +++ b/cloudinit/log/__init__.py @@ -0,0 +1,30 @@ +# This file is part of cloud-init. See LICENSE file for license information. + +from cloudinit.log.log_util import error, log_time, logexc, multi_log +from cloudinit.log.loggers import ( + DEPRECATED, + TRACE, + CustomLoggerType, + LogExporter, + configure_root_logger, + flush_loggers, + reset_logging, + setup_basic_logging, + setup_logging, +) + +__all__ = [ + "CustomLoggerType", + "DEPRECATED", + "LogExporter", + "TRACE", + "configure_root_logger", + "error", + "flush_loggers", + "logexc", + "log_time", + "multi_log", + "reset_logging", + "setup_basic_logging", + "setup_logging", +] diff --git a/cloudinit/log/log_util.py b/cloudinit/log/log_util.py new file mode 100644 index 000000000000..0d01f8eb20a4 --- /dev/null +++ b/cloudinit/log/log_util.py @@ -0,0 +1,120 @@ +import logging +import os +import sys +import time + + +def logexc( + log, msg, *args, log_level: int = logging.WARNING, exc_info=True +) -> None: + log.log(log_level, msg, *args) + log.debug(msg, exc_info=exc_info, *args) + + +class log_time: + """ + Log time that it takes for context to run if the time exceeds a threshold. + + :param msg: Name of the thing that is being measured + :param threshold: Threashold, in seconds. When the context exceeds this + threshold, a log will be made. + :param skip_log: Do not log a message. If a message would have been + logged it can be accessed from the context manager's 'output' variable + This is useful for consolidating the message with other logs. + + usage: + + this call: + + ``` + with log_time("Configuring the network"): + run_configure() + ``` + + might produce this log: + + ``` + Configuring the network took 0.02 seconds + ``` + """ + + def __init__(self, msg, threshold: float = 0.01, skip_log: bool = False): + self._msg = msg + self._threshold = threshold + self._skip_log = skip_log + self.output = "" + self.start = 0.0 + + def __enter__(self): + self.start = time.monotonic() + + def __exit__(self, exc_type, exc_val, exc_tb): + delta = time.monotonic() - self.start + if delta > self._threshold: + if not self._skip_log: + LOG.debug("%s took %0.3f seconds", self._msg, delta) + self.output = f"{self._msg} took {delta:.3f} seconds" + + +def write_to_console(conpath, text): + with open(conpath, "w") as wfh: + wfh.write(text) + wfh.flush() + + +def multi_log( + text, + console=True, + stderr=True, + log=None, + log_level=logging.DEBUG, + fallback_to_stdout=True, +): + if stderr: + sys.stderr.write(text) + if console: + conpath = "/dev/console" + writing_to_console_worked = False + if os.path.exists(conpath): + try: + write_to_console(conpath, text) + writing_to_console_worked = True + except OSError: + console_error = "Failed to write to /dev/console" + sys.stdout.write(f"{console_error}\n") + if log: + log.log(logging.WARNING, console_error) + + if fallback_to_stdout and not writing_to_console_worked: + # A container may lack /dev/console (arguably a container bug). + # Additionally, /dev/console may not be writable to on a VM (again + # likely a VM bug or virtualization bug). + # + # If either of these is the case, then write output to stdout. + # This will result in duplicate stderr and stdout messages if + # stderr was True. + # + # even though systemd might have set up output to go to + # /dev/console, the user may have configured elsewhere via + # cloud-config 'output'. If there is /dev/console, messages will + # still get there. + sys.stdout.write(text) + if log: + if text[-1] == "\n": + log.log(log_level, text[:-1]) + else: + log.log(log_level, text) + + +def error(msg, rc=1, fmt="Error:\n{}", sys_exit=False): + r"""Print error to stderr and return or exit + + @param msg: message to print + @param rc: return code (default: 1) + @param fmt: format string for putting message in (default: 'Error:\n {}') + @param sys_exit: exit when called (default: false) + """ + print(fmt.format(msg), file=sys.stderr) + if sys_exit: + sys.exit(rc) + return rc diff --git a/cloudinit/log.py b/cloudinit/log/loggers.py similarity index 100% rename from cloudinit/log.py rename to cloudinit/log/loggers.py diff --git a/cloudinit/net/__init__.py b/cloudinit/net/__init__.py index 26fdf057328f..b7a6b0be5494 100644 --- a/cloudinit/net/__init__.py +++ b/cloudinit/net/__init__.py @@ -14,7 +14,7 @@ from typing import Any, Callable, Dict, List, Optional, Tuple from urllib.parse import urlparse -from cloudinit import subp, util +from cloudinit import log, subp, util from cloudinit.net.netops.iproute2 import Iproute2 from cloudinit.url_helper import UrlError, readurl diff --git a/cloudinit/signal_handler.py b/cloudinit/signal_handler.py index 00eb4f4af050..242e83286e93 100644 --- a/cloudinit/signal_handler.py +++ b/cloudinit/signal_handler.py @@ -11,7 +11,7 @@ import sys from io import StringIO -from cloudinit import util +from cloudinit import log from cloudinit import version as vr LOG = logging.getLogger(__name__) @@ -44,7 +44,7 @@ def _handle_exit(signum, frame): contents = StringIO() contents.write("%s\n" % (msg)) _pprint_frame(frame, 1, BACK_FRAME_TRACE_DEPTH, contents) - util.multi_log(contents.getvalue(), log=LOG, log_level=logging.ERROR) + log.multi_log(contents.getvalue(), log=LOG, log_level=logging.ERROR) sys.exit(rc) diff --git a/cloudinit/sources/DataSourceScaleway.py b/cloudinit/sources/DataSourceScaleway.py index eb7bfe65a9af..83e3c38cb45d 100644 --- a/cloudinit/sources/DataSourceScaleway.py +++ b/cloudinit/sources/DataSourceScaleway.py @@ -19,7 +19,7 @@ from urllib3.connection import HTTPConnection from urllib3.poolmanager import PoolManager -from cloudinit import dmi, sources, url_helper, util +from cloudinit import dmi, log, sources, url_helper, util from cloudinit.event import EventScope, EventType from cloudinit.net.dhcp import NoDHCPLeaseError from cloudinit.net.ephemeral import EphemeralDHCPv4, EphemeralIPv6Network diff --git a/cloudinit/sources/__init__.py b/cloudinit/sources/__init__.py index 058db3fd19c9..fe61b57ca1d5 100644 --- a/cloudinit/sources/__init__.py +++ b/cloudinit/sources/__init__.py @@ -19,7 +19,15 @@ from enum import Enum, unique from typing import Any, Dict, List, Optional, Tuple, Union -from cloudinit import atomic_helper, dmi, importer, lifecycle, net, type_utils +from cloudinit import ( + atomic_helper, + dmi, + importer, + lifecycle, + log, + net, + type_utils, +) from cloudinit import user_data as ud from cloudinit import util from cloudinit.atomic_helper import write_json diff --git a/cloudinit/util.py b/cloudinit/util.py index a03b1e73b07e..3846e01e3021 100644 --- a/cloudinit/util.py +++ b/cloudinit/util.py @@ -71,6 +71,7 @@ url_helper, version, ) +from cloudinit.log import log_time, logexc from cloudinit.settings import CFG_BUILTIN, PER_ONCE if TYPE_CHECKING: @@ -438,56 +439,6 @@ def get_modules_from_dir(root_dir: str) -> dict: return entries -def write_to_console(conpath, text): - with open(conpath, "w") as wfh: - wfh.write(text) - wfh.flush() - - -def multi_log( - text, - console=True, - stderr=True, - log=None, - log_level=logging.DEBUG, - fallback_to_stdout=True, -): - if stderr: - sys.stderr.write(text) - if console: - conpath = "/dev/console" - writing_to_console_worked = False - if os.path.exists(conpath): - try: - write_to_console(conpath, text) - writing_to_console_worked = True - except OSError: - console_error = "Failed to write to /dev/console" - sys.stdout.write(f"{console_error}\n") - if log: - log.log(logging.WARNING, console_error) - - if fallback_to_stdout and not writing_to_console_worked: - # A container may lack /dev/console (arguably a container bug). - # Additionally, /dev/console may not be writable to on a VM (again - # likely a VM bug or virtualization bug). - # - # If either of these is the case, then write output to stdout. - # This will result in duplicate stderr and stdout messages if - # stderr was True. - # - # even though systemd might have set up output to go to - # /dev/console, the user may have configured elsewhere via - # cloud-config 'output'. If there is /dev/console, messages will - # still get there. - sys.stdout.write(text) - if log: - if text[-1] == "\n": - log.log(log_level, text[:-1]) - else: - log.log(log_level, text) - - @lru_cache() def is_Linux(): """deprecated: prefer Distro object's `is_linux` property @@ -1847,13 +1798,6 @@ def get_config_logfiles(cfg: Dict[str, Any]): return list(set(logs + rotated_logs)) -def logexc( - log, msg, *args, log_level: int = logging.WARNING, exc_info=True -) -> None: - log.log(log_level, msg, *args) - log.debug(msg, exc_info=exc_info, *args) - - def hash_blob(blob, routine: str, mlen=None) -> str: hasher = hashlib.new(routine) hasher.update(encode_text(blob)) @@ -2842,40 +2786,6 @@ def has_mount_opt(path, opt: str) -> bool: return opt in mnt_opts.split(",") -@contextlib.contextmanager -def log_time(logger, msg, threshold: float = 0.01): - """ - Log time that it takes for context to run if the time exceeds a threshold. - - :param logger: Logger of the calling context - :param msg: Name of the thing that is being measured - :param threshold: Threashold, in seconds. When the context exceeds this - threshold, a log will be made. - - usage: - - this call: - - ``` - with log_time(LOG, "Configuring the network"): - run_configure() - ``` - - might produce this log: - - ``` - Configuring the network took 0.02 seconds - ``` - """ - start = time.monotonic() - try: - yield - finally: - delta = time.monotonic() - start - if delta > threshold: - logger.debug("%s took %0.3f seconds", msg, delta) - - def expand_dotted_devname(dotted): toks = dotted.rsplit(".", 1) if len(toks) > 1: @@ -3147,21 +3057,6 @@ def udevadm_settle(exists=None, timeout=None): return subp.subp(settle_cmd) -def error(msg, rc=1, fmt="Error:\n{}", sys_exit=False): - r""" - Print error to stderr and return or exit - - @param msg: message to print - @param rc: return code (default: 1) - @param fmt: format string for putting message in (default: 'Error:\n {}') - @param sys_exit: exit when called (default: false) - """ - print(fmt.format(msg), file=sys.stderr) - if sys_exit: - sys.exit(rc) - return rc - - def read_hotplug_enabled_file(paths: "Paths") -> dict: content: dict = {"scopes": []} try: diff --git a/tests/unittests/config/test_cc_keys_to_console.py b/tests/unittests/config/test_cc_keys_to_console.py index f77e6234acd8..ce018cddb17b 100644 --- a/tests/unittests/config/test_cc_keys_to_console.py +++ b/tests/unittests/config/test_cc_keys_to_console.py @@ -20,7 +20,7 @@ class TestHandle: should be expanded to cover the full functionality. """ - @mock.patch("cloudinit.config.cc_keys_to_console.util.multi_log") + @mock.patch("cloudinit.config.cc_keys_to_console.log.multi_log") @mock.patch("cloudinit.config.cc_keys_to_console.os.path.exists") @mock.patch("cloudinit.config.cc_keys_to_console.subp.subp") @pytest.mark.parametrize( diff --git a/tests/unittests/config/test_cc_set_passwords.py b/tests/unittests/config/test_cc_set_passwords.py index d37faedd4c64..6ee109fa7f8b 100644 --- a/tests/unittests/config/test_cc_set_passwords.py +++ b/tests/unittests/config/test_cc_set_passwords.py @@ -36,7 +36,7 @@ @pytest.fixture(autouse=True) def common_fixtures(mocker): mocker.patch("cloudinit.distros.uses_systemd", return_value=True) - mocker.patch("cloudinit.util.write_to_console") + mocker.patch("cloudinit.log.log_util.write_to_console") class TestHandleSSHPwauth: @@ -268,7 +268,7 @@ def test_bsd_calls_custom_pw_cmds_to_set_and_expire_passwords( ) def test_random_passwords(self, user_cfg, mocker, caplog): """handle parses command set random passwords.""" - m_multi_log = mocker.patch(f"{MODPATH}util.multi_log") + m_multi_log = mocker.patch(f"{MODPATH}log.multi_log") mocker.patch(f"{MODPATH}subp.subp") cloud = get_cloud() diff --git a/tests/unittests/config/test_cc_zypper_add_repo.py b/tests/unittests/config/test_cc_zypper_add_repo.py index 772d132ffdfd..21f033afd8a3 100644 --- a/tests/unittests/config/test_cc_zypper_add_repo.py +++ b/tests/unittests/config/test_cc_zypper_add_repo.py @@ -8,7 +8,6 @@ from cloudinit import util from cloudinit.config import cc_zypper_add_repo from tests.unittests import helpers -from tests.unittests.helpers import mock LOG = logging.getLogger(__name__) @@ -98,8 +97,7 @@ def test_config_write(self): if item not in expected: self.assertIsNone(item) - @mock.patch("cloudinit.log.logging") - def test_config_write_skip_configdir(self, mock_logging): + def test_config_write_skip_configdir(self): """Write configuration but skip writing 'configdir' setting""" cfg = { "config": { diff --git a/tests/unittests/test_log.py b/tests/unittests/test_log.py index d67c3552157d..8563eb34de49 100644 --- a/tests/unittests/test_log.py +++ b/tests/unittests/test_log.py @@ -12,6 +12,7 @@ from cloudinit import lifecycle, log, util from cloudinit.analyze.dump import CLOUD_INIT_ASCTIME_FMT +from cloudinit.log import loggers from tests.unittests.helpers import CiTestCase @@ -24,7 +25,7 @@ def setUp(self): self.ci_logs = io.StringIO() self.ci_root = logging.getLogger() console = logging.StreamHandler(self.ci_logs) - console.setFormatter(logging.Formatter(log.DEFAULT_LOG_FORMAT)) + console.setFormatter(logging.Formatter(loggers.DEFAULT_LOG_FORMAT)) console.setLevel(logging.DEBUG) self.ci_root.addHandler(console) self.ci_root.setLevel(logging.DEBUG) @@ -124,7 +125,7 @@ def test_deprecate_log_level_based_on_features( ) def test_log_deduplication(self, caplog): - log.define_extra_loggers() + loggers.define_extra_loggers() lifecycle.deprecate( deprecated="stuff", deprecated_version="19.1", diff --git a/tests/unittests/test_util.py b/tests/unittests/test_util.py index 190eca7610e7..e2103130f5c1 100644 --- a/tests/unittests/test_util.py +++ b/tests/unittests/test_util.py @@ -27,6 +27,7 @@ features, importer, lifecycle, + log, subp, url_helper, util, @@ -2369,85 +2370,85 @@ def setUp(self): def test_stderr_used_by_default(self): logged_string = "test stderr output" - util.multi_log(logged_string) + log.multi_log(logged_string) self.assertEqual(logged_string, self.stderr.getvalue()) def test_stderr_not_used_if_false(self): - util.multi_log("should not see this", stderr=False) + log.multi_log("should not see this", stderr=False) self.assertEqual("", self.stderr.getvalue()) def test_logs_go_to_console_by_default(self): self._createConsole(self.root) logged_string = "something very important" - util.multi_log(logged_string) + log.multi_log(logged_string) with open("/dev/console") as f: self.assertEqual(logged_string, f.read()) def test_logs_dont_go_to_stdout_if_console_exists(self): self._createConsole(self.root) - util.multi_log("something") + log.multi_log("something") self.assertEqual("", self.stdout.getvalue()) def test_logs_go_to_stdout_if_console_does_not_exist(self): logged_string = "something very important" - util.multi_log(logged_string) + log.multi_log(logged_string) self.assertEqual(logged_string, self.stdout.getvalue()) def test_logs_dont_go_to_stdout_if_fallback_to_stdout_is_false(self): - util.multi_log("something", fallback_to_stdout=False) + log.multi_log("something", fallback_to_stdout=False) self.assertEqual("", self.stdout.getvalue()) @mock.patch( - M_PATH + "write_to_console", + "cloudinit.log.log_util.write_to_console", mock.Mock(side_effect=OSError("Failed to write to console")), ) def test_logs_go_to_stdout_if_writing_to_console_fails_and_fallback_true( self, ): self._createConsole(self.root) - util.multi_log("something", fallback_to_stdout=True) + log.multi_log("something", fallback_to_stdout=True) self.assertEqual( "Failed to write to /dev/console\nsomething", self.stdout.getvalue(), ) @mock.patch( - M_PATH + "write_to_console", + "cloudinit.log.log_util.write_to_console", mock.Mock(side_effect=OSError("Failed to write to console")), ) def test_logs_go_nowhere_if_writing_to_console_fails_and_fallback_false( self, ): self._createConsole(self.root) - util.multi_log("something", fallback_to_stdout=False) + log.multi_log("something", fallback_to_stdout=False) self.assertEqual( "Failed to write to /dev/console\n", self.stdout.getvalue() ) def test_logs_go_to_log_if_given(self): - log = mock.MagicMock() + logger = mock.MagicMock() logged_string = "something very important" - util.multi_log(logged_string, log=log) + log.multi_log(logged_string, log=logger) self.assertEqual( - [((mock.ANY, logged_string), {})], log.log.call_args_list + [((mock.ANY, logged_string), {})], logger.log.call_args_list ) def test_newlines_stripped_from_log_call(self): - log = mock.MagicMock() + logger = mock.MagicMock() expected_string = "something very important" - util.multi_log("{0}\n".format(expected_string), log=log) - self.assertEqual((mock.ANY, expected_string), log.log.call_args[0]) + log.multi_log("{0}\n".format(expected_string), log=logger) + self.assertEqual((mock.ANY, expected_string), logger.log.call_args[0]) def test_log_level_defaults_to_debug(self): - log = mock.MagicMock() - util.multi_log("message", log=log) - self.assertEqual((logging.DEBUG, mock.ANY), log.log.call_args[0]) + logger = mock.MagicMock() + log.multi_log("message", log=logger) + self.assertEqual((logging.DEBUG, mock.ANY), logger.log.call_args[0]) def test_given_log_level_used(self): - log = mock.MagicMock() + logger = mock.MagicMock() log_level = mock.Mock() - util.multi_log("message", log=log, log_level=log_level) - self.assertEqual((log_level, mock.ANY), log.log.call_args[0]) + log.multi_log("message", log=logger, log_level=log_level) + self.assertEqual((log_level, mock.ANY), logger.log.call_args[0]) class TestMessageFromString(helpers.TestCase):