it-swarm.com.de

Apache Spark-Protokollierung in Scala

Ich bin auf der Suche nach einer Lösung, um zusätzliche Daten zu protokollieren, wenn Code auf Apache Spark-Knoten ausgeführt wird, der später einige Probleme untersuchen kann, die während der Ausführung auftreten können. Der Versuch, eine herkömmliche Lösung wie beispielsweise com.typesafe.scalalogging.LazyLogging zu verwenden, schlägt fehl, da die Protokollinstanz in einer verteilten Umgebung wie Apache Spark nicht serialisiert werden kann.

Ich habe dieses Problem untersucht und die Lösung, die ich gefunden habe, war, die org.Apache.spark.Logging-Eigenschaft wie folgt zu verwenden:

class SparkExample with Logging {
  val someRDD = ...
  someRDD.map {
    rddElement => logInfo(s"$rddElement will be processed.")
    doSomething(rddElement)
  }
}

Es scheint jedoch, dass die Eigenschaft Protokollierung keine dauerhafte Lösung für Apache Spark ist, da sie als @DeveloperApi markiert ist und in der Klassendokumentation erwähnt wird: 

Dies wird wahrscheinlich in zukünftigen Versionen geändert oder entfernt.

Ich frage mich, ob es sich um eine bekannte Protokollierungslösung handelt, die ich verwenden kann und die es mir ermöglicht, Daten zu protokollieren, wenn die RDDs auf Apache Spark-Knoten ausgeführt werden.

@Later Edit : In einigen Kommentaren wird empfohlen, Log4J zu verwenden. Ich habe versucht, Log4J zu verwenden, aber ich habe immer noch Probleme, wenn ich Logger von einer Scala-Klasse (und nicht von einem Scala-Objekt) verwende ... .. Hier ist mein vollständiger Code:

import org.Apache.log4j.Logger
import org.Apache.spark._

object Main {
 def main(args: Array[String]) {
  new LoggingTestWithRDD().doTest()
 }
}

class LoggingTestWithRDD extends Serializable {

  val log = Logger.getLogger(getClass.getName)

  def doTest(): Unit = {
   val conf = new SparkConf().setMaster("local[4]").setAppName("LogTest")
   val spark = new SparkContext(conf)

   val someRdd = spark.parallelize(List(1, 2, 3))
   someRdd.map {
     element =>
       log.info(s"$element will be processed")
       element + 1
    }
   spark.stop()
 }

}

Die Ausnahme, die ich sehe, ist:

Ausnahme im Thread "main" org.Apache.spark.SparkException: Task nicht serialisierbar -> Verursacht durch: Java.io.NotSerializableException: org.Apache.log4j.Logger

45
Bogdan N

Sie können Akhils Lösung verwenden, die in vorgeschlagen wird
https://www.mail-archive.com/[email protected]/msg29010.html . Ich habe es selbst benutzt und es funktioniert. 

Akhil Das Mo, 25. Mai 2015 08:20:40 -0700
Versuchen Sie es so:

object Holder extends Serializable {      
   @transient lazy val log = Logger.getLogger(getClass.getName)    
}


val someRdd = spark.parallelize(List(1, 2, 3)).foreach { element =>
   Holder.log.info(element)
}
40
florins

Verwenden Sie Log4j 2.x. Der Kernlogger wurde serialisierbar gemacht. Problem gelöst.

Jira Diskussion: https://issues.Apache.org/jira/browse/LOG4J2-801

"org.Apache.logging.log4j" % "log4j-api" % "2.x.x"

"org.Apache.logging.log4j" % "log4j-core" % "2.x.x"

"org.Apache.logging.log4j" %% "log4j-api-scala" % "2.x.x"
3
Ryan Stack
val log = Logger.getLogger(getClass.getName),

Sie können "log" verwenden, um Protokolle zu schreiben. Wenn Sie die Logger-Eigenschaften ändern müssen, müssen Sie log4j.properties im Ordner/conf haben. Standardmäßig haben wir eine Vorlage an diesem Ort.

1
Venkata Karthik

Dies ist ein alter Beitrag, aber ich möchte meine funktionierende Lösung bereitstellen, die ich gerade nach vielem Kämpfen bekommen habe und für andere trotzdem nützlich sein kann:

Ich möchte Rdd-Inhalte in der Rdd.map-Funktion drucken, aber Task Not Serializalable Error bekommen. Dies ist meine Lösung für dieses Problem mit dem statischen Scala-Objekt, das Java.io.Serializable erweitert:

import org.Apache.log4j.Level

object MyClass extends Serializable{

val log = org.Apache.log4j.LogManager.getLogger("name of my spark log")

log.setLevel(Level.INFO)

def main(args:Array[String])
{

rdd.map(t=>

//Using object's logger here

val log =MyClass.log

log.INFO("count"+rdd.count)
)
}

}
0
khushbu kanojia

Wenn Sie vor und nach einer map-, filter- oder einer anderen RDD-Funktion Code ausführen möchten, verwenden Sie mapPartition, wobei der zugrunde liegende Iterator explizit übergeben wird.

Beispiel:

val log = ??? // this gets captured and produced serialization error
rdd.map { x =>
  log.info(x)
  x+1
}

Wird:

rdd.mapPartition { it =>
  val log = ??? // this is freshly initialized in worker nodes
  it.map { x =>
    log.info(x)
    x + 1
  }
}

Jede grundlegende RDD-Funktion wird immer mit einer mapPartition implementiert.

Vergewissern Sie sich, dass Sie den Partitionierer explizit behandeln und nicht verlieren. Siehe Parameter Scaladoc, preservesPartitioning. Dies ist für die Performance von entscheidender Bedeutung.

0
ragazzojp

Hier ist meine Lösung:

Ich verwende SLF4j (mit Log4j-Bindung), In meiner Basisklasse für jeden Funkenjob, den ich ungefähr so ​​habe: 

import org.slf4j.LoggerFactory
val LOG = LoggerFactory.getLogger(getClass) 

Kurz vor dem Ort, an dem ich LOG in verteiltem Funktionscode verwende, kopiere ich die Loggerreferenz in eine lokale Konstante.

val LOG = this.LOG

Es hat für mich funktioniert!

0
Thamme Gowda