Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions reframe.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#!/usr/bin/env python3
import sys
import reframe.frontend.cli as cli

if __name__ == '__main__':
Expand Down
21 changes: 11 additions & 10 deletions reframe/core/environments.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
import subprocess
import reframe.utility.os as os_ext

from reframe.core.exceptions import ReframeError, CommandError, \
CompilationError
from reframe.core.exceptions import ReframeError, CommandError, CompilationError
from reframe.core.fields import *
from reframe.core.modules import *

Expand Down Expand Up @@ -40,18 +39,20 @@ def set_variable(self, name, value):

def load(self):
"""Load environment."""
for k, v in self.variables.items():
if k in os.environ:
self._saved_variables[k] = os.environ[k]
os.environ[k] = v

# conlicted module list must be filled at the time of load
# conflicted module list must be filled at the time of load
for m in self.modules:
if module_present(m):
self._preloaded.add(m)

self._conflicted += module_force_load(m)

for k, v in self.variables.items():
if k in os.environ:
self._saved_variables[k] = os.environ[k]

os.environ[k] = os.path.expandvars(v)

self.loaded = True


Expand Down Expand Up @@ -94,15 +95,15 @@ def emit_load_instructions(self, builder):
# FIXME: Does not correspond to the actual process in unload()
def emit_unload_instructions(self, builder):
"""Emit shell instructions for loading this environment."""
for k, v in self.variables.items():
builder.unset_variable(k)

for m in self.modules:
builder.verbatim('module unload %s' % m)

for m in self._conflicted:
builder.verbatim('module load %s' % m)

for k, v in self.variables.items():
builder.unset_variable(k)


def __eq__(self, other):
return \
Expand Down
27 changes: 15 additions & 12 deletions reframe/core/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def __str__(self):
return self.message


class RegressionFatalError(ReframeError):
class ReframeFatalError(ReframeError):
pass


Expand All @@ -31,18 +31,19 @@ class ConfigurationError(ReframeError):

class CommandError(ReframeError):
def __init__(self, command, stdout, stderr, exitcode, timeout=0):
if not isinstance(command, str):
self.command = ' '.join(command)
else:
self.command = command

if timeout:
super().__init__(
"Command '%s' timed out after %d s" % (command, timeout))
"Command `%s' timed out after %d s" % (self.command, timeout))

else:
super().__init__(
"Command '%s' failed with exit code: %d" % (command, exitcode))

if not isinstance(command, str):
self.command = ' '.join(command)
else:
self.command = command
"Command `%s' failed with exit code: %d" % \
(self.command, exitcode))

self.stdout = stdout
self.stderr = stderr
Expand All @@ -51,10 +52,12 @@ def __init__(self, command, stdout, stderr, exitcode, timeout=0):


def __str__(self):
return '{command : "%s", stdout : "%s", stderr : "%s", ' \
'exitcode : %s, timeout : %d }' % \
(self.command, self.stdout, self.stderr,
self.exitcode, self.timeout)
ret = '\n' + super().__str__() + \
"\n=== STDOUT ===\n" + \
self.stdout + \
"\n=== STDERR ===\n" + \
self.stderr
return ret


class CompilationError(CommandError):
Expand Down
256 changes: 256 additions & 0 deletions reframe/core/logging.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
import logging
import os
import logging.handlers
import sys
import shutil

from datetime import datetime

from reframe.settings import settings
from reframe.core.exceptions import ConfigurationError, ReframeError

# Reframe's log levels
CRITICAL = 50
ERROR = 40
WARNING = 30
INFO = 20
VERBOSE = 19
DEBUG = 10
NOTSET = 0


_log_level_names = {
CRITICAL : 'critical',
ERROR : 'error',
WARNING : 'warning',
INFO : 'info',
VERBOSE : 'verbose',
DEBUG : 'debug',
NOTSET : 'undefined'
}

_log_level_values = {
'critical' : CRITICAL,
'error' : ERROR,
'warning' : WARNING,
'info' : INFO,
'verbose' : VERBOSE,
'debug' : DEBUG,
'undefined' : NOTSET,
'notset' : NOTSET
}

def _check_level(level):
if isinstance(level, int):
ret = level
elif isinstance(level, str):
norm_level = level.lower()
if norm_level not in _log_level_values:
raise ReframeError('logger level %s not available' % level)
else:
ret = _log_level_values[norm_level]
else:
raise TypeError('logger level %s not an int or a valid string' % level)

return ret


# Redefine handlers so as to use our levels

class Handler(logging.Handler):
def setLevel(self, level):
self.level = _check_level(level)


class StreamHandler(Handler, logging.StreamHandler):
pass


class RotatingFileHandler(Handler, logging.handlers.RotatingFileHandler):
pass


