Introduction
If you run a Minecraft server in Linux then you'll probably like to keep track of what's going on in the server log. I found that it was a real pain having to remote shell into the server all the time and if it was available via a web browser I could keep track of it more conveniently, or by using my phone or tablet.I decided that I would use PHP (even though PERL is generally my scripting weapon of choice) and once I'd found the correct commands and syntax it came together quite quickly. If you fancy giving it a try you just need a web server (such as Apache) and of course PHP running on your server.
How it works
It started out being a simple process of opening the server.log file and outputting each line as pre-formatted html.Something like this..
<html>
<head><title>Minecraft Log</title></head>
<body><pre>
<?php
$filepath = "/opt/minecraft/server.log";
$file = file($filepath);
foreach($file as $line) {
echo $line."<br/>\n";
}
?>
</pre></body>
</html>
<head><title>Minecraft Log</title></head>
<body><pre>
<?php
$filepath = "/opt/minecraft/server.log";
$file = file($filepath);
foreach($file as $line) {
echo $line."<br/>\n";
}
?>
</pre></body>
</html>
This was great at first, but after we'd been running for a few weeks the server.log file gets too big and the web page gets slower and slower to load. What we needed was a way of only showing the current days log entries, and maybe a date selector so you can reference older stuff.
So I created version two with the following enhancements:-
- Default to showing today's log entries.
- A date selector is added to the top of the web page which sends the new value if altered.
- If page requested with a date parameter then use this instead of today's date.
- If no date has been passed then run a javascript function to scroll the log section to the bottom.
There's a nice feature in the log where text is coloured. This would typically be for chat or system warnings, but as it stood they left messy control codes littered about the log. Rather than remove these I decided to replace them and style the text to match these colours.
This enhancement required the following:-
This enhancement required the following:-
- Create a series of regular expression replaces to match the control codes.
- Each colour code instance is replaced by a span tag which corresponds to a set of pre-defined styles (the css for these is defined in the head section).
- Put log into a scrollable div.
<!DOCTYPE html>
<html>
<head>
<title>Minecraft Log</title>
<style>
#log{
height:400px;
overflow-y: scroll;
margin: 5px 0 0 1px;
border: 1px solid;
}
.green{color: green;}
.red{color: red;}
.purple{color: purple;}
.black{color: black;}
.blue{color: darkblue;}
.gold{color: gold;}
.cyan{color: darkcyan;}
.aqua{color: cadetblue;}
.gray{color: #888;}
.bold{ font-weight:bold;}
</style>
<script>
function scroll(){
//scroll div to bottom
var objDiv = document.getElementById("log");
objDiv.scrollTop = objDiv.scrollHeight;
}
</script>
</head>
<?php
$pass = htmlspecialchars($_POST["date"]);
if (!$pass){
echo "<body onLoad='scroll()'>";
}
else {
echo "<body>";
}
$filepath = "/opt/minecraft/server.log";
$file = file($filepath);
$dates = array();
$last = "";
foreach($file as $line) {
$date = substr($line, 0, 10);
if ($date != $last){
if(preg_match('/\d{4}\-\d{2}\-\d{2}/',$date)) {
array_push($dates, substr($line, 0, 10));
$last = $date;
}
}
}
/* If Date Provided Use This */
if(preg_match('/\d{4}\-\d{2}\-\d{2}/',$pass)) {
$last = $pass;
}
/* Add Form Element and Date Selector */
echo "<form method='post'>Select date: <select name='date' onchange='this.form.submit()'>\n";
foreach($dates as $value){
$select = "";
if ($value == $last){ $select = " selected='selected'"; }
echo "<option value='".$value."'".$select.">".$value."</option>\n";
}
echo "</select></form>\n";
/* Output Log For Required Date */
echo "<div id='log'>";
foreach($file as $line) {
/* Remap Problem Characters */
$line = preg_replace("/</","<",$line);
$line = preg_replace("/>/","</span>>",$line);
$date = substr($line, 0, 10);
if ($date == $last) {
/* Remove Unrequired Formatting Codes */
$line = str_replace("[m","",$line);
$line = str_replace("[21m","",$line);
$line = str_replace("[3m","",$line);
/* Split Log Line Into Sections to Using First Formatting Code Style */
$segarray = preg_split( '/(\[0|\[m)/', $line );
for ($i = 1; $i < count($segarray); ++$i){
/* Do Replace to Add Styled Spans */
if (preg_match('/;\d{2};\d+m/', $segarray[$i])) {
$segarray[$i] = preg_replace("/;30/","<span class='black",$segarray[$i]);
$segarray[$i] = preg_replace("/;31/","<span class='red",$segarray[$i]);
$segarray[$i] = preg_replace("/;32/","<span class='green",$segarray[$i]);
$segarray[$i] = preg_replace("/;33/","<span class='gold",$segarray[$i]);
$segarray[$i] = preg_replace("/;34/","<span class='blue",$segarray[$i]);
$segarray[$i] = preg_replace("/;35/","<span class='purple",$segarray[$i]);
$segarray[$i] = preg_replace("/;36/","<span class='aqua",$segarray[$i]);
$segarray[$i] = preg_replace("/;37/","<span class='gray",$segarray[$i]);
$segarray[$i] = preg_replace("/;22m/","'>",$segarray[$i]);
$segarray[$i] = preg_replace("/;1m/"," bold'>",$segarray[$i]);
$segarray[$i] = $segarray[$i]."</span>";
}
}
/* Rejoin Then Split Log Line Using Second Formatting Code Style */
$line = join("",$segarray);
$segarray = preg_split( '/§/', $line );
for ($i = 1; $i < count($segarray); ++$i){
/* Do Replace to Add Styled Spans */
$segarray[$i] = preg_replace("/^0/","<span class='black'>",$segarray[$i]);
$segarray[$i] = preg_replace("/^1/","<span class='blue'>",$segarray[$i]);
$segarray[$i] = preg_replace("/^2/","<span class='green'>",$segarray[$i]);
$segarray[$i] = preg_replace("/^3/","<span class='aqua'>",$segarray[$i]);
$segarray[$i] = preg_replace("/^4/","<span class='red'>",$segarray[$i]);
$segarray[$i] = preg_replace("/^5/","<span class='purple'>",$segarray[$i]);
$segarray[$i] = preg_replace("/^6/","<span class='gold'>",$segarray[$i]);
$segarray[$i] = preg_replace("/^7/","<span class='gray'>",$segarray[$i]);
$segarray[$i] = preg_replace("/^8/","<span class='gray'>",$segarray[$i]);
$segarray[$i] = preg_replace("/^9/","<span class='blue'>",$segarray[$i]);
$segarray[$i] = preg_replace("/^a/","<span class='green'>",$segarray[$i]);
$segarray[$i] = preg_replace("/^b/","<span class='aqua'>",$segarray[$i]);
$segarray[$i] = preg_replace("/^c/","<span class='red'>",$segarray[$i]);
$segarray[$i] = preg_replace("/^d/","<span class='purple'>",$segarray[$i]);
$segarray[$i] = preg_replace("/^e/","<span class='gold'>",$segarray[$i]);
$segarray[$i] = preg_replace("/^f/","<span class='black'>",$segarray[$i]);
$segarray[$i] = preg_replace("/^r/","<span class='black'>",$segarray[$i]);
$segarray[$i] = $segarray[$i]."</span>";
}
/* Rejoin and Output to Webpage */
$line = join("",$segarray);
echo $line."<br/>\n";
}
}
echo "</div>";
?>
</body>
</html>
<html>
<head>
<title>Minecraft Log</title>
<style>
#log{
height:400px;
overflow-y: scroll;
margin: 5px 0 0 1px;
border: 1px solid;
}
.green{color: green;}
.red{color: red;}
.purple{color: purple;}
.black{color: black;}
.blue{color: darkblue;}
.gold{color: gold;}
.cyan{color: darkcyan;}
.aqua{color: cadetblue;}
.gray{color: #888;}
.bold{ font-weight:bold;}
</style>
<script>
function scroll(){
//scroll div to bottom
var objDiv = document.getElementById("log");
objDiv.scrollTop = objDiv.scrollHeight;
}
</script>
</head>
<?php
$pass = htmlspecialchars($_POST["date"]);
if (!$pass){
echo "<body onLoad='scroll()'>";
}
else {
echo "<body>";
}
$filepath = "/opt/minecraft/server.log";
$file = file($filepath);
$dates = array();
$last = "";
/*
Get List Of Dates */foreach($file as $line) {
$date = substr($line, 0, 10);
if ($date != $last){
if(preg_match('/\d{4}\-\d{2}\-\d{2}/',$date)) {
array_push($dates, substr($line, 0, 10));
$last = $date;
}
}
}
/* If Date Provided Use This */
if(preg_match('/\d{4}\-\d{2}\-\d{2}/',$pass)) {
$last = $pass;
}
/* Add Form Element and Date Selector */
echo "<form method='post'>Select date: <select name='date' onchange='this.form.submit()'>\n";
foreach($dates as $value){
$select = "";
if ($value == $last){ $select = " selected='selected'"; }
echo "<option value='".$value."'".$select.">".$value."</option>\n";
}
echo "</select></form>\n";
/* Output Log For Required Date */
echo "<div id='log'>";
foreach($file as $line) {
/* Remap Problem Characters */
$line = preg_replace("/</","<",$line);
$line = preg_replace("/>/","</span>>",$line);
$date = substr($line, 0, 10);
if ($date == $last) {
/* Remove Unrequired Formatting Codes */
$line = str_replace("[m","",$line);
$line = str_replace("[21m","",$line);
$line = str_replace("[3m","",$line);
/* Split Log Line Into Sections to Using First Formatting Code Style */
$segarray = preg_split( '/(\[0|\[m)/', $line );
for ($i = 1; $i < count($segarray); ++$i){
/* Do Replace to Add Styled Spans */
if (preg_match('/;\d{2};\d+m/', $segarray[$i])) {
$segarray[$i] = preg_replace("/;30/","<span class='black",$segarray[$i]);
$segarray[$i] = preg_replace("/;31/","<span class='red",$segarray[$i]);
$segarray[$i] = preg_replace("/;32/","<span class='green",$segarray[$i]);
$segarray[$i] = preg_replace("/;33/","<span class='gold",$segarray[$i]);
$segarray[$i] = preg_replace("/;34/","<span class='blue",$segarray[$i]);
$segarray[$i] = preg_replace("/;35/","<span class='purple",$segarray[$i]);
$segarray[$i] = preg_replace("/;36/","<span class='aqua",$segarray[$i]);
$segarray[$i] = preg_replace("/;37/","<span class='gray",$segarray[$i]);
$segarray[$i] = preg_replace("/;22m/","'>",$segarray[$i]);
$segarray[$i] = preg_replace("/;1m/"," bold'>",$segarray[$i]);
$segarray[$i] = $segarray[$i]."</span>";
}
}
/* Rejoin Then Split Log Line Using Second Formatting Code Style */
$line = join("",$segarray);
$segarray = preg_split( '/§/', $line );
for ($i = 1; $i < count($segarray); ++$i){
/* Do Replace to Add Styled Spans */
$segarray[$i] = preg_replace("/^0/","<span class='black'>",$segarray[$i]);
$segarray[$i] = preg_replace("/^1/","<span class='blue'>",$segarray[$i]);
$segarray[$i] = preg_replace("/^2/","<span class='green'>",$segarray[$i]);
$segarray[$i] = preg_replace("/^3/","<span class='aqua'>",$segarray[$i]);
$segarray[$i] = preg_replace("/^4/","<span class='red'>",$segarray[$i]);
$segarray[$i] = preg_replace("/^5/","<span class='purple'>",$segarray[$i]);
$segarray[$i] = preg_replace("/^6/","<span class='gold'>",$segarray[$i]);
$segarray[$i] = preg_replace("/^7/","<span class='gray'>",$segarray[$i]);
$segarray[$i] = preg_replace("/^8/","<span class='gray'>",$segarray[$i]);
$segarray[$i] = preg_replace("/^9/","<span class='blue'>",$segarray[$i]);
$segarray[$i] = preg_replace("/^a/","<span class='green'>",$segarray[$i]);
$segarray[$i] = preg_replace("/^b/","<span class='aqua'>",$segarray[$i]);
$segarray[$i] = preg_replace("/^c/","<span class='red'>",$segarray[$i]);
$segarray[$i] = preg_replace("/^d/","<span class='purple'>",$segarray[$i]);
$segarray[$i] = preg_replace("/^e/","<span class='gold'>",$segarray[$i]);
$segarray[$i] = preg_replace("/^f/","<span class='black'>",$segarray[$i]);
$segarray[$i] = preg_replace("/^r/","<span class='black'>",$segarray[$i]);
$segarray[$i] = $segarray[$i]."</span>";
}
/* Rejoin and Output to Webpage */
$line = join("",$segarray);
echo $line."<br/>\n";
}
}
echo "</div>";
?>
</body>
</html>
nb. Notice the log must now be read twice!
Installation Instructions
Copy the code into a file called index.php and drop it into a directory accessible to your web server and ensure you grant the file execution rights.Alter the $filepath = "/opt/minecraft/server.log"; line so that it points to your server log and ensure the web server has read rights to this file.
How to Use
Just open the PHP code in your browser and the a page should open showing today's log entries, scrolled to the bottom of the log.If you then select an earlier date the page will refresh and show this dates entries without the auto-scrolling.