Stefan Bechtold: PHP in PERL umschreiben

Hi,

ich habe eine Klasse geschrieben, um aus einer Webalizer Logfile eine CSV getrennte File zu erstellen, die Statistiken fuer die Verzeichniszugriffe auswirft. Hintergrund dieser Sache ist, dass der Webalizer lediglich Stats fuer die Files rauswirft.

Die Logfile des Webalizers ist recht simple aufgebaut und kann daher durch die schnellen explodes (sofern mir bekannt ist laufen diese schneller als preg_match-Abfragen) aufgesplittet werden.

Um die ganze Sache abzurunden habe ich ein paar Filter eingebaut, die dazu da sind, die Logfile nach bestimmten Kriterien zu filtern. So kann man zum Beispiel nur Zugriffe zaehlen, die erfolgreich waren, bzw. die Stats von gewissen Dateitypen abhaengig machen ("htm,html,usw.") oder nach Datum aussortieren, etc.

Da unsere Files nun aber knapp 400MB groß sind ist dem Script auch nicht mehr durch ein set_time_limit(15*60) (15 Minuten) und ini_set("memory_limit", "500M") (Speicher auf 500MB setzen) zu helfen. Außerdem ist die Beanspruchung des Servers viiiiieeeel zu hoch =:o)

Aus diesem Grund haben wir ueberlegt das Script mal in PERL zu schreiben, nur dass leider keiner von uns von PERL ne Ahnung hat ;o)

