Stocker la sortie d'une commande dans un tampon en anneau

16

J'ai une commande de longue durée qui génère beaucoup de sortie sur stdout. Je voudrais pouvoir conserver, par exemple, uniquement les trois derniers jours ou le dernier gibibyte (en évitant de couper les lignes au milieu), et, si possible, en morceaux de fichiers ne dépassant pas 20 Mio. Chaque bloc de fichier est nommé avec un suffixe numérique ou un horodatage.

Quelque chose comme:

my-cmd | magic-command --output-file-template=my-cmd-%t \
                       --keep-bytes=1G \
                       --keep-time=3d \
                       --max-chunk-size=20M \
                       --compress=xz

Aurait écrit:

my-cmd-2014-09-05T10:04:23Z

Quand il atteint 20M, il le comprime et en ouvre un nouveau, et ainsi de suite, et après un certain temps, il commence à supprimer les fichiers les plus anciens.

Une telle commande existe-t-elle?

Je connais logrotateet sa capacité à gérer des fichiers écrits par d'autres applications, mais je cherche quelque chose de plus simple qui n'implique pas d'avoir à configurer un travail cron, à spécifier des règles, à suspendre le processus, etc.

Stéphane Chazelas
la source
Qu'est-ce qu'un "gibibyte"?
Peter Mortensen
@PeterMortensen Wikipedia: Gibibyte
jw013

Réponses:

6

Vous pouvez obtenir une partie de ce que vous voulez via pipelog , qui "permet de faire tourner ou d'effacer le journal d'un processus en cours en le canalisant via un intermédiaire qui répond aux signaux externes", par exemple:

spewstuff | pipelog spew.log -p /tmp/spewpipe.pid -x "gzip spew.log.1"

Vous pouvez ensuite obtenir le pid /tmp/spewpipe.pidet:

kill -s USR1 $(</tmp/spewpipe.pid)

Mais que vous auriez à mettre en place avec cron ou quelque chose. Il y a cependant un problème à cela. Remarquez I gzip spew.log.1- c'est parce que la -xcommande est exécutée après la rotation du journal. Vous avez donc le problème supplémentaire d'écraser à spew.log.1.gzchaque fois, sauf si vous écrivez un court script pour faire le gzip et déplacer le fichier par la suite, et l'utiliser comme -xcommande.