class FileHandler(Handler, logging.FileHandler):
pass


class NullHandler(Handler, logging.NullHandler):
pass


def load_from_dict(logging_config):
if not isinstance(logging_config, dict):
raise ConfigurationError('logging configuration is not a dict')

level = logging_config.get('level', 'info').lower()
handlers_dict = logging_config.get('handlers', None)

# if not handlers_dict:
# raise ConfigurationError('no entry for handlers was found')

logger = Logger('reframe')
logger.setLevel(_log_level_values[level])

for handler in _extract_handlers(handlers_dict):
logger.addHandler(handler)

return logger


def _extract_handlers(handlers_dict):
handlers = []
if not handlers_dict:
raise ConfigurationError('no handlers are defined for logger')

for filename, handler_config in handlers_dict.items():
if not isinstance(handler_config, dict):
raise ConfigurationError(
'handler %s is not a dictionary' % filename
)

level = handler_config.get('level', 'debug').lower()
fmt = handler_config.get('format', '%(message)s')
datefmt = handler_config.get('datefmt', '%FT%T')
append = handler_config.get('append', False)
timestamp = handler_config.get('timestamp', None)

if filename == '&1':
hdlr = StreamHandler(stream=sys.stdout)
elif filename == '&2':
hdlr = StreamHandler(stream=sys.stderr)
else:
if timestamp:
basename, ext = os.path.splitext(filename)
filename = '%s_%s%s' % (
basename, datetime.now().strftime(timestamp), ext
)

hdlr = RotatingFileHandler(
filename, mode='a+' if append else 'w+'
)

hdlr.setFormatter(logging.Formatter(fmt=fmt, datefmt=datefmt))
hdlr.setLevel(level)
handlers.append(hdlr)

return handlers


class Logger(logging.Logger):
def __init__(self, name, level=logging.NOTSET):
# We will set the logger level ourselves so as to bypass the base class'
# check
super().__init__(name, logging.NOTSET)
self.level = _check_level(level)
self.check = None


def setLevel(self, level):
self.level = _check_level(level)


def makeRecord(self, name, level, fn, lno, msg, args, exc_info,
func=None, extra=None, sinfo=None):
# Setup dynamic fields of the check
if self.check and self.check.job:
extra['check_jobid'] = self.check.job.jobid

record = super().makeRecord(name, level, fn, lno, msg, args, exc_info,
func, extra, sinfo)
try:
# Fill in our name for the record
record.levelname = _log_level_names[level]
except KeyError:
# Go with the default level name of Python logging
pass

return record


# Override all the convenience logging functions, because we want to make
# sure that they map to our level definitions

def critical(self, msg, *args, **kwargs):
return self.log(CRITICAL, msg, *args, **kwargs)


def error(self, msg, *args, **kwargs):
return self.log(ERROR, msg, *args, **kwargs)


def warning(self, msg, *args, **kwargs):
return self.log(WARNING, msg, *args, **kwargs)


def info(self, msg, *args, **kwargs):
return self.log(INFO, msg, *args, **kwargs)


def verbose(self, message, *args, **kwargs):
self.log(VERBOSE, message, *args, **kwargs)


def debug(self, message, *args, **kwargs):
self.log(DEBUG, message, *args, **kwargs)


class LoggerAdapter(logging.LoggerAdapter):
def __init__(self, logger = None, check = None):
super().__init__(
logger,
{
'check_name' : check.name if check else 'reframe',
'check_jobid' : '-1'
}
)
if self.logger:
self.logger.check = check


def setLevel(self, level):
if self.logger:
super().setLevel(level)


# Override log() function to treat `None` loggers
def log(self, level, msg, *args, **kwargs):
if self.logger:
super().log(level, msg, *args, **kwargs)


def verbose(self, message, *args, **kwargs):
self.log(VERBOSE, message, *args, **kwargs)


# A logger that doesn't log anything
null_logger = LoggerAdapter()

_logger = None
_frontend_logger = null_logger

def configure_logging(config):
global _logger
global _frontend_logger

if config == None:
_logger = None
_frontend_logger = null_logger
return

_logger = load_from_dict(config)
_frontend_logger = LoggerAdapter(_logger)


def save_log_files(dest):
os.makedirs(dest, exist_ok=True)
for hdlr in _logger.handlers:
if isinstance(hdlr, logging.FileHandler):
shutil.copy(hdlr.baseFilename, dest, follow_symlinks=True)

def getlogger(logger_kind, *args, **kwargs):
if logger_kind == 'frontend':
return _frontend_logger
elif logger_kind == 'check':
return LoggerAdapter(_logger, *args, **kwargs)
else:
raise ReframeError('unknown kind of logger: %s' % logger_kind)
Loading