Display the Contents of a Log File as It Is Updated

Display the contents of a log file as it is updated

Use a Flask view to continuously read from the file forever and stream the response. Use JavaScript to read from the stream and update the page. This example sends the entire file, you may want to truncate that at some point to save bandwidth and memory. This example sleeps between reads to reduce cpu load from the endless loop and allow other threads more active time.

from time import sleep
from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def index():
return render_template('index.html')

@app.route('/stream')
def stream():
def generate():
with open('job.log') as f:
while True:
yield f.read()
sleep(1)

return app.response_class(generate(), mimetype='text/plain')

app.run()
<pre id="output"></pre>
<script>
var output = document.getElementById('output');

var xhr = new XMLHttpRequest();
xhr.open('GET', '{{ url_for('stream') }}');
xhr.send();

setInterval(function() {
output.textContent = xhr.responseText;
}, 1000);
</script>

This is almost the same as this answer, which describes how to stream and parse messages, although reading from an external file forever was novel enough to be it's own answer. The code here is simpler because we don't care about parsing messages or ending the stream, just tailing the file forever.

Display the last 100 lines of a log file in real time

This is a basic solution.

Here is your html and js index.php

<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Read Log File</title>

</head>

<body>
<div>
<ul id="log">

</ul>
</div>
<script src="./jquery-3.2.1.min.js"></script>
<script>

$(function(){

setInterval(function(){
$.getJSON( "./getLog.php", function( data ) {
var $log = $('#log');
$.each( data, function( key, val ) {
$log.prepend( "<li>" + val + "</li>" );
});
});

},5000);

});

</script>
</body>
</html>

Here is your php file getLog.php

  <?php

session_start();

$file = '/path/to/your/file_log';

$total_lines = shell_exec('cat ' . escapeshellarg($file) . ' | wc -l');

if(isset($_SESSION['current_line']) && $_SESSION['current_line'] < $total_lines){

$lines = shell_exec('tail -n' . ($total_lines - $_SESSION['current_line']) . ' ' . escapeshellarg($file));

} else if(!isset($_SESSION['current_line'])){

$lines = shell_exec('tail -n100 ' . escapeshellarg($file));

}

$_SESSION['current_line'] = $total_lines;

$lines_array = array_filter(preg_split('#[\r\n]+#', trim($lines)));

if(count($lines_array)){
echo json_encode($lines_array);
}

?>

How to create an activity that displays a log file and observe it to refresh its layout?

Finally here is my solution (even if the Arkadiusz's one is nice).

I have implemented a Service that is observing the file and I've registered it in my LogActivity :


The Service :


public class LogService extends Service {

public static final String BROADCAST_FILE_LOG_UPDATE = "my.package.app.log.update";
private String fileName = "/data/data/my.package.app/files/log.txt";

/**
* The intent thanks to which is forwarded the alarm to display.
*/
private Intent logIntent;

private FileObserver fileObserver;

@Override
public void onCreate() {
Log.e("LogService", "oncreate");
logIntent = new Intent(BROADCAST_FILE_LOG_UPDATE);
fileObserver = new FileObserver(fileName) {
@Override
public void onEvent(int event, String path) {
if (event == FileObserver.MODIFY) {
broadcastLogUpdate();
}
}
};
}

private void broadcastLogUpdate() {
sendBroadcast(logIntent);
}

@Override
public void onStart(Intent intent, int startid) {
fileObserver.startWatching();
}

@Override
public void onDestroy() {
fileObserver.stopWatching();
}

@Override
public IBinder onBind(Intent intent) {
return null;
}
}

The LogActivity :


public class LogActivity extends Activity {

private TextView textView;
private String fileName = "/data/data/my.package.app/files/log.txt";

private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
setTextView();
}
};
private Intent serviceIntent;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
serviceIntent = new Intent(getApplicationContext(), LogService.class);
setContentView(R.layout.log);
Typeface tf = Typeface.createFromAsset(getAssets(), "CONSOLA.TTF");
textView = (TextView) findViewById(R.id.textView1);
textView.setTypeface(tf);
setTextView();
}

