1

So some time ago I developed a web application that allows users to launch a system application and view the debug log of that application in browser. I used Server Side EventSource to update a <div> each time a new line is written to the debug file. The end result is something like a "tail -f" in browser. Everything was working pretty well until someone started using the application in a different way where > 10,000 lines are written to the debug file.

Depending on the system and browser, anything over ~8-10k lines causes the browser to hang and respond extremely slowly. Unfortunately I can not place controls on what is written to the debug file but I thought that I could only display the last 2k lines. However, this page is brought up immediately after the system application is launched and the user stays on this page until the system application call completes..... So is there any way to clear what is written to the <div> tag in a FIFO manner? Otherwise, is there a way to improve performance to handle larger data size? Here are some relevant code snips from the PHP..

This is the page that has the <div> that is updated:

    <script>
if(typeof(EventSource) !== "undefined") {
    var source = new EventSource("stream.php<?php echo $built_url; ?>&inserted_procedure_id=<?php
    echo $_GET['inserted_procedure_id'];
    ?>");
    source.onmessage = function(event) {
        document.getElementById("debug").innerHTML += event.data + "<br>";
        var objDiv = document.getElementById('debug');
  objDiv.scrollTop = objDiv.scrollHeight;
    };
} else {
    document.getElementById("debug").innerHTML = "Sorry, real time debug requires a modern browser with support for server side events.  Please use current version of Firefox, Chrome or Safari for real-time debug...  ";
}
</script>

This is from stream.php which is called by the previous

if($_SESSION['done'] == 0 ){

clearstatcache();
$currentSize = filesize($file);
//echo "data: Test running, please wait ".$size."\n\n";
if ($size != $currentSize) {

    $fh = fopen($file, "r");
    fseek($fh, $size);
    $dcounter=0;
    sleep(5);
    while ($d = fgets($fh)) {
        //$d=str_replace(' ','&nbsp;',$d);
        if (preg_match("/Executing test case:/", $d)) {
            echo "data: <div style='position: fixed; bottom: 16px; width: 55px; height: 30px; z-index: 1002;'><form name='abort' id='abort' method='post' enctype='multipart/form-data' action='executor_state_change.php'><input type=hidden name=kill value=".$pid."/><font color=black><input type='button' value='Abort' onclick=openPopup();></font></form></div>\n\n";        
        }
        echo "data: ".htmlspecialchars($d)."\n\n";
        if (preg_match("/Please select continue when above instruction is complete/", $d)) {
            $sql="SELECT wait FROM test_procedure_execution WHERE id=".$proc_id.";";
            $sql_result=mysqli_query($con,"$sql");
            $sql_row=mysqli_fetch_row($sql_result);
            if($sql_row[0]==1){
                echo "data: <form name='continue' id='continue' method='post' enctype='multipart/form-data' action='continue.php'><input type=hidden name=test_procedure_execution_id value=".$proc_id." /><font color=black><input type='submit' value='Continue' onclick='button.style.display =none;'></font></form>\n\n";       
                echo "data: <audio autoplay loop><source src='sounds/alert.wav' type='audio/wav'></audio>\n\n";
            }                   
        }
        $dcounter++;
        //ob_flush();
        //flush();
    }
    fclose($fh);

    $_SESSION["size"] = $currentSize;
}
Mr Lister
  • 45,515
  • 15
  • 108
  • 150
Joel Foust
  • 110
  • 1
  • 8

1 Answers1

2

After appending the message HTML of the last event to document.getElementById("debug").innerHTML, you should do the processing. Possibly something like this:

var content = document.getElementById("debug").innerHTML + event.data + "<br>";
var lines = content.split("<br>");
lines = lines.splice(lines.length - 2000, 2000);
document.getElementById("debug").innerHTML = lines.join("<br>");
aross
  • 3,325
  • 3
  • 34
  • 42
  • Thanks! So this seems to work pretty well but I have to reduce the number of lines to about 500 or reduce the update frequency to keep the browser happy. I guess this can work but I'd really like to get a little more debug out at reasonable speed... I'm not the best at Javascript so excuse my ignorance with this followup question.... The code above looks to me like it stores all those lines in var content and in var lines. Not sure how GC handles this but is there anything further I can do to improve performance? – Joel Foust Feb 25 '16 at 16:03
  • If you wanna reduce the performance bottleneck on the client-side you could try to prevent writing to the DOM (which causes expensive re-flowing) whenever possible. I don't think GC will really be a problem here. In this case, you're only writing to DOM once per iteration, so you should either reduce the frequency or the volume like you said. What might also work (requires more serious refactor) is preventing the need for the browser to convert a string to HTML, by creating DOM elements on the fly and then injecting them. It wouldn't prevent re-flow though, which I think is the most expensive. – aross Feb 25 '16 at 16:50
  • Thanks again for your expert advice. Moving the debug output to a new window with minimal css and elements seems to solve most of the issues I was experiencing related to performance. Although it's not as beautifully integrated with the tool, it's waaaaay more functional for the end users! – Joel Foust Feb 25 '16 at 19:22