feat(command): improve command logging
This commit is contained in:
+37
-12
@@ -1,11 +1,14 @@
|
|||||||
__all__ = (
|
__all__ = (
|
||||||
"CommandError",
|
"CommandError",
|
||||||
|
"CommandLogger",
|
||||||
"CommandExecutor",
|
"CommandExecutor",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import threading
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
logger = logging.getLogger("command")
|
logger = logging.getLogger("command")
|
||||||
@@ -14,7 +17,31 @@ logger = logging.getLogger("command")
|
|||||||
class CommandError(RuntimeError):
|
class CommandError(RuntimeError):
|
||||||
@property
|
@property
|
||||||
def output(self):
|
def output(self):
|
||||||
return self.args[0].decode("utf-8").strip()
|
return self.args[0]
|
||||||
|
|
||||||
|
|
||||||
|
class CommandLogger(threading.Thread):
|
||||||
|
def __init__(self, level: int):
|
||||||
|
super().__init__()
|
||||||
|
self.daemon = False
|
||||||
|
self.level = level
|
||||||
|
self.fdRead, self.fdWrite = os.pipe()
|
||||||
|
self.pipeReader = os.fdopen(self.fdRead)
|
||||||
|
self.lines = []
|
||||||
|
self.start()
|
||||||
|
|
||||||
|
def fileno(self):
|
||||||
|
return self.fdWrite
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
for line in iter(self.pipeReader.readline, ""):
|
||||||
|
logger.log(self.level, line.strip("\n"))
|
||||||
|
self.lines.append(line)
|
||||||
|
|
||||||
|
self.pipeReader.close()
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
os.close(self.fdWrite)
|
||||||
|
|
||||||
|
|
||||||
class CommandExecutor:
|
class CommandExecutor:
|
||||||
@@ -25,19 +52,17 @@ class CommandExecutor:
|
|||||||
|
|
||||||
def run(self, basepath: Path):
|
def run(self, basepath: Path):
|
||||||
self._logger.debug(self._command)
|
self._logger.debug(self._command)
|
||||||
|
out_logger = CommandLogger(logging.TRACE)
|
||||||
|
err_logger = CommandLogger(logging.TRACE)
|
||||||
|
try:
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
self._command.split(" "),
|
self._command.split(" "),
|
||||||
cwd=basepath,
|
cwd=basepath,
|
||||||
stdout=subprocess.PIPE,
|
stdout=out_logger,
|
||||||
stderr=subprocess.PIPE,
|
stderr=err_logger,
|
||||||
)
|
)
|
||||||
self._logstd(result.stdout, logging.TRACE)
|
finally:
|
||||||
self._logstd(result.stderr, logging.TRACE)
|
out_logger.close()
|
||||||
|
err_logger.close()
|
||||||
if result.returncode != 0:
|
if result.returncode != 0:
|
||||||
raise CommandError(result.stderr)
|
raise CommandError("".join(err_logger.lines or out_logger.lines))
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _logstd(cls, data: bytes, level: int):
|
|
||||||
if data:
|
|
||||||
output = data.decode("utf-8").strip()
|
|
||||||
cls._logger.log(level, f"\n{output}\n")
|
|
||||||
|
|||||||
Reference in New Issue
Block a user