def __init__(self, status_code, subdir, operation, message, fields, timestamp=None): """Construct a status.log entry. @param status_code: A message status code. Must match the codes accepted by autotest_lib.common_lib.log.is_valid_status. @param subdir: A valid job subdirectory, or None. @param operation: Description of the operation, or None. @param message: A printable string describing event to be recorded. @param fields: A dictionary of arbitrary alphanumeric key=value pairs to be included in the log, or None. @param timestamp: An optional integer timestamp, in the same format as a time.time() timestamp. If unspecified, the current time is used. @raise ValueError: if any of the parameters are invalid """ if not log.is_valid_status(status_code): raise ValueError('status code %r is not valid' % status_code) self.status_code = status_code if subdir and self.BAD_CHAR_REGEX.search(subdir): raise ValueError('Invalid character in subdir string') self.subdir = subdir if operation and self.BAD_CHAR_REGEX.search(operation): raise ValueError('Invalid character in operation string') self.operation = operation # break the message line into a single-line message that goes into the # database, and a block of additional lines that goes into the status # log but will never be parsed message_lines = message.split('\n') self.message = message_lines[0].replace('\t', ' ' * 8) self.extra_message_lines = message_lines[1:] if self.BAD_CHAR_REGEX.search(self.message): raise ValueError('Invalid character in message %r' % self.message) if not fields: self.fields = {} else: self.fields = fields.copy() for key, value in self.fields.iteritems(): if self.BAD_CHAR_REGEX.search(key + value): raise ValueError('Invalid character in %r=%r field' % (key, value)) # build up the timestamp if timestamp is None: timestamp = int(time.time()) self.fields[self.TIMESTAMP_FIELD] = str(timestamp) self.fields[self.LOCALTIME_FIELD] = time.strftime( '%b %d %H:%M:%S', time.localtime(timestamp))
def __init__(self, status_code, subdir, operation, message, fields, timestamp=None): """Construct a status.log entry. @param status_code: A message status code. Must match the codes accepted by autotest_lib.common_lib.log.is_valid_status. @param subdir: A valid job subdirectory, or None. @param operation: Description of the operation, or None. @param message: A printable string describing event to be recorded. @param fields: A dictionary of arbitrary alphanumeric key=value pairs to be included in the log, or None. @param timestamp: An optional integer timestamp, in the same format as a time.time() timestamp. If unspecified, the current time is used. @raise ValueError: if any of the parameters are invalid """ if not log.is_valid_status(status_code): raise ValueError('status code %r is not valid' % status_code) self.status_code = status_code if subdir and self.BAD_CHAR_REGEX.search(subdir): raise ValueError('Invalid character in subdir string') self.subdir = subdir if operation and self.BAD_CHAR_REGEX.search(operation): raise ValueError('Invalid character in operation string') self.operation = operation # break the message line into a single-line message that goes into the # database, and a block of additional lines that goes into the status # log but will never be parsed message_lines = message.split('\n') self.message = message_lines[0].replace('\t', ' ' * 8) self.extra_message_lines = message_lines[1:] if self.BAD_CHAR_REGEX.search(self.message): raise ValueError('Invalid character in message %r' % self.message) if not fields: self.fields = {} else: self.fields = fields.copy() for key, value in self.fields.iteritems(): if type(value) is int: value = str(value) if self.BAD_CHAR_REGEX.search(key + value): raise ValueError('Invalid character in %r=%r field' % (key, value)) # build up the timestamp if timestamp is None: timestamp = int(time.time()) self.fields[self.TIMESTAMP_FIELD] = str(timestamp) self.fields[self.LOCALTIME_FIELD] = time.strftime( '%b %d %H:%M:%S', time.localtime(timestamp))
def _render_record(self, status_code, subdir, operation, status='', epoch_time=None, record_prefix=None, optional_fields=None): """ Internal Function to generate a record to be written into a status log. For use by server_job.* classes only. """ if subdir: if re.match(r'[\n\t]', subdir): raise ValueError('Invalid character in subdir string') substr = subdir else: substr = '----' if not log.is_valid_status(status_code): raise ValueError('Invalid status code supplied: %s' % status_code) if not operation: operation = '----' if re.match(r'[\n\t]', operation): raise ValueError('Invalid character in operation string') operation = operation.rstrip() status = status.rstrip() status = re.sub(r"\t", " ", status) # Ensure any continuation lines are marked so we can # detect them in the status file to ensure it is parsable. status = re.sub(r"\n", "\n" + self.record_prefix + " ", status) if not optional_fields: optional_fields = {} # Generate timestamps for inclusion in the logs if epoch_time is None: epoch_time = int(time.time()) local_time = time.localtime(epoch_time) optional_fields["timestamp"] = str(epoch_time) optional_fields["localtime"] = time.strftime("%b %d %H:%M:%S", local_time) fields = [status_code, substr, operation] fields += ["%s=%s" % x for x in optional_fields.iteritems()] fields.append(status) if record_prefix is None: record_prefix = self.record_prefix msg = '\t'.join(str(x) for x in fields) return record_prefix + msg + '\n'
def record(self, status_code, subdir, operation, status = '', optional_fields=None): """ Record job-level status The intent is to make this file both machine parseable and human readable. That involves a little more complexity, but really isn't all that bad ;-) Format is <status code>\t<subdir>\t<operation>\t<status> status code: (GOOD|WARN|FAIL|ABORT) or START or END (GOOD|WARN|FAIL|ABORT) subdir: MUST be a relevant subdirectory in the results, or None, which will be represented as '----' operation: description of what you ran (e.g. "dbench", or "mkfs -t foobar /dev/sda9") status: error message or "completed sucessfully" ------------------------------------------------------------ Initial tabs indicate indent levels for grouping, and is governed by self.group_level multiline messages have secondary lines prefaced by a double space (' ') """ if subdir: if re.match(r'[\n\t]', subdir): raise ValueError("Invalid character in subdir string") substr = subdir else: substr = '----' if not log.is_valid_status(status_code): raise ValueError("Invalid status code supplied: %s" % status_code) if not operation: operation = '----' if re.match(r'[\n\t]', operation): raise ValueError("Invalid character in operation string") operation = operation.rstrip() if not optional_fields: optional_fields = {} status = status.rstrip() status = re.sub(r"\t", " ", status) # Ensure any continuation lines are marked so we can # detect them in the status file to ensure it is parsable. status = re.sub(r"\n", "\n" + "\t" * self.group_level + " ", status) # Generate timestamps for inclusion in the logs epoch_time = int(time.time()) # seconds since epoch, in UTC local_time = time.localtime(epoch_time) optional_fields["timestamp"] = str(epoch_time) optional_fields["localtime"] = time.strftime("%b %d %H:%M:%S", local_time) fields = [status_code, substr, operation] fields += ["%s=%s" % x for x in optional_fields.iteritems()] fields.append(status) msg = '\t'.join(str(x) for x in fields) msg = '\t' * self.group_level + msg msg_tag = "" if "." in self.log_filename: msg_tag = self.log_filename.split(".", 1)[1] self.harness.test_status_detail(status_code, substr, operation, status, msg_tag) self.harness.test_status(msg, msg_tag) # log to stdout (if enabled) #if self.log_filename == self.DEFAULT_LOG_FILENAME: logging.info(msg) # log to the "root" status log status_file = os.path.join(self.resultdir, self.log_filename) open(status_file, "a").write(msg + "\n") # log to the subdir status log (if subdir is set) if subdir: dir = os.path.join(self.resultdir, subdir) status_file = os.path.join(dir, self.DEFAULT_LOG_FILENAME) open(status_file, "a").write(msg + "\n")