def report(self, reporter, tag, ts, delta, level, message, alevel, attachments): """ Report messages to the console or logfile in a line-by-line format prefixing each line. """ # FIXME: rework the streaming object so it can multiplex the # output to two file descriptors *and* decide based on # verbosity; this way we do not have to walk the attachmen # tree and format twice if the log file is enabled. # the prefix to each line is stored in thraed-local-storage # (with commonl.tls_prefix_c), where it is picked up by the # stream buffer object commonl.io_tls_prefix_lines_c. This, # before printing, adds the prefix to each line. _prefix = "%s%d/%s\t%s [+%.1fs]: " % ( tag, level, tcfl.msgid_c.ident(), reporter._report_prefix, delta ) with commonl.tls_prefix_c(self.tls, _prefix): console_p, logfile_p = self._shall_do(level) message += "\n" if console_p: self.consolef.write(message) if self.logf and logfile_p: self.logf.write(message) if attachments != None: assert isinstance(attachments, dict) console_p, logfile_p = self._shall_do(alevel) if console_p or logfile_p: _aprefix = "%s%d/%s\t%s [+%.1fs]: " % ( tag, alevel, tcfl.msgid_c.ident(), reporter._report_prefix, delta ) with commonl.tls_prefix_c(self.tls, _aprefix): if console_p: commonl.data_dump_recursive_tls(attachments, self.tls, of = self.consolef) if self.logf and logfile_p: commonl.data_dump_recursive_tls(attachments, self.tls, of = self.logf) self.consolef.flush() if self.logf: self.logf.flush()
def report(self, testcase, target, tag, ts, delta, level, message, alevel, attachments): """ Writes data to per-testcase/target temporary logfiles to render upon completion all the configured templates. We don't even check the levels, we log everything here by INFO <= 4. We report to the file ``TAG LEVEL CODE MESSAGE`` which we'll parse later to generate the report. When a *COMPLETION* message is reported, we assume the testcase is completed and call _mkreport() to render the templates. """ if testcase == tc.tc_global: # ignore the global reporter return # FIXME: config if tag == "INFO" and level > 4: # ignore way chatty stuff return # Who's is this coming from? if target: tgname = "@" + target.fullid else: tgname = "@local" # Note we open the file for every thing we report -- we can be # running *A LOT* of stuff in parallel and run out of file # descriptors. get stream LRU caches them -- pass arguments # like that (instead of passing the testcase) so the LRU cache # decorator in _get_fd() can use it to hash. # NOTE WE ALWAYS save relative to the testcase's tmpdir, not # the reporter.tmpdir, which might be different (if the # reporter is a target) of = self._get_fd(testcase.ticket, testcase.tmpdir) # Remove the ticket from the ident string, as it will be # the same for all and makes no sense to have it. ident = testcase.ident() if ident == "": # If empty, give it a to snip token that we'll replace # later in mkreport ident = "<snip>" # The of file descriptor uses a buffer implementation that # takes the prefix from a thread-local-storage for every line # it writes, so just use that to flush the message and the # attachments. _prefix = u"%s %d %s %s\t " % (tag, level, ident, tgname) with commonl.tls_prefix_c(self.tls, _prefix): if not message.endswith('\n'): message += "\n" # @of writes _prefix for us, because we set it with # commonl.tls_prefix_c and @of is a # commonl.io_tls_prefix_lines_c(), which prefixes _prefix # on each line. # the timestamp is a horrible hack which we have to fix # properly by propagating it as a field in the temporary # log so later the templates can decide how to render it # if timezone is not set we will use local, if it is, we # change it to the one set by the user. # we need to localize the timezone before changing it, we can get # it in UTC straight from the timestamp, so no need to be guessing # the timezone of the machine. if self.timezone: try: d = datetime.datetime.fromtimestamp( ts, pytz.timezone(self.timezone)) except pytz.exceptions.UnknownTimeZoneError: logging.warning( f"bad timezone '{self.timezone}' set for reporting." f" REPORT_TZ: {os.environ.get('REPORT_TZ', 'n/a/')}," f" TZ: {os.environ.get('TZ', 'n/a')}; defaulting to local" ) d = datetime.datetime.fromtimestamp(ts) else: d = datetime.datetime.fromtimestamp(ts) # e.g. 22-02-28.12:56:00 d = d.strftime('%y-%m-%d.%H:%M:%S') of.write(f"[{d} +{delta:.1f}s] " + message) if attachments != None: # FIXME: \x01\x01 hack to denote an attachment, will # replace in _log_iterator() because the intermediate # format we have splits spaces--real fix will be to # convert that format to something more flexible _prefix = u"%s %d %s %s\t \x01\x01" % (tag, level, ident, tgname) with commonl.tls_prefix_c(self.tls, _prefix): assert isinstance(attachments, dict) commonl.data_dump_recursive_tls(attachments, self.tls, of=of) of.flush() # This is an indication that the testcase is done and we # can generate final reports if message.startswith("COMPLETION "): of.flush() self._mkreport(tag, testcase.ticket, testcase, message) # Wipe the file, it might have errors--it might be not # a file, so wipe hard #commonl.rm_f(self.fs[reporter.ticket]) del self.fs[testcase.ticket] # can't remove from the _get_fd() cache, but it will be # removed once it's unused of.close() del of
def report(self, reporter, tag, ts, delta, level, message, alevel, attachments): """ Writes data to per-testcase/target temporary logfiles to render upon completion all the configured templates. We don't even check the levels, we log everything here by INFO <= 4. We report to the file ``TAG LEVEL CODE MESSAGE`` which we'll parse later to generate the report. When a *COMPLETION* message is reported, we assume the testcase is completed and call _mkreport() to render the templates. """ if reporter == tc.tc_global: # ignore the global reporter return # FIXME: config if tag == "INFO" and level > 4: # ignore way chatty stuff return # Note we open the file for every thing we report -- we can be # running *A LOT* of stuff in parallel and run out of file # descriptors. get stream LRU caches them -- pass arguments # like that (instead of passing the testcase) so the LRU cache # decorator in _get_fd() can use it to hash. of = self._get_fd(reporter.ticket, reporter.tmpdir) # Extract the target name where this message came from (if the # reporter is a target, otherwise we consider it a local message) if isinstance(reporter, tc.target_c): tgname = "@" + reporter.fullid + reporter.bsp_suffix() else: tgname = "@local" # Remove the ticket from the ident string, as it will be # the same for all and makes no sense to have it. ident = self.ident_simplify(tcfl.msgid_c.ident(), reporter.kws.get('runid', ''), reporter.kws.get('tc_hash', "")) if ident == "": # If empty, give it a to snip token that we'll replace # later in mkreport ident = "<snip>" # The of file descriptor uses a buffer implementation that # takes the prefix from a thread-local-storage for every line # it writes, so just use that to flush the message and the # attachments. _prefix = u"%s %d %s %s\t " % (tag, level, ident, tgname) with commonl.tls_prefix_c(self.tls, _prefix): if not message.endswith('\n'): message += "\n" of.write(message) if attachments != None: assert isinstance(attachments, dict) commonl.data_dump_recursive_tls(attachments, self.tls, of = of) of.flush() # This is an indication that the testcase is done and we # can generate final reports if message.startswith("COMPLETION "): of.flush() self._mkreport(tag, reporter.ticket, reporter, message) # Wipe the file, it might have errors--it might be not # a file, so wipe hard #commonl.rm_f(self.fs[reporter.ticket]) del self.fs[reporter.ticket] # can't remove from the _get_fd() cache, but it will be # removed once it's unused of.close() del of
def report(self, testcase, target, tag, ts, delta, level, message, alevel, attachments): """ Report messages to the console or logfile in a line-by-line format prefixing each line. """ # FIXME: rework the streaming object so it can multiplex the # output to two file descriptors *and* decide based on # verbosity; this way we do not have to walk the attachmen # tree and format twice if the log file is enabled. # the prefix to each line is stored in thraed-local-storage # (with commonl.tls_prefix_c), where it is picked up by the # stream buffer object commonl.io_tls_prefix_lines_c. This, # before printing, adds the prefix to each line. if target: fullid = "|" + target.fullid else: fullid = "" tag_to_color = { "PASS": ("green", ), "ERRR": ("magenta", ), "FAIL": ("red", ), "BLCK": ("yellow", ), "SKIP": ('cyan', ), # no orange available... 'DATA': ("blue", ), } console_p, logfile_p = self._shall_do(testcase, level, message) if console_p: # if this is a terminal, colorize the TAG in the main # message so we can easily locate issues; note we don't # colorize the tag for attachments, just for the main # message. # We don't colorize INFO nor DATA # we don't do windows yet if sys.platform != "win32" and self.consolef.isatty( ) and tag in tag_to_color: args = tag_to_color[tag] _taglevel = termcolor.colored(f"{tag}{level}", *args, attrs=['bold']) _prefix = _taglevel + \ f"/{termcolor.colored(testcase.runid_hashid, attrs = [ 'bold' ])}{testcase.ident()}" \ f" {termcolor.colored(testcase._report_prefix, *args)}{fullid}" \ f" [+{delta:0.1f}s]: " else: _prefix = \ f"{tag}{level}/{testcase.runid_hashid}{testcase.ident()}" \ f" {testcase._report_prefix}{fullid} [+{delta:0.1f}s]: " with commonl.tls_prefix_c(self.tls, _prefix): message += "\n" self.consolef.write(message) if logfile_p: _prefix = \ f"{tag}{level}/{testcase.runid_hashid}{testcase.ident()}" \ f" {testcase._report_prefix}{fullid} [+{delta:0.1f}s]: " with commonl.tls_prefix_c(self.tls, _prefix): message += "\n" if self.logf and logfile_p: self.logf.write(message) if attachments != None: assert isinstance(attachments, dict) console_p, logfile_p = self._shall_do(testcase, alevel) if console_p or logfile_p: # note the extra two spaces at the end, those are so # the attachments are printed indented; it's much # easier to read _aprefix = \ f"{tag}{alevel}/{testcase.runid_hashid}{testcase.ident()}" \ f" {testcase._report_prefix}{fullid} [+{delta:0.1f}s]: " with commonl.tls_prefix_c(self.tls, _aprefix): if console_p: commonl.data_dump_recursive_tls(attachments, self.tls, of=self.consolef) if self.logf and logfile_p: commonl.data_dump_recursive_tls(attachments, self.tls, of=self.logf) self.consolef.flush() if self.logf: self.logf.flush()