首页 > 解决方案 > 记录和 paramiko 奇怪的行为

问题描述

我正在使用 paramiko 设置将执行测试的远程桌面,并且我创建了一个小模块,它使用 paramiko 的 sftp 实现传输必要的文件。这是我的代码的相关部分:

class MacSSHAgent(paramiko.SSHClient):
    def __init__(self, hostname: str, username: str, password: str, workspace: pathlib.Path = None):
        super(MacSSHAgent, self).__init__()
        self._host = hostname
        self._user = username
        self._password = password
        self._workspace = workspace
        self._sftp = None

        self.logger = logging.getLogger(__name__)
        self.logger.setLevel(logging.INFO)
        handler = logging.StreamHandler()
        handler.setLevel(logging.INFO)
        handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
        self.logger.addHandler(handler)
        return

    def __enter__(self):
        self.open()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        was_transport_active = self.get_transport().is_active()
        self.close()
        if exc_type is RebootException:
            if was_transport_active:
                self.logger.warning(f"Failed to reboot the host '{self._host}'!")
            else:
                self.logger.info(f"The host '{self._host}' is rebooting...")
            return True

    def open(self):
        self.logger.info(f"Connecting to host '{self._host}' with user '{self._user}' via ssh...")
        self.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        self.connect(hostname=self._host, username=self._user, password=self._password)
        self._sftp = self.open_sftp()
        self.logger.info(f"Changing directory on host '{self._host}' to {self._workspace}")
        self._sftp.chdir(str(self._workspace))
        return

    @handle_ssh_commands(log_output=True, raise_exception=RebootException)
    def reboot(self):
        return self.exec_command(f"echo {self._password} | sudo -S shutdown -r now\n")

    def transfer_file(self, path: pathlib.Path):
        dir_content = self._sftp.listdir()
        if path.exists() and path.name not in dir_content and self._sftp.getcwd() == str(self._workspace):
            self.logger.info(f"Transferring file '{path.name}' to host '{self._host}'...")
            self.logger.info(f"File size: {path.stat().st_size // 1024}KB")
            self._sftp.put(path.absolute(), path.name)
            self.logger.info(f"File '{path.name}' transferred to host '{self._host}' successfully!")
        elif not path.exists():
            self.logger.warning(f"File '{path.name}' doesn't exist locally!")
        elif path.name in dir_content:
            self.logger.warning(f"File '{path.name}' already exists on host '{self._host}'!")
        return

    def close(self):
        self._sftp.close()
        super(MacSSHAgent, self).close()
        return


@click.command()
@click.argument("ips", nargs=-1, required=True, type=IpAddressType())
@click.option("-u", "--user", required=True, type=click.STRING, help="Login username on the host(s)")
@click.option("-pwd", "--password", required=True, type=click.STRING, help="Password on the host(s)")
@click.option("-ws", "--workspace", type=PathType(file_okay=False, unix_style=True), help="Workspace directory on the host(s), user directory if not specified")
@click.option("-td", "--transfer_dir", default="mac_files", type=PathType(exists=True, file_okay=False), help="Directory which contains the necessary files for transfer")
@click.option("-r", "--reboot", is_flag=True, help="Use this flag to reboot the host(s)")
@click.option("-tf", "--transfer_files", is_flag=True, help="Use this flag to transfer files to the host(s)")
def start_mac_ssh_agent(ips: Tuple[str], user: str, password: str, workspace: pathlib.Path,
                        transfer_dir: pathlib.Path, reboot: bool, transfer_files: bool):
    """
    Reboot or upload necessary files to the Mac computers with specified IP addresses.

    IPS - one or more IP address for the host Mac computers
    """
    workspace = workspace if workspace else pathlib.PurePosixPath(f"/Users/{user}")
    for ip in ips:
        with MacSSHAgent(ip, user, password, workspace) as mac_ssh_agent:
            if transfer_files:
                for file_path in pathlib.Path(transfer_dir).glob("*"):
                    mac_ssh_agent.transfer_file(file_path)
            if reboot:
                mac_ssh_agent.reboot()
    return


if __name__ == '__main__':
    start_mac_ssh_agent()

我开始这样的脚本:

setup.py -u test -pwd 1234 -td mac_files -tf 192.168.1.1 192.168.1.2 192.168.1.3

输出如下所示:

