Skip to content

Refactor chunkLog function for better performance#3288

Open
jpty wants to merge 9 commits intojeedom:fix/chunk-logfrom
jpty:jpty-patch-1
Open

Refactor chunkLog function for better performance#3288
jpty wants to merge 9 commits intojeedom:fix/chunk-logfrom
jpty:jpty-patch-1

Conversation

@jpty
Copy link
Copy Markdown
Contributor

@jpty jpty commented Apr 20, 2026

  • Truncates log files only if the file size or row number thresholds are exceeded.

  • Using uniqid() instead of tempnam() because

    • tempnam() crée un fichier temporaire dans /tmp/jeedom en mode 0600 avec comme propriétaire www-data
    • Sur mon Jeedom /tmp/jeedom est un répertoire avec le sticky bit (drwxrwxrwt propriétaire root)
    • sudo bash -o pipefail -c " tail -c | tail -n > tmpFile ..." n'a pas les droits d'écrire dans tmpFile puisqu'il n'en est pas le propriétaire. Ca donne Permission denied tail: error writing 'standard output': Broken pipe
    • uniqid() ne génére qu'un nom de fichier qui est utilisé ensuite dans la commande de tronquage.

Description

Suggested changelog entry

Related issues/external references

Fixes #

Types of changes

  • Bug fix (non-breaking change which fixes)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
    • This change is only breaking for integrators, not for external standards or end-users.
  • Documentation improvement

PR checklist

jpty and others added 3 commits April 20, 2026 15:17
- Truncates log files only if the file size or row number thresholds are exceeded.
- Using uniqid() instead of tempnam()
Comment thread core/class/log.class.php Outdated
Comment thread core/class/log.class.php Outdated
Comment thread core/class/log.class.php Outdated
@Mips2648
Copy link
Copy Markdown
Collaborator

Attention de pas casser le format et de respecter les indentations existantes sur le fichier, j'ai modifié

@Mips2648
Copy link
Copy Markdown
Collaborator

Concernant le changement, je trouve que la fonction devient trop complex à lire... et il y a trop de if/else imbriqué
les log en debug sont inutiles à moins d'avoir le log level par défaut en debug et ca alourdi visuellement le code aussi

Le but est la perf, ok mais tu as su mesurer? est-ce que complexifier le code et donc la maintenance vaut le gain de perf?

le wc -l s'est bien mais ca va couter chère si la config permet 100Mo de fichier...

si vous voulez garder le principe pour raison de perf, je veux refacto pour simplifier le code mais je pense que le mieux est l'ennemi du bien

@jpty
Copy link
Copy Markdown
Contributor Author

jpty commented Apr 20, 2026

Les log::add, c'est pour la phase de debuggage.
Si on les enlève, il n'y a plus aucun else dans la fonction et donc juste un if dans un autre if.

Les mesures que j'ai faites, c'est le temps d'exécution de jeedom::cronDaily sur 166 fichiers de log. 19s avant optimisation, 2s après . Il n'y a qu'une dizaine de fichiers à tronquer par le nombre de lignes. Je suppose que la carte SD me remercie de ne pas réécrire le fichier à l'identique à chaque exécution.

le wc -l s'est bien mais ca va couter chère si la config permet 100Mo de fichier...

Tout dépend ce que l'on appelle cher, sur un fichier de 290Mo, 0.2 seconde:
image

Il y a peut-être une limite raisonnable à fixer dans la config. Pour le moment, c'est du texte libre. 0 est possible ...
Après les derniers commits, maxSizeLog est bornée entre 1 et 10Mo.
Le comptage des lignes n'est fait que si la taille du log est inférieure à ce chiffre de la config. La valeur par défaut dans la config est 5Mo.

@jpty
Copy link
Copy Markdown
Contributor Author

jpty commented Apr 20, 2026

Attention de pas casser le format et de respecter les indentations existantes sur le fichier, j'ai modifié

