TS: Linux: Datei vorne abschneiden

Hello,

wie kann ich eine lange Textdatei, die im Appendmodus in Produktion ist, während des Betriebes vorne abschneiden, ohne dass die hinzufügenden Programme davon beeinträchtigt werden?

Beispiel:

Die Datei hat inzwischen 81.000 Zeilen.
Die ersten 20.000 sollen ausgelagert und dann aus der laufenden Datei entfernt werden, sooft, bis die Logdatei selber noch ca. 20.000 Zeilen hat.

Zur Verfügung stehen bash und/oder PHP, auch als CLI.

Glück Auf
Tom vom Berg

--
Es gibt nichts Gutes, außer man tut es!
Das Leben selbst ist der Sinn.
  1. Eine Datei vorne abschneiden geht eigentlich nur über Kopieren des Rests in eine neue Datei. Zu dieser Anforderung habe ich auch schon mal was gesucht, aber nichts gefunden. Ist auch von daher nachvollziehbar, dass man nicht einfach mal eben den Dateizeiger auf eine andere Position setzen kann. Wenn das ginge, dann nur in Block-weisen Schritten aber nicht beliebig und das bringt dann sowieso nichts.

    Kannst du das so umbauen dass die laufende Datei bei Erreichen einer bestimmten Größe automatisch umbenannt wird und die Prozesse sich eine neue Datei anlegen? Dann hättest du die Aufteilung in entsprechende Blöcke ohne weiteren Kopieraufwand.

    Die jetzige große Datei müsstest du auch umbenennen (damit sie nicht weiter beschrieben wird). Dann liest du sie zeilenweise aus und schreibst neue Dateien mit jeweils 20000 Zeilen.

    Das ganze natürlich sauber mit den Prozessen synchronisiert, damit die alle immer wissen wo sie hinschreiben müssen und warten bis es wieder eine Zieldatei gibt.

    1. Hello,

      [•••]

      Das ganze natürlich sauber mit den Prozessen synchronisiert, damit die alle immer wissen wo sie hinschreiben müssen und warten bis es wieder eine Zieldatei gibt.

      Genau das gute alte TOCTTOU-Problem treibt mich da um.
      Ich bin mir jetzt unsicher, ob man das Schreiben mittels Append auch kontrollieren kann. Das macht mMn das OS (Linux) schon mikromatisch (also es serialisiert die Aufträge unterschiedlicher Prozesse im Kleinsten), aber es gibt darüber hinaus keine Möglichkeit der Verzögerung.

      Wenn ich jetzt die Datei sperren würde, umbenennen, den Schwanz kopieren in eine neue Datei mit dem alten Namen (sofern das innerhalb desselben Prozesses überhaupt geht), dann könnte ich den verbliebenen Rest anschließend aufteilen auf die "Old-Versionen".

      Leider dauert das bei 80.000 Zeilen auch eine gewisse Zeit. Wo lasse ich in der Zwischenzeit die neuen Einträge. Da hängen dann die Prozesse solange.

      Und wie macht das logrotate? Dessen Aufgabe ist ja sehr ähnlich.
      Und wie machen das Spooldateien? Die werden doch auch vorne gekürzt und nicht hinten.

      Wenn ich die laufende Datei einfach umbenennen würde, wann gibt das OS den alten Namen dann wieder frei? Wenn dann eine Appendanforderung käme, solange noch keine neue "Schwanzdatei" besteht, würde die ggf. automatisch angelegt werden. Das muss aber solange verhindert werden, bis die Schwanzdatei die geforderten ca. 20.000 Einträge enthält.

      Glück Auf
      Tom vom Berg

      --
      Es gibt nichts Gutes, außer man tut es!
      Das Leben selbst ist der Sinn.
      1. Ich kenne die Details nicht so genau dass ich dir mehr zu den Hintergründen sagen könnte.

        Aber eine Idee kommt mir da, in Linux ist ja alles eine Datei.
        Lässt sich da nicht die Zieldatei auf etwas verlinken das die Zeilen entgegennimmt und gleich richtig auf die letztendlichen Ablagedateien verteilt?
        Alles in /sysfs oder /proc sind ja auch nur virtuelle Files hinter denen ein Stück Code steckt das die verwaltet.

  2. Moin,

    wie kann ich eine lange Textdatei, die im Appendmodus in Produktion ist, während des Betriebes vorne abschneiden, ohne dass die hinzufügenden Programme davon beeinträchtigt werden?

    meines Wissens gar nicht. Aber wenn du nochmal von vorne anfangen willst und kannst, wäre das ein Fall für den Einsatz von logrotate.

    Ciao,
     Martin

    --
    Ich stamme aus Ironien, einem Land am sarkastischen Ozean.
    1. Hello,

      Moin,

      wie kann ich eine lange Textdatei, die im Appendmodus in Produktion ist, während des Betriebes vorne abschneiden, ohne dass die hinzufügenden Programme davon beeinträchtigt werden?

      meines Wissens gar nicht. Aber wenn du nochmal von vorne anfangen willst und kannst, wäre das ein Fall für den Einsatz von logrotate.

      Das befürchte ich auch.

      Dann müssen aber sämtliche Anzeige- und Auswertungsroutinen neu erstellt bzw. überarbeitet werden, weil die dann immer die letzten beiden Logdateien berücksichtigen müssen, damit immer (möglichst) die Mindestanzahl von Zeilen vorhanden ist.

      Dafür schiebt Logrotate sogar den Archivnamen weiter, sodass ich immer nur bla.dat und bla.dat.1 berücksichtigen müsste ;-)

      Und es komprimiert dann ab bla.dat.2 automatisch und löscht ab bla.dat.### auch die Uraltversionen, wenn man das will.

      Wäre also zu überlegen, was mehr Arbeit macht :-O

      Wenn ich die Logrotate-Variante nehme, hätte ich zwar vermutlich am Ende unter dem Strich Zeit gespart, würde aber immer noch nicht verstehen, wie Linux das TOCTTOU-Problem in diesem Fall löst.

      Glück Auf
      Tom vom Berg

      --
      Es gibt nichts Gutes, außer man tut es!
      Das Leben selbst ist der Sinn.
    2. wie kann ich eine lange Textdatei, die im Appendmodus in Produktion ist, während des Betriebes vorne abschneiden, ohne dass die hinzufügenden Programme davon beeinträchtigt werden?

      meines Wissens gar nicht. Aber wenn du nochmal von vorne anfangen willst und kannst, wäre das ein Fall für den Einsatz von logrotate.

      Jepp! Oder: @TS, nimmt doch etwas, was das kann -> Datenbank. So lange die „hinzufügenden Programme“ in ein PIPE loggen können sollte das auch kein Problem sein.

      Dazu reicht z.B. ein laufendes MySQL und ein als Dienst etabliertes Schell-Skript, welches einmal die Verbindung zur Datenbank aufbaut, den Logstring mit read str; entgegennimmt und nach Entschärfung den Insert macht.

      Und wie machen das Spooldateien?

      Die Dateien machen da gar nichts. Die werden von genau einem Programm oder einer genau definierten und „sich aus der Kinderstube kennenden“ Gruppe von Programmen behandelt, die zwecks TOCTTOU-Vermeidung miteinander über den Staus der Spooldateien kommunizieren, und die Bestellungen der anderen Programme entgegennehmen und deren Wünsche (hoffentlich) erfüllen.

      Und wie macht das logrotate?

      Einfache Antwort:

      Wie in /etc/logrotate.conf und /etc/logrotate.d/* ersichtlich. Der Apache wird z.B. neu geladen. Grund: der benutzt (wohl aus Performancegründen ) nicht den üblichen Syslogmechanismus (e.g. rsyslogd), also einen Pipe nach logger -t facility, sondern loggt direkt.

      Würde der Apache rsyslogd bz. journald benutzen (Man kann das durchaus konfigurieren), dann wäre rsyslogd dafür zuständig, den Rest mit logrotate abzuhandeln.

      Andererseits hat der Apache einen eigenen Logrotierer. Der aber scheint bei den Linux-Distributoren unbeliebt zu sein - kann man verstehen, der braucht dann einen eigenen Cronjob und weicht also vom üblichen Mechanismus ab.

      1. Ansage war:

        Dazu reicht z.B. ein laufendes MySQL und ein als Dienst etabliertes Schell-Skript, welches einmal die Verbindung zur Datenbank aufbaut, den Logstring mit read str; entgegennimmt und nach Entschärfung den Insert macht.

        Ich hab mir gedacht, ich mach das mal in PHP...

        <?php
        
        /* database: test, table: phplog
        MariaDB [test]> describe test.phplog\G
        *************************** 1. row ***************************
          Field: id
           Type: int(11)
           Null: NO
            Key: PRI
        Default: NULL
          Extra: auto_increment
        *************************** 2. row ***************************
          Field: timestamp
           Type: timestamp
           Null: YES
            Key: MUL
        Default: CURRENT_TIMESTAMP
          Extra: 
        *************************** 3. row ***************************
          Field: entry
           Type: varchar(1000)
           Null: YES
            Key: 
        Default: NULL
          Extra: 
        */
        
        
        $FH = fopen( 'php://stdin', 'r' );
        $mysqli = mysqli_connect("localhost", "test", "hallo", "test");
        $sql    ='INSERT INTO `phplog` (`entry`) values (?)';
        $stmt   = $mysqli->prepare( $sql );
        $entry  = 'Connected from '. __FILE__;
        $stmt -> bind_param( "s", $entry );
        $stmt -> execute();
        
        while ( $entry = fgets( $FH ) ) {
        	$entry = substr( trim( $entry ), 0, 1000 ) ;
        	$stmt -> bind_param( 's', $entry );
        	$stmt -> execute();
        }
        

        Test mit /var/log/syslog (Derzeit 303 Zeilen):

        wc -l /var/log/syslog; time cat /var/log/syslog | php test.php
        303 /var/log/syslog
        
        real  0m0,276s
        user  0m0,029s
        sys	  0m0,015s
        

        Wie weiter? Man kann das Skript wie folgt umbauen:

        #$FH = fopen( 'php://stdin', 'r' );
        $FH = fopen( '/tmp/phplogger', 'r+' );
        

        dazu den angebenen FiFO bauen:

        mkfifo /tmp/phplogger
        

        Das Zeug starten:

        php /tmp/test.php
        

        und dann so loggen:

        echo "Eintrag" > /tmp/phplogger 
        

        Ist der Eintrag drin?

        MariaDB [test]> select * from phplog order by id desc limit 1;
        +------+---------------------+---------+
        | id   | timestamp           | entry   |
        +------+---------------------+---------+
        | 3096 | 2020-03-11 14:35:54 | Eintrag |
        +------+---------------------+---------+