def translate(self, lname, show_popup=False, customer=None): ''' Translate a long name into a short name, maybe. Memorize all names, translated or not Strip leading/trailing double quotes :param lname: the name :return: If shortened HTML string of shortened name with popup containing long name else not-so-long name. ''' if lname.startswith("\"") and lname.endswith("\""): lname = lname[1:-1] try: idx = self.longnames.index(lname) except: self.longnames.append(lname) idx = self.longnames.index(lname) # return as-given if short enough if customer is not None: self.customer_dict[lname].append(customer) if len(lname) < self.threshold: return lname sname = self.prefix + "_" + str(idx) if customer is not None: self.customer_dict[sname].append(customer) if show_popup: return "<span title=\"" + common.html_escape(lname) + "\">" + sname + "</span>" else: return sname
def translate(self, lname, show_popup=False, customer=None): ''' Translate a long name into a short name, maybe. Memorize all names, translated or not Strip leading/trailing double quotes :param lname: the name :param show_popup: if true then embellish returned name with long name popup :param customer: optional ParsedLogLine for customer_dict :return: If shortened HTML string of shortened name with popup containing long name else not-so-long name. ''' if lname.startswith("\"") and lname.endswith("\""): lname = lname[1:-1] try: idx = self.longnames.index(lname) except: self.longnames.append(lname) idx = self.longnames.index(lname) # return as-given if short enough if customer is not None: self.customer_dict[lname].append(customer) if len(lname) < self.threshold: return lname sname = self.prefix + "_" + str(idx) if customer is not None: self.customer_dict[sname].append(customer) if show_popup: return "<span title=\"" + common.html_escape(lname) + "\">" + sname + "</span>" else: return sname
def longname(self, idx, html_escape=False): ''' Get the common.html_escape'd long name :param idx: :param html_escape: true if caller wants the string for html display :return: ''' return common.html_escape(self.longnames[idx]) if html_escape else self.longnames[idx]
def sname_to_popup(self, sname): if not sname.startswith(self.prefix): raise ValueError("Short name '%s' does not start with prefix '%s'" % (sname, self.prefix)) try: lname = self.longnames[ int(sname[ (len(self.prefix) + 1): ])] except: raise ValueError("Short name '%s' did not translate to a long name" % (sname)) return "<span title=\"" + common.html_escape(lname) + sname + "</span>"
def sname_to_popup(self, sname): if not sname.startswith(self.prefix): raise ValueError("Short name '%s' does not start with prefix '%s'" % (sname, self.prefix)) try: lname = self.longnames[int(sname[(len(self.prefix) + 1):])] except: raise ValueError("Short name '%s' did not translate to a long name" % (sname)) return "<span title=\"" + common.html_escape(lname) + sname + "</span>"
def htmlDump(self, with_link=False): ''' Print the name table as an unnumbered list to stdout long names are common.html_escape'd :param with_link: true if link name link name is hyperlinked targeting itself :return: null ''' if len(self.longnames) > 0: print ("<h3>" + self.prefix + " Name Index</h3>") print ("<ul>") for i in range(0, len(self.longnames)): name = self.prefix + "_" + str(i) dump_anchor = "<a name=\"%s_dump\"></a>" % (name) if with_link: name = "<a href=\"#%s\">%s</a>" % (name, name) print ("<li> " + dump_anchor + name + " - " + common.html_escape(self.longnames[i]) + "</li>") print ("</ul>")
def htmlDump(self, with_link=False): ''' Print the name table as an unnumbered list to stdout long names are common.html_escape'd :param with_link: true if link name link name is hyperlinked targeting itself :return: null ''' if len(self.longnames) > 0: print("<h3>" + self.prefix + " Name Index</h3>") print("<ul>") for i in range(0, len(self.longnames)): name = self.prefix + "_" + str(i) dump_anchor = "<a name=\"%s_dump\"></a>" % (name) if with_link: name = "<a href=\"#%s\">%s</a>" % (name, name) print("<li> " + dump_anchor + name + " - " + common.html_escape(self.longnames[i]) + "</li>") print("</ul>")
def write_subfiles(self): # Q: Where to put the generated files? A: odir odir = self.odir() odirs = ['dummy'] # dirs indexed by log of n-lines os.makedirs(odir) for i in range(1, self.hist_max): nrange = ("10e%d" % (i)) ndir = os.path.join(odir, nrange) os.makedirs(ndir) odirs.append(ndir) for k, c in dict_iteritems(self.connections): cdir = odirs[self.log_of(len(c.lines))] opath = os.path.join(cdir, (c.disp_name() + ".log")) with open(opath, 'w') as f: for l in c.lines: f.write(l) xfer0 = 0 for rc in self.conns_by_size_transfer: if rc.transfers == 0: xfer0 += 1 print("<a name=\"c_conn_xfersize\"></a>") print("<h3>Connections by transfer count (N=%d)</h3>" % (len(self.conns_by_size_transfer) - xfer0)) print("<table>") n = 1 print("<tr><th>N</th><th>Connection</th> <th>Transfers</th> <th>Log lines</th> <th>Type</th> <th>AMQP detail<th></tr>") for rc in self.conns_by_size_transfer: if rc.transfers > 0: print("<tr><td>%d</td><td><a href=\"%s/%s\">%s</a></td> <td>%d</td> <td>%d</td> <td>%s</td> <td>%s</td></tr>" % (n, rc.logfile.odir(), rc.path_name, rc.disp_name(), rc.transfers, len(rc.lines), rc.peer_type, common.html_escape(rc.peer_open))) n += 1 print("</table>") print("<hr>") print("<a name=\"c_conn_xfer0\"></a>") print("<h3>Connections with no AMQP transfers (N=%d)</h3>" % (xfer0)) print("<table>") n = 1 print("<tr><th>N</th><th>Connection</th> <th>Transfers</th> <th>Log lines</th> <th>Type</th> <th>AMQP detail<th></tr>") for rc in self.conns_by_size_transfer: if rc.transfers == 0: print("<tr><td>%d</td><td><a href=\"%s/%s\">%s</a></td> <td>%d</td> <td>%d</td> <td>%s</td> <td>%s</td></tr>" % (n, rc.logfile.odir(), rc.path_name, rc.disp_name(), rc.transfers, len(rc.lines), rc.peer_type, common.html_escape(rc.peer_open))) n += 1 print("</table>") print("<hr>") print("<a name=\"c_conn_logsize\"></a>") print("<h3>Connections by total log line count (N=%d)</h3>" % (len(self.conns_by_size_loglines))) print("<table>") n = 1 print("<tr><th>N</th><th>Connection</th> <th>Transfers</th> <th>Log lines</th> <th>Type</th> <th>AMQP detail<th></tr>") for rc in self.conns_by_size_loglines: print("<tr><td>%d</td><td><a href=\"%s/%s\">%s</a></td> <td>%d</td> <td>%d</td> <td>%s</td> <td>%s</td></tr>" % (n, rc.logfile.odir(), rc.path_name, rc.disp_name(), rc.transfers, len(rc.lines), rc.peer_type, common.html_escape(rc.peer_open))) n += 1 print("</table>") print("<hr>")
def summarize_connections(self): # sort connections based on transfer count and on n log lines self.conns_by_size_transfer = self.sort_sizes(lambda x: x.transfers, lambda x: len(x.lines)) self.conns_by_size_loglines = self.sort_sizes(lambda x: len(x.lines), lambda x: x.transfers) # compute log_n and file name facts for all connections for k, v in dict_iteritems(self.connections): v.log_n_lines = self.log_of(len(v.lines)) v.generate_paths() # Write the web doc to stdout print ("""<!DOCTYPE html> <html> <head> <title>%s qpid-dispatch log split</title> <style> * { font-family: sans-serif; } table { border-collapse: collapse; } table, td, th { border: 1px solid black; padding: 3px; } </style> <script src="http://ajax.googleapis.com/ajax/libs/dojo/1.4/dojo/dojo.xd.js" type="text/javascript"></script> <!-- <script src="http://ajax.googleapis.com/ajax/libs/dojo/1.4/dojo/dojo.xd.js" type="text/javascript"></script> --> <script type="text/javascript"> function node_is_visible(node) { if(dojo.isString(node)) node = dojo.byId(node); if(!node) return false; return node.style.display == "block"; } function set_node(node, str) { if(dojo.isString(node)) node = dojo.byId(node); if(!node) return; node.style.display = str; } function toggle_node(node) { if(dojo.isString(node)) node = dojo.byId(node); if(!node) return; set_node(node, (node_is_visible(node)) ? 'none' : 'block'); } function hide_node(node) { set_node(node, 'none'); } function show_node(node) { set_node(node, 'block'); } """ % self.log_fn) print("</script>") print("</head>") print("<body>") print(""" <h3>Contents</h3> <table> <tr> <th>Section</th> <th>Description</th> </tr> <tr><td><a href=\"#c_summary\" >Summary</a></td> <td>Summary</td></tr> <tr><td><a href=\"#c_restarts\" >Router restarts</a></td> <td>Router reboot records</td></tr> <tr><td><a href=\"#c_router_conn\" >Interrouter connections</a></td> <td>Probable interrouter connections</td></tr> <tr><td><a href=\"#c_broker_conn\" >Broker connections</a></td> <td>Probable broker connections</td></tr> <tr><td><a href=\"#c_errors\" >AMQP errors</a></td> <td>AMQP errors</td></tr> <tr><td><a href=\"#c_conn_xfersize\" >Conn by N transfers</a></td> <td>Connections sorted by transfer log count</td></tr> <tr><td><a href=\"#c_conn_xfer0\" >Conn with no transfers</a></td> <td>Connections with no transfers</td></tr> <tr><td><a href=\"#c_conn_logsize\" >Conn by N log lines</a></td> <td>Connections sorted by total log line count</td></tr> <tr><td><a href=\"#c_addresses\" >Addresses</a></td> <td>AMQP address usage</td></tr> </table> <hr> """) print("<a name=\"c_summary\"></a>") print("<table>") print("<tr><th>Statistic</th> <th>Value</th></tr>") print("<tr><td>File</td> <td>%s</td></tr>" % self.log_fn) print("<tr><td>Router starts</td> <td>%s</td></tr>" % str(self.instance)) print("<tr><td>Connections</td> <td>%s</td></tr>" % str(len(self.connections))) print("<tr><td>Router connections</td> <td>%s</td></tr>" % str(len(self.router_connections))) print("<tr><td>AMQP log lines</td> <td>%s</td></tr>" % str(self.amqp_lines)) print("<tr><td>AMQP errors</td> <td>%s</td></tr>" % str(len(self.errors))) print("<tr><td>AMQP transfers</td> <td>%s</td></tr>" % str(self.transfers)) print("<tr><td>AMQP attaches</td> <td>%s</td></tr>" % str(self.attaches)) print("</table>") print("<hr>") # Restarts print("<a name=\"c_restarts\"></a>") print("<h3>Restarts</h3>") for i in range(1, (self.instance + 1)): rr = self.restarts[i-1] print("(%d) - %s<br>" % (i, rr), end='') print("<hr>") # interrouter connections print("<a name=\"c_router_conn\"></a>") print("<h3>Probable inter-router connections (N=%d)</h3>" % (len(self.router_connections))) print("<table>") print("<tr><th>Connection</th> <th>Transfers</th> <th>Log lines</th> <th>AMQP Open<th></tr>") for rc in self.router_connections: print("<tr><td><a href=\"%s/%s\">%s</a></td><td>%d</td><td>%d</td><td>%s</td></tr>" % (rc.logfile.odir(), rc.path_name, rc.disp_name(), rc.transfers, len(rc.lines), common.html_escape(rc.peer_open))) print("</table>") print("<hr>") # broker connections print("<a name=\"c_broker_conn\"></a>") print("<h3>Probable broker connections (N=%d)</h3>" % (len(self.broker_connections))) print("<table>") print("<tr><th>Connection</th> <th>Transfers</th> <th>Log lines</th> <th>AMQP Open<th></tr>") for rc in self.broker_connections: print("<tr><td><a href=\"%s/%s\">%s</a></td><td>%d</td><td>%d</td><td>%s</td></tr>" % (rc.logfile.odir(), rc.path_name, rc.disp_name(), rc.transfers, len(rc.lines), common.html_escape(rc.peer_open))) print("</table>") print("<hr>") ## histogram #for cursize in self.sizelist: # self.histogram[self.log_of(cursize)] += len(self.sizemap[str(cursize)]) #print() #print("Log lines per connection distribution") #for i in range(1, self.hist_max): # print("N < 10e%d : %d" %(i, self.histogram[i])) #print("N >= 10e%d : %d" % ((self.hist_max - 1), self.histogram[self.hist_max])) # errors print("<a name=\"c_errors\"></a>") print("<h3>AMQP errors (N=%d)</h3>" % (len(self.errors))) print("<table>") print("<tr><th>N</th> <th>AMQP error</th></tr>") for i in range(len(self.errors)): print("<tr><td>%d</td> <td>%s</td></tr>" % (i, common.html_escape(self.errors[i].strip()))) print("</table>") print("<hr>")
def main_except(argv): # Instantiate a common block comn = common.Common() # optparse - look for data-inhibit and program mode control p = argparse.ArgumentParser() p.add_argument( '--skip-all-data', '-sa', action='store_true', help= 'Max load shedding: do not store/index transfer, disposition, flow or EMPTY_FRAME data' ) p.add_argument( '--skip-detail', '-sd', action='store_true', help='Load shedding: do not produce Connection Details tables') p.add_argument( '--skip-msg-progress', '-sm', action='store_true', help='Load shedding: do not produce Message Progress tables') p.add_argument('--split', '-sp', action='store_true', help='A single file is split into per-connection data.') p.add_argument( '--time-start', '-ts', help= 'Ignore log records earlier than this. Format: "2018-08-13 13:15:00.123456"' ) p.add_argument( '--time-end', '-te', help= 'Ignore log records later than this. Format: "2018-08-13 13:15:15.123456"' ) p.add_argument('--files', '-f', nargs="+") del argv[0] comn.args = p.parse_args(argv) if not comn.args.time_start is None: try: comn.args.time_start = datetime.datetime.strptime( comn.args.time_start, "%Y-%m-%d %H:%M:%S.%f") except: sys.exit( "ERROR: Failed to parse time_start '%s'. Use format 'YYYY-MM-DD HH:MM:SS.n_uS'" % comn.args.time_start) if not comn.args.time_end is None: try: comn.args.time_end = datetime.datetime.strptime( comn.args.time_end, "%Y-%m-%d %H:%M:%S.%f") except: sys.exit( "ERROR: Failed to parse time_end '%s'. Use format 'YYYY-MM-DD HH:MM:SS.n_uS'" % comn.args.time_end) # process split function if comn.args.split: # Split processes only a single file if len(comn.args.files) > 1: sys.exit('--split mode takes only one file name') return splitter_main(comn.args.files[0]) # process the log files and add the results to router_array for log_i in range(len(comn.args.files)): arg_log_file = comn.args.files[log_i] comn.log_fns.append(arg_log_file) comn.n_logs += 1 if not os.path.exists(arg_log_file): sys.exit('ERROR: log file %s was not found!' % arg_log_file) # parse the log file rtrs = parser.parse_log_file(arg_log_file, log_i, comn) comn.routers.append(rtrs) # Create lists of various things sorted by time tree = [] # log line ls_tree = [] # link state lines rr_tree = [] # restart records for rtrlist in comn.routers: for rtr in rtrlist: tree += rtr.lines ls_tree += rtr.router_ls rr_tree.append(rtr.restart_rec) tree = sorted(tree, key=lambda lfl: lfl.datetime) ls_tree = sorted(ls_tree, key=lambda lfl: lfl.datetime) rr_tree = sorted(rr_tree, key=lambda lfl: lfl.datetime) # post_extract the shortened names comn.shorteners.short_link_names.sort_main() comn.shorteners.short_data_names.sort_main() for plf in tree: plf.post_extract_names() # marshall connection facts for rtr_list in comn.routers: for rtr in rtr_list: rtr.discover_connection_facts(comn) # Back-propagate a router name/version/mode to each list's router0. # Complain if container name or version changes between instances. # Fill in container_id and shortened display_name tables for fi in range(comn.n_logs): rtrlist = comn.routers[fi] if len(rtrlist) > 1: if rtrlist[0].container_name is None: rtrlist[0].container_name = rtrlist[1].container_name if rtrlist[0].version is None: rtrlist[0].version = rtrlist[1].version if rtrlist[0].mode is None: rtrlist[0].mode = rtrlist[1].mode for i in range(0, len(rtrlist) - 1): namei = rtrlist[i].container_name namej = rtrlist[i + 1].container_name if namei != namej: sys.exit( 'Inconsistent container names, log file %s, instance %d:%s but instance %d:%s' % (comn.log_fns[fi], i, namei, i + 1, namej)) namei = rtrlist[i].version namej = rtrlist[i + 1].version if namei != namej: sys.exit( 'Inconsistent router versions, log file %s, instance %d:%s but instance %d:%s' % (comn.log_fns[fi], i, namei, i + 1, namej)) namei = rtrlist[i].mode namej = rtrlist[i + 1].mode if namei != namej: sys.exit( 'Inconsistent router modes, log file %s, instance %d:%s but instance %d:%s' % (comn.log_fns[fi], i, namei, i + 1, namej)) name = rtrlist[0].container_name if len( rtrlist) > 0 and rtrlist[0].container_name is not None else ( "Unknown_%d" % fi) mode = rtrlist[0].mode if len( rtrlist) > 0 and rtrlist[0].mode is not None else "standalone" comn.router_ids.append(name) comn.router_display_names.append( comn.shorteners.short_rtr_names.translate(name)) comn.router_modes.append(mode) # aggregate connection-to-frame maps into big map for rtrlist in comn.routers: for rtr in rtrlist: comn.conn_to_frame_map.update(rtr.conn_to_frame_map) # generate router-to-router connection peer relationships peer_list = [] for plf in tree: if plf.data.name == "open" and plf.data.direction_is_in(): cid = plf.data.conn_id # the router that generated this log file if "properties" in plf.data.described_type.dict: peer_conn = plf.data.described_type.dict["properties"].get( ':"qd.conn-id"', "") # router that sent the open if peer_conn != "" and plf.data.conn_peer != "": pid_peer = plf.data.conn_peer.strip('\"') rtr, rtridx = router.which_router_id_tod( comn.routers, pid_peer, plf.datetime) if rtr is not None: pid = rtr.conn_id(peer_conn) hit = sorted((cid, pid)) if hit not in peer_list: peer_list.append(hit) for (key, val) in peer_list: if key in comn.conn_peers_connid: sys.exit('key val messed up') if val in comn.conn_peers_connid: sys.exit('key val messed up') comn.conn_peers_connid[key] = val comn.conn_peers_connid[val] = key cn_k = comn.router_ids[common.index_of_log_letter(key)] cn_v = comn.router_ids[common.index_of_log_letter(val)] comn.conn_peers_display[ key] = comn.shorteners.short_rtr_names.translate(cn_v) comn.conn_peers_display[ val] = comn.shorteners.short_rtr_names.translate(cn_k) # sort transfer short name customer lists comn.shorteners.short_data_names.sort_customers() comn.shorteners.short_link_names.sort_customers() # compute settlement and index AMQP addresses if not comn.args.skip_detail: for rtrlist in comn.routers: for rtr in rtrlist: rtr.details.compute_settlement() rtr.details.index_addresses() rtr.details.evaluate_credit() # # Start producing the output stream # print(text.web_page_head()) # # Generate javascript # # output the frame show/hide functions into the header for conn_id, plfs in common.dict_iteritems(comn.conn_to_frame_map): print("function show_%s() {" % conn_id) for plf in plfs: print(" javascript:show_node(\'%s\');" % plf.fid) print("}") print("function hide_%s() {" % conn_id) for plf in plfs: print(" javascript:hide_node(\'%s\');" % plf.fid) print("}") # manipulate checkboxes print("function show_if_cb_sel_%s() {" % conn_id) print(" if (document.getElementById(\"cb_sel_%s\").checked) {" % conn_id) print(" javascript:show_%s();" % conn_id) print(" } else {") print(" javascript:hide_%s();" % conn_id) print(" }") print("}") print("function select_cb_sel_%s() {" % conn_id) print(" document.getElementById(\"cb_sel_%s\").checked = true;" % conn_id) print(" javascript:show_%s();" % conn_id) print("}") print("function deselect_cb_sel_%s() {" % conn_id) print(" document.getElementById(\"cb_sel_%s\").checked = false;" % conn_id) print(" javascript:hide_%s();" % conn_id) print("}") print("function toggle_cb_sel_%s() {" % conn_id) print(" if (document.getElementById(\"cb_sel_%s\").checked) {" % conn_id) print(" document.getElementById(\"cb_sel_%s\").checked = false;" % conn_id) print(" } else {") print(" document.getElementById(\"cb_sel_%s\").checked = true;" % conn_id) print(" }") print(" javascript:show_if_cb_sel_%s();" % conn_id) print("}") # Select/Deselect/Toggle All Connections functions print("function select_all() {") for conn_id, frames_ids in common.dict_iteritems(comn.conn_to_frame_map): print(" javascript:select_cb_sel_%s();" % conn_id) print("}") print("function deselect_all() {") for conn_id, frames_ids in common.dict_iteritems(comn.conn_to_frame_map): print(" javascript:deselect_cb_sel_%s();" % conn_id) print("}") print("function toggle_all() {") for conn_id, frames_ids in common.dict_iteritems(comn.conn_to_frame_map): print(" javascript:toggle_cb_sel_%s();" % conn_id) print("}") # print("</script>") print("</head>") print("<body>") # # Table of contents print(text.web_page_toc()) # Report how much data was skipped if --no-data switch in effect if comn.args.skip_all_data: print("--skip-all-data switch is in effect. %d log lines skipped" % comn.data_skipped) print("<p><hr>") # file(s) included in this doc print("<a name=\"c_logfiles\"></a>") print("<h3>Log files</h3>") print( "<table><tr><th>Log</th> <th>Container name</th> <th>Version</th> <th>Mode</th>" "<th>Instances</th> <th>Log file path</th></tr>") for i in range(comn.n_logs): rtrlist = comn.routers[i] if len(rtrlist) > 0: print( "<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>" % (common.log_letter_of(i), rtrlist[0].container_name, rtrlist[0].version, rtrlist[0].mode, str( len(rtrlist)), os.path.abspath(comn.log_fns[i]))) else: print( "<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>" % (common.log_letter_of(i), text.nbsp(), text.nbsp(), str(len(rtrlist)), os.path.abspath(comn.log_fns[i]))) print("</table>") print("<hr>") # reboot chronology print("<a name=\"c_rtrinstances\"></a>") print("<h3>Router Reboot Chronology</h3>") print("<table><tr><th>Log</th> <th>Time</th> <th>Container name</th> ") for i in range(len(comn.routers)): print("<td>%s</td>" % common.log_letter_of(i)) print("</tr>") for rr in rr_tree: print("<tr><td>%s</td><td>%s</td><td>%s</td>" % (rr.router.iname, rr.datetime, rr.router.container_name)) for i in range(len(comn.routers)): print( "<td>%s</td> " % (rr.router.iname if i == rr.router.log_index else text.nbsp())) print("</tr>") print("</table>") print("<hr>") # print the connection peer tables # # +------+--------------------+-----+--------------------+-------+-------+----------+--------+-------+ # | View | Router | Dir | Peer | Log | N | Transfer | AMQP | unset | # | +-----------+--------+ +--------+-----------+ lines | links | bytes | errors | tled | # | | container | connid | | connid | container | | | | | | # +------+-----------+--------+-----+--------+-----------+-------+-------+----------+--------+-------+ print("<a name=\"c_connections\"></a>") print("<h3>Connections</h3>") print("<p>") print("<button onclick=\"javascript:select_all()\">Select All</button>") print( "<button onclick=\"javascript:deselect_all()\">Deselect All</button>") print("<button onclick=\"javascript:toggle_all()\">Toggle All</button>") print("</p>") print("<h3>Connections by ConnectionId</h3>") print( "<table><tr> <th rowspan=\"2\">View</th> <th colspan=\"2\">Router</th> <th rowspan=\"2\">Dir</th> <th colspan=\"2\">Peer</th> <th rowspan=\"2\">Log lines</th> " "<th rowspan=\"2\">N links</th><th rowspan=\"2\">Transfer bytes</th> %s </tr>" % amqp_detail.Counts.show_table_heads1()) print( "<tr> <th>container</th> <th>connid</th> <th>connid</th> <th>container</th> %s </tr>" % amqp_detail.Counts.show_table_heads2()) tConn = 0 tLines = 0 tBytes = 0 tErrs = 0 tLinks = 0 for rtrlist in comn.routers: for rtr in rtrlist: rid = rtr.container_name for conn in rtr.conn_list: tConn += 1 id = rtr.conn_id(conn) # this router's full connid 'A0_3' peer = rtr.conn_peer_display.get(id, "") # peer container id peerconnid = comn.conn_peers_connid.get(id, "") n_links = rtr.details.links_in_connection(id) tLinks += n_links conn_details = rtr.details.conn_details[id] tErrs += conn_details.counts.errors print("<tr>") print("<td> <input type=\"checkbox\" id=\"cb_sel_%s\" " % id) print( "checked=\"true\" onclick=\"javascript:show_if_cb_sel_%s()\"> </td>" % (id)) print( "<td>%s</td><td><a href=\"#cd_%s\">%s</a></td><td>%s</td><td>%s</td><td>%s</td><td>%s</td>" "<td>%d</td><td>%s</td> %s </tr>" % (rid, id, id, rtr.conn_dir[id], peerconnid, peer, rtr.conn_log_lines[id], n_links, rtr.conn_xfer_bytes[id], conn_details.counts.show_table_data())) tLines += rtr.conn_log_lines[id] tBytes += rtr.conn_xfer_bytes[id] print( "<td>Total</td><td>%d</td><td> </td><td> </td><td> </td><td> </td><td>%d</td><td>%d</td><td>%d</td><td>%d</td></tr>" % (tConn, tLines, tLinks, tBytes, tErrs)) print("</table>") print("<a name=\"c_connectchrono\"></a>") print("<h3>Router Restart and Connection chronology</h3>") cl = [] for rtrlist in comn.routers: for rtr in rtrlist: rid = rtr.container_name cl.append( common.RestartRec(rtr.iname, rtr, "restart", rtr.restart_rec.datetime)) for conn in rtr.conn_list: id = rtr.conn_id(conn) if id in rtr.conn_open_time: cl.append( common.RestartRec(id, rtr, "open", rtr.conn_open_time[id].datetime)) if id in rtr.conn_close_time: cl.append( common.RestartRec(id, rtr, "close", rtr.conn_close_time[id].datetime)) cl = sorted(cl, key=lambda lfl: lfl.datetime) print( "<table><tr> <th>Time</th> <th>Id</th> <th>Event</th> <th>container</th> <th>connid</th> " "<th>Dir</th> <th>connid</th> <th>container</th>") for i in range(len(comn.routers)): print("<td>%s</td>" % common.log_letter_of(i)) print("</tr>") for c in cl: if c.event == "restart": rid = c.router.container_name print( "<tr><td>%s</td> <td>%s</td> <td><span style=\"background-color:yellow\">%s</span></td><td>%s</td> " "<td>%s</td> <td>%s</td><td>%s</td> <td>%s</td>" % (c.datetime, c.id, c.event, rid, "", "", "", "")) for i in range(len(comn.routers)): print("<td>%s</td> " % (c.id if i == c.router.log_index else text.nbsp())) print("</tr>") else: rid = c.router.container_name cdir = c.router.conn_dir[c.id] peer = c.router.conn_peer_display.get(c.id, "") # peer container id peerconnid = comn.conn_peers_connid.get(c.id, "") print( "<tr><td>%s</td> <td>%s</td> <td>%s</td><td>%s</td> <td>%s</td> <td>%s</td><td>%s</td> <td>%s</td>" % (c.datetime, c.id, c.event, rid, c.id, cdir, peerconnid, peer)) for i in range(len(comn.routers)): print("<td>%s</td> " % (text.nbsp())) print("</tr>") print("</table>") print("<hr>") # address overview print("<a name=\"c_addresses\"></a>") print("<h3>AMQP Addresses Overview</h3>") # compute with and without data lists w_data = [] wo_data = [] for i in range(0, comn.shorteners.short_addr_names.len()): sname = comn.shorteners.short_addr_names.shortname(i) lname = comn.shorteners.short_addr_names.longnames[i] links = comn.shorteners.short_addr_names.customers(sname) showthis = ("<a href=\"javascript:toggle_node('@@addr_%d')\">%s</a>" % (i, text.lozenge())) visitthis = ("<a href=\"#@@addr_%d_data\">%s</a>" % (i, lname)) n_frames = sum(len(linkd.frame_list) for linkd in links) n_transfers = 0 for linkd in links: n_transfers += sum(1 for plf in linkd.frame_list if plf.data.transfer) n_unsettled = sum(linkd.counts.unsettled for linkd in links) line = ( "<tr><td>%s %s</td> <td>%d</td> <td>%d</td> <td>%d</td> <td>%d</td></tr>" % (showthis, visitthis, len(links), n_frames, n_transfers, n_unsettled)) if n_transfers == 0: wo_data.append(line) else: w_data.append(line) # loop to print table with no expanded data print("<h4>With transfer data (N=%d)</h4>" % len(w_data)) print( "<table><tr><th>Address</th> <th>N Links</th> <th>Frames</th> <th>Transfers</th> <th>Unsettled</th> </tr>" ) for line in w_data: print(line) print("</table>") print("<h4>With no transfer data (N=%d)</h4>" % len(wo_data)) print( "<table><tr><th>Address</th> <th>N Links</th> <th>Frames</th> <th>Transfers</th> <th>Unsettled</th> </tr>" ) for line in wo_data: print(line) print("</table>") # loop to print expandable sub tables print("<h3>AMQP Addresses Details</h3>") for i in range(0, comn.shorteners.short_addr_names.len()): sname = comn.shorteners.short_addr_names.shortname(i) lname = comn.shorteners.short_addr_names.longnames[i] links = comn.shorteners.short_addr_names.customers(sname) print( "<div id=\"@@addr_%d\" style=\"display:none; margin-top: 2px; margin-bottom: 2px; margin-left: 10px\">" % (i)) print("<a name=\"@@addr_%d_data\"></a>" % (i)) print("<h4>Address %s - %s</h4>" % (sname, lname)) print( "<table><tr> <th colspan=\"2\">Router</th> <th rowspan=\"2\">Dir</th> <th colspan=\"2\">Peer</th> <th rowspan=\"2\">Role</th> " "<th rowspan=\"2\">Link</th> <th rowspan=\"2\">Frames</th> <th rowspan=\"2\">Transfers</th> <th rowspan=\"2\">Unsettled</th></tr>" ) print( "<tr> <th>container</th> <th>connid</th> <th>connid</th> <th>container</th></tr>" ) for linkd in links: # linkd # LinkDetail sessd = linkd.session_detail # SessionDetail connd = sessd.conn_detail # ConnectionDetail rtr = connd.router # Router conn = connd.conn # connection number type int rid = rtr.container_name id = rtr.conn_id(conn) peer = rtr.conn_peer_display.get(id, "") # peer container id peerconnid = comn.conn_peers_connid.get(id, "") role = "receiver" if linkd.is_receiver else "sender" transfers = sum(1 for plf in linkd.frame_list if plf.data.transfer) showall = ( "<a href=\"javascript:void(0)\" onclick=\"show_node(%s_data); show_node(%s_sess_%s); show_node(%s_sess_%s_link_%s)\">%s</a>" % (id, id, sessd.conn_epoch, id, sessd.conn_epoch, linkd.session_seq, text.lozenge())) visitthis = ( "<a href=\"#%s_sess_%s_link_%s_data\">%s</a>" % (id, sessd.conn_epoch, linkd.session_seq, linkd.display_name)) print( "<tr> <td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> <td>%s %s</td> <td>%d</td> <td>%d</td> <td>%d</td> </tr>" % (rid, id, rtr.conn_dir[id], peerconnid, peer, role, showall, visitthis, len( linkd.frame_list), transfers, linkd.counts.unsettled)) print("</table>") print("</div>") print("<hr>") # connection details print("<a name=\"c_conndetails\"></a>") print("<h3>Connection Details</h3>") if not comn.args.skip_detail: for rtrlist in comn.routers: for rtr in rtrlist: rtr.details.show_html() else: print("details suppressed<br>") print("<hr>") # noteworthy log lines: highlight errors and stuff print("<a name=\"c_noteworthy\"></a>") print("<h3>Noteworthy</h3>") n_errors = 0 n_settled = 0 n_more = 0 n_resume = 0 n_aborted = 0 n_drain = 0 n_unsettled = 0 n_unsettled_no_parent = 0 # without link/session can't match xfers with dispositions for plf in tree: if plf.data.amqp_error: n_errors += 1 if plf.data.transfer_settled: n_settled += 1 if plf.data.transfer_more: n_more += 1 if plf.data.transfer_resume: n_resume += 1 if plf.data.transfer_aborted: n_aborted += 1 if plf.data.flow_drain: n_drain += 1 if common.transfer_is_possibly_unsettled(plf): if plf.data.no_parent_link: n_unsettled_no_parent += 1 # possibly unsettled else: n_unsettled += 1 # probably unsettled # amqp errors print( "<a href=\"javascript:toggle_node('noteworthy_errors')\">%s%s</a> AMQP errors: %d<br>" % (text.lozenge(), text.nbsp(), n_errors)) print( " <div width=\"100%%\"; " "style=\"display:none; font-weight: normal; margin-bottom: 2px; margin-left: 10px\" " "id=\"noteworthy_errors\">") for plf in tree: if plf.data.amqp_error: show_noteworthy_line(plf, comn) print("</div>") # transfers with settled=true print( "<a href=\"javascript:toggle_node('noteworthy_settled')\">%s%s</a> Presettled transfers: %d<br>" % (text.lozenge(), text.nbsp(), n_settled)) print( " <div width=\"100%%\"; " "style=\"display:none; font-weight: normal; margin-bottom: 2px; margin-left: 10px\" " "id=\"noteworthy_settled\">") for plf in tree: if plf.data.transfer_settled: show_noteworthy_line(plf, comn) print("</div>") # transfers with more=true print( "<a href=\"javascript:toggle_node('noteworthy_more')\">%s%s</a> Partial transfers with 'more' set: %d<br>" % (text.lozenge(), text.nbsp(), n_more)) print( " <div width=\"100%%\"; " "style=\"display:none; font-weight: normal; margin-bottom: 2px; margin-left: 10px\" " "id=\"noteworthy_more\">") for plf in tree: if plf.data.transfer_more: show_noteworthy_line(plf, comn) print("</div>") # transfers with resume=true, whatever that is print( "<a href=\"javascript:toggle_node('noteworthy_resume')\">%s%s</a> Resumed transfers: %d<br>" % (text.lozenge(), text.nbsp(), n_resume)) print( " <div width=\"100%%\"; " "style=\"display:none; font-weight: normal; margin-bottom: 2px; margin-left: 10px\" " "id=\"noteworthy_resume\">") for plf in tree: if plf.data.transfer_resume: show_noteworthy_line(plf, comn) print("</div>") # transfers with abort=true print( "<a href=\"javascript:toggle_node('noteworthy_aborts')\">%s%s</a> Aborted transfers: %d<br>" % (text.lozenge(), text.nbsp(), n_aborted)) print( " <div width=\"100%%\"; " "style=\"display:none; font-weight: normal; margin-bottom: 2px; margin-left: 10px\" " "id=\"noteworthy_aborts\">") for plf in tree: if plf.data.transfer_aborted: show_noteworthy_line(plf, comn) print("</div>") # flow with drain=true print( "<a href=\"javascript:toggle_node('noteworthy_drain')\">%s%s</a> Flow with 'drain' set: %d<br>" % (text.lozenge(), text.nbsp(), n_drain)) print( " <div width=\"100%%\"; " "style=\"display:none; font-weight: normal; margin-bottom: 2px; margin-left: 10px\" " "id=\"noteworthy_drain\">") for plf in tree: if plf.data.flow_drain: show_noteworthy_line(plf, comn) print("</div>") # probable unsettled transfers print( "<a href=\"javascript:toggle_node('noteworthy_unsettled')\">%s%s</a> Probable unsettled transfers: %d<br>" % (text.lozenge(), text.nbsp(), n_unsettled)) print( " <div width=\"100%%\"; " "style=\"display:none; font-weight: normal; margin-bottom: 2px; margin-left: 10px\" " "id=\"noteworthy_unsettled\">") for plf in tree: if common.transfer_is_possibly_unsettled( plf) and not plf.data.no_parent_link: show_noteworthy_line(plf, comn) print("</div>") # possible unsettled transfers print( "<a href=\"javascript:toggle_node('noteworthy_unsettled_qm')\">%s%s</a> Possible unsettled transfers: %d<br>" % (text.lozenge(), text.nbsp(), n_unsettled_no_parent)) print( " <div width=\"100%%\"; " "style=\"display:none; font-weight: normal; margin-bottom: 2px; margin-left: 10px\" " "id=\"noteworthy_unsettled_qm\">") for plf in tree: if common.transfer_is_possibly_unsettled( plf) and plf.data.no_parent_link: show_noteworthy_line(plf, comn) print("</div>") print("<hr>") # the proton log lines # log lines in f_A_116 # log line details in f_A_116_d print("<a name=\"c_logdata\"></a>") print("<h3>Log data</h3>") for plf in tree: l_dict = plf.data.described_type.dict print( "<div width=\"100%%\" style=\"display:block margin-bottom: 2px\" id=\"%s\">" % plf.fid) print("<a name=\"%s\"></a>" % plf.fid) detailname = plf.fid + "_d" # type: str loz = "<a href=\"javascript:toggle_node('%s')\">%s%s</a>" % ( detailname, text.lozenge(), text.nbsp()) rtr = plf.router rid = comn.router_display_names[rtr.log_index] peerconnid = "%s" % comn.conn_peers_connid.get(plf.data.conn_id, "") peer = rtr.conn_peer_display.get(plf.data.conn_id, "") # peer container id print(loz, plf.datetime, ("%s#%d" % (plf.prefixi, plf.lineno)), rid, ("[%s]" % plf.data.conn_id), plf.data.direction, ("[%s]" % peerconnid), peer, plf.data.web_show_str, plf.data.disposition_display, "<br>") print( " <div width=\"100%%\"; " "style=\"display:none; font-weight: normal; margin-bottom: 2px; margin-left: 10px\" " "id=\"%s\">" % detailname) for key in sorted(common.dict_iterkeys(l_dict)): val = l_dict[key] print("%s : %s <br>" % (key, common.html_escape(str(val)))) if plf.data.name == "transfer": print("Header and annotations : %s <br>" % plf.data.transfer_hdr_annos) print("</div>") print("</div>") print("<hr>") # data traversing network print("<a name=\"c_messageprogress\"></a>") print("<h3>Message progress</h3>") if not comn.args.skip_msg_progress: for i in range(0, comn.shorteners.short_data_names.len()): sname = comn.shorteners.short_data_names.shortname(i) size = 0 for plf in comn.shorteners.short_data_names.customers(sname): size = plf.data.transfer_size break print("<a name=\"%s\"></a> <h4>%s (%s)" % (sname, sname, size)) print(" <span> <a href=\"javascript:toggle_node('%s')\"> %s</a>" % ("data_" + sname, text.lozenge())) print( " <div width=\"100%%\"; style=\"display:none; font-weight: normal; margin-bottom: 2px\" id=\"%s\">" % ("data_" + sname)) print(" ", comn.shorteners.short_data_names.longname(i, True)) print("</div> </span>") print("</h4>") print("<table>") print( "<tr><th>Src</th> <th>Time</th> <th>Router</th> <th>ConnId</th> <th>Dir</th> <th>ConnId</th> <th>Peer</th> " "<th>T delta</th> <th>T elapsed</th><th>Settlement</th><th>S elapsed</th></tr>" ) t0 = None tlast = None for plf in comn.shorteners.short_data_names.customers(sname): if t0 is None: t0 = plf.datetime tlast = plf.datetime delta = "0.000000" epsed = "0.000000" else: delta = time_offset(plf.datetime, tlast) epsed = time_offset(plf.datetime, t0) tlast = plf.datetime sepsed = "" if plf.data.final_disposition is not None: sepsed = time_offset(plf.data.final_disposition.datetime, t0) rid = plf.router.iname peerconnid = "%s" % comn.conn_peers_connid.get( plf.data.conn_id, "") peer = plf.router.conn_peer_display.get( plf.data.conn_id, "") # peer container id print( "<tr><td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> " "<td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> </tr>" % (plf.adverbl_link_to(), plf.datetime, rid, plf.data.conn_id, plf.data.direction, peerconnid, peer, delta, epsed, plf.data.disposition_display, sepsed)) print("</table>") print("<hr>") # link names traversing network print("<a name=\"c_linkprogress\"></a>") print("<h3>Link name propagation</h3>") for i in range(0, comn.shorteners.short_link_names.len()): if comn.shorteners.short_link_names.len() == 0: break sname = comn.shorteners.short_link_names.prefixname(i) print("<a name=\"%s\"></a> <h4>%s" % (sname, sname)) print( " <span> <div width=\"100%%\"; style=\"display:block; font-weight: normal; margin-bottom: 2px\" >" ) print(comn.shorteners.short_link_names.longname(i, True)) print("</div> </span>") print("</h4>") print("<table>") print( "<tr><th>src</th> <th>Time</th> <th>Router</th> <th>ConnId</th> <th>Dir</th> <th>ConnId> <th>Peer</th> " "<th>T delta</th> <th>T elapsed</th></tr>") t0 = None tlast = None for plf in comn.shorteners.short_link_names.customers(sname): if t0 is None: t0 = plf.datetime delta = "0.000000" epsed = "0.000000" else: delta = time_offset(plf.datetime, tlast) epsed = time_offset(plf.datetime, t0) tlast = plf.datetime rid = plf.router.iname peerconnid = "%s" % comn.conn_peers_connid.get( plf.data.conn_id, "") peer = plf.router.conn_peer_display.get(plf.data.conn_id, "") # peer container id print( "<tr><td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> " "<td>%s</td> <td>%s</td> <td>%s</td></tr>" % (plf.adverbl_link_to(), plf.datetime, rid, plf.data.conn_id, plf.data.direction, peerconnid, peer, delta, epsed)) print("</table>") print("<hr>") # short data index print("<a name=\"c_rtrdump\"></a>") comn.shorteners.short_rtr_names.htmlDump(False) print("<hr>") print("<a name=\"c_peerdump\"></a>") comn.shorteners.short_peer_names.htmlDump(False) print("<hr>") print("<a name=\"c_linkdump\"></a>") comn.shorteners.short_link_names.htmlDump(True) print("<hr>") print("<a name=\"c_msgdump\"></a>") comn.shorteners.short_data_names.htmlDump(with_link=True, log_strings=True) print("<hr>") # link state info # merge link state and restart records into single time based list cl = [] for rtrlist in comn.routers: for rtr in rtrlist: rid = rtr.container_name cl.append( common.RestartRec(rtr.iname, rtr, "restart", rtr.restart_rec.datetime)) for plf in ls_tree: if "costs" in plf.line: cl.append(common.RestartRec("ls", plf, "ls", plf.datetime)) cl = sorted(cl, key=lambda lfl: lfl.datetime) # create a map of lists for each router # the list holds the name of other routers for which the router publishes a cost costs_pub = {} for i in range(0, comn.n_logs): costs_pub[comn.router_ids[i]] = [] # cur_costs is a 2D array of costs used to tell when cost calcs have stabilized # Each incoming LS cost line replaces a row in this table # cur_costs tracks only interior routers interior_rtrs = [] for rtrs in comn.routers: if rtrs[0].is_interior(): interior_rtrs.append(rtrs[0].container_name) PEER_COST_REBOOT = -1 PEER_COST_ABSENT = 0 def new_costs_row(val): """ return a costs row. :param val: -1 when router reboots, 0 when router log line processed :return: """ res = {} for rtr in interior_rtrs: res[rtr] = val return res cur_costs = {} for rtr in interior_rtrs: cur_costs[rtr] = new_costs_row(PEER_COST_REBOOT) print("<a name=\"c_ls\"></a>") print("<h3>Routing link state</h3>") print("<h4>Link state costs</h4>") print("<table>") print("<tr><th>Time</th> <th>Router</th>") for i in range(0, comn.n_logs): print("<th>%s</th>" % common.log_letter_of(i)) print("</tr>") for c in cl: if c.event == "ls": # link state computed costs and router reachability plf = c.router # cruel overload here: router is a parsed line not a router # Processing: Computed costs: {u'A': 1, u'C': 51L, u'B': 101L} print("<tr><td>%s</td> <td>%s</td>" % (plf.datetime, ("%s#%d" % (plf.router.iname, plf.lineno)))) try: line = plf.line sti = line.find("{") line = line[sti:] try: l_dict = common.ls_eval(line) except: traceback.print_exc() sys.stderr.write( "Failed to parse router %s, line %d : %s. Analysis continuing...\n" % (plf.router.iname, plf.lineno, plf.line)) l_dict = {} costs_row = new_costs_row(PEER_COST_ABSENT) for i in range(0, comn.n_logs): if len(comn.routers[i]) > 0: tst_name = comn.routers[i][0].container_name if tst_name in l_dict: val = l_dict[tst_name] costs_row[tst_name] = val elif i == plf.router.log_index: val = text.nbsp() else: val = "<span style=\"background-color:orange\">%s</span>" % ( text.nbsp() * 2) else: val = "<span style=\"background-color:orange\">%s</span>" % ( text.nbsp() * 2) print("<td>%s</td>" % val) # track costs published when there is no column to put the number tgts = costs_pub[c.router.router.container_name] for k, v in common.dict_iteritems(l_dict): if k not in comn.router_ids: if k not in tgts: tgts.append(k) # this cost went unreported # update this router's cost view in running table if plf.router.is_interior(): cur_costs[plf.router.container_name] = costs_row except: raise print("</tr>") # if the costs are stable across all routers then put an indicator in table costs_stable = True for c_rtr in interior_rtrs: for r_rtr in interior_rtrs: if r_rtr != c_rtr \ and (cur_costs[r_rtr][c_rtr] != cur_costs[c_rtr][r_rtr] \ or cur_costs[c_rtr][r_rtr] <= PEER_COST_ABSENT): costs_stable = False break if not costs_stable: break if costs_stable: print( "<tr><td><span style=\"background-color:green\">stable</span></td></tr>" ) else: # restart print("<tr><td>%s</td> <td>%s</td>" % (c.datetime, ("%s restart" % (c.router.iname)))) for i in range(0, comn.n_logs): color = "green" if i == c.router.log_index else "orange" print( "<td><span style=\"background-color:%s\">%s</span></td>" % (color, text.nbsp() * 2)) print("</tr>") if c.router.is_interior(): cur_costs[c.router.container_name] = new_costs_row( PEER_COST_REBOOT) print("</table>") print("<br>") # maybe display cost declarations that were not displayed costs_clean = True for k, v in common.dict_iteritems(costs_pub): if len(v) > 0: costs_clean = False break if not costs_clean: print( "<h4>Router costs declared in logs but not displayed in Link state cost table</h4>" ) print("<table>") print("<tr><th>Router</th><Peers whose logs are absent</th></tr>") for k, v in common.dict_iteritems(costs_pub): if len(v) > 0: print("<tr><td>%s</td><td>%s</td></tr>" % (k, str(v))) print("</table>") print("<br>") print( "<a href=\"javascript:toggle_node('ls_costs')\">%s%s</a> Link state costs data<br>" % (text.lozenge(), text.nbsp())) print( " <div width=\"100%%\"; " "style=\"display:none; font-weight: normal; margin-bottom: 2px; margin-left: 10px\" " "id=\"ls_costs\">") print("<table>") print("<tr><th>Time</th> <th>Router</th> <th>Name</th> <th>Log</th></tr>") for plf in ls_tree: if "costs" in plf.line: print("<tr><td>%s</td> <td>%s</td>" % (plf.datetime, ("%s#%d" % (plf.router.iname, plf.lineno)))) print("<td>%s</td>" % plf.router.container_name) print("<td>%s</td></tr>" % plf.line) print("</table>") print("</div>") print("<hr>") print("</body>")
def main_except(argv): # Instantiate a common block comn = common.Common() # optparse - look for data-inhibit and program mode control p = argparse.ArgumentParser() p.add_argument('--skip-all-data', '-sa', action='store_true', help='Max load shedding: do not store/index transfer, disposition, flow or EMPTY_FRAME data') p.add_argument('--skip-detail', '-sd', action='store_true', help='Load shedding: do not produce Connection Details tables') p.add_argument('--skip-msg-progress', '-sm', action='store_true', help='Load shedding: do not produce Message Progress tables') p.add_argument('--split', '-sp', action='store_true', help='A single file is split into per-connection data.') p.add_argument('--time-start', '-ts', help='Ignore log records earlier than this. Format: "2018-08-13 13:15:00.123456"') p.add_argument('--time-end', '-te', help='Ignore log records later than this. Format: "2018-08-13 13:15:15.123456"') p.add_argument('--files', '-f', nargs="+") del argv[0] comn.args = p.parse_args(argv) if not comn.args.time_start is None: try: comn.args.time_start = datetime.datetime.strptime(comn.args.time_start, "%Y-%m-%d %H:%M:%S.%f") except: sys.exit("ERROR: Failed to parse time_start '%s'. Use format 'YYYY-MM-DD HH:MM:SS.n_uS'" % comn.args.time_start) if not comn.args.time_end is None: try: comn.args.time_end = datetime.datetime.strptime(comn.args.time_end, "%Y-%m-%d %H:%M:%S.%f") except: sys.exit("ERROR: Failed to parse time_end '%s'. Use format 'YYYY-MM-DD HH:MM:SS.n_uS'" % comn.args.time_end) # process split function if comn.args.split: # Split processes only a single file if len(comn.args.files) > 1: sys.exit('--split mode takes only one file name') return splitter_main(comn.args.files[0]) # process the log files and add the results to router_array for log_i in range(len(comn.args.files)): arg_log_file = comn.args.files[log_i] comn.log_fns.append(arg_log_file) comn.n_logs += 1 if not os.path.exists(arg_log_file): sys.exit('ERROR: log file %s was not found!' % arg_log_file) # parse the log file rtrs = parser.parse_log_file(arg_log_file, log_i, comn) comn.routers.append(rtrs) # marshall facts about the run for rtr in rtrs: rtr.discover_connection_facts(comn) # Create lists of various things sorted by time tree = [] # log line ls_tree = [] # link state lines rr_tree = [] # restart records for rtrlist in comn.routers: for rtr in rtrlist: tree += rtr.lines ls_tree += rtr.router_ls rr_tree.append(rtr.restart_rec) tree = sorted(tree, key=lambda lfl: lfl.datetime) ls_tree = sorted(ls_tree, key=lambda lfl: lfl.datetime) rr_tree = sorted(rr_tree, key=lambda lfl: lfl.datetime) # Back-propagate a router name/version/mode to each list's router0. # Complain if container name or version changes between instances. # Fill in container_id and shortened display_name tables for fi in range(comn.n_logs): rtrlist = comn.routers[fi] if len(rtrlist) > 1: if rtrlist[0].container_name is None: rtrlist[0].container_name = rtrlist[1].container_name if rtrlist[0].version is None: rtrlist[0].version = rtrlist[1].version if rtrlist[0].mode is None: rtrlist[0].mode = rtrlist[1].mode for i in range(0, len(rtrlist) - 1): namei = rtrlist[i].container_name namej = rtrlist[i + 1].container_name if namei != namej: sys.exit('Inconsistent container names, log file %s, instance %d:%s but instance %d:%s' % (comn.log_fns[fi], i, namei, i + 1, namej)) namei = rtrlist[i].version namej = rtrlist[i + 1].version if namei != namej: sys.exit('Inconsistent router versions, log file %s, instance %d:%s but instance %d:%s' % (comn.log_fns[fi], i, namei, i + 1, namej)) namei = rtrlist[i].mode namej = rtrlist[i + 1].mode if namei != namej: sys.exit('Inconsistent router modes, log file %s, instance %d:%s but instance %d:%s' % (comn.log_fns[fi], i, namei, i + 1, namej)) name = rtrlist[0].container_name if len(rtrlist) > 0 and rtrlist[0].container_name is not None else ("Unknown_%d" % fi) mode = rtrlist[0].mode if len(rtrlist) > 0 and rtrlist[0].mode is not None else "standalone" comn.router_ids.append(name) comn.router_display_names.append(comn.shorteners.short_rtr_names.translate(name)) comn.router_modes.append(mode) # aggregate connection-to-frame maps into big map for rtrlist in comn.routers: for rtr in rtrlist: comn.conn_to_frame_map.update(rtr.conn_to_frame_map) # generate router-to-router connection peer relationships peer_list = [] for plf in tree: if plf.data.name == "open" and plf.data.direction_is_in(): cid = plf.data.conn_id # the router that generated this log file if "properties" in plf.data.described_type.dict: peer_conn = plf.data.described_type.dict["properties"].get(':"qd.conn-id"', "") # router that sent the open if peer_conn != "" and plf.data.conn_peer != "": pid_peer = plf.data.conn_peer.strip('\"') rtr, rtridx = router.which_router_id_tod(comn.routers, pid_peer, plf.datetime) if rtr is not None: pid = rtr.conn_id(peer_conn) hit = sorted((cid, pid)) if hit not in peer_list: peer_list.append(hit) for (key, val) in peer_list: if key in comn.conn_peers_connid: sys.exit('key val messed up') if val in comn.conn_peers_connid: sys.exit('key val messed up') comn.conn_peers_connid[key] = val comn.conn_peers_connid[val] = key cn_k = comn.router_ids[common.index_of_log_letter(key)] cn_v = comn.router_ids[common.index_of_log_letter(val)] comn.conn_peers_display[key] = comn.shorteners.short_rtr_names.translate(cn_v) comn.conn_peers_display[val] = comn.shorteners.short_rtr_names.translate(cn_k) # sort transfer short name customer lists comn.shorteners.short_data_names.sort_customers() comn.shorteners.short_link_names.sort_customers() # compute settlement and index AMQP addresses if not comn.args.skip_detail: for rtrlist in comn.routers: for rtr in rtrlist: rtr.details.compute_settlement() rtr.details.index_addresses() rtr.details.evaluate_credit() # # Start producing the output stream # print(text.web_page_head()) # # Generate javascript # # output the frame show/hide functions into the header for conn_id, plfs in common.dict_iteritems(comn.conn_to_frame_map): print("function show_%s() {" % conn_id) for plf in plfs: print(" javascript:show_node(\'%s\');" % plf.fid) print("}") print("function hide_%s() {" % conn_id) for plf in plfs: print(" javascript:hide_node(\'%s\');" % plf.fid) print("}") # manipulate checkboxes print("function show_if_cb_sel_%s() {" % conn_id) print(" if (document.getElementById(\"cb_sel_%s\").checked) {" % conn_id) print(" javascript:show_%s();" % conn_id) print(" } else {") print(" javascript:hide_%s();" % conn_id) print(" }") print("}") print("function select_cb_sel_%s() {" % conn_id) print(" document.getElementById(\"cb_sel_%s\").checked = true;" % conn_id) print(" javascript:show_%s();" % conn_id) print("}") print("function deselect_cb_sel_%s() {" % conn_id) print(" document.getElementById(\"cb_sel_%s\").checked = false;" % conn_id) print(" javascript:hide_%s();" % conn_id) print("}") print("function toggle_cb_sel_%s() {" % conn_id) print(" if (document.getElementById(\"cb_sel_%s\").checked) {" % conn_id) print(" document.getElementById(\"cb_sel_%s\").checked = false;" % conn_id) print(" } else {") print(" document.getElementById(\"cb_sel_%s\").checked = true;" % conn_id) print(" }") print(" javascript:show_if_cb_sel_%s();" % conn_id) print("}") # Select/Deselect/Toggle All Connections functions print("function select_all() {") for conn_id, frames_ids in common.dict_iteritems(comn.conn_to_frame_map): print(" javascript:select_cb_sel_%s();" % conn_id) print("}") print("function deselect_all() {") for conn_id, frames_ids in common.dict_iteritems(comn.conn_to_frame_map): print(" javascript:deselect_cb_sel_%s();" % conn_id) print("}") print("function toggle_all() {") for conn_id, frames_ids in common.dict_iteritems(comn.conn_to_frame_map): print(" javascript:toggle_cb_sel_%s();" % conn_id) print("}") # print("</script>") print("</head>") print("<body>") # # Table of contents print(text.web_page_toc()) # Report how much data was skipped if --no-data switch in effect if comn.args.skip_all_data: print("--skip-all-data switch is in effect. %d log lines skipped" % comn.data_skipped) print("<p><hr>") # file(s) included in this doc print("<a name=\"c_logfiles\"></a>") print("<h3>Log files</h3>") print("<table><tr><th>Log</th> <th>Container name</th> <th>Version</th> <th>Mode</th>" "<th>Instances</th> <th>Log file path</th></tr>") for i in range(comn.n_logs): rtrlist = comn.routers[i] if len(rtrlist) > 0: print("<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>" % (common.log_letter_of(i), rtrlist[0].container_name, rtrlist[0].version, rtrlist[0].mode, str(len(rtrlist)), os.path.abspath(comn.log_fns[i]))) else: print("<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>" % (common.log_letter_of(i), text.nbsp(), text.nbsp(), str(len(rtrlist)), os.path.abspath(comn.log_fns[i]))) print("</table>") print("<hr>") # reboot chronology print("<a name=\"c_rtrinstances\"></a>") print("<h3>Router Reboot Chronology</h3>") print("<table><tr><th>Log</th> <th>Time</th> <th>Container name</th> ") for i in range(len(comn.routers)): print("<td>%s</td>" % common.log_letter_of(i)) print("</tr>") for rr in rr_tree: print("<tr><td>%s</td><td>%s</td><td>%s</td>" % (rr.router.iname, rr.datetime, rr.router.container_name)) for i in range(len(comn.routers)): print("<td>%s</td> " % (rr.router.iname if i == rr.router.log_index else text.nbsp())) print("</tr>") print("</table>") print("<hr>") # print the connection peer tables # # +------+--------------------+-----+--------------------+-------+-------+----------+--------+-------+ # | View | Router | Dir | Peer | Log | N | Transfer | AMQP | unset | # | +-----------+--------+ +--------+-----------+ lines | links | bytes | errors | tled | # | | container | connid | | connid | container | | | | | | # +------+-----------+--------+-----+--------+-----------+-------+-------+----------+--------+-------+ print("<a name=\"c_connections\"></a>") print("<h3>Connections</h3>") print("<p>") print("<button onclick=\"javascript:select_all()\">Select All</button>") print("<button onclick=\"javascript:deselect_all()\">Deselect All</button>") print("<button onclick=\"javascript:toggle_all()\">Toggle All</button>") print("</p>") print("<h3>Connections by ConnectionId</h3>") print( "<table><tr> <th rowspan=\"2\">View</th> <th colspan=\"2\">Router</th> <th rowspan=\"2\">Dir</th> <th colspan=\"2\">Peer</th> <th rowspan=\"2\">Log lines</th> " "<th rowspan=\"2\">N links</th><th rowspan=\"2\">Transfer bytes</th> %s </tr>" % amqp_detail.Counts.show_table_heads1()) print("<tr> <th>container</th> <th>connid</th> <th>connid</th> <th>container</th> %s </tr>" % amqp_detail.Counts.show_table_heads2()) tConn = 0 tLines = 0 tBytes = 0 tErrs = 0 tLinks = 0 for rtrlist in comn.routers: for rtr in rtrlist: rid = rtr.container_name for conn in rtr.conn_list: tConn += 1 id = rtr.conn_id(conn) # this router's full connid 'A0_3' peer = rtr.conn_peer_display.get(id, "") # peer container id peerconnid = comn.conn_peers_connid.get(id, "") n_links = rtr.details.links_in_connection(id) tLinks += n_links conn_details = rtr.details.conn_details[id] tErrs += conn_details.counts.errors print("<tr>") print("<td> <input type=\"checkbox\" id=\"cb_sel_%s\" " % id) print("checked=\"true\" onclick=\"javascript:show_if_cb_sel_%s()\"> </td>" % (id)) print("<td>%s</td><td><a href=\"#cd_%s\">%s</a></td><td>%s</td><td>%s</td><td>%s</td><td>%s</td>" "<td>%d</td><td>%s</td> %s </tr>" % (rid, id, id, rtr.conn_dir[id], peerconnid, peer, rtr.conn_log_lines[id], n_links, rtr.conn_xfer_bytes[id], conn_details.counts.show_table_data() )) tLines += rtr.conn_log_lines[id] tBytes += rtr.conn_xfer_bytes[id] print( "<td>Total</td><td>%d</td><td> </td><td> </td><td> </td><td> </td><td>%d</td><td>%d</td><td>%d</td><td>%d</td></tr>" % (tConn, tLines, tLinks, tBytes, tErrs)) print("</table>") print("<a name=\"c_connectchrono\"></a>") print("<h3>Router Restart and Connection chronology</h3>") cl = [] for rtrlist in comn.routers: for rtr in rtrlist: rid = rtr.container_name cl.append(common.RestartRec(rtr.iname, rtr, "restart", rtr.restart_rec.datetime)) for conn in rtr.conn_list: id = rtr.conn_id(conn) if id in rtr.conn_open_time: cl.append(common.RestartRec(id, rtr, "open", rtr.conn_open_time[id].datetime)) if id in rtr.conn_close_time: cl.append(common.RestartRec(id, rtr, "close", rtr.conn_close_time[id].datetime)) cl = sorted(cl, key=lambda lfl: lfl.datetime) print("<table><tr> <th>Time</th> <th>Id</th> <th>Event</th> <th>container</th> <th>connid</th> " "<th>Dir</th> <th>connid</th> <th>container</th>") for i in range(len(comn.routers)): print("<td>%s</td>" % common.log_letter_of(i)) print("</tr>") for c in cl: if c.event == "restart": rid = c.router.container_name print("<tr><td>%s</td> <td>%s</td> <td><span style=\"background-color:yellow\">%s</span></td><td>%s</td> " "<td>%s</td> <td>%s</td><td>%s</td> <td>%s</td>" % (c.datetime, c.id, c.event, rid, "", "", "", "")) for i in range(len(comn.routers)): print("<td>%s</td> " % (c.id if i == c.router.log_index else text.nbsp())) print("</tr>") else: rid = c.router.container_name cdir = c.router.conn_dir[c.id] peer = c.router.conn_peer_display.get(c.id, "") # peer container id peerconnid = comn.conn_peers_connid.get(c.id, "") print("<tr><td>%s</td> <td>%s</td> <td>%s</td><td>%s</td> <td>%s</td> <td>%s</td><td>%s</td> <td>%s</td>" % (c.datetime, c.id, c.event, rid, c.id, cdir, peerconnid, peer)) for i in range(len(comn.routers)): print("<td>%s</td> " % (text.nbsp())) print("</tr>") print("</table>") print("<hr>") # address overview print("<a name=\"c_addresses\"></a>") print("<h3>AMQP Addresses Overview</h3>") # compute with and without data lists w_data = [] wo_data = [] for i in range(0, comn.shorteners.short_addr_names.len()): sname = comn.shorteners.short_addr_names.shortname(i) lname = comn.shorteners.short_addr_names.longnames[i] links = comn.shorteners.short_addr_names.customers(sname) showthis = ("<a href=\"javascript:toggle_node('@@addr_%d')\">%s</a>" % (i, text.lozenge())) visitthis = ("<a href=\"#@@addr_%d_data\">%s</a>" % (i, lname)) n_frames = sum(len(linkd.frame_list) for linkd in links) n_transfers = 0 for linkd in links: n_transfers += sum(1 for plf in linkd.frame_list if plf.data.transfer) n_unsettled = sum(linkd.counts.unsettled for linkd in links) line = ("<tr><td>%s %s</td> <td>%d</td> <td>%d</td> <td>%d</td> <td>%d</td></tr>" % (showthis, visitthis, len(links), n_frames, n_transfers, n_unsettled)) if n_transfers == 0: wo_data.append(line) else: w_data.append(line) # loop to print table with no expanded data print("<h4>With transfer data (N=%d)</h4>" % len(w_data)) print("<table><tr><th>Address</th> <th>N Links</th> <th>Frames</th> <th>Transfers</th> <th>Unsettled</th> </tr>") for line in w_data: print(line) print("</table>") print("<h4>With no transfer data (N=%d)</h4>" % len(wo_data)) print("<table><tr><th>Address</th> <th>N Links</th> <th>Frames</th> <th>Transfers</th> <th>Unsettled</th> </tr>") for line in wo_data: print(line) print("</table>") # loop to print expandable sub tables print("<h3>AMQP Addresses Details</h3>") for i in range(0, comn.shorteners.short_addr_names.len()): sname = comn.shorteners.short_addr_names.shortname(i) lname = comn.shorteners.short_addr_names.longnames[i] links = comn.shorteners.short_addr_names.customers(sname) print("<div id=\"@@addr_%d\" style=\"display:none; margin-top: 2px; margin-bottom: 2px; margin-left: 10px\">" % (i)) print("<a name=\"@@addr_%d_data\"></a>" % (i)) print("<h4>Address %s - %s</h4>" % (sname, lname)) print("<table><tr> <th colspan=\"2\">Router</th> <th rowspan=\"2\">Dir</th> <th colspan=\"2\">Peer</th> <th rowspan=\"2\">Role</th> " "<th rowspan=\"2\">Link</th> <th rowspan=\"2\">Frames</th> <th rowspan=\"2\">Transfers</th> <th rowspan=\"2\">Unsettled</th></tr>") print("<tr> <th>container</th> <th>connid</th> <th>connid</th> <th>container</th></tr>") for linkd in links: # linkd # LinkDetail sessd = linkd.session_detail # SessionDetail connd = sessd.conn_detail # ConnectionDetail rtr = connd.router # Router conn = connd.conn # connection number type int rid = rtr.container_name id = rtr.conn_id(conn) peer = rtr.conn_peer_display.get(id, "") # peer container id peerconnid = comn.conn_peers_connid.get(id, "") role = "receiver" if linkd.is_receiver else "sender" transfers = sum(1 for plf in linkd.frame_list if plf.data.transfer) showall = ("<a href=\"javascript:void(0)\" onclick=\"show_node(%s_data); show_node(%s_sess_%s); show_node(%s_sess_%s_link_%s)\">%s</a>" % (id, id, sessd.conn_epoch, id, sessd.conn_epoch, linkd.session_seq, text.lozenge())) visitthis = ("<a href=\"#%s_sess_%s_link_%s_data\">%s</a>" % (id, sessd.conn_epoch, linkd.session_seq, linkd.display_name)) print("<tr> <td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> <td>%s %s</td> <td>%d</td> <td>%d</td> <td>%d</td> </tr>" % (rid, id, rtr.conn_dir[id], peerconnid, peer, role, showall, visitthis, len(linkd.frame_list), transfers, linkd.counts.unsettled)) print("</table>") print("</div>") print("<hr>") # connection details print("<a name=\"c_conndetails\"></a>") print("<h3>Connection Details</h3>") if not comn.args.skip_detail: for rtrlist in comn.routers: for rtr in rtrlist: rtr.details.show_html() else: print ("details suppressed<br>") print("<hr>") # noteworthy log lines: highlight errors and stuff print("<a name=\"c_noteworthy\"></a>") print("<h3>Noteworthy</h3>") n_errors = 0 n_settled = 0 n_more = 0 n_resume = 0 n_aborted = 0 n_drain = 0 n_unsettled = 0 n_unsettled_no_parent = 0 # without link/session can't match xfers with dispositions for plf in tree: if plf.data.amqp_error: n_errors += 1 if plf.data.transfer_settled: n_settled += 1 if plf.data.transfer_more: n_more += 1 if plf.data.transfer_resume: n_resume += 1 if plf.data.transfer_aborted: n_aborted += 1 if plf.data.flow_drain: n_drain += 1 if common.transfer_is_possibly_unsettled(plf): if plf.data.no_parent_link: n_unsettled_no_parent += 1 # possibly unsettled else: n_unsettled += 1 # probably unsettled # amqp errors print("<a href=\"javascript:toggle_node('noteworthy_errors')\">%s%s</a> AMQP errors: %d<br>" % (text.lozenge(), text.nbsp(), n_errors)) print(" <div width=\"100%%\"; " "style=\"display:none; font-weight: normal; margin-bottom: 2px; margin-left: 10px\" " "id=\"noteworthy_errors\">") for plf in tree: if plf.data.amqp_error: show_noteworthy_line(plf, comn) print("</div>") # transfers with settled=true print("<a href=\"javascript:toggle_node('noteworthy_settled')\">%s%s</a> Presettled transfers: %d<br>" % (text.lozenge(), text.nbsp(), n_settled)) print(" <div width=\"100%%\"; " "style=\"display:none; font-weight: normal; margin-bottom: 2px; margin-left: 10px\" " "id=\"noteworthy_settled\">") for plf in tree: if plf.data.transfer_settled: show_noteworthy_line(plf, comn) print("</div>") # transfers with more=true print("<a href=\"javascript:toggle_node('noteworthy_more')\">%s%s</a> Partial transfers with 'more' set: %d<br>" % (text.lozenge(), text.nbsp(), n_more)) print(" <div width=\"100%%\"; " "style=\"display:none; font-weight: normal; margin-bottom: 2px; margin-left: 10px\" " "id=\"noteworthy_more\">") for plf in tree: if plf.data.transfer_more: show_noteworthy_line(plf, comn) print("</div>") # transfers with resume=true, whatever that is print("<a href=\"javascript:toggle_node('noteworthy_resume')\">%s%s</a> Resumed transfers: %d<br>" % (text.lozenge(), text.nbsp(), n_resume)) print(" <div width=\"100%%\"; " "style=\"display:none; font-weight: normal; margin-bottom: 2px; margin-left: 10px\" " "id=\"noteworthy_resume\">") for plf in tree: if plf.data.transfer_resume: show_noteworthy_line(plf, comn) print("</div>") # transfers with abort=true print("<a href=\"javascript:toggle_node('noteworthy_aborts')\">%s%s</a> Aborted transfers: %d<br>" % (text.lozenge(), text.nbsp(), n_aborted)) print(" <div width=\"100%%\"; " "style=\"display:none; font-weight: normal; margin-bottom: 2px; margin-left: 10px\" " "id=\"noteworthy_aborts\">") for plf in tree: if plf.data.transfer_aborted: show_noteworthy_line(plf, comn) print("</div>") # flow with drain=true print("<a href=\"javascript:toggle_node('noteworthy_drain')\">%s%s</a> Flow with 'drain' set: %d<br>" % (text.lozenge(), text.nbsp(), n_drain)) print(" <div width=\"100%%\"; " "style=\"display:none; font-weight: normal; margin-bottom: 2px; margin-left: 10px\" " "id=\"noteworthy_drain\">") for plf in tree: if plf.data.flow_drain: show_noteworthy_line(plf, comn) print("</div>") # probable unsettled transfers print("<a href=\"javascript:toggle_node('noteworthy_unsettled')\">%s%s</a> Probable unsettled transfers: %d<br>" % (text.lozenge(), text.nbsp(), n_unsettled)) print(" <div width=\"100%%\"; " "style=\"display:none; font-weight: normal; margin-bottom: 2px; margin-left: 10px\" " "id=\"noteworthy_unsettled\">") for plf in tree: if common.transfer_is_possibly_unsettled(plf) and not plf.data.no_parent_link: show_noteworthy_line(plf, comn) print("</div>") # possible unsettled transfers print("<a href=\"javascript:toggle_node('noteworthy_unsettled_qm')\">%s%s</a> Possible unsettled transfers: %d<br>" % (text.lozenge(), text.nbsp(), n_unsettled_no_parent)) print(" <div width=\"100%%\"; " "style=\"display:none; font-weight: normal; margin-bottom: 2px; margin-left: 10px\" " "id=\"noteworthy_unsettled_qm\">") for plf in tree: if common.transfer_is_possibly_unsettled(plf) and plf.data.no_parent_link: show_noteworthy_line(plf, comn) print("</div>") print("<hr>") # the proton log lines # log lines in f_A_116 # log line details in f_A_116_d print("<a name=\"c_logdata\"></a>") print("<h3>Log data</h3>") for plf in tree: l_dict = plf.data.described_type.dict print("<div width=\"100%%\" style=\"display:block margin-bottom: 2px\" id=\"%s\">" % plf.fid) print("<a name=\"%s\"></a>" % plf.fid) detailname = plf.fid + "_d" # type: str loz = "<a href=\"javascript:toggle_node('%s')\">%s%s</a>" % (detailname, text.lozenge(), text.nbsp()) rtr = plf.router rid = comn.router_display_names[rtr.log_index] peerconnid = "%s" % comn.conn_peers_connid.get(plf.data.conn_id, "") peer = rtr.conn_peer_display.get(plf.data.conn_id, "") # peer container id print(loz, plf.datetime, ("%s#%d" % (plf.prefixi, plf.lineno)), rid, ("[%s]" % plf.data.conn_id), plf.data.direction, ("[%s]" % peerconnid), peer, plf.data.web_show_str, plf.data.disposition_display, "<br>") print(" <div width=\"100%%\"; " "style=\"display:none; font-weight: normal; margin-bottom: 2px; margin-left: 10px\" " "id=\"%s\">" % detailname) for key in sorted(common.dict_iterkeys(l_dict)): val = l_dict[key] print("%s : %s <br>" % (key, common.html_escape(str(val)))) if plf.data.name == "transfer": print("Header and annotations : %s <br>" % plf.data.transfer_hdr_annos) print("</div>") print("</div>") print("<hr>") # data traversing network print("<a name=\"c_messageprogress\"></a>") print("<h3>Message progress</h3>") if not comn.args.skip_msg_progress: for i in range(0, comn.shorteners.short_data_names.len()): sname = comn.shorteners.short_data_names.shortname(i) size = 0 for plf in comn.shorteners.short_data_names.customers(sname): size = plf.data.transfer_size break print("<a name=\"%s\"></a> <h4>%s (%s)" % (sname, sname, size)) print(" <span> <a href=\"javascript:toggle_node('%s')\"> %s</a>" % ("data_" + sname, text.lozenge())) print(" <div width=\"100%%\"; style=\"display:none; font-weight: normal; margin-bottom: 2px\" id=\"%s\">" % ("data_" + sname)) print(" ", comn.shorteners.short_data_names.longname(i, True)) print("</div> </span>") print("</h4>") print("<table>") print( "<tr><th>Src</th> <th>Time</th> <th>Router</th> <th>ConnId</th> <th>Dir</th> <th>ConnId</th> <th>Peer</th> " "<th>T delta</th> <th>T elapsed</th><th>Settlement</th><th>S elapsed</th></tr>") t0 = None tlast = None for plf in comn.shorteners.short_data_names.customers(sname): if t0 is None: t0 = plf.datetime tlast = plf.datetime delta = "0.000000" epsed = "0.000000" else: delta = time_offset(plf.datetime, tlast) epsed = time_offset(plf.datetime, t0) tlast = plf.datetime sepsed = "" if plf.data.final_disposition is not None: sepsed = time_offset(plf.data.final_disposition.datetime, t0) rid = plf.router.iname peerconnid = "%s" % comn.conn_peers_connid.get(plf.data.conn_id, "") peer = plf.router.conn_peer_display.get(plf.data.conn_id, "") # peer container id print("<tr><td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> " "<td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> </tr>" % (plf.adverbl_link_to(), plf.datetime, rid, plf.data.conn_id, plf.data.direction, peerconnid, peer, delta, epsed, plf.data.disposition_display, sepsed)) print("</table>") print("<hr>") # link names traversing network print("<a name=\"c_linkprogress\"></a>") print("<h3>Link name propagation</h3>") for i in range(0, comn.shorteners.short_link_names.len()): if comn.shorteners.short_link_names.len() == 0: break sname = comn.shorteners.short_link_names.prefixname(i) print("<a name=\"%s\"></a> <h4>%s" % (sname, sname)) print(" <span> <div width=\"100%%\"; style=\"display:block; font-weight: normal; margin-bottom: 2px\" >") print(comn.shorteners.short_link_names.longname(i, True)) print("</div> </span>") print("</h4>") print("<table>") print("<tr><th>src</th> <th>Time</th> <th>Router</th> <th>ConnId</th> <th>Dir</th> <th>ConnId> <th>Peer</th> " "<th>T delta</th> <th>T elapsed</th></tr>") t0 = None tlast = None for plf in comn.shorteners.short_link_names.customers(sname): if t0 is None: t0 = plf.datetime delta = "0.000000" epsed = "0.000000" else: delta = time_offset(plf.datetime, tlast) epsed = time_offset(plf.datetime, t0) tlast = plf.datetime rid = plf.router.iname peerconnid = "%s" % comn.conn_peers_connid.get(plf.data.conn_id, "") peer = plf.router.conn_peer_display.get(plf.data.conn_id, "") # peer container id print("<tr><td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> " "<td>%s</td> <td>%s</td> <td>%s</td></tr>" % (plf.adverbl_link_to(), plf.datetime, rid, plf.data.conn_id, plf.data.direction, peerconnid, peer, delta, epsed)) print("</table>") print("<hr>") # short data index print("<a name=\"c_rtrdump\"></a>") comn.shorteners.short_rtr_names.htmlDump(False) print("<hr>") print("<a name=\"c_peerdump\"></a>") comn.shorteners.short_peer_names.htmlDump(False) print("<hr>") print("<a name=\"c_linkdump\"></a>") comn.shorteners.short_link_names.htmlDump(True) print("<hr>") print("<a name=\"c_msgdump\"></a>") comn.shorteners.short_data_names.htmlDump(True) print("<hr>") # link state info # merge link state and restart records into single time based list cl = [] for rtrlist in comn.routers: for rtr in rtrlist: rid = rtr.container_name cl.append(common.RestartRec(rtr.iname, rtr, "restart", rtr.restart_rec.datetime)) for plf in ls_tree: if "costs" in plf.line: cl.append(common.RestartRec("ls", plf, "ls", plf.datetime)) cl = sorted(cl, key=lambda lfl: lfl.datetime) # create a map of lists for each router # the list holds the name of other routers for which the router publishes a cost costs_pub = {} for i in range(0, comn.n_logs): costs_pub[comn.router_ids[i]] = [] # cur_costs is a 2D array of costs used to tell when cost calcs have stabilized # Each incoming LS cost line replaces a row in this table # cur_costs tracks only interior routers interior_rtrs = [] for rtrs in comn.routers: if rtrs[0].is_interior(): interior_rtrs.append(rtrs[0].container_name) PEER_COST_REBOOT = -1 PEER_COST_ABSENT = 0 def new_costs_row(val): """ return a costs row. :param val: -1 when router reboots, 0 when router log line processed :return: """ res = {} for rtr in interior_rtrs: res[rtr] = val return res cur_costs = {} for rtr in interior_rtrs: cur_costs[rtr] = new_costs_row(PEER_COST_REBOOT) print("<a name=\"c_ls\"></a>") print("<h3>Routing link state</h3>") print("<h4>Link state costs</h4>") print("<table>") print("<tr><th>Time</th> <th>Router</th>") for i in range(0, comn.n_logs): print("<th>%s</th>" % common.log_letter_of(i)) print("</tr>") for c in cl: if c.event == "ls": # link state computed costs and router reachability plf = c.router # cruel overload here: router is a parsed line not a router # Processing: Computed costs: {u'A': 1, u'C': 51L, u'B': 101L} print("<tr><td>%s</td> <td>%s</td>" % (plf.datetime, ("%s#%d" % (plf.router.iname, plf.lineno)))) try: line = plf.line sti = line.find("{") line = line[sti:] l_dict = ast.literal_eval(line) costs_row = new_costs_row(PEER_COST_ABSENT) for i in range(0, comn.n_logs): if len(comn.routers[i]) > 0: tst_name = comn.routers[i][0].container_name if tst_name in l_dict: val = l_dict[tst_name] costs_row[tst_name] = val elif i == plf.router.log_index: val = text.nbsp() else: val = "<span style=\"background-color:orange\">%s</span>" % (text.nbsp() * 2) else: val = "<span style=\"background-color:orange\">%s</span>" % (text.nbsp() * 2) print("<td>%s</td>" % val) # track costs published when there is no column to put the number tgts = costs_pub[c.router.router.container_name] for k, v in common.dict_iteritems(l_dict): if k not in comn.router_ids: if k not in tgts: tgts.append(k) # this cost went unreported # update this router's cost view in running table if plf.router.is_interior(): cur_costs[plf.router.container_name] = costs_row except: pass print("</tr>") # if the costs are stable across all routers then put an indicator in table costs_stable = True for c_rtr in interior_rtrs: for r_rtr in interior_rtrs: if r_rtr != c_rtr \ and (cur_costs[r_rtr][c_rtr] != cur_costs[c_rtr][r_rtr] \ or cur_costs[c_rtr][r_rtr] <= PEER_COST_ABSENT): costs_stable = False break if not costs_stable: break if costs_stable: print("<tr><td><span style=\"background-color:green\">stable</span></td></tr>") else: # restart print("<tr><td>%s</td> <td>%s</td>" % (c.datetime, ("%s restart" % (c.router.iname)))) for i in range(0, comn.n_logs): color = "green" if i == c.router.log_index else "orange" print("<td><span style=\"background-color:%s\">%s</span></td>" % (color, text.nbsp() * 2)) print("</tr>") if c.router.is_interior(): cur_costs[c.router.container_name] = new_costs_row(PEER_COST_REBOOT) print("</table>") print("<br>") # maybe display cost declarations that were not displayed costs_clean = True for k, v in common.dict_iteritems(costs_pub): if len(v) > 0: costs_clean = False break if not costs_clean: print("<h4>Router costs declared in logs but not displayed in Link state cost table</h4>") print("<table>") print("<tr><th>Router</th><Peers whose logs are absent</th></tr>") for k, v in common.dict_iteritems(costs_pub): if len(v) > 0: print("<tr><td>%s</td><td>%s</td></tr>" % (k, str(v))) print("</table>") print("<br>") print("<a href=\"javascript:toggle_node('ls_costs')\">%s%s</a> Link state costs data<br>" % (text.lozenge(), text.nbsp())) print(" <div width=\"100%%\"; " "style=\"display:none; font-weight: normal; margin-bottom: 2px; margin-left: 10px\" " "id=\"ls_costs\">") print("<table>") print("<tr><th>Time</th> <th>Router</th> <th>Name</th> <th>Log</th></tr>") for plf in ls_tree: if "costs" in plf.line: print("<tr><td>%s</td> <td>%s</td>" % (plf.datetime, ("%s#%d" % (plf.router.iname, plf.lineno)))) print("<td>%s</td>" % plf.router.container_name) print("<td>%s</td></tr>" % plf.line) print("</table>") print("</div>") print("<hr>") print("</body>")
def write(self, text, esc_all=False): self.write_raw(html_escape(text))
def __init__(self, _log_index, _instance, _lineno, _line, _comn, _router, opaque=None): """ Process a naked qpid-dispatch log line A log line looks like this: 2018-07-20 10:58:40.179187 -0400 SERVER (trace) [2]:0 -> @begin(17) [next-outgoing-id=0, incoming-window=2147483647, outgoing-window=2147483647] (/home/chug/git/qpid-dispatch/src/server.c:106) The process is: 1. If the line ends with a filename:fileline then strip that away 2. Peel off the leading time of day and put that into data.datetime. Lines with no datetime are presumed start-of-epoch. 3. Find (SERVER) or (POLICY). If absent then raise to reject message. 4. If connection number in square brackets '[2]' is missing then raise. 5. Extract connection number; save in data.conn_num 6. Create decorated data.conn_id "A0_2" 7. Extract data.channel if present. Raise if malformed. 8. Create a web_show_str for lines that may not parse any further. Like policy lines. 9. Extract the direction arrows The log line is now reduced to a described type: @describedtypename(num) [key=val [, key=val ...]] except for transfers that have the funky transfer data at end. :param _log_index: The router prefix index 0 for A, 1 for B, ... :param _instance The router instance :param _lineno: :param _line: :param _comn: :param _router: """ if not (ParsedLogLine.server_trace_key in _line or ParsedLogLine.protocol_trace_key in _line or (ParsedLogLine.policy_trace_key in _line and "lookup_user:"******"Line is not a candidate for parsing") self.index = _log_index # router prefix 0 for A, 1 for B self.instance = _instance # router instance in log file self.lineno = _lineno # log line number self.comn = _comn self.router = _router self.opaque = opaque self.prefixi = common.log_letter_of(self.index) + str(self.instance) # prefix+instance A0 self.fid = "f_" + self.prefixi + "_" + str(self.lineno) # frame id A0_100 self.shorteners = _comn.shorteners # name shorteners self.line = _line # working line chopped, trimmed self.data = LogLineData() # parsed line fact store # strip optional trailing file:line field self.line = self.line.rstrip() hasFileLine = False if self.line.endswith(')'): idxOP = self.line.rfind('(') idxColon = self.line.rfind(':') if idxOP != -1 and idxColon != -1: if idxColon > idxOP: lNumStr = self.line[(idxColon + 1): (-1)] try: lnum = int(lNumStr) hasFileLine = True except: pass if hasFileLine: self.line = self.line[:self.line.rfind('(')].rstrip() # Handle optional timestamp # This whole project is brain dead without a timestamp. Just sayin'. self.datetime = None try: self.datetime = datetime.strptime(self.line[:26], '%Y-%m-%d %H:%M:%S.%f') except: # old routers flub the timestamp and don't print leading zero in uS time # 2018-11-18 11:31:08.269 should be 2018-11-18 11:31:08.000269 td = self.line[:26] parts = td.split('.') us = parts[1] parts_us = us.split(' ') if len(parts_us[0]) < 6: parts_us[0] = '0' * (6 - len(parts_us[0])) + parts_us[0] parts[1] = ' '.join(parts_us) td = '.'.join(parts) try: self.datetime = datetime.strptime(td[:26], '%Y-%m-%d %H:%M:%S.%f') except: self.datetime = datetime(1970, 1, 1) # Apply time-of-day filters if self.datetime is not None: if self.comn.args.time_start is not None: if self.datetime < self.comn.args.time_start: raise ValueError("Line too early outside time-of-day limits") if self.comn.args.time_end is not None: if self.datetime > self.comn.args.time_end: raise ValueError("Line too late outside time-of-day limits") # Pull out scraper literal logs sti = self.line.find(self.scraper_key) if sti > 0: # strip datetime and show literal string sti += len("SCRAPER") self.data.is_scraper = True self.data.web_show_str = ("<strong>SCRAPER</strong> %s" % common.html_escape(self.line[sti:])) stcp = self.line[sti:].find(')') # close paren after log level if stcp < 0: stcp = 0 self.data.sdorg_str = self.line[sti + stcp + 1:] return # extract connection number sti = self.line.find(self.server_trace_key) if sti < 0: sti = self.line.find(self.protocol_trace_key) if sti < 0: sti = self.line.find(self.policy_trace_key) if sti < 0: sti = self.line.find(self.server_info_key) if sti < 0: sti = self.line.find(self.router_ls_key) if sti < 0: raise ValueError("Log keyword/level not found in line %s" % (self.line)) else: self.line = self.line[sti + len(self.router_ls_key):] self.data.is_router_ls = True # this has no relationship to AMQP log lines return else: self.line = self.line[sti + len(self.server_info_key):] self.data.is_server_info = True else: self.line = self.line[sti + len(self.policy_trace_key):] self.data.is_policy_trace = True else: self.line = self.line[sti + len(self.protocol_trace_key):] else: self.line = self.line[sti + len(self.server_trace_key):] ste = self.line.find(']') if ste < 0: print("Failed to parse line ", _lineno, " : ", _line) traceback.print_exc() raise ValueError("'%s' not found in line %s" % ("]", self.line)) self.data.conn_num = self.line[:ste] if self.data.conn_num.startswith("C"): self.data.conn_num = self.data.conn_num[1:] self.line = self.line[ste + 1:] # create decorated connection id self.data.conn_id = self.prefixi + "_" + self.data.conn_num # get the session (channel) number if self.line.startswith(':'): self.line = self.line[1:] if self.line.startswith(self.proton_frame_key): self.line = self.line[len(self.proton_frame_key):] sti = self.line.find(' ') if sti < 0: raise ValueError("space not found after channel number at head of line %s" % (self.line)) if sti > 0: self.data.channel = self.line[:sti] self.line = self.line[sti + 1:] self.line = self.line.lstrip() # cover for traces that don't get any better self.data.web_show_str = ("<strong>%s</strong>" % self.line) # policy lines have no direction and described type fields if self.data.is_policy_trace or self.data.is_server_info: return # direction if self.line.startswith('<') or self.line.startswith('-'): self.data.direction = self.line[:2] self.line = self.line[3:] self.data.web_show_str = ("<strong>%s</strong>" % self.line) # The log line is now reduced to a described type: # @describedtypename(num) [key=val [, key=val ...]] # extract descriptor name dname = self.line.split()[0] self.line = self.line[(len(dname) + 1):] # Dispose of the transfer data if dname == self.transfer_key: # Look for the '] (NNN) "' that separates the described type fields # from the '(size) "data"'. Stick the required '(size) data' into # data.transfer_data and delete it from the line. rz = re.compile(r'\] \(\d+\) \"').search(self.line) # aborted transfers may or may not have size/data in the log line if rz is not None and len(rz.regs) > 0: splitSt, splitTo = rz.regs[0] self.data.transfer_size = self.line[splitSt + 3: splitTo - 3] self.data.transfer_data = self.line[splitTo - 1:] # discard (NNN) size field self.line = self.line[: splitSt + 1] # try to isolate the bare message sti = self.data.transfer_data.find(r"\x00Ss") if sti > 0: self.data.transfer_hdr_annos = self.data.transfer_data[:sti] self.data.transfer_bare = self.data.transfer_data[sti:] else: self.data.transfer_hdr_annos = '' self.data.transfer_bare = self.data.transfer_data else: self.data.transfer_size = "0" self.data.transfer_data = "(none)" if DescribedType.is_dtype_name(dname): self.data.described_type.parse_dtype_line(dname, self.line) # data fron incoming line is now parsed out into facts in .data # Now cook the data to get useful displays self.extract_facts()