Skip to content

Commit 339d3d8

Browse files
author
Vasileios Karakasis
authored
Merge branch 'master' into bugfix/dom_static_linkage
2 parents e0b05b4 + 4fac6b0 commit 339d3d8

File tree

8 files changed

+638
-30
lines changed

8 files changed

+638
-30
lines changed

docs/running.rst

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -849,7 +849,7 @@ All handlers accept the following set of attributes (keys) in their configuratio
849849

850850
* ``level``: (default: ``DEBUG``) The lowest level of log records that this handler can process.
851851
* ``format`` (default: ``'%(message)s'``): Format string for the printout of the log record.
852-
ReFrame supports all the `format strings <https://docs.python.org/3.6/library/logging.html#logrecord-attributes>`__ from Python's logging library and provides the following additional ones:
852+
ReFrame supports all the `log record attributes <https://docs.python.org/3.6/library/logging.html#logrecord-attributes>`__ from Python's logging library and provides the following additional ones:
853853

854854
- ``check_environ``: The programming environment a test is currently executing for.
855855
- ``check_info``: Print live information of the currently executing check.
@@ -877,15 +877,20 @@ All handlers accept the following set of attributes (keys) in their configuratio
877877
- ``osgroup``: The group name of the OS user running ReFrame.
878878
- ``version``: The ReFrame version.
879879

880-
* ``datefmt`` (default: ``'%FT%T'``) The format that will be used for outputting timestamps (i.e., the ``%(asctime)s`` field).
881-
Acceptable formats must conform to standard library's `time.strftime() <https://docs.python.org/3.6/library/time.html#time.strftime>`__ function.
880+
* ``datefmt`` (default: ``'%FT%T'``) The format that will be used for outputting timestamps (i.e., the ``%(asctime)s`` and the ``%(check_job_completion_time)s`` fields).
881+
In addition to the format directives supported by the standard library's `time.strftime() <https://docs.python.org/3.6/library/time.html#time.strftime>`__ function, ReFrame allows you to use the ``%:z`` directive -- a GNU ``date`` extension -- that will print the time zone difference in a RFC3339 compliant way, i.e., ``+/-HH:MM`` instead of ``+/-HHMM``.
882882

883883
.. caution::
884884
The ``testcase_name`` logging attribute is replaced with the ``check_info``, which is now also configurable
885885

886886
.. versionchanged:: 2.10
887887

888888

889+
.. note::
890+
Support for fully RFC3339 compliant time zone formatting.
891+
892+
.. versionadded:: 3.0
893+
889894

