J’ai récemment été confronté à un souci de taille de fichiers de traces pour le débogage d’une application. Pour résoudre mon problème j’ai écrit un petit programme en ligne de commande permettant de répartir la sortie d’un pipeline du shell dans des fichiers quotidiens.
J’ai appelé cet outil dailyfile
et je vous le présente ici, en espérant qu’il puisse servir à d’autres.
Présentation
Problématique
J’ai développé il y a quelques temps pour un client un programme sous Linux qui s’occupe d’initialiser et de superviser des communications entre des applications distantes et des serveurs. La mise au point est un peu compliquée car les communications reposant sur des modems 2G des difficultés inattendues apparaissent dans la gestion des déconnexions intempestives.
J’ai prévu, comme d’habitude, dans mon programme de nombreux messages de débogage que l’on peut activer ou non en fonction d’un niveau paramétrable à la compilation.
Un script démarre l’application, qui écrira tous ses messages de débogage sur la sortie stderr
. Comme je n’ai pas accès directement à ce serveur, les données sont redirigées dans un fichier que mon client me transmet par mail quand un problème survient. Le script est un peu plus complexe, mais on peut imaginer quelque chose comme :
$ ./my-application >logfile.txt 2>&1
La première redirection envoie la sortie standard stdout
du programme dans le fichier de trace, la seconde redirection 2>&1
redirige la sortie stderr
de la même manière. Le script réel est plus compliqué car on gère un redémarrage automatique en cas de crash et un fichier de trace dont le nom contient la date de démarrage.
Les erreurs se produisant somme toute assez rarement, le programme tourne parfois pendant plusieurs jours entre deux redémarrages, et le fichier de trace pèse facilement plusieurs centaines de mégaoctets. Son transfert et l’analyse de son contenu deviennent alors plus compliqués.
On ne peut pas facilement répartir le contenu dans différents fichiers sans arrêter l’application, ce qui est problématique dans notre cas. L’utilisation de logrotate
est un peu complexe, d’autant que nous n’avons pas les droits d’administration sur le serveur.
Solution
Mon idée a été de remplacer la redirection « >
» vers un fichier par une redirection « |
» dans un pipeline. De l’autre côté du pipeline un programme lira les données, et les copiera dans un fichier, en changeant tous les jours.
La programmation proprement dite est simple, et le code source est disponible dans mon dépôt Github.
Installation
La compilation du programme ne présente en principe pas de difficulté :
$ git clone https://github.com/cpb-/Dailyfile Clonage dans 'Dailyfile'... remote: Counting objects: 33, done. remote: Compressing objects: 100% (21/21), done. remote: Total 33 (delta 12), reused 31 (delta 10), pack-reused 0 Dépaquetage des objets: 100% (33/33), fait. Vérification de la connectivité... fait. $ cd dailyfile/ $ make clean && make rm -f dailyfile dailyfile.o *~ gcc -Wall -W -DPROGRAM_VERSION="0.3" -c dailyfile.c gcc -o dailyfile dailyfile.o $ sudo make install cp dailyfile /usr/local/bin/ $
Utilisation
L’utilisation de base est plutôt simple :
$ commande-qui-dure-longtemps | dailyfile
Toutes les données écrites par la première commande dans le pipeline seront enregistrées dans des fichiers nommés day-YYYY-MM-DD.log
où YYYY
, MM
, et DD
représentent respectivement l’année, le mois et le jour de création du fichier.
En voici un exemple d’exécution réelle :
$ while true; do date; sleep 5; done | dailyfile
Cette commande affiche la date toutes les cinq secondes. On retrouve plusieurs jours plus tard les résultats dans les fichiers :
$ ls -l total 2148 -rw-r--r-- 1 cpb cpb 583906 avril 29 01:59 day-2017-04-28.log -rw-r--r-- 1 cpb cpb 727838 avril 30 01:59 day-2017-04-29.log -rw-r--r-- 1 cpb cpb 750812 mai 1 01:59 day-2017-04-30.log -rw-r--r-- 1 cpb cpb 113240 mai 1 06:08 day-2017-05-01.log
On peut remarquer que l’heure de la dernière modification des fichiers écoulés est à 1 heure 59. C’est normal, le basculement est effectué en se basant sur l’heure G.M.T, décalée de deux heures l’été par rapport à l’heure locale affichée par ls
.
Options
Il existe quelques options pour modifier le comportement du programme.
-b size
ou--buffer-size=size
: modifier la taille du buffer interne de copie des données. Par défaut 16384 octets.-c seconds
ou--cycle=seconds
: fixer la durée de commutation en secondes. Par défaut 86400 secondes (une journée).-d dir
ou--directory=dir
: indiquer le répertoire où stocker les fichiers. Par défaut, ils sont enregistrés dans le répertoire courant.-h
ou--help
: afficher un rappel des commandes.-l
ou--localtime
: utiliser l’heure locale lors de la création des noms de fichiers. Faux par défaut, on utilise l’heure G.M.T.-p string
ou--prefix=string
: préciser le préfixe à écrire avant la date dans le nom du fichier. Par défaut, c’est «day-
« .-s string
ou--suffix=string
: indiquer le préfixe à écrire après la date dans le nom du fichier. Par défaut, c’est «.log
« .-v
ou--version
: afficher la version de l’outildailyfile
.
Lorsque la période de commutation (option -c
) est inférieure à une journée, le nom des fichiers inclut des champs heure, minute, et seconde de création.
Exemple
Voici un exemple d’exécution où je modifie le préfixe du fichier pour le remplacer par « date-
« , son suffixe devient « .txt
« , et sa période est changée pour avoir un basculement de fichier à chaque changement d’heure :
$ while true; do date; sleep 5; done | dailyfile -c 3600 -p 'date-' -s '.txt' -l
Après quelques heures on observe des fichiers dont le nom inclut l’heure de création. On peut observer des petites fluctuations dans le champ « seconde », dûes aux périodes de réveil du shell.
$ ls -l -rw-r--r-- 1 cpb cpb 2255 avril 27 12:59 date-2017-04-27-12-55-24.txt -rw-r--r-- 1 cpb cpb 29520 avril 27 13:59 date-2017-04-27-13-00-00.txt -rw-r--r-- 1 cpb cpb 29520 avril 27 14:59 date-2017-04-27-14-00-02.txt -rw-r--r-- 1 cpb cpb 29479 avril 27 15:59 date-2017-04-27-15-00-04.txt -rw-r--r-- 1 cpb cpb 29520 avril 27 16:59 date-2017-04-27-16-00-02.txt -rw-r--r-- 1 cpb cpb 29479 avril 27 17:59 date-2017-04-27-17-00-04.txt -rw-r--r-- 1 cpb cpb 29479 avril 27 18:59 date-2017-04-27-18-00-02.txt -rw-r--r-- 1 cpb cpb 29520 avril 27 19:59 date-2017-04-27-19-00-00.txt -rw-r--r-- 1 cpb cpb 29479 avril 27 20:59 date-2017-04-27-20-00-03.txt -rw-r--r-- 1 cpb cpb 29479 avril 27 21:59 date-2017-04-27-21-00-02.txt -rw-r--r-- 1 cpb cpb 29520 avril 27 22:59 date-2017-04-27-22-00-00.txt -rw-r--r-- 1 cpb cpb 29479 avril 27 23:59 date-2017-04-27-23-00-04.txt -rw-r--r-- 1 cpb cpb 31636 avril 28 00:59 date-2017-04-28-00-00-02.txt -rw-r--r-- 1 cpb cpb 31680 avril 28 01:59 date-2017-04-28-01-00-01.txt -rw-r--r-- 1 cpb cpb 31636 avril 28 02:59 date-2017-04-28-02-00-04.txt -rw-r--r-- 1 cpb cpb 31636 avril 28 03:59 date-2017-04-28-03-00-03.txt -rw-r--r-- 1 cpb cpb 31636 avril 28 04:59 date-2017-04-28-04-00-01.txt -rw-r--r-- 1 cpb cpb 31680 avril 28 05:59 date-2017-04-28-05-00-00.txt -rw-r--r-- 1 cpb cpb 31636 avril 28 06:59 date-2017-04-28-06-00-04.txt [...]
Les suggestions, remarques et pull requests sur Github sont les bienvenues !