如何从Python Spark脚本登录
我有一个Python Spark程序,我使用如何从Python Spark脚本登录,python,logging,apache-spark,Python,Logging,Apache Spark,我有一个Python Spark程序,我使用Spark submit运行该程序。我想把日志记录语句放进去 logging.info("This is an informative message.") logging.debug("This is a debug message.") 我想使用Spark使用的相同记录器,以便日志消息以相同的格式显示,并且级别由相同的配置文件控制。我该怎么做 我尝试将logging语句放入代码中,并从logging.getLogger()开始。在这两种情况下,我
Spark submit
运行该程序。我想把日志记录语句放进去
logging.info("This is an informative message.")
logging.debug("This is a debug message.")
我想使用Spark使用的相同记录器,以便日志消息以相同的格式显示,并且级别由相同的配置文件控制。我该怎么做
我尝试将logging
语句放入代码中,并从logging.getLogger()
开始。在这两种情况下,我都能看到Spark的日志消息,但我看不到。我一直在看这张照片,但没能从那里弄清楚
不确定这是否是特定于提交给Spark的脚本的内容,或者只是我不了解日志记录的工作原理。您需要为Spark本身获取记录器,默认情况下,
getLogger()
将为您自己的模块返回记录器。尝试以下方法:
logger = logging.getLogger('py4j')
logger.info("My test info statement")
它也可能是'pyspark'
而不是'py4j'
如果您在spark程序中使用的函数(并执行一些日志记录)与主函数定义在同一个模块中,则会出现一些序列化错误
本文对此进行了解释,并给出了同一个人的一个例子
我还在spark 1.3.1上测试了这个
编辑:
要将日志记录从STDERR
更改为STDOUT
,必须删除当前的StreamHandler
,然后添加一个新的
查找现有的流处理程序(完成后可以删除此行)
为sys.stdout
import sys # Put at top if not already there
sh = logging.StreamHandler(sys.stdout)
sh.setLevel(logging.DEBUG)
logger.addHandler(sh)
您可以从SparkContext对象获取记录器:
log4jLogger = sc._jvm.org.apache.log4j
LOGGER = log4jLogger.LogManager.getLogger(__name__)
LOGGER.info("pyspark script logger initialized")
在我的例子中,我很高兴将我的日志消息添加到workers stderr中,以及通常的spark日志消息 如果这符合您的需要,那么诀窍就是将特定的Python记录器重定向到
stderr
例如,以下灵感来源于,非常适合我:
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
用法:
def tst_log():
logger = getlogger('my-worker')
logger.debug('a')
logger.info('b')
logger.warning('c')
logger.error('d')
logger.critical('e')
...
输出(加上上下文中的几行):
pyspark与javalog4j交互的关键是jvm。 下面是python代码,conf缺少url,但这是关于日志记录的
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")
我们需要从执行者而不是从驱动程序节点进行日志记录。因此,我们采取了以下措施:
def emit(self, record):
"""Forward a log message for log4j."""
Logger = self.spark_session._jvm.org.apache.log4j.Logger
logger = Logger.getLogger(record.name)
if record.levelno >= logging.CRITICAL:
# Fatal and critical seem about the same.
logger.fatal(record.getMessage())
elif record.levelno >= logging.ERROR:
logger.error(record.getMessage())
elif record.levelno >= logging.WARNING:
logger.warn(record.getMessage())
elif record.levelno >= logging.INFO:
logger.info(record.getMessage())
elif record.levelno >= logging.DEBUG:
logger.debug(record.getMessage())
else:
pass
/etc/rsyslog.d/spark.conf
(使用Amazon Elastic Map Reduce的引导方法),以便核心节点将syslog
local1`消息转发给主节点local
消息记录到/var/log/local1.log
logging
模块Syslog记录器logging.info()
登录了map
函数让我们了解了很多Spark的工作原理
import logging
# Logger
logging.basicConfig(format='%(asctime)s %(filename)s %(funcName)s %(lineno)d %(message)s')
logger = logging.getLogger('driver_logger')
logger.setLevel(logging.DEBUG)
从pyspark登录的最简单方法!您可以在一个类中实现
logging.Handler
接口,该类将日志消息转发到Spark下的log4j。然后使用logging.root.addHandler()
(以及可选的logging.root.removeHandler()
)安装该处理程序
处理程序应具有如下方法:
def emit(self, record):
"""Forward a log message for log4j."""
Logger = self.spark_session._jvm.org.apache.log4j.Logger
logger = Logger.getLogger(record.name)
if record.levelno >= logging.CRITICAL:
# Fatal and critical seem about the same.
logger.fatal(record.getMessage())
elif record.levelno >= logging.ERROR:
logger.error(record.getMessage())
elif record.levelno >= logging.WARNING:
logger.warn(record.getMessage())
elif record.levelno >= logging.INFO:
logger.info(record.getMessage())
elif record.levelno >= logging.DEBUG:
logger.debug(record.getMessage())
else:
pass
初始化Spark会话后,应立即安装处理程序:
spark = SparkSession.builder.appName("Logging Example").getOrCreate()
handler = CustomHandler(spark_session)
# Replace the default handlers with the log4j forwarder.
root_handlers = logging.root.handlers[:]
for h in self.root_handlers:
logging.root.removeHandler(h)
logging.root.addHandler(handler)
# Now you can log stuff.
logging.debug("Installed log4j log handler.")
这里有一个更完整的例子:我是否必须将
logger
对象作为参数传递给所有使用它的组件?是否有某种方法可以全局设置它?只要您不执行线程或多处理,就应该能够在模块顶部设置它,并在任何地方使用它。只需更改日志即可。
到记录器。
任何时候你要记录一些东西。谢谢。它是这样工作的。但消息总是传到stderr。我们如何才能直接到stdout或stderr?我更新了我的答案,为你解决这个问题。可能有一种方法可以更新现有的StreamHandler,我不确定,但以上是我知道如何做的。我很想否决投票这个答案是因为它不适用于我。通过pyspark源代码查看,pyspark从未配置py4j记录器,py4j使用java.utils.logging而不是spark使用的log4j记录器,因此我怀疑这是否可行。我认为这可能适用于主节点上的代码,但不适用于在t上运行的任何东西他是工人。我明白了问题:logger=logging.getLogger('py4j')TypeError:'JavaPackage'对象不可调用这绝对允许我像Spark那样进行日志记录(谢谢!)。除了从SparkContext获取日志外,还有其他方法获取此日志吗?我有一些日志必须在生成SparkContext之前打印created@marlieg在创建spark上下文之前,您无法访问spark日志记录。我在PySpark中尝试使用此想法时出错。我所做的是尝试将记录器存储为全局日志记录,但没有我的用例能够在foreach函数中对我的执行器进行日志调用(在foreach函数中没有spark上下文)。异常:您似乎试图从广播变量、操作或转换引用SparkContext。SparkContext只能在驱动程序上使用,不能在工作程序上运行的代码中使用。有关详细信息,请参阅SPARK-5063。“我可以这样做,但无法确定日志存储在哪里,有人能帮我吗?在getlogger函数中导入日志和导入系统有什么特殊原因吗?这在工作人员中不起作用…似乎只在驱动程序中起作用。您可能看不到您的日志记录。”
def emit(self, record):
"""Forward a log message for log4j."""
Logger = self.spark_session._jvm.org.apache.log4j.Logger
logger = Logger.getLogger(record.name)
if record.levelno >= logging.CRITICAL:
# Fatal and critical seem about the same.
logger.fatal(record.getMessage())
elif record.levelno >= logging.ERROR:
logger.error(record.getMessage())
elif record.levelno >= logging.WARNING:
logger.warn(record.getMessage())
elif record.levelno >= logging.INFO:
logger.info(record.getMessage())
elif record.levelno >= logging.DEBUG:
logger.debug(record.getMessage())
else:
pass
spark = SparkSession.builder.appName("Logging Example").getOrCreate()
handler = CustomHandler(spark_session)
# Replace the default handlers with the log4j forwarder.
root_handlers = logging.root.handlers[:]
for h in self.root_handlers:
logging.root.removeHandler(h)
logging.root.addHandler(handler)
# Now you can log stuff.
logging.debug("Installed log4j log handler.")