890895
File log handlers
891896
"""""""""""""""""

reframe/core/logging.py

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@
55
import numbers
66
import os
77
import pprint
8+
import re
89
import shutil
910
import sys
1011
import socket
11-
from datetime import datetime
12+
import time
1213

1314
import reframe
1415
import reframe.utility.color as color
@@ -132,6 +133,34 @@ def close(self):
132133
super().close()
133134

134135

136+
def _format_time_rfc3339(timestamp, datefmt):
137+
tz_suffix = time.strftime('%z', timestamp)
138+
tz_rfc3339 = tz_suffix[:-2] + ':' + tz_suffix[-2:]
139+
140+
# Python < 3.7 truncates the `%`, whereas later versions don't
141+
return re.sub(r'(%)?\:z', tz_rfc3339, time.strftime(datefmt, timestamp))
142+
143+
144+
class RFC3339Formatter(logging.Formatter):
145+
def formatTime(self, record, datefmt=None):
146+
datefmt = datefmt or self.default_time_format
147+
if '%:z' not in datefmt:
148+
return super().formatTime(record, datefmt)
149+
else:
150+
timestamp = self.converter(record.created)
151+
return _format_time_rfc3339(timestamp, datefmt)
152+
153+
def format(self, record):
154+
datefmt = self.datefmt or self.default_time_format
155+
if record.check_job_completion_time is not None:
156+
ct = self.converter(record.check_job_completion_time)
157+
record.check_job_completion_time = _format_time_rfc3339(
158+
ct, datefmt
159+
)
160+
161+
return super().format(record)
162+
163+
135164
def load_from_dict(logging_config):
136165
if not isinstance(logging_config, collections.abc.Mapping):
137166
raise TypeError('logging configuration is not a dict')
@@ -177,8 +206,7 @@ def _create_file_handler(handler_config):
177206
timestamp = handler_config.get('timestamp', None)
178207
if timestamp:
179208
basename, ext = os.path.splitext(filename)
180-
filename = '%s_%s%s' % (basename,
181-
datetime.now().strftime(timestamp), ext)
209+
filename = '%s_%s%s' % (basename, time.strftime(timestamp), ext)
182210

183211
append = handler_config.get('append', False)
184212
return logging.handlers.RotatingFileHandler(filename,
@@ -305,7 +333,7 @@ def _extract_handlers(handlers_list):
305333
level = handler_config.get('level', 'debug').lower()
306334
fmt = handler_config.get('format', '%(message)s')
307335
datefmt = handler_config.get('datefmt', '%FT%T')
308-
hdlr.setFormatter(logging.Formatter(fmt=fmt, datefmt=datefmt))
336+
hdlr.setFormatter(RFC3339Formatter(fmt=fmt, datefmt=datefmt))
309337
hdlr.setLevel(_check_level(level))
310338
handlers.append(hdlr)
311339

@@ -428,11 +456,7 @@ def _update_check_extras(self):
428456
if self.check.job:
429457
self.extra['check_jobid'] = self.check.job.jobid
430458
if self.check.job.completion_time:
431-
# Use the logging handlers' date format to format
432-
# completion_time
433-
# NOTE: All handlers use the same date format
434-
fmt = self.logger.handlers[0].formatter.datefmt
435-
ct = self.check.job.completion_time.strftime(fmt)
459+
ct = self.check.job.completion_time
436460
self.extra['check_job_completion_time'] = ct
437461

438462
def log_performance(self, level, tag, value, ref,

reframe/core/schedulers/__init__.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#
44

55
import abc
6-
from datetime import datetime
6+
import time
77

88
import reframe.core.environments as env
99
import reframe.core.fields as fields
@@ -17,6 +17,8 @@
1717
class JobScheduler(abc.ABC):
1818
@abc.abstractmethod
1919
def completion_time(self, job):
20+
'''The completion time of this job expressed in seconds from the Epoch.
21+
'''
2022
pass
2123

2224
@abc.abstractmethod
@@ -332,7 +334,7 @@ def wait(self):
332334
raise JobNotStartedError('cannot wait an unstarted job')
333335

334336
self.scheduler.wait(self)
335-
self._completion_time = self._completion_time or datetime.now()
337+
self._completion_time = self._completion_time or time.time()
336338

337339
def cancel(self):
338340
if self.jobid is None:
@@ -346,7 +348,7 @@ def finished(self):
346348

347349
done = self.scheduler.finished(self)
348350
if done:
349-
self._completion_time = self._completion_time or datetime.now()
351+
self._completion_time = self._completion_time or time.time()
350352

351353
return done
352354

reframe/core/schedulers/slurm.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ def completion_time(self, job):
103103
not slurm_state_completed(job.state)):
104104
return self._completion_time
105105

106-
with env.temp_environment(variables={'SLURM_TIME_FORMAT': 'standard'}):
106+
with env.temp_environment(variables={'SLURM_TIME_FORMAT': '%s'}):
107107
completed = os_ext.run_command(
108108
'sacct -S %s -P -j %s -o jobid,end' %
109109
(datetime.now().strftime('%F'), job.jobid),
@@ -116,11 +116,7 @@ def completion_time(self, job):
116116
if not state_match:
117117
return None
118118

119-
self._completion_time = max(
120-
datetime.strptime(s.group('end'), '%Y-%m-%dT%H:%M:%S')
121-
for s in state_match
122-
)
123-
119+
self._completion_time = max(float(s.group('end')) for s in state_match)
124120
return self._completion_time
125121

126122
def _format_option(self, var, option):

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
jsonschema
12
pytest>=3.5.0
23
coverage
34
setuptools

0 commit comments

Comments
 (0)