Skip to content

Commit

Permalink
Refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
dc-snl committed Feb 11, 2021
1 parent c5447a9 commit 25ca040
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 109 deletions.
44 changes: 22 additions & 22 deletions logger/classes.py
Original file line number Diff line number Diff line change
@@ -1,61 +1,61 @@
#!/usr/bin/env python3
from .util import runCommandWithConsole, checkIfProgramExistsInPath
from .util import run_teed_command, program_exists_in_path
from abc import abstractmethod
from collections import namedtuple
import inspect
from multiprocessing import Process, Manager
from pathlib import Path
import time

def traceCollector(command, **kwargs):
traceName = kwargs["trace"]
Collectors = [c for c in Trace.subclasses if c.traceName == traceName]
def trace_collector(command, **kwargs):
trace_name = kwargs["trace"]
Collectors = [c for c in Trace.subclasses if c.trace_name == trace_name]
if len(Collectors) == 1:
Collector = Collectors[0]
return Collector(command, **kwargs)
elif len(Collectors) == 0:
raise RuntimeError(f"Unsupported trace type: {traceName}")
raise RuntimeError(f"Unsupported trace type: {trace_name}")
else:
raise RuntimeError(f"Multiple trace types match {traceName}")
raise RuntimeError(f"Multiple trace types match {trace_name}")

def statsCollectors(**kwargs):
def stats_collectors(**kwargs):
collectors = []
if "measure" in kwargs:
interval = kwargs["interval"] if "interval" in kwargs else 1.0
stats = {}
manager = Manager()
for Collector in StatsCollector.subclasses:
if Collector.statName in kwargs["measure"]:
if Collector.stat_name in kwargs["measure"]:
collectors.append(Collector(interval, manager))
return collectors

Stat = namedtuple("Stat", ["data", "svg"])

class Trace:
traceName = "undefined"
trace_name = "undefined"
subclasses = []
def subclass(TraceSubclass):
if (issubclass(TraceSubclass, Trace)):
Trace.subclasses.append(TraceSubclass)
return TraceSubclass
def __init__(self, command, **kwargs):
checkIfProgramExistsInPath(self.traceName)
program_exists_in_path(self.trace_name)
if kwargs.get("trace_path"):
self.outputPath = Path(kwargs["trace_path"])
self.output_path = Path(kwargs["trace_path"])
else:
self.outputPath = Path(f"{self.traceName}.log")
self.output_path = Path(f"{self.trace_name}.log")
self.command = command
@property
@abstractmethod
def traceArgs(self):
def trace_args(self):
raise AbstractMethod()
def __call__(self, **kwargs):
command = f"{self.traceArgs} -- {self.command}"
completedProcess = runCommandWithConsole(command, **kwargs)
return completedProcess
command = f"{self.trace_args} -- {self.command}"
completed_process = run_teed_command(command, **kwargs)
return completed_process

class StatsCollector:
statName = "undefined"
stat_name = "undefined"
subclasses = []
def subclass(StatsCollectorSubclass):
if (issubclass(StatsCollectorSubclass, StatsCollector)):
Expand All @@ -74,11 +74,11 @@ def loop(self):
def collect(self):
raise AbstractMethod()
@abstractmethod
def unproxiedStats(self):
def unproxied_stats(self):
raise AbstractMethod()
def finish(self):
self.process.terminate()
return self.unproxiedStats()
return self.unproxied_stats()

class FileAlreadyExists(RuntimeError):
def __init__(self, file):
Expand All @@ -87,7 +87,7 @@ def __init__(self, file):

class AbstractMethod(NotImplementedError):
def __init__(self):
className = inspect.stack()[1].frame.f_locals['self'].__class__.__name__
methodName = inspect.stack()[1].function
super().__init__(f"{className} must implement {methodName}()")
class_name = inspect.stack()[1].frame.f_locals['self'].__class__.__name__
method_name = inspect.stack()[1].function
super().__init__(f"{class_name} must implement {method_name}()")

120 changes: 57 additions & 63 deletions logger/logger.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env python3