Oui. J'ai vu. Désolé. Je me suis contenté de copier / coller depuis mon éditeur. Mon éditeur convertit toutes les tabulations en 2 espaces dès que je modifie un fichier.
Il y a des conventions d'écriture quelque part ou c'est selon le fichier ?
Je ne sais même pas où trouver comment le fichier que j'édite dans Github est indenté.

@Mips2648
Copy link
Copy Markdown
Collaborator

Mips2648 commented Apr 20, 2026

bon, j'ai poussé une autre version mais j'ai gardé l'idée des circuit-breaker si taille < autorisée && nbr lines < autorisée mais j'ai simplifié
j'ai laissé mes comments justement pour justifier/valider les détails

Comment thread core/class/log.class.php Outdated
Comment thread core/class/log.class.php Outdated
Comment thread core/class/log.class.php
@jpty
Copy link
Copy Markdown
Contributor Author

jpty commented Apr 20, 2026

j'ai laissé mes comments justement pour justifier/valider les détails

Pas vu de comments

@Mips2648
Copy link
Copy Markdown
Collaborator

pas dans le code, sur la PR, ici plus haut

@jpty
Copy link
Copy Markdown
Contributor Author

jpty commented Apr 21, 2026

Bonjour,
Testé cette nuit sur ma "prod" pour avoir des vrais logs à traiter.
Exécution en 3 secondes. 110 fichiers de log et seulement 15 à tronquer. 4 par la taille, 11 par le nombre de lignes.
Et vous ?

Co-authored-by: jpty <1798636+jpty@users.noreply.github.com>
Comment thread core/class/log.class.php Outdated
jpty and others added 2 commits April 22, 2026 00:04
Co-authored-by: jpty <1798636+jpty@users.noreply.github.com>
Co-authored-by: kwizer15 <kwizer15@users.noreply.github.com>
Comment thread core/class/log.class.php Outdated
jpty added 2 commits April 22, 2026 00:50
Co-authored-by: jpty <1798636+jpty@users.noreply.github.com>
Copy link
Copy Markdown
Collaborator

@Mips2648 Mips2648 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems my last review was not submitted or discarded

Comment thread core/class/log.class.php Outdated

clearstatcache(true, $rawPath);
$fSize = filesize($rawPath);
if ($fSize < ($maxLineLog * 50)) { // 50 characters per line
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ce n'est pas le role de la fonction de décider de ne pas faire un chunk si le fichier est trop petit
la règle business c'est de limiter en taille et/ou en nombre de ligne; une exception sur la taille est déjà gérée par chunk()

Copy link
Copy Markdown
Contributor Author

@jpty jpty Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

L'exception sur la taille gérée par chunk() est faite sans bornage de maxSizeLog:
if ($_onlyIfSizeExceeded && filesize($path) < (self::getConfig('maxSizeLog') * 1024 * 1024))

chunkLog est aussi appelé dans log::get() fonction deprecated qui ne signale pas qu'elle l'est et pourra donc générer des erreurs quand elle sera appelée mais supprimée.

Comment thread core/class/log.class.php Outdated
}

$sudo = system::getCmdSudo();
$tmpFile = escapeshellarg(jeedom::getTmpFolder() . '/log_chunk_' . uniqid('', true));
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pourquoi concaténer le prefix au lieu de le passer à uniqid() ?
et pourquoi augmenter entropie? on n'est pas dans un truc critique ni à bombarder des milliers de cas par seconde
le but c'était juste d'éviter une collision évidente si 2 chunk arrive en même temps
on peut gagner du temps cpu en gardant la valeur par défaut

Copy link
Copy Markdown
Contributor Author

@jpty jpty Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Il faut déjà concaténer le /
Pour le temps cpu, 1 million d'exécution d'uniqid() = 1s avec ou sans entropie.
Je n'ai pas modifié le code.