@Override
protected void onResume() {
super.onResume();
try {
startService(serviceIntent);
registerReceiver(broadcastReceiver, new IntentFilter(LogService.BROADCAST_FILE_LOG_UPDATE));
} catch (IllegalArgumentException e) {}
}

@Override
protected void onStop() {
super.onStop();
try {
unregisterReceiver(broadcastReceiver);
} catch (IllegalArgumentException e) {}
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.log_activity, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_reload:
Toast.makeText(getApplicationContext(), "reload", Toast.LENGTH_LONG).show();
setTextView();
break;
case R.id.menu_empty:
new AlertDialog.Builder(this).setIcon(android.R.drawable.ic_dialog_alert)
.setTitle("Erase log file ?").setMessage("Sure ?")
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
File file = new File(fileName);
file.delete();
try {
file.createNewFile();
unregisterReceiver(broadcastReceiver);
stopService(serviceIntent);
startService(serviceIntent);
registerReceiver(broadcastReceiver, new IntentFilter(
LogService.BROADCAST_FILE_LOG_UPDATE));

} catch (IOException e) {
e.printStackTrace();
}
setTextView();
}

}).setNegativeButton("No", null).show();
break;
default:
break;
}
return super.onOptionsItemSelected(item);
}

private void setTextView() {
try {
FileReader fileReader = new FileReader(new File(fileName));
BufferedReader bufferedReader = new BufferedReader(fileReader);

String line = "";
StringBuilder builder = new StringBuilder("");
while ((line = bufferedReader.readLine()) != null) {
builder.insert(0, line + "\n");
}
textView.setText(builder.toString());
bufferedReader.close();

} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

}

Of course, don't forget to add the new service in the manifest.


The manifest :


    <service android:name=".LogService" />

It works very well.

I hope it would be useful for somebody else.

Bash script log file display to screen continuously

Try the tail command:

tail -f filename

Show only newly added lines of logfile in terminal

Try

$ watch 'tac FILE | grep -m1 -C2 PATTERN | tac'

where

PATTERN is any keyword (or regexp) to identify errors you seek in the log,

tac prints the lines in reverse,

-m is a max count of matching lines to grep,

-C is any number of lines of context (before and after the match) to show (optional).

That would be similar to

$ tail -f FILE | grep -C2 PATTERN

if you didn't mind just appending occurrences to the output in real-time.

But if you don't know any generic PATTERN to look for at all,

you'd have to just follow all the updates as the logfile grows:

$ tail -n0 -f FILE

Or even, create a copy of the logfile and then do a diff:

  1. Copy: cp file.log{,.old}
  2. Refresh the webpage with your .php code (or whatever, to trigger the error)
  3. Run: diff file.log{,.old}

    (or, if you prefer sort to diff: $ sort file.log{,.old} | uniq -u)

The curly braces is shorthand for both filenames (see Brace Expansion in $ man bash)

If you must avoid any temp copies, store the line count in memory:

  1. z=$(grep -c ^ file.log)
  2. Refresh the webpage to trigger an error
  3. tail -n +$z file.log

The latter approach can be built upon, to create a custom scripting solution more suitable for your needs (check timestamps, clear screen, filter specific errors, etc). For example, to only show the lines that belong to the last error message in the log file updated in real-time:

$ clear; z=$(grep -c ^ FILE); while true; do d=$(date -r FILE); sleep 1; b=$(date -r FILE); if [ "$d" != "$b" ]; then clear; tail -n +$z FILE; z=$(grep -c ^ FILE); fi; done

where

FILE is, obviously, your log file name;

grep -c ^ FILE counts all lines in a file (that is almost, but not entirely unlike cat FILE|wc -l that would only count newlines);

sleep 1 sets the pause/delay between checking the file timestamps to 1 second, but you could change it to even a floating point number (the less the interval, the higher the CPU usage).

To simplify any repetitive invocations in future, you could save this compound command in a Bash script that could take a target logfile name as an argument, or define a shell function, or create an alias in your shell, or just reverse-search your bash history with CTRL+R. Hope it helps!



Related Topics



Leave a reply



Submit