File: /home/xedaptot/be.naniguide.com/vendor/louis/log-viewer/src/PhpTail.php
<?php
namespace Acelle\Extra\LogViewer;
class PhpTail
{
/**
* Location of the log file we're tailing.
*
* @var string
*/
private $log = '';
/**
* The time between AJAX requests to the server.
*
* Setting this value too high with an extremly fast-filling log will cause your PHP application to hang.
*
* @var int
*/
private $updateTime;
/**
* This variable holds the maximum amount of bytes this application can load into memory (in bytes).
*
* @var string
*/
private $maxSizeToLoad;
/**
* PHPTail constructor.
*
* @param string $log the location of the log file
* @param int $defaultUpdateTime The time between AJAX requests to the server.
* @param int $maxSizeToLoad This variable holds the maximum amount of bytes this application can load into memory (in bytes). Default is 2 Megabyte = 2097152 byte
*/
public function __construct($log, $defaultUpdateTime = 2000, $maxSizeToLoad = 102400)
{
$this->log = is_array($log) ? $log : array($log);
$this->updateTime = $defaultUpdateTime;
$this->maxSizeToLoad = $maxSizeToLoad;
}
/**
* PHPTail constructor.
*
* @param string $log the location of the log file
* @param int $defaultUpdateTime The time between AJAX requests to the server.
* @param int $maxSizeToLoad This variable holds the maximum amount of bytes this application can load into memory (in bytes). Default is 2 Megabyte = 2097152 byte
*/
public static function fromDirectory($directory)
{
$files = preg_grep('/^([^.])/', scandir(storage_path('logs')));
$list = [];
foreach ($files as $key => $file) {
$list[$file] = storage_path("logs/{$file}");
}
return new self($list);
}
/**
* This function is in charge of retrieving the latest lines from the log file.
*
* @param string $lastFetchedSize The size of the file when we lasted tailed it.
* @param string $grepKeyword The grep keyword. This will only return rows that contain this word
*
* @return Returns the JSON representation of the latest file size and appended lines.
*/
public function getNewLines($file, $lastFetchedSize, $grepKeyword, $invert)
{
/*
* Clear the stat cache to get the latest results
*/
clearstatcache();
/*
* Define how much we should load from the log file
* @var
*/
if (empty($file)) {
$file = key(array_slice($this->log, 0, 1, true));
}
$fsize = filesize($this->log[$file]);
$maxLength = ($fsize - $lastFetchedSize);
/*
* Verify that we don't load more data then allowed.
*/
if ($maxLength > $this->maxSizeToLoad) {
$maxLength = ($this->maxSizeToLoad / 2);
}
/*
* Actually load the data
*/
$data = array();
if ($maxLength > 0) {
$fp = fopen($this->log[$file], 'r');
fseek($fp, -$maxLength, SEEK_END);
$data = explode("\n", fread($fp, $maxLength));
}
/*
* Run the grep function to return only the lines we're interested in.
*/
if ($invert == 0) {
$data = preg_grep("/$grepKeyword/", $data);
} else {
$data = preg_grep("/$grepKeyword/", $data, PREG_GREP_INVERT);
}
/*
* If the last entry in the array is an empty string lets remove it.
*/
if (end($data) == '') {
array_pop($data);
}
return json_encode(array('size' => $fsize, 'file' => $this->log[$file], 'data' => $data));
}
/**
* This function will print out the required HTML/CSS/JS.
*/
public function generateGUI()
{
?>
<!DOCTYPE html>
<html lang="en">
<head>
<input type="hidden" name="_token" id="csrf-token" value="<?php echo csrf_token() ?>" />
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Bootstrap 101 Template</title>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css">
<link rel="stylesheet" href="//ajax.googleapis.com/ajax/libs/jqueryui/1.11.0/themes/smoothness/jquery-ui.css" />
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
<style type="text/css">
#grepKeyword, #settings {
font-size: 80%;
}
.float {
background: white;
border-bottom: 1px solid black;
padding: 10px 0 10px 0;
margin: 0px;
height: 30px;
width: 100%;
text-align: left;
}
.contents {
margin-top: 30px;
}
.results {
padding-bottom: 20px;
font-family: monospace;
font-size: small;
white-space: pre;
}
</style>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.11.0/jquery-ui.min.js"></script>
<script type="text/javascript">
/* <![CDATA[ */
//Last know size of the file
lastSize = 0;
//Grep keyword
grep = "";
//Should the Grep be inverted?
invert = 0;
//Last known document height
documentHeight = 0;
//Last known scroll position
scrollPosition = 0;
//Should we scroll to the bottom?
scroll = true;
lastFile = window.location.hash != "" ? window.location.hash.substr(1) : "";
console.log(lastFile);
$(document).ready(function() {
// Setup the settings dialog
$("#settings").dialog({
modal : true,
resizable : false,
draggable : false,
autoOpen : false,
width : 590,
height : 270,
buttons : {
Close : function() {
$(this).dialog("close");
}
},
open : function(event, ui) {
scrollToBottom();
},
close : function(event, ui) {
grep = $("#grep").val();
invert = $('#invert input:radio:checked').val();
$("#results").text("");
lastSize = 0;
$("#grepspan").html("Grep keyword: \"" + grep + "\"");
$("#invertspan").html("Inverted: " + (invert == 1 ? 'true' : 'false'));
}
});
//Close the settings dialog after a user hits enter in the textarea
$('#grep').keyup(function(e) {
if (e.keyCode == 13) {
$("#settings").dialog('close');
}
});
//Focus on the textarea
$("#grep").focus();
//Settings button into a nice looking button with a theme
//Settings button opens the settings dialog
$("#grepKeyword").click(function() {
$("#settings").dialog('open');
$("#grepKeyword").removeClass('ui-state-focus');
});
$(".file").click(function(e) {
$("#results").text("");
lastSize = 0;
lastFile = $(e.target).text();
});
$(".empty-file").click(function(e) {
emptyFile(e.target.name);
});
//Set up an interval for updating the log. Change updateTime in the PHPTail contstructor to change this
setInterval("updateLog()", <?php echo $this->updateTime; ?>);
//Some window scroll event to keep the menu at the top
$(window).scroll(function(e) {
if ($(window).scrollTop() > 0) {
$('.float').css({
position : 'fixed',
top : '0',
left : 'auto'
});
} else {
$('.float').css({
position : 'static'
});
}
});
//If window is resized should we scroll to the bottom?
$(window).resize(function() {
if (scroll) {
scrollToBottom();
}
});
//Handle if the window should be scrolled down or not
$(window).scroll(function() {
documentHeight = $(document).height();
scrollPosition = $(window).height() + $(window).scrollTop();
if (documentHeight <= scrollPosition) {
scroll = true;
} else {
scroll = false;
}
});
scrollToBottom();
});
//This function scrolls to the bottom
function scrollToBottom() {
$("html, body").animate({scrollTop: $(document).height()}, "fast");
}
//This function queries the server for updates.
function updateLog() {
$.getJSON('?ajax=1&file=' + lastFile + '&lastsize=' + lastSize + '&grep=' + grep + '&invert=' + invert, function(data) {
lastSize = data.size;
$("#current").text(data.file);
$.each(data.data, function(key, value) {
$("#results").append('' + value + '<br/>');
});
if (scroll) {
scrollToBottom();
}
});
}
function emptyFile(file) {
var ask = confirm('Are you sure you want to empty "' + file + '"?');
if (!ask) {
return false;
}
$.ajax({type: 'POST', url: "<?php echo route('log-viewer'); ?>/" + file + "/empty", data: {_token: $('#csrf-token').val() }}, function() {
// alert( "success" );
})
.done(function() {
window.location = "<?php echo route('log-viewer'); ?>/";
})
.fail(function() {
alert( "error" );
})
.always(function() {
//alert( "finished" );
});
}
/* ]]> */
</script>
</head>
<body>
<div class="navbar navbar-default navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="#">PHP Tail</a>
</div>
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Files<span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<?php foreach ($this->log as $title => $f): ?>
<li><a class="file" href="#<?php echo $title; ?>"><?php echo $title; ?></a></li>
<?php endforeach; ?>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Empty<span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<?php foreach ($this->log as $title => $f): ?>
<li><a class="empty-file" name="<?php echo $title; ?>" href="#<?php echo $title; ?>"><?php echo $title; ?></a></li>
<?php endforeach; ?>
</ul>
</li>
<li><a href="#" id="grepKeyword">Settings</a></li>
<li><span class="navbar-text" id="grepspan"></span></li>
<li><span class="navbar-text" id="invertspan"></span></li>
</ul>
<p class="navbar-text navbar-right" id="current"></p>
</div>
</div>
</div>
<div class="contents">
<div id="results" class="results"></div>
<div id="settings" title="PHPTail settings">
<p>Grep keyword (return results that contain this keyword)</p>
<input id="grep" type="text" value="" />
<p>Should the grep keyword be inverted? (Return results that do NOT contain the keyword)</p>
<div id="invert">
<input type="radio" value="1" id="invert1" name="invert" /><label for="invert1">Yes</label>
<input type="radio" value="0" id="invert2" name="invert" checked="checked" /><label for="invert2">No</label>
</div>
</div>
</div>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
</body>
</html>
<?php
}
}