Divulgation complète: j'ai écrit cela, donc cela fonctionne bien sûr parfaitement . ;) Je garderai à l'esprit une option de compression, ou quelque chose qui la facilite mieux, pour la version 0.2 (l'objectif prévu de -xest quelque peu différent, mais cela fonctionnera comme ci-dessus). Le survol automatisé est également une bonne idée ... la première version est intentionnellement minimale car j'ai résisté à la tentation d'ajouter des fonctionnalités qui n'étaient pas nécessaires (il n'est pas si difficile de configurer un travail cron pour cela, après tout).

Notez qu'il est destiné à la sortie de texte ; s'il y a des octets nuls potentiels, vous devez utiliser -z- qui remplace le zéro par autre chose. Il s'agissait d'un compromis pour simplifier la mise en œuvre.

boucle d'or
la source
Merci. J'ai hâte de pipelog-0.3;-). Je suis également tombé sur metacpan.org/release/File-Write-Rotate . Notez que les tâches cron n'aideront pas beaucoup pour la rotation basée sur la taille du fichier.
Stéphane Chazelas
Rotation en fonction de la taille!?! Il garde la sortie vide, vous pouvez donc stat le fichier à intervalles ...
goldilocks
Vous ne pouvez pas garder la taille sous 20M (comme dans mes exigences de question) de manière fiable.
Stéphane Chazelas
L'autre chose est que c'est à peu près du texte uniquement (j'ai ajouté un dernier paragraphe à ce sujet).
goldilocks
4

Le multilog de Dan Bernstein peut apparemment le faire - ou peut-être la plupart, tout en fournissant une sortie via des descripteurs de fichiers à ! Processeur pour compenser la différence comme vous le souhaitez - bien que les spécifications de taille de 20M / 1G puissent prendre un peu de fin car il semble que 16M soit son limite supérieure par journal. Ce qui suit est, dans la majorité, une sélection de copier-coller à partir du lien ci-dessus, bien que le lien détaille également d'autres options telles que l'horodatage par ligne, en conservant [un] autre fichier [s] contenant uniquement le modèle de correspondance de ligne le plus récent et plus encore .

Interface

 multilog script

... le script se compose d'un certain nombre d'arguments. Chaque argument spécifie une action. Les actions sont exécutées dans l'ordre pour chaque ligne d'entrée.

Sélection de lignes

Chaque ligne est initialement sélectionnée. L'action...

-pattern

... désélectionne la ligne si le motif correspond à la ligne. L'action...

+pattern

sélectionne la ligne si le motif correspond à la ligne.

... le motif est une chaîne d'étoiles et de non-étoiles. Il correspond à toute concaténation de chaînes correspondant à toutes les étoiles et non-étoiles dans le même ordre. Un non-star se correspond. Une étoile avant la fin du modèle correspond à toute chaîne qui n'inclut pas le caractère suivant dans le modèle. Une étoile à la fin du motif correspond à n'importe quelle chaîne.

Journaux pivotés automatiquement

Si dir commence par un point ou une barre oblique, l'action ...

 dir

... ajoute chaque ligne sélectionnée à un journal nommé dir . Si dir n'existe pas, le multilogcrée.

Le format du journal est le suivant:

  1. dir est un répertoire contenant un certain nombre d'anciens fichiers journaux, un fichier journal nommé courant et d'autres fichiers pour multiloggarder une trace de ses actions.

  2. Chaque ancien fichier journal a un nom commençant par @ , se poursuivant par un horodatage précis indiquant la fin du fichier et se terminant par l'un des codes suivants:

    • .s : ce fichier est entièrement traité et écrit en toute sécurité sur le disque.
    • .u : Ce fichier était en cours de création au moment d'une panne. Il a peut-être été tronqué. Il n'a pas été traité.

L'action...

 ssize

... définit la taille maximale du fichier pour les actions dir suivantes . multilogdécidera que le courant est assez grand si le courant a des octets de taille . ( multilogdécidera également que le courant est suffisamment grand s'il voit une nouvelle ligne à moins de 2000 octets de la taille de fichier maximale; il essaie de terminer les fichiers journaux aux limites des lignes.) la taille doit être comprise entre 4096 et 16777215. La taille de fichier maximale par défaut est 99999.

Dans les versions 0.75 et ci - dessus: Si multilogreçoit un ALRM signal, il décide immédiatement courant est assez grand, si courant est non vide.

(Remarque: je soupçonne que le zsh schedulemodule intégré peut être facilement persuadé d'envoyer un ALRMà des intervalles spécifiés si nécessaire.)

L'action...

 nnum

... définit le nombre de fichiers journaux pour les actions dir suivantes . Après avoir renommé en cours , se multilogvoit num ou plus anciens fichiers journaux, il supprime l'ancien fichier journal avec le plus petit horodatage. num doit être au moins 2. Le nombre de fichiers journaux par défaut est 10.

L'action...

 !processor

... définit un processeur pour les actions dir suivantes . multilogalimentera le courant via le processeur et enregistrera la sortie dans un ancien fichier journal au lieu du courant . multilogenregistrera également toute sortie que le processeur écrit dans le descripteur 5 et rendra cette sortie lisible sur le descripteur 4 lors de l'exécution du processeur dans le fichier journal suivant. Pour plus de fiabilité, le processeur doit quitter différent de zéro s'il a du mal à créer sa sortie; multilogl'exécutera à nouveau. Notez que l'exécution du processeur peut bloquer toute entrée d'alimentation de programme multilog.

mikeserv
la source
2

Le meilleur que j'ai pu trouver dans la mesure où une approximation qui n'implique pas d'écrire de gros morceaux de code est ce zshcode:

autoload zmv
mycmd |
  while head -c20M > mycmd.log && [ -s mycmd.log ]; do
    zmv -f '(mycmd.log)(|.(<->))(|.gz)(#qnOn)' '$1.$(($3+1))$4'
    {rm -f mycmd.log.1 mycmd.log.50.gz; (gzip&) > mycmd.log.1.gz} < mycmd.log.1
  done

Ici, fractionnement et rotation en au plus 51 gros fichiers de 20 Mo.

Stéphane Chazelas
la source
peut-être ... des montages en boucle? btrfspeut également être monté avec compress-force=zlib.
mikeserv
2

Voici un script python piraté pour faire quelque chose comme ce que vous demandez:

#!/bin/sh
''':'
exec python "$0" "$@"
'''

KEEP = 10
MAX_SIZE = 1024 # bytes
LOG_BASE_NAME = 'log'

from sys import stdin
from subprocess import call

log_num = 0
log_size = 0
log_name = LOG_BASE_NAME + '.' + str(log_num)
log_fh = open(log_name, 'w', 1)

while True:
        line = stdin.readline()
        if len(line) == 0:
                log_fh.close()
                call(['gzip', '-f', log_name])
                break
        log_fh.write(line)
        log_size += len(line)
        if log_size >= MAX_SIZE:
                log_fh.close()
                call(['gzip', '-f', log_name])
                if log_num < KEEP:
                        log_num += 1
                else:
                        log_num = 0
                log_size = 0
                log_name = LOG_BASE_NAME + '.' + str(log_num)
                log_fh = open(log_name, 'w', 1)
Mark Wagner
la source
1
Y a-t-il une raison de l'avoir comme script shell qui execest python en premier lieu au lieu d'utiliser le hashbang pythonou env python?
peterph