J'ai un programme dont je redirige la sortie vers un fichier journal:
./my_app > log
Je voudrais effacer (c'est-à-dire vide) le journal de temps en temps (sur demande) et j'ai essayé diverses choses comme
cat "" > log
Cependant, il semble toujours que le canal d'origine est alors perturbé et que le programme ne redirige plus sa sortie vers le fichier journal.
Y a-t-il un moyen de le faire?
Mise à jour
Notez que je ne peux pas modifier l'application produisant la sortie. Il le crache simplement sur stdout et je veux l'enregistrer dans un journal afin de pouvoir l'inspecter quand j'en ai besoin et l'effacer quand je le veux. Cependant, je ne devrais pas avoir besoin de redémarrer l'application.
bash
io-redirection
bangnab
la source
la source
syslogd
oulogrotate
./my_app >> log
(pour forcer l'ajout) etcp /dev/null log
pour le tronquer?cat "" > log
n'est pas unecat
commande valide car aucun fichier n'est appelé""
.Réponses:
Une autre forme de ce problème se produit avec les applications de longue durée dont les journaux sont périodiquement tournés. Même si vous déplacez le journal d'origine (par exemple,
mv log.txt log.1
) et le remplacez immédiatement par un fichier du même nom avant toute journalisation réelle, si le processus maintient le fichier ouvert, il finira par écrire danslog.1
(car cela peut toujours être l'inode ouvert) ou à rien.Une façon courante de résoudre ce problème (l'enregistreur système lui-même fonctionne de cette façon) consiste à implémenter un gestionnaire de signal dans le processus qui fermera et rouvrira ses journaux. Ensuite, lorsque vous souhaitez déplacer ou effacer (en supprimant) le journal, envoyez ce signal au processus immédiatement après.
Voici une démonstration simple pour bash - pardonnez mes compétences de shell cruddy (mais si vous allez modifier cela pour les meilleures pratiques, etc., assurez-vous de comprendre d'abord la fonctionnalité et de tester votre révision avant de la modifier):
Commencez par bifurquer en arrière-plan:
Notez qu'il signale son PID au terminal, puis commence à se connecter
log.txt
. Vous avez maintenant 2 minutes pour jouer. Attendez quelques secondes et essayez:Tout simplement
kill -2 12356
peut aussi fonctionner pour vous ici. Le signal 2 est SIGINT (c'est aussi ce que fait Ctrl-C, vous pouvez donc essayer cela au premier plan et déplacer ou supprimer le fichier journal d'un autre terminal), ce quitrap
devrait intercepter. Vérifier;Voyons maintenant s'il écrit toujours dans un
log.txt
même si nous l'avons déplacé:Remarquez qu'il a continué là où il s'était arrêté. Si vous ne souhaitez pas conserver l'enregistrement, effacez simplement le journal en le supprimant
Vérifier:
Marche encore.
Vous ne pouvez pas le faire dans un script shell pour un sous-processus exécuté, malheureusement, car s'il est au premier plan, les propres gestionnaires de signaux de bash
trap
sont suspendus et si vous le bifurquez en arrière-plan, vous ne pouvez pas réaffecter son production. C'est-à-dire, c'est quelque chose que vous devez implémenter dans votre application.Pourtant...
Si vous ne pouvez pas modifier l'application (par exemple, parce que vous ne l'avez pas écrite), j'ai un utilitaire CLI que vous pouvez utiliser comme intermédiaire. Vous pouvez également implémenter une version simple de ceci dans un script qui sert de canal vers le journal:
Appelons ça
pipetrap.sh
. Maintenant, nous avons besoin d'un programme distinct pour tester, imitant l'application que vous souhaitez enregistrer:Ce sera
test.sh
:Il s'agit de deux processus distincts avec des PID distincts. Pour effacer
test.sh
la sortie de, qui est acheminée viapipetrap.sh
:Vérifier:
15858,
test.sh
est toujours en cours d'exécution et sa sortie est en cours d'enregistrement. Dans ce cas, aucune modification de l'application n'est nécessaire.la source
TL; DR
Ouvrez votre fichier journal en mode ajout :
Ensuite, vous pouvez le tronquer en toute sécurité avec:
Détails
Avec un shell de type Bourne, il existe 3 façons principales d'ouvrir un fichier pour l'écriture. En mode écriture seule (
>
), lecture + écriture (<>
) ou ajout (et écriture seule>>
).Dans les deux premiers, le noyau se souvient de la position actuelle dans laquelle vous (par vous, je veux dire, la description du fichier ouvert , partagée par tous les descripteurs de fichier qui l'ont dupliqué ou hérité en forçant à partir de celui sur lequel vous avez ouvert le fichier) fichier.
Quand vous faites:
log
est ouvert en mode écriture seule par le shell pour la sortie standard decmd
.cmd
(son processus initial engendré par le shell et tous les enfants possibles) lors de l'écriture sur leur stdout, écrivez à la position actuelle du curseur détenue par la description du fichier ouvert qu'ils partagent sur ce fichier.Par exemple, si l'
cmd
écriture initialezzz
, la position sera au décalage d'octets 4 dans le fichier, et la prochaine fois quecmd
ses enfants écrivent dans le fichier, c'est là que les données seront écrites, que le fichier ait augmenté ou diminué dans l'intervalle. .Si le fichier a diminué, par exemple s'il a été tronqué avec un
et
cmd
écritxx
, ceux-xx
ci seront écrits en offset4
, et les 3 premiers caractères seront remplacés par des caractères NUL.Cela signifie que vous ne pouvez pas tronquer un fichier qui a été ouvert en mode écriture seule (et c'est la même chose en lecture + écriture ) comme si vous le faisiez, les processus qui avaient des descripteurs de fichier ouverts sur le fichier, laisseront des caractères NUL au début de la fichier (ceux-ci, sauf sur OS / X, ne prennent généralement pas d'espace sur le disque, ils deviennent des fichiers rares).
Au lieu de cela (et vous remarquerez que la plupart des applications le font lorsqu'elles écrivent dans des fichiers journaux), vous devez ouvrir le fichier en mode ajout :
ou
si vous souhaitez démarrer sur un fichier vide.
En mode ajout, toutes les écritures sont effectuées à la fin du fichier, quelle que soit la dernière écriture:
C'est aussi plus sûr que si deux processus ont ouvert (de cette manière) le fichier par erreur (comme par exemple si vous avez démarré deux instances du même démon), leur sortie ne se remplacera pas.
Sur les versions récentes de Linux, vous pouvez vérifier la position actuelle et si un descripteur de fichier a été ouvert en mode ajout en regardant
/proc/<pid>/fdinfo/<fd>
:Ou avec:
Ces drapeaux correspondent aux drapeaux O ..._ passés à l'
open
appel système.(
O_APPEND
est 0x400 ou octal 02000)Ainsi, le shell
>>
ouvre le fichier avecO_WRONLY|O_APPEND
(et 0100000 ici est O_LARGEFILE qui n'est pas pertinent pour cette question) alors que>
estO_WRONLY
seulement (et<>
estO_RDWR
seulement).Si vous faites:
pour rechercher des fichiers ouverts avec
O_APPEND
, vous trouverez la plupart des fichiers journaux actuellement ouverts pour l'écriture sur votre système.la source
:
(deux-points) dans: >
?:
. Sans commande, le comportement varie selon les shells.Si je comprends bien, cela
tee
semble être une approche raisonnable:la source
Comme solution rapide, vous pouvez utiliser un journal avec rotation (rotation quotidienne par exemple):
et rediriger la journalisation vers celui-ci
./my_app >> log$date.log
la source
C'est un problème qui a été résolu depuis longtemps avec syslog (dans toutes ses variantes) mais il existe deux outils qui résoudraient votre problème particulier avec un minimum d'effort.
La première solution, plus portable mais moins polyvalente, est l'enregistreur (indispensable pour toute boîte à outils pour les administrateurs). Il s'agit d'un simple utilitaire qui copie l'entrée standard dans syslog. (passer la balle et faire de la rotation des fichiers le problème de logrotate et syslog)
La deuxième solution, plus élégante mais moins portable, est syslog-ng qui, en plus d'accepter les messages de journal des sockets de syslog standard, peut exécuter des programmes dont la sortie est filtrée via l'enregistreur. (Je n'ai pas encore utilisé cette fonctionnalité, mais elle semble parfaite pour ce que vous voulez faire.)
la source