it-swarm.com.de

Wie protokolliere ich mein Python Spark-Skript?

Ich habe ein Python Spark-Programm, das ich mit spark-submit laufe. Ich möchte Logging-Anweisungen einfügen.

logging.info("This is an informative message.")
logging.debug("This is a debug message.")

Ich möchte denselben Logger verwenden, den Spark verwendet, damit die Protokollnachrichten im gleichen Format ausgegeben werden und der Pegel von denselben Konfigurationsdateien gesteuert wird. Wie mache ich das?

Ich habe versucht, die logging-Anweisungen in den Code einzufügen und mit einer logging.getLogger() zu beginnen. In beiden Fällen sehe ich Spark-Protokollnachrichten, aber nicht meine. Ich habe mir die Python-Protokollierungsdokumentation angesehen, konnte es aber nicht herausfinden.

Nicht sicher, ob dies spezifisch für Skripts ist, die an Spark übermittelt werden, oder ob ich nicht weiß, wie die Protokollierung funktioniert.

27
W.P. McNeill

Sie können den Logger vom SparkContext-Objekt abrufen:

log4jLogger = sc._jvm.org.Apache.log4j
LOGGER = log4jLogger.LogManager.getLogger(__name__)
LOGGER.info("pyspark script logger initialized")
36
Alex Q

Sie müssen den Logger für spark selbst abrufen. Standardmäßig gibt getLogger () den Logger für Ihr eigenes Modul zurück. Versuchen Sie etwas wie:

logger = logging.getLogger('py4j')
logger.info("My test info statement")

Es könnte auch "Pyspark" anstelle von "Py4j" sein.

Wenn die Funktion, die Sie in Ihrem Spark-Programm verwenden (und welche Protokollierung übernimmt) in demselben Modul wie die Hauptfunktion definiert ist, wird ein Serialisierungsfehler ausgegeben.

Dies wird erklärt hier und ein Beispiel von derselben Person wird gegeben hier

Ich habe das auch an spark 1.3.1 getestet

BEARBEITEN: 

Um die Protokollierung von STDERR in STDOUT zu ändern, müssen Sie den aktuellen StreamHandler entfernen und einen neuen hinzufügen.

Bestehenden Stream-Handler finden (Diese Zeile kann entfernt werden, wenn Sie fertig sind)

print(logger.handlers)
# will look like [<logging.StreamHandler object at 0x7fd8f4b00208>]

Es wird wahrscheinlich nur eine einzige geben, aber wenn nicht, müssen Sie die Position aktualisieren.

logger.removeHandler(logger.handlers[0])

Neuen Handler für sys.stdout hinzufügen

import sys # Put at top if not already there
sh = logging.StreamHandler(sys.stdout)
sh.setLevel(logging.DEBUG)
logger.addHandler(sh)
13
CasualDemon

Der Schlüssel für die Interaktion von pyspark und Java log4j ist jvm . Dies ist der Python-Code, der conf fehlt die URL, aber hier geht es um die Protokollierung.

from pyspark.conf import SparkConf
from pyspark.sql import SparkSession

my_jars = os.environ.get("SPARK_HOME")
myconf = SparkConf()
myconf.setMaster("local").setAppName("DB2_Test")
myconf.set("spark.jars","%s/jars/log4j-1.2.17.jar" % my_jars)
spark = SparkSession\
 .builder\
 .appName("DB2_Test")\
 .config(conf = myconf) \
 .getOrCreate()


Logger= spark._jvm.org.Apache.log4j.Logger
mylogger = Logger.getLogger(__name__)
mylogger.error("some error trace")
mylogger.info("some info trace")

Wir mussten sich von den Executors anmelden , nicht vom Treiberknoten. Also haben wir folgendes gemacht:

  1. Wir haben auf allen Knoten einen /etc/rsyslog.d/spark.conf erstellt (mithilfe einer Bootstrap-Methode mit Amazon Elastic Map Reduceso that the Core nodes forwarded sysloglocal1`-Nachrichten an den Master-Knoten.

  2. Auf dem Master-Knoten haben wir die UDP- und TCP -Syslog-Listener aktiviert und so eingerichtet, dass alle local-Nachrichten in /var/log/local1.log protokolliert wurden. 

  3. Wir haben in unserer Map-Funktion einen Python logging-Modul Syslog-Logger erstellt.

  4. Jetzt können wir uns mit logging.info() einloggen. ...

Wir haben festgestellt, dass dieselbe Partition gleichzeitig auf mehreren Executoren verarbeitet wird. Scheinbar tut Spark dies ständig, wenn zusätzliche Ressourcen zur Verfügung stehen. Dies behandelt den Fall, wenn ein Executor auf mysteriöse Weise verspätet ist oder ausfällt. 

Die Protokollierung der map-Funktionen hat uns viel über die Funktionsweise von Spark gelehrt.

2
vy32

In meinem Fall bin ich einfach froh, dass meine Protokollmeldungen zusammen mit den üblichen Funkenprotokollmeldungen zum Worker stderr hinzugefügt werden.

Wenn dies Ihren Bedürfnissen entspricht, besteht der Trick darin, den jeweiligen Python-Logger auf stderr umzuleiten.

Zum Beispiel funktioniert das Folgende, inspiriert aus dieser Antwort für mich gut:

def getlogger(name, level=logging.INFO):
    import logging
    import sys

    logger = logging.getLogger(name)
    logger.setLevel(level)
    if logger.handlers:
        # or else, as I found out, we keep adding handlers and duplicate messages
        pass
    else:
        ch = logging.StreamHandler(sys.stderr)
        ch.setLevel(level)
        formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
        ch.setFormatter(formatter)
        logger.addHandler(ch)
    return logger

Verwendungszweck:

def tst_log():
    logger = getlogger('my-worker')
    logger.debug('a')
    logger.info('b')
    logger.warning('c')
    logger.error('d')
    logger.critical('e')
    ...

Ausgabe (plus ein paar umgebende Zeilen für Kontext):

17/05/03 03:25:32 INFO MemoryStore: Block broadcast_24 stored as values in memory (estimated size 5.8 KB, free 319.2 MB)
2017-05-03 03:25:32,849 - my-worker - INFO - b
2017-05-03 03:25:32,849 - my-worker - WARNING - c
2017-05-03 03:25:32,849 - my-worker - ERROR - d
2017-05-03 03:25:32,849 - my-worker - CRITICAL - e
17/05/03 03:25:32 INFO PythonRunner: Times: total = 2, boot = -40969, init = 40971, finish = 0
17/05/03 03:25:32 INFO Executor: Finished task 7.0 in stage 20.0 (TID 213). 2109 bytes result sent to driver
1
Pierre D