2019-12-10 16:13:56,146 - INFO - Connecting to host '192.168.1.1' with user 'test' via ssh...
2019-12-10 16:13:56,771 - INFO - Changing directory on host '192.168.1.71' to /Users/test
2019-12-10 16:13:56,936 - WARNING - File 'clean.sh' already exists on host '192.168.1.71'!
2019-12-10 16:13:57,042 - WARNING - File 'Test.jpg' already exists on host '192.168.1.71'!
2019-12-10 16:13:57,150 - WARNING - File 'TestBig.pdf' already exists on host '192.168.1.71'!
2019-12-10 16:13:57,152 - INFO - Connecting to host '192.168.1.2' with user 'test' via ssh...
2019-12-10 16:13:57,152 - INFO - Connecting to host '192.168.1.2' with user 'test' via ssh...
2019-12-10 16:13:57,781 - INFO - Changing directory on host '192.168.1.2' to /Users/test
2019-12-10 16:13:57,781 - INFO - Changing directory on host '192.168.1.2' to /Users/test
2019-12-10 16:13:57,943 - WARNING - File 'clean.sh' already exists on host '192.168.1.2'!
2019-12-10 16:13:57,943 - WARNING - File 'clean.sh' already exists on host '192.168.1.2'!
2019-12-10 16:13:58,052 - WARNING - File 'Test.jpg' already exists on host '192.168.1.2'!
2019-12-10 16:13:58,052 - WARNING - File 'Test.jpg' already exists on host '192.168.1.2'!
2019-12-10 16:13:58,158 - WARNING - File 'TestBig.pdf' already exists on host '192.168.1.2'!
2019-12-10 16:13:58,158 - WARNING - File 'TestBig.pdf' already exists on host '192.168.1.2'!
2019-12-10 16:13:58,158 - INFO - Connecting to host '192.168.1.3' with user 'test' via ssh...
2019-12-10 16:13:58,158 - INFO - Connecting to host '192.168.1.3' with user 'test' via ssh...
2019-12-10 16:13:58,158 - INFO - Connecting to host '192.168.1.3' with user 'test' via ssh...
2019-12-10 16:13:58,735 - INFO - Changing directory on host '192.168.1.3' to /Users/test
2019-12-10 16:13:58,735 - INFO - Changing directory on host '192.168.1.3' to /Users/test
2019-12-10 16:13:58,735 - INFO - Changing directory on host '192.168.1.3' to /Users/test
2019-12-10 16:13:58,903 - WARNING - File 'clean.sh' already exists on host '192.168.1.3'!
2019-12-10 16:13:58,903 - WARNING - File 'clean.sh' already exists on host '192.168.1.3'!
2019-12-10 16:13:58,903 - WARNING - File 'clean.sh' already exists on host '192.168.1.3'!
2019-12-10 16:13:59,008 - WARNING - File 'Test.jpg' already exists on host '192.168.1.3'!
2019-12-10 16:13:59,008 - WARNING - File 'Test.jpg' already exists on host '192.168.1.3'!
2019-12-10 16:13:59,008 - WARNING - File 'Test.jpg' already exists on host '192.168.1.3'!
2019-12-10 16:13:59,116 - WARNING - File 'TestBig.pdf' already exists on host '192.168.1.3'!
2019-12-10 16:13:59,116 - WARNING - File 'TestBig.pdf' already exists on host '192.168.1.3'!
2019-12-10 16:13:59,116 - WARNING - File 'TestBig.pdf' already exists on host '192.168.1.3'!

正如您所看到的,由于某种原因,日志成倍增加,但时间戳看起来还不错。为什么?

标签: pythonloggingparamiko

解决方案


您正在向__init__多次调用的记录器添加新的处理程序,因为您创建了许多 MacSSHAgent 对象:

    for ip in ips:
        with MacSSHAgent(ip, user, password, workspace) as mac_ssh_agent:

我会在全局级别或静态类字段配置记录器,因为所有对象已经共享相同的记录器。logging.getLogger使用相同名称调用时返回相同的记录器:

class MacSSHAgent(paramiko.SSHClient):
    logger = logging.getLogger(__name__)
    logger.setLevel(logging.INFO)
    handler = logging.StreamHandler()
    handler.setLevel(logging.INFO)
    handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
    logger.addHandler(handler)

如果您真的想配置它,__init__请检查 logger 是否没有处理程序:

if self.logger.handlers

推荐阅读