angle-uparrow-clockwisearrow-counterclockwisearrow-down-uparrow-leftatcalendarcard-listchatcheckenvelopefolderhouseinfo-circlepencilpeoplepersonperson-fillperson-plusphoneplusquestion-circlesearchtagtrashx

Python Anwendungsprotokollierung mit Docker

Docker Best Practices empfehlen die Protokollierung in stdout , aber es gibt einige Probleme.

5 Dezember 2022
post main image
https://unsplash.com/@agk42

Wenn Sie eine Softwareanwendung entwickeln, werden Sie wahrscheinlich als erstes die Protokollierung einrichten. Zunächst nur auf der Konsole, aber bald werden Sie Protokolldateien hinzufügen.

Vor einigen Jahren begann ich, Docker für die Entwicklung und Produktion zu verwenden. Bei der Umstellung auf Docker habe ich keine großen Änderungen vorgenommen. Die Anwendungsprotokolldateien befinden sich immer noch in einem Protokollverzeichnis auf einem Docker Volume. Dies bedeutet, dass die Protokolldateien Teil der Anwendung framework sind.

Best Practices im Internet empfehlen die Verwendung von Docker Logging. Das bedeutet, dass unsere Docker -Anwendung Protokollsätze an stdout (und/oder sterr) ausgeben muss, anstatt sie in unsere Anwendungsprotokolldateien zu schreiben. Wenn wir unsere Protokolldatensätze in Docker an stdout senden, können wir einen Protokolltreiber angeben, um sie in ein externes Tool wie Syslog zu exportieren, indem wir von Docker bereitgestellte Protokolltreiber oder Protokolltreiber von Drittanbietern verwenden. Nun gut, ich verstehe, dass dies Vorteile haben kann oder sogar unerlässlich ist.

Auch hier möchte ich nicht zu viel ändern und bleibe bei dem Standard-Protokolltreiber in Docker: json-file. Unsere Log-Aufzeichnungen werden in den Dateien vorhanden sein:

/var/lib/docker/containers/<container id>/<container id>-json.log

Eines meiner aktuellen Python -Projekte ist eine Anwendung, die aus vielen (Mikro-)Services besteht. Ein Service wird durch einen Container repräsentiert, wobei die Logdateien noch im Kontext / framework des Services stehen. In diesem Beitrag konvertiere ich meine bestehende Anwendungslogging-Methode in Docker -Logging.

Ändern des Logging-Moduls

Für meine Anwendungen habe ich ein benutzerdefiniertes Logging-Modul entwickelt. Innerhalb dieses Moduls befinden sich die Standard Python Logging-Methoden, d.h. mein Anwendungscode enthält Zeilen wie:

logger.debug(...)
logger.info(...)
logger.error(...)

Ich verwende dieses Protokollierungsmodul überall. Das bedeutet, dass ich nur einen neuen Protokollierungsmodus hinzufügen musste, der eine Variable, use_docker_logging=True, verwendet, die angibt, dass die Protokolldaten in stdout statt in eine Datei geschrieben werden sollen.

Wir können dies sehr einfach mit logging.StreamHandler(stdout) tun. Bei Verwendung des Protokollierungsmoduls Python werden die Daten nach jedem Datensatz gespült. Das bedeutet, daß es nicht notwendig ist, Python als 'python -u' zu starten oder die Umgebungsvariable PYTHONUNBUFFERED zu verwenden.

Docker Exec, Probleme, wo sind meine Protokollsätze?

Leider sind wir noch nicht fertig, es gibt ein Problem. Ich weiß nicht, wie es Ihnen geht, aber ich verwende heute für meine gesamte Entwicklung Docker -Container. In vielen Fällen habe ich den Befehl in der Datei docker-compose.yml wie:

command: tail -f /dev/null

Das heißt, starte den Container und halte ihn am Leben. Dann gebe ich 'Docker Exec' in den Container (Shell) ein und führe die Python -Skripte aus. Als ich dies tat, erschienen die Log-Einträge der 'Docker Exec'-Sitzung nicht in den Docker -Logs.

Nach einer Suche im Internet fand ich diese (Docker issues) Seite 'Proposal: additional logging options for docker exec', siehe Links unten.

Es scheint, dass, wenn Sie 'Docker Exec' in einen Container ausführen, die stdout dieser Sitzung nicht die stdout der ursprünglichen Sitzung ist. Dafür wird es Gründe geben, wir können es nicht ändern, und wir müssen damit umgehen.

Die Lösung besteht darin, die Protokolldatensätze nach umzuleiten:

/proc/1/fd/1

Dann fand ich eine andere (Docker issues) Seite im Internet 'Echoing to /dev/stdout does not appear in docker logs', siehe Links unten. Einer der Vorschläge ist die Protokollierung in einer sym-verknüpften Datei. Wir erstellen die symlink in der Dockerfile:

RUN ln -sf /proc/1/fd/1 /var/log/test.log

Zurück zu unserer bisherigen Lösung, ersetzen wir die logging.StreamHandler durch die logging.FileHandler:

logging.FileHandler('/var/log/test.log')

Jetzt erscheinen die Protokollsätze von Python -Skripten, die in der Sitzung "Docker Exec" ausgeführt werden, in den Docker -Protokollen.

Docker Exec-Sitzung, keine Konsolenprotokollierung

Leider sind wir noch nicht fertig, es gibt ein weiteres Problem. Da wir den logging.FileHandler verwendet haben, protokollieren wir nur in einer Datei, stdout, die die Docker -Logs sind. Um die Protokollsätze auf dem Bildschirm in der Sitzung "Docker Exec" zu sehen, müssen wir die Datei logging.StreamHandler wieder hinzufügen.

