Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🔨 Faster and less bandwidth consuming logs display #2224

Merged
merged 5 commits into from May 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 13 additions & 0 deletions core/ajax/log.ajax.php
Expand Up @@ -62,6 +62,19 @@
ajax::success(log::get(init('log'), init('start', 0), init('nbLine', 99999)));
}

if (init('action') == 'getDelta') {
ajax::success(
log::getDelta(
init('log'),
intval(init('position', 0)),
init('search'),
intval(init('colored', 0)),
init('numbered', true),
intval(init('numberStart', 0))
)
);
}

throw new Exception(__('Aucune méthode correspondante à :', __FILE__) . ' ' . init('action'));
/* * *********Catch exeption*************** */
} catch (Exception $e) {
Expand Down
106 changes: 105 additions & 1 deletion core/class/log.class.php
Expand Up @@ -234,11 +234,11 @@ public static function removeAll() {
* @return boolean|array
*/
public static function get($_log = 'core', $_begin, $_nbLines) {
self::chunk($_log);
$path = (!file_exists($_log) || !is_file($_log)) ? self::getPathToLog($_log) : $_log;
if (!file_exists($path)) {
return false;
}
self::chunkLog($path);
$page = array();
$log = new SplFileObject($path);
if ($log) {
Expand All @@ -260,6 +260,110 @@ public static function get($_log = 'core', $_begin, $_nbLines) {
return $page;
}

/*
* Get the log delta from $_position to the end of the file
* New position is stored in $_position when eof is reached
*
* @param string $_log Log filename (default 'core')
* @param int $_position Bytes representing position from the begining of the file (default 0)
* @param string $_search Text to find in log file (default '')
* @param int $_colored Should lines be colored (default 0) [0: no ; 1: global log colors ; 2: scenario colors]
* @param boolean $_numbered Should lines be numbered (default true)
* @param int $_numStart At what number should lines number start (default 0)
* @param int $_max Max number of returned lines (default 4000)
* @return array Array containing log to append to buffer and new position for next call
*/
public static function getDelta($_log = 'core', $_position = 0, $_search = '', $_colored = 0, $_numbered = true, $_numStart = 0, $_max = 4000) {
// Check if log file exists
$path = (file_exists($_log) && is_file($_log)) ? $_log : self::getPathToLog($_log);
if (!file_exists($path))
return false;
// Prepare file for reading
if (!$fp = fopen($path, 'r'))
return false;
// Set file pointer at requested position
fseek($fp, $_position);
// Iterate the file
$logs = array();
while (!feof($fp)) {
// Get a new line
$line = mb_convert_encoding(trim(fgets($fp)), 'UTF-8');
if ($line == '')
continue;
// Append line to array if not empty
if ($_search === '' || mb_stripos($line, $_search) !== false) {
if ($_numbered) {
array_push($logs, str_pad($_numStart, 4, '0', STR_PAD_LEFT) . '|' . trim($line) . "\n");
} else {
array_push($logs, trim($line) . "\n");
}
}
$_numStart++;
}
// Store new file position in $_position
$_position = ftell($fp);
fclose($fp);

// Display only the last jeedom.log.maxLines
$logText = '';
$nbLogs = count($logs);
// If logs are TRUNCATED, then add a message
if (count($logs) > $_max) {
$logText .= "-------------------- TRUNCATED LOG --------------------\n";
$logs = array_slice($logs, -$_max, $_max);
}
// Merge all lignes
$logText .= implode('', $logs);

// Apply color in system logs
if ($_colored == 1) {
$search = array(
'<',
'>',
'WARNING:',
'Erreur',
'OK',
'[INFO]',
'[DEBUG]',
'[WARNING]',
'[ALERT]',
'[ERROR]',
'-------------------- TRUNCATED LOG --------------------'
);
$replace = array(
'&lt;',
'&gt;',
'<span class="warning">WARNING</span>',
'<span class="danger">Erreur</span>',
'<strong>OK</strong>',
'<span class="label label-xs label-info">INFO</span>',
'<span class="label label-xs label-success">DEBUG</span>',
'<span class="label label-xs label-warning">WARNING</span>',
'<span class="label label-xs label-warning">ALERT</span>',
'<span class="label label-xs label-danger">ERROR</span>',
'<span class="label label-xl label-danger">-------------------- TRUNCATED LOG --------------------</span>'
);
$logText = str_replace($search, $replace, $logText);
}
// Apply color in scenario logs
elseif ($_colored == 2) {
$search = array();
$replace = array();
foreach($GLOBALS['JEEDOM_SCLOG_TEXT'] as $item) {
$search[] = $item['txt'];
$replace[] = str_replace('::', $item['txt'], $item['replace']);
}
$search[] = ' Start : ';
$replace[] = '<strong> -- Start : </strong>';
$search[] = 'Log :';
$replace[] = '<span class="success">&ensp;&ensp;&ensp;Log :</span>';
$logText = str_replace($search, $replace, $logText);
}

// Return the lines to the end of the file, the new position and line number
return array('position' => $_position, 'line' => $_numStart, 'logText' => $logText);
}

public static function liste($_filtre = null) {
$return = array();
foreach (ls(self::getPathToLog(''), '*') as $log) {
Expand Down
178 changes: 177 additions & 1 deletion core/js/log.class.js
Expand Up @@ -18,6 +18,7 @@ jeedom.log = function() {};
jeedom.log.timeout = null
jeedom.log.currentAutoupdate = []
jeedom.log.coloredThreshold = 300000
jeedom.log.maxLines = 4000

jeedom.log.list = function(_params) {
var paramsRequired = [];
Expand Down Expand Up @@ -91,6 +92,32 @@ jeedom.log.get = function(_params) {
domUtils.ajax(paramsAJAX);
}

jeedom.log.getDelta = function(_params) {
var paramsRequired = ['log', 'position'];
var paramsSpecifics = {
global: _params.global || true,
};
try {
jeedom.private.checkParamsRequired(_params || {}, paramsRequired);
} catch (e) {
(_params.error || paramsSpecifics.error || jeedom.private.default_params.error)(e);
return;
}
var params = domUtils.extend({}, jeedom.private.default_params, paramsSpecifics, _params || {});
var paramsAJAX = jeedom.private.getParamsAJAX(params);
paramsAJAX.url = 'core/ajax/log.ajax.php';
paramsAJAX.data = {
action: 'getDelta',
log: _params.log,
position: _params.position,
search: _params.search,
colored: _params.colored,
numbered: _params.numbered,
numberStart: _params.numberStart
};
domUtils.ajax(paramsAJAX);
}

jeedom.log.remove = function(_params) {
var paramsRequired = ['log'];
var paramsSpecifics = {
Expand Down Expand Up @@ -300,6 +327,155 @@ jeedom.log.autoupdate = function(_params) {
});
}

jeedom.log.updateBtn = function(_control) {
if (_control.getAttribute('data-state') == 1) {
_control.removeClass('btn-success').addClass('btn-warning')
_control.innerHTML = '<i class="fa fa-pause"></i><span class="hidden-768"> {{Pause}}</span>'
} else {
_control.removeClass('btn-warning').addClass('btn-success')
_control.innerHTML = '<i class="fa fa-play"></i><span class="hidden-768"> {{Reprendre}}</span>'
}
}

jeedom.log.autoUpdateDelta = function(_params) {
// Normalize parameters
if (!isset(_params.callNumber)) _params.callNumber = 0
if (!isset(_params.position)) _params.position = 0;
if (!isset(_params.lineNum)) _params.lineNum = 0;
if (!isset(_params.log)) return
if (!isset(_params.display)) return

// Deprecated use with jQuery objects by plugins
if (_params.callNumber == 0) {
if (isElement_jQuery(_params.log)) _params.log = _params.log[0]
if (isElement_jQuery(_params.display)) _params.display = _params.display[0]
if (isElement_jQuery(_params.search)) _params.search = _params.search[0]
if (isElement_jQuery(_params.control)) _params.control = _params.control[0]
}

// Exit if invisible
if (!_params.display.isVisible()) return

// Exit if Paused
if (_params.callNumber > 0 && isset(_params.control) && _params.control.getAttribute('data-state') != 1) {
return
}
// Exit if a newer instance on another log is running
if (_params.callNumber > 0 && isset(jeedom.log.currentAutoupdate[_params.display.getAttribute('id')]) && jeedom.log.currentAutoupdate[_params.display.getAttribute('id')].log != _params.log) {
return
}

if (_params.callNumber == 0) {
// Empty space on first call
_params.position = 0;
_params.lineNum = 0;
_params.display.empty();

if (isset(_params.default_search)) {
_params.search.value = _params.default_search
}
_params.display.scrollTop = _params.display.offsetHeight + _params.display.scrollHeight + 1
if (_params.control.getAttribute('data-state') == 0) {
_params.control.setAttribute('data-state', 1)
jeedom.log.updateBtn(_params.control)
}

// Setup callback: On control button click
_params.control.unRegisterEvent('click').registerEvent('click', function (event) {
if (this.getAttribute('data-state') == 1) {
// On "Pause" requested
this.setAttribute('data-state', 0)
jeedom.log.updateBtn(this)
} else {
// On "Continue" requested
this.setAttribute('data-state', 1)
jeedom.log.updateBtn(this)
_params.display.scrollTop = _params.display.offsetHeight + _params.display.scrollHeight + 1
jeedom.log.autoUpdateDelta(_params)
}
})

// Setup callback: On search field update
_params.search.unRegisterEvent('keypress').registerEvent('keypress', function (event) {
// Reset log position and empty view
_params.position = 0;
_params.lineNum = 0;
_params.display.empty();
// Enable refresh/scrolling if disabled
if (_params.control.getAttribute('data-state') == 0) {
_params.control.click()
}
})
}
_params.callNumber++

jeedom.log.currentAutoupdate[_params.display.getAttribute('id')] = {
log: _params.log
}

// Disable auto update if log is scrolled
if (_params.callNumber > 1 && (_params.display.scrollTop + _params.display.offsetHeight + 10) < _params.display.scrollHeight) {
if (_params.control.getAttribute('data-state') == 1) {
_params.control.click()
}
return
}

var dom_brutlogcheck = document.getElementById('brutlogcheck')
// If first call AND element exists AND (enabled OR should be enabled), Then enable it
if (_params.callNumber == 1 && dom_brutlogcheck != null && dom_brutlogcheck.checked && dom_brutlogcheck.getAttribute('autoswitch') == 1) {
dom_brutlogcheck.checked = false
}

// If (element exists AND is disabled) OR (first call AND (element does not exists OR should be enabled)), Then colored
var colorMe = ((dom_brutlogcheck != null && !dom_brutlogcheck.checked) || (_params.callNumber == 1 && (dom_brutlogcheck == null || dom_brutlogcheck.getAttribute('autoswitch') == 1)))

jeedom.log.getDelta({
log: _params.log,
slaveId: _params.slaveId,
position: _params.position,
search: (!isset(_params.search)) ? '' : _params.search.value.toLowerCase(),
colored: (colorMe ? ((_params.display.id == 'pre_scenariolog') ? 2 : 1) : 0), // 0: no color ; 1: global log colors ; 2: Scenario colors
numbered: (_params.display.id == 'pre_globallog'),
numberStart: _params.lineNum,
global: (_params.callNumber == 1),
success: function(result) {
// Optimise search operation
var searchString = (!isset(_params.search)) ? '' : _params.search.value.toLowerCase()
// Store log file current position
_params.position = result.position;
// Store log file current line
_params.lineNum = result.line;

// Get logs as text from ajax
if (result.logText.length > 0) {
if (colorMe) {
// log = (_params.display.id == 'pre_scenariolog') ? jeedom.log.scenarioColorReplace(log) : jeedom.log.stringColorReplace(log);
_params.display.innerHTML += result.logText
} else {
_params.display.textContent += result.logText
}
}

_params.display.scrollTop = _params.display.offsetHeight + _params.display.scrollHeight + 1
if (jeedom.log.timeout !== null) {
clearTimeout(jeedom.log.timeout)
}
jeedom.log.timeout = setTimeout(function() {
jeedom.log.autoUpdateDelta(_params)
}, 1000)
},
error: function() {
if (jeedom.log.timeout !== null) {
clearTimeout(jeedom.log.timeout)
}
jeedom.log.timeout = setTimeout(function() {
jeedom.log.autoUpdateDelta(_params)
}, 1000)
},
});
}

//Standard log replacement:
jeedom.log.colorReplacement = {
'WARNING:': '--startTg--span class="warning"--endTg--WARNING--startTg--/span--endTg--:',
Expand Down Expand Up @@ -338,7 +514,7 @@ jeedom.log.getScTranslations({
}
},
error: function() {
console.log('Unable to get jeedom scenario traductions')
console.log('Unable to get jeedom scenario translations')
}
})

Expand Down