- Posted on
- • Programming
PHP CLI Error Log Viewer
- Author
-
-
- User
- Mr Blinky
- Posts by this author
- Posts by this author
-
I have been doing quite a bit of PHP programming lately. One thing I have found is repeatedly having to check up on my PHP error log. You know the thing, you write some code, move down the page, write some more, up a bit, little more, down to the bottom and finish. There, all good, and.... HTTP 500. I have two options, go through all the code I have just written and look for that missing semicolon or bracket, or open the log, look at the last entry and go from there.
I have been doing a lot of the latter of late. Something doesn't work so look in the error log. Ooh, line 57.... I thought, wouldn't it be better if I had a live readout of the log. Just a window I can have open on my desktop and if any errors occur, they pop into the error log window. So I wrote a PHP CLI error log viewer.
This will show the last 200k tail and also any new log entries as they happen. In just one day programming it has helped enormously (because I'm a rubbish programmer). Really, it is worth it's weight in code to me. 😂 It updates as new lines are written. You can clear the log down and start over from a button. You can pause it when your code is so broke it it literally raining log lines. And not much else.
First thing to do is to get PHP to save/append it's error log in a location you know. Oh yeah, I forgot to mention, this can be used remotely if you are FTPing your files, or something else. This runs in the browser, so you can get to it anywhere. If live on the net, sort your own security. A PHP error log can expose details or even entry points into your network, so bear that in mind! Alright, back to the job at hand. We need to save the log in a place the webserver can get to it, so pop open your php.ini and either amend or add your logging method, such as:
; log_errors
log_errors = On
error_reporting = E_ALL
error_log = "C:\php\php_cli_errors.log"
This is on Windows so change the path to suit if using linux. Apache on windows (at least my apache on windows) can see the file in C:\php, so all good for me.
Once you have done that and confirm the error log is being created and appended in that location, drop this simple PHP script that AI wrote for me (you didn't think I wrote this did you? 😝 I'm a rubbish programmer, remember!), call it index.php (so you can www.mysite.com/php_cli_errors/ without extra page names) or whatever, but make sure that the $logFile variable at the top is set to exactly as you have PHP saving your logfile. So just create a blank .php file and drop this code in:
<?php
declare(strict_types=1);
$logFile = 'C:/php/php_cli_errors.log'; // <-- Change this to what your logfile is called and located
$maxInitialBytes = 200000; // 200 KB tail on first load
function json_out(array $data): never {
header('Content-Type: application/json; charset=utf-8');
echo json_encode($data);
exit;
}
if (isset($_GET['action'])) {
$action = $_GET['action'];
if (!file_exists($logFile)) {
touch($logFile);
}
if ($action === 'read') {
$offset = max(0, (int)($_GET['offset'] ?? 0));
$size = filesize($logFile) ?: 0;
// Log was cleared/truncated.
if ($offset > $size) {
$offset = 0;
}
// First load: only read the end of a large file.
if ($offset === 0 && $size > $maxInitialBytes) {
$offset = $size - $maxInitialBytes;
}
$fh = fopen($logFile, 'rb');
if (!$fh) {
json_out(['ok' => false, 'error' => 'Could not open log file']);
}
fseek($fh, $offset);
$data = stream_get_contents($fh);
fclose($fh);
json_out([
'ok' => true,
'content' => $data,
'offset' => $size,
'size' => $size,
]);
}
if ($action === 'clear') {
file_put_contents($logFile, '');
json_out(['ok' => true, 'offset' => 0, 'size' => 0]);
}
json_out(['ok' => false, 'error' => 'Unknown action']);
}
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>PHP CLI Error Log</title>
<style>
body {
margin: 0;
font-family: system-ui, sans-serif;
background: #111;
color: #eee;
}
header {
padding: 12px 16px;
background: #222;
display: flex;
gap: 12px;
align-items: center;
}
button {
padding: 8px 12px;
cursor: pointer;
}
#status {
color: #aaa;
font-size: 14px;
}
#log {
white-space: pre-wrap;
overflow-wrap: anywhere;
padding: 16px;
height: calc(100vh - 58px);
overflow-y: auto;
box-sizing: border-box;
font-family: Consolas, Monaco, monospace;
font-size: 13px;
line-height: 1.45;
}
</style>
</head>
<body>
<header>
<button id="clearBtn">Clear log</button>
<button id="pauseBtn">Pause</button>
<span id="status">Loading…</span>
</header>
<div id="log"></div>
<script>
let offset = 0;
let paused = false;
const logEl = document.getElementById('log');
const statusEl = document.getElementById('status');
async function readLog() {
if (paused) return;
try {
const res = await fetch(`?action=read&offset=${offset}`, { cache: 'no-store' });
const json = await res.json();
if (!json.ok) throw new Error(json.error || 'Read failed');
if (json.offset < offset) {
logEl.textContent = '';
}
if (json.content) {
const atBottom = logEl.scrollTop + logEl.clientHeight >= logEl.scrollHeight - 20;
logEl.textContent += json.content;
if (atBottom) logEl.scrollTop = logEl.scrollHeight;
}
offset = json.offset;
statusEl.textContent = `Size: ${json.size.toLocaleString()} bytes`;
} catch (err) {
statusEl.textContent = `Error: ${err.message}`;
}
}
document.getElementById('clearBtn').onclick = async () => {
if (!confirm('Clear the PHP CLI error log completely?')) return;
const res = await fetch('?action=clear', { method: 'POST', cache: 'no-store' });
const json = await res.json();
if (json.ok) {
offset = 0;
logEl.textContent = '';
statusEl.textContent = 'Log cleared';
}
};
document.getElementById('pauseBtn').onclick = e => {
paused = !paused;
e.target.textContent = paused ? 'Resume' : 'Pause';
};
readLog();
setInterval(readLog, 1500);
</script>
</body>
</html>
Then save and browse to the new live error log viewer, magic! And if it doesn't work, you know where your php log is now, so go and have a look and see what's broke. 🤣
On a very serious note, despite the shits and giggles, having your PHP error log file exposed to the internet is bad, like really bad. The errors can give insight to an opportunist hacker, or AI, big data, whoever, so do bear that in mind. It would be best to not have the file accessible by anyone bar yourself. Sort your own security out if leaving this live on the internet and don't blame me if something goes awry. If using this live on the net, close it off afterwards when you have finished using it. Other than that, enjoy!
Have at it and all the best!
Add Comment
This policy contains information about your privacy. By posting, you are declaring that you understand this policy:
- Your name, rating, website address, town, country, state and comment will be publicly displayed if entered.
- Aside from the data entered into these form fields, other stored data about your comment will include:
- Your IP address (not displayed)
- The time/date of your submission (displayed)
- Your email address will not be shared. It is collected for only two reasons:
- Administrative purposes, should a need to contact you arise.
- To inform you of new comments, should you subscribe to receive notifications.
- A cookie may be set on your computer. This is used to remember your inputs. It will expire by itself.
This policy is subject to change at any time and without notice.
These terms and conditions contain rules about posting comments. By submitting a comment, you agree with these rules:
- Although the administrator will attempt to moderate comments, not all comments can be moderated at all times.
- You acknowledge that all comments express the opinions of the original author and not those of the administrator.
- You will not post material which is knowingly false, obscene, hateful, threatening, harassing or invasive of privacy.
- The administrator has the right to edit, move or remove any comment for any reason and without notice.
Failure to comply with these rules may result in being banned from submitting further comments.
These terms and conditions are subject to change at any time and without notice.
Comments