Aber halt, das müssen wir nur für die "Docker Exec"-Sitzung tun, sonst sehen wir doppelte Protokollsätze in den Docker -Protokollen.

Ich habe dieses Problem auf eine etwas umständliche Weise gelöst, indem ich den Namen des obersten übergeordneten Prozesses ermittelt habe. Wenn dieser Prozessname 'sh' oder 'bash' lautet, gehe ich davon aus, dass wir Docker Exec verwendet haben, um den Container zu betreten.

import psutil

    ...
    # get 'top' parent process of this docker exec session
    parent_process_pid = os.getpid()
    parent_process_name = None
    while True:
        #print('parent_process_pid = {}'.format(parent_process_pid))
        parent_process = psutil.Process(parent_process_pid)
        pid = parent_process.ppid()
        parent_process_name = parent_process.name()
        if pid == 0:
            break
        parent_process_pid = pid

    if parent_name in ['sh', 'bash']:
        # add console logging
        ...
    else:
        # no console logging
        ...
    ...

Protokollzeilen mit zusätzlichen Feldern

Alle Log-Einträge eines Containers befinden sich jetzt in einer einzigen Log-Datei. Wenn Sie mehrere Dienste (Prozesse) in einem Container laufen haben, möchten Sie wahrscheinlich zusätzliche Felder zu den Protokollzeilen hinzufügen, die den Dienst (Prozess) identifizieren. Dies geschieht zusätzlich zu den Feldern logging.DEBUG, logging.ERROR usw., die wir in die Protokollzeilen einfügen.

In der Python -Protokollierung können wir mit der Methode logging.setLogRecordFactory() ein zusätzliches Feld in eine Protokollzeile einfügen. Ein Beispiel wird auf der Seite 'Using LogRecordFactory in python to add custom fields for logging' gegeben, siehe Links unten.

Andere Änderungen

Beachten Sie, dass mit dem Standard Docker Logging-Treiber nur ein String in stdout geschrieben werden kann, das Schreiben eines JSON-Objekts (Dictionary) ist nicht möglich. Eine schwerwiegende Einschränkung, aber wir müssen damit leben.

Wenn wir allen (!) Protokolleinträgen Tags hinzufügen wollen, können wir Labels in Docker-Compose verwenden:

Beispiel:

  some-service:
    image: ...
    ports:
      - "8082:8000"
    labels:
      log_for_application: "myapp"

Beim Loggen in Docker wird der Zeitstempel automatisch von Docker hinzugefügt. Das bedeutet, dass wir ihn aus unserer Python -Logzeile entfernen müssen. Meine endgültige Logger-Formatierungszeichenfolge sieht so aus:

'%(proc_id)-15.15s %(levelname)-8.8s [%(filename)-30s%(funcName)20s():%(lineno)03s] %(message)s'

Anzeige der Docker -Protokolle aller Container mit Dozzle

Es gibt viele Lösungen, um die Docker -Protokolle aller Container anzuzeigen. Ich habe einige ausprobiert, und Dozzle war am einfachsten einzurichten und zu benutzen. Dozzle ist ein Echtzeit-Log-Viewer für Docker-Container, siehe Links unten. Um es auszuführen, ziehen Sie einfach den Container, und Sie sind bereit zu gehen. Dozzle sieht aus wie Logs Explorer, eine Erweiterung für Docker Desktop, siehe Links unten.

Seien Sie vorsichtig, standardmäßig verbindet Dozzle Sie mit Google Analytics, stellen Sie sicher, dass Sie Dozzle mit dem '--no-analytics' Flag starten:

docker run --name dozzle -d --volume=/var/run/docker.sock:/var/run/docker.sock -p 8888:8080 amir20/dozzle:latest --no-analytics

Sobald er gestartet ist, rufen Sie Ihren Browser auf:

http://127.0.0.1:8888

Mit Dozzle ist es sehr einfach, Ihre Docker -Protokolle einzusehen.

Die Suche verwendet regex, d.h. wenn Sie z.B. nach zwei Begriffen filtern wollen, 'Term1' und 'Term2',
können Sie in das Suchfeld eingeben:

Term1.*?Term2

Eine weitere nette Sache mit Dozzle ist, dass wir es auch für einen einzelnen Container oder ein paar Container starten können. Was wirklich fehlt, ist eine Möglichkeit, die Suchen zu speichern und wieder auszuwählen. Aber ich will mich nicht beschweren. Tolles Tool!

Zusammenfassung

Dies hat viel mehr Zeit in Anspruch genommen, als ich erwartet hatte. Es war viel mehr als nur das Drucken nach stdout. Ist es die Mühe wert? Für die aktuelle Anwendung, die aus vielen Diensten besteht, die durch Container repräsentiert werden, glaube ich, dass sie es ist.

Da die Protokollierung nun über Docker erfolgt, kann ich andere Möglichkeiten zur Anzeige der Protokolle, zur Erzeugung von Warnmeldungen usw. prüfen.

Links / Impressum

Docker - Configure logging drivers
https://docs.docker.com/config/containers/logging/configure

Dozzle, a real-time log viewer for docker containers
https://dozzle.dev

Echoing to /dev/stdout does not appear in 'docker logs' #19616
https://github.com/moby/moby/issues/19616

Proposal: additional logging options for docker exec #8662
https://github.com/moby/moby/issues/8662

Using LogRecordFactory in python to add custom fields for logging
https://stackoverflow.com/questions/59585861/using-logrecordfactory-in-python-to-add-custom-fields-for-logging

Einen Kommentar hinterlassen

Kommentieren Sie anonym oder melden Sie sich zum Kommentieren an.

Kommentare

Eine Antwort hinterlassen

Antworten Sie anonym oder melden Sie sich an, um zu antworten.