Gibt's hier jemanden, der daran interesse haben koennte?
Falls interesse besteht kann ich die Klasse auch posten...

  1. Sup!

    Perl ist gar nicht so schwer ("Programming Perl" - Larry Wall, O'Reilly).

    Vielleicht programmiert ihr auch einfach ziemlich ... dämlich.
    Wenn man eine Datei Zeile für Zeile abarbeitet, dann sollte der Speicherbedarf kaum über ein paar K ansteigen.
    Wenn man nicht alles gleichzeitig verarbeitet, sondern immer Stück für Stück Filter für Filter sequentiell anwendet (Datei A -> Filter -> Datei B -> Filter -> Datei C/D...  Datei C -> Filter -> Datei C2, Datei D-> Filter -> Datei D2) , dann könnte es möglicherweise mit weniger Speicheraufwand und schneller gehen.
    Ein Monsterskript auf einen Monsterdatenhaufen anzusetzen belastet den Speicher enorm. Nacheinander mehrere Skriptteile jeweils auf kleine Datenhaufen anzusetzen ist womöglich effizienter.
    Das RAM und die CPU-Caches würde es entlasten, wenn man jeweils einen Filter nimmt und dann die Daten häppchenweise durchschickt anstatt auf ein Riesen-Datenstück eine Reihe von Filtern anzuwenden (dann muß die CPU andauernd neue Filter-Skripte laden).
    Wenn man ein Riesen-Skript und eine Riesen-Datenmenge hat, dann kommt die Maschine schnell ins Swappen (Auslagern von Daten/Programmen), und dann wird alles grottenlangsam.

    Gruesse,

    Bio

    --
    Elite ist mein zweiter Vorname
    1. Hi!

      Perl ist gar nicht so schwer ("Programming Perl" - Larry Wall, O'Reilly).

      das nicht, aber PHP ist wunderbar für sowas geeignet ;-)

      Vielleicht programmiert ihr auch einfach ziemlich ... dämlich.

      vermutlich..., der Satz:

      Die Logfile des Webalizers ist recht simple aufgebaut und kann daher durch die schnellen explodes (sofern mir bekannt ist laufen diese schneller als preg_match-Abfragen) aufgesplittet werden.

      legt das nahe. (btw: vermutlich meint Ihr die Logfiles des Webservers)

      Ich denke ihr lest die Datei mit file() ein, oder? Dann würde die ganze Datei erst geparst und komplett inein Array gechreben, wozu dann entsprechend viel Speicher verbraucht wird.

      Wenn man nicht alles gleichzeitig verarbeitet, sondern immer Stück für Stück Filter für Filter sequentiell anwendet (Datei A -> Filter -> Datei B -> Filter -> Datei C/D...  Datei C -> Filter -> Datei C2, Datei D-> Filter -> Datei D2) , dann könnte es möglicherweise mit weniger Speicheraufwand und schneller gehen.

      meinst Du "Datei" oder "Zeile"?

      Ich würde das einlesen etwas so machen:

      <?php
      $file ='access_log';
      $fp = fopen ($file, 'r');
      do {
          $line = fgets($fp,4096);
          if (strlen($line) == 0) {
              break;
          }
          // hier die Filterfunktion
      } while(true);
      fclose ($fp);
      ?>

      Das setzt allerdings voraus dass es keine leerzeilen gibt, was ja eigentlich der Normalfall ist. Zur Not kann man ja noch die strlen aufaddieren und mit der Dateigröße vergleichen. Der Sinn dahinter ist dass nicht 1 feof() Aufruf pro Zeile notwendig wird, strlen() ist da erheblich performanter.
      Praktisch wäre es, die Zeile direkt mit fgetcsv() in ein Array zu lesen, nur ist das glaube ich nicht möglich da die "enclosure" Zeichen nicht durchgängig verwendet werden, ich glaube zumindest das es dann nicht geht.

      Das Problem ist, dass Du die einzelnen Elemente nicht so ohne weiteres per explode() tennen kannst, denn anhand welches Trennzeichen? " " geht nicht. Das heißt dass Du Dir entweder eine  eigene spezielle Funktion hierfür schreiben musst, oder per preg_match verwenden musst.

      Achte auch darauf dass Du jetzt nicht doch einen Array verwendest in den Du die geamte Logfile nach und nach reinschreibst, sondern überschreibe die Elemente, oder am besten greife direkt drauf zu, vielleicht könntest Du mit einer eigenen Funktion nur die wichtigen Eckpunkte der jeweiligen Strings innerhalb von $data merken, also in einen Array schreiben, und dann nur mit substr() drauf zugreifen.

      Ich würde evtl. auch eine Referenz auf den geparsten String verwenden falls Du den kopierst oder an eine Funktion übergibts. Naja, es kommt drauf an was Du genau machst, man kann das ganze mit solchen Spielereien natürlich auch verlangsamen.

      Teste einfach mal ein bisschen rum und vergleiche am Ende die Performance. Ich habe das auch mal irgendwo gemacht, und dabei kam raus das preg_match durchaus performant ist.

      Viele Grüße
      Andreas

      1. Hallo Andreas,

        <?php
        $file ='access_log';
        $fp = fopen ($file, 'r');
        do {
            $line = fgets($fp,4096);
            if (strlen($line) == 0) {
                break;
            }
            // hier die Filterfunktion
        } while(true);
        fclose ($fp);
        ?>

        Sinnvoller:

        while(($line = fgets($fp)) !== false) {
          // tu was
        }

        Gruesse,
         CK

        --
        http://cforum.teamone.de/
        http://wishlist.tetekum.de/
        If God had meant for us to be in the Army, we would have been born with green, baggy skin.
        1. Hi!

          Sinnvoller:

          while(($line = fgets($fp)) !== false) {
            // tu was
          }

          Stimmt, ich hatte das mit fread() verwendet, das funktioniert ja etwas anders. Allerdings wusste ich auch nicht dass fgets FALSE zurück gibt wenn EOF erreicht ist. So ist es sicher das effizienteste.

          Grüße
          Andreas

  2. Hallo Stefan,

    Da unsere Files nun aber knapp 400MB groß sind ist dem
    Script auch nicht mehr durch ein set_time_limit(15*60) (15
    Minuten) und ini_set("memory_limit", "500M") (Speicher auf
    500MB setzen) zu helfen. Außerdem ist die Beanspruchung des
    Servers viiiiieeeel zu hoch =:o)

    Dann habt ihr bei der Programmierung schon etwas falsch
    gemacht. Wenn ihr das Script mal postet, koennte man mal
    drueber schauen.

    Aus diesem Grund haben wir ueberlegt das Script mal in
    PERL zu schreiben, nur dass leider keiner von uns von PERL
    ne Ahnung hat ;o)

    Das wird wahrscheinlich keinen (grossen) Unterschied machen.
    Die Hauptarbeit duerfte hier darin bestehen, eine Datei
    einzulesen. Und da ist der Engpass nunmal HD.

    Gruesse,
     CK

    --
    http://cforum.teamone.de/
    http://wishlist.tetekum.de/
    If God had meant for us to be in the Army, we would have been born with green, baggy skin.
  3. Hi,

    Hi,

    ich habe eine Klasse geschrieben, um aus einer Webalizer Logfile eine CSV getrennte File zu erstellen, die Statistiken fuer die Verzeichniszugriffe auswirft. Hintergrund dieser Sache ist, dass der Webalizer lediglich Stats fuer die Files rauswirft.

    »»

    Hatte ich auch schon vor einiger Zeit gemacht.

    Und neben den CSV-Files erstellt es noch eine Statistik aller
    Virtuellen Domains auf meinen Server.

    Schau mal hier:
    http://cgi.xwolf.de/freecode/analyze2csv.pl
    http://cgi.xwolf.de/freecode/transfer_analyse.pl

    Da es Perl ist, ist der Code ja selbsterklaerend :)

    Ciao,
      Wolfgang