Comment thread core/class/log.class.php Outdated
if ($fSize < $maxBytes) { // Check by nLines
$cmd = "wc -l " . escapeshellarg($rawPath);
$output = trim(com_shell::execute($cmd));
$nLines = (int) explode(' ', $output)[0];
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

je pense qu'on peut éviter l'array et l'explode avec un wc -l < file

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Je suis d'accord. Pas pensé à faire wc -l <
C'est fait dans la version actuelle.

Comment thread core/class/log.class.php Outdated
Comment thread core/class/log.class.php Outdated

$sudo = system::getCmdSudo();
$tmpFile = escapeshellarg(jeedom::getTmpFolder() . '/log_chunk_' . uniqid('', true));
$shellPath = escapeshellarg(realpath($rawPath));
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

y avait une raison d'ajouter le realpath()?

Copy link
Copy Markdown
Contributor Author

@jpty jpty Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Juste résoudre une seule fois les ../.. présents dans le rawPath avant de les passer à bash.
J'avais aussi utilisé shellPath dans mes log::add pour plus de lisibilité.
C'est supprimé dans la version actuelle.

Comment thread core/class/log.class.php Outdated
Comment thread core/class/log.class.php
Comment thread core/class/log.class.php

$maxSizeLog = (int)self::getConfig('maxSizeLog', 10);
$maxSizeLog = max(1, $maxSizeLog);
$maxSizeLog = max(1, min(10, $maxSizeLog));
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

c'est un changement fonctionnel, pourquoi limiter à 10 ici? il ne faut pas faire ca

Copy link
Copy Markdown
Contributor Author

@jpty jpty Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/tmp/jeedom a une taille de 256Mo sur la VM de mon NAS.
Sur la Smart debian 12, /tmp est un tmpfs. Taille 955Mo
Dans la config maxSizeLog est du texte libre et tu as écrit plus haut que ca pourrait être 100Mo voir plus.
10 est la limite dans chunkLog avant les modifs.

Comment thread core/class/log.class.php

$fSize = filesize($rawPath);
if ($fSize === false) {
if ($fSize === false || $fSize < ($maxLineLog * 50) ) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pourquoi avoir remis cette ligne?
je suis contre, ce 50 est complétement arbitraire
ca apporte quoi?

Copy link
Copy Markdown
Contributor Author

@jpty jpty Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ca sert à ne pas perdre de temps à compter des lignes sur des petits fichiers pour dans le meilleur des cas gagner quelques octets.
com_shell::execute("wc -l ... prend 2ms sur un fichier vide.

Pour les 50, c'est déduit de mes fichiers de log.
Sur mon relevé d'hier et 21Mo de log, la moyenne du nombre de caractères par ligne est 200.
A voir pour augmenter le 50.

Suggested change
if ($fSize === false || $fSize < ($maxLineLog * 50) ) {
if ($fSize === false || $fSize < ($maxLineLog * 80) ) {

Il faut aussi changer filesize en @filesize puisque la vérification de l'existence du fichier n'est plus faite au début et ainsi éviter le PHP Warning dans http.error

Comment thread core/class/log.class.php

clearstatcache(true, $rawPath);
if (file_exists($rawPath) && filesize($rawPath) > (1024 * 1024 * $maxSizeLog)) {
if (file_exists($rawPath) && filesize($rawPath) > (10 *1024 *1024)) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pourquoi remettre en dur 10 au lieu d'utiliser la variable qui calcul déjà ca?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Je trouve anormal de vider le fichier pour quelques octets en trop.
10 est l'ancienne limite de taille.

@jpty
Copy link
Copy Markdown
Contributor Author

jpty commented Apr 23, 2026

Bonjour,
J'ai écrit plus haut que je n'avais pas vu vos commentaires. Donc plutôt not submitted.

@jpty
Copy link
Copy Markdown
Contributor Author

jpty commented Apr 23, 2026

Je crois avoir commenté tous vos commentaires. A vous de commenter.

Ce ne sont que des logs. Les daemons continuent d'y écrire. Il est fortement possible que le nombre de lignes soit déjà dépassé dès la fin d'une exécution réussie.

Comment thread core/class/log.class.php
$maxSizeLog = max(1, min(10, $maxSizeLog));
$maxBytes = $maxSizeLog * 1024 * 1024;

$fSize = filesize($rawPath);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pour éviter les PHP Warning filesize(): stat failed for

Suggested change
$fSize = filesize($rawPath);
$fSize = @filesize($rawPath);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants