web-dev-qa-db-fra.com

Apache Spark se connectant dans Scala

Je cherche une solution pour pouvoir enregistrer des données supplémentaires lors de l’exécution de code sur des nœuds Apache Spark, ce qui pourrait permettre d’examiner ultérieurement certains problèmes susceptibles de survenir lors de l’exécution. Essayer d'utiliser une solution traditionnelle telle que par exemple com.typesafe.scalalogging.LazyLogging échoue car l'instance de journal ne peut pas être sérialisée sur un environnement distribué tel qu'Apache Spark.

J'ai étudié ce problème et pour l'instant la solution que j'ai trouvée consistait à utiliser le trait org.Apache.spark.Logging comme ceci:

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

Cependant, il semble que le trait de journalisation ne soit pas une solution permanente pour Apache Spark, car il est marqué comme @DeveloperApi et la documentation de la classe mentionne: 

Cela sera probablement modifié ou supprimé dans les prochaines versions.

Je me demande si je peux utiliser une solution de journalisation connue qui me permettra de consigner des données lorsque les RDD seront exécutés sur des nœuds Apache Spark.

@Later Edit : Certains des commentaires ci-dessous suggèrent d'utiliser Log4J. J'ai essayé d'utiliser Log4J mais je rencontre toujours des problèmes lors de l'utilisation d'un enregistreur d'une classe Scala (et non d'un objet Scala) . Voici mon code complet:

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()
 }

}

L'exception que je vois est:

Exception dans le fil "principal" org.Apache.spark.SparkException: tâche non sérialisable -> Causée par: Java.io.NotSerializableException: org.Apache.log4j.Logger

45
Bogdan N

Vous pouvez utiliser la solution proposée par Akhil dans
https://www.mail-archive.com/[email protected]/msg29010.html . Je me suis servi de moi-même et ça marche. 

Akhil Das lun., 25 mai 2015 08:20:40 -0700
Essayez de cette façon:

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

Utilisez Log4j 2.x. L'enregistreur de base a été rendu sérialisable. Problème résolu.

Discussion Jira: 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),

Vous pouvez utiliser "log" pour écrire des journaux. De plus, si vous avez besoin de modifier les propriétés du consignateur, vous devez avoir le fichier log4j.properties dans le dossier/conf. Par défaut, nous aurons un modèle à cet endroit.

1
Venkata Karthik

Ceci est un vieux post mais je veux fournir ma solution de travail que je viens de recevoir après avoir beaucoup lutté et qui peut toujours être utile pour d'autres:

Je veux imprimer le contenu de rdd dans la fonction rdd.map mais en obtenant Task Not Serializalable Error. Voici ma solution à ce problème en utilisant un objet statique scala qui étend Java.io.Serializable:

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

Si vous avez besoin que du code soit exécuté avant et après une fonction map, filter ou une autre fonction RDD, essayez d'utiliser mapPartition, où l'itérateur sous-jacent est passé explicitement.

Exemple:

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

Devient:

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

Chaque fonction RDD de base est toujours implémentée avec une mapPartition.

Assurez-vous de manipuler le partitionneur de manière explicite et de ne pas le perdre: voir Scaladoc, paramètre preservesPartitioning, il est essentiel pour les performances.

0
ragazzojp

Voici ma solution:

J'utilise SLF4j (avec la liaison Log4j), Dans ma classe de base de chaque job d'allumage, j'ai quelque chose comme ceci: 

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

Juste avant l'endroit où j'utilise LOG dans le code fonctionnel distribué, je copie la référence de l'enregistreur dans une constante locale.

val LOG = this.LOG

Cela a fonctionné pour moi!

0
Thamme Gowda