from .classes import Trace, StatsCollector, Stat, traceCollector, statsCollectors
from .util import makeSVGLineChart, runCommandWithConsole, nestedSimpleNamespaceToDict
from .classes import Trace, StatsCollector, Stat, trace_collector, stats_collectors
from .util import make_svg_line_chart, run_teed_command, nested_SimpleNamespace_to_dict
from collections.abc import Iterable, Mapping
import datetime
import distutils.dir_util as dir_util
Expand Down Expand Up @@ -44,7 +44,7 @@ def default(self, obj):
elif isinstance(obj, (int, float, str, bytes)):
return obj
elif isinstance(obj, Mapping):
return { k:self.default(v) for k, v in obj.items() }
return {k:self.default(v) for k,v in obj.items()}
elif isinstance(obj, tuple):
tup = {
'__type__': 'tuple',
Expand Down Expand Up @@ -698,8 +698,8 @@ def log(self, msg, cmd, cwd=None, live_stdout=False,
stdout_str=return_info,
stderr_str=return_info,
trace_str=return_info,
stdout_file=stdout_path,
stderr_file=stderr_path,
stdout_path=stdout_path,
stderr_path=stderr_path,
trace_path=trace_path,
devnull_stdin=stdin_redirect,
pwd=cwd,
Expand All @@ -710,45 +710,57 @@ def log(self, msg, cmd, cwd=None, live_stdout=False,
sec = int(result.wall / 1000) % 60
log["duration"] = f"{hrs}h {min}m {sec}s"
log["return_code"] = result.returncode
log = {**log, **nestedSimpleNamespaceToDict(result)}
log = {**log, **nested_SimpleNamespace_to_dict(result)}

self.log_book.append(log)

return {'return_code': log['return_code'],
'stdout': result.stdout, 'stderr': result.stderr}

def run(self, command, **kwargs):
oldPWD = os.getcwd()
if kwargs.get("pwd"):
os.chdir(kwargs.get("pwd"))
completed_process, trace_output = None, None
collectors = stats_collectors(**kwargs)
stats = {} if len(collectors) > 0 else None
for key in ["stdout_str", "stderr_str", "trace_str"]:
if key not in kwargs:
kwargs[key] = True
auxInfo = auxiliaryInformation()
traceOutput, stats, completedProcess = runCommand(command, **kwargs)
setattr(completedProcess, "trace_path", traceOutput)
setattr(completedProcess, "stats", stats)
if kwargs.get("trace_str") and traceOutput:
with open(traceOutput) as f:
setattr(completedProcess, "trace", f.read())
old_pwd = os.getcwd()
if kwargs.get("pwd"):
os.chdir(kwargs.get("pwd"))
aux_info = auxiliary_information()
for collector in collectors:
collector.start()
if "trace" in kwargs:
trace = trace_collector(command, **kwargs)
completed_process = trace(**kwargs)
trace_output = trace.output_path
else:
completed_process = run_teed_command(command, **kwargs)
for collector in collectors:
stats[collector.stat_name] = collector.finish()
setattr(completed_process, "trace_path", trace_output)
setattr(completed_process, "stats", stats)
if kwargs.get("trace_str") and trace_output:
with open(trace_output) as f:
setattr(completed_process, "trace", f.read())
else:
setattr(completedProcess, "trace", None)
setattr(completed_process, "trace", None)
if kwargs.get("pwd"):
os.chdir(oldPWD)
return SimpleNamespace(**completedProcess.__dict__, **auxInfo.__dict__)
os.chdir(old_pwd)
return SimpleNamespace(**completed_process.__dict__, **aux_info.__dict__)

def auxiliaryInformation():
def auxiliary_information():
return SimpleNamespace(
pwd = auxiliaryCommandOutput(posix="pwd", nt="cd", strip=True),
environment = auxiliaryCommandOutput(posix="env", nt="set"),
umask = auxiliaryCommandOutput(posix="umask", strip=True),
user = auxiliaryCommandOutput(posix="whoami", nt="whoami", strip=True),
group = auxiliaryCommandOutput(posix="id -gn", strip=True),
shell = auxiliaryCommandOutput(posix="printenv SHELL", strip=True),
ulimit = auxiliaryCommandOutput(posix="ulimit -a")
pwd = auxiliary_command_output(posix="pwd", nt="cd", strip=True),
environment = auxiliary_command_output(posix="env", nt="set"),
umask = auxiliary_command_output(posix="umask", strip=True),
user = auxiliary_command_output(posix="whoami", nt="whoami", strip=True),
group = auxiliary_command_output(posix="id -gn", strip=True),
shell = auxiliary_command_output(posix="printenv SHELL", strip=True),
ulimit = auxiliary_command_output(posix="ulimit -a")
)

def auxiliaryCommandOutput(**kwargs):
def auxiliary_command_output(**kwargs):
stdout = None
if os.name in kwargs:
c = subprocess.run(kwargs[os.name], capture_output=True, shell=True, check=True)
Expand All @@ -757,34 +769,16 @@ def auxiliaryCommandOutput(**kwargs):
stdout = stdout.strip()
return stdout

def runCommand(command, **kwargs):
completedProcess, traceOutput = None, None
collectors = statsCollectors(**kwargs)
stats = {} if len(collectors) > 0 else None
for collector in collectors:
collector.start()
if "trace" in kwargs:
traceOutput, completedProcess = trace(command, **kwargs)
else:
completedProcess = runCommandWithConsole(command, **kwargs)
for collector in collectors:
stats[collector.statName] = collector.finish()
return traceOutput, stats, completedProcess

def trace(command, **kwargs):
trace = traceCollector(command, **kwargs)
return trace.outputPath, trace(**kwargs)

@Trace.subclass
class Strace(Trace):
traceName = "strace"
trace_name = "strace"
def __init__(self, command, **kwargs):
super().__init__(command, **kwargs)
self.summary = True if kwargs.get("summary") else False
self.expression = kwargs.get("expression")
@property
def traceArgs(self):
args = f"strace -f -o {self.outputPath}"
def trace_args(self):
args = f"strace -f -o {self.output_path}"
if self.summary:
args += " -c"
if self.expression:
Expand All @@ -793,14 +787,14 @@ def traceArgs(self):

@Trace.subclass
class Ltrace(Trace):
traceName = "ltrace"
trace_name = "ltrace"
def __init__(self, command, **kwargs):
super().__init__(command, **kwargs)
self.summary = True if kwargs.get("summary") else False
self.expression = kwargs.get("expression")
@property
def traceArgs(self):
args = f"ltrace -C -f -o {self.outputPath}"
def trace_args(self):
args = f"ltrace -C -f -o {self.output_path}"
if self.summary:
args += " -c"
if self.expression:
Expand All @@ -809,7 +803,7 @@ def traceArgs(self):

@StatsCollector.subclass
class DiskStatsCollector(StatsCollector):
statName = "disk"
stat_name = "disk"
def __init__(self, interval, manager):
super().__init__(interval, manager)
self.stats = manager.dict()
Expand All @@ -820,38 +814,38 @@ def collect(self):
timestamp = round(time.time() * 1000)
for m in self.mountpoints:
self.stats[m].append((timestamp, psutil.disk_usage(m).percent))
def unproxiedStats(self):
def makeStat(stat):
def unproxied_stats(self):
def make_stat(stat):
data = list(stat)
svg = makeSVGLineChart(data)
svg = make_svg_line_chart(data)
return Stat(data, svg)
return { k:makeStat(v) for k, v in self.stats.items() }
return {k:make_stat(v) for k,v in self.stats.items()}

@StatsCollector.subclass
class CPUStatsCollector(StatsCollector):
statName = "cpu"
stat_name = "cpu"
def __init__(self, interval, manager):
super().__init__(interval, manager)
self.stats = manager.list()
def collect(self):
timestamp = round(time.time() * 1000)
self.stats.append((timestamp, psutil.cpu_percent(interval=None)))
def unproxiedStats(self):
def unproxied_stats(self):
data = list(self.stats)
svg = makeSVGLineChart(data)
svg = make_svg_line_chart(data)
return Stat(data, svg)

@StatsCollector.subclass
class MemoryStatsCollector(StatsCollector):
statName = "memory"
stat_name = "memory"
def __init__(self, interval, manager):
super().__init__(interval, manager)
self.stats = manager.list()
def collect(self):
timestamp = round(time.time() * 1000)
self.stats.append((timestamp, psutil.virtual_memory().percent))
def unproxiedStats(self):
def unproxied_stats(self):
data = list(self.stats)
svg = makeSVGLineChart(data)
svg = make_svg_line_chart(data)
return Stat(data, svg)

Loading

0 comments on commit 25ca040

Please sign in to comment.