【发布时间】:2020-12-07 00:14:25
【问题描述】:
我创建了一个自定义 log4j 附加程序,用于在 databricks 中将作业错误记录到 slack:
package aaa.bbb.slackAppender
import org.apache.http.client.methods.{CloseableHttpResponse, HttpPost}
import org.apache.http.entity.StringEntity
import org.apache.http.impl.client.HttpClientBuilder
import org.apache.http.util.EntityUtils
import org.apache.log4j.{AppenderSkeleton, Level}
import org.apache.log4j.spi.LoggingEvent
import org.json.simple.JSONValue
class SlackAppender extends AppenderSkeleton {
override def append(event: LoggingEvent): Unit = {
val level = event.getLevel
if (level == Level.ERROR) {
val escaped = JSONValue.escape(event.getMessage.toString)
val escapedMessage = "{\"text\":\"" + escaped + "\"}"
val post = new HttpPost("https://hooks.slack.com/services/XXXX/YYYY/ZZZZ")
post.setHeader("Content-type", "application/json")
post.setEntity(new StringEntity(escapedMessage))
val client = HttpClientBuilder.create.build
val response:CloseableHttpResponse = client.execute(post)
val entity = response.getEntity
val str = EntityUtils.toString(entity,"UTF-8")
println("Error post response code is " + str)
}
}
override def close(): Unit = {}
override def requiresLayout(): Boolean = true
}
从这样的笔记本或作业运行时,它基本上可以工作:
import org.apache.log4j.{LogManager, Level, Logger}
import org.apache.commons.logging.LogFactory
import aaa.bbb.slackAppender.SlackAppender
Logger.getRootLogger().addAppender(new SlackAppender());
val log = LogFactory.getLog("misc-test-log")
log.error("errorA")
log.error("errorB")
log.error("errorC")
但是,当从笔记本运行时,如果我已经运行笔记本 N 次,它会复制日志 N 次。换句话说,在我在 Slack 中看到的第一次运行之后:
errorA
errorB
errorC
运行 5 次后,我看到如下内容:
errorA
errorA
errorA
errorA
errorA
errorB
errorB
errorB
errorB
errorB
errorC
errorC
errorC
errorC
errorC
似乎日志以某种方式保存并每次重新记录。谢天谢地,当我将笔记本作为作业运行时,我没有看到同样的情况。
有谁知道为什么会发生这种情况和/或我怎样才能让它停止?
【问题讨论】:
-
你知道是否有任何其他副作用重复吗?笔记本通常因奇怪的执行语义而臭名昭著,只有在出现副作用时才会直接出现。
-
@LeviRamsey 我不知道任何其他副作用,但我确实发现每次调用 rootLogger.addAppender(new SlackAppender) 都会添加另一个附加程序!我不确定这是副作用还是正常的笔记本行为。
-
虽然我不能说我对笔记本(或 log4j,就此而言)太熟悉了,但这听起来确实像我在笔记本中听到的那种奇怪的执行语义。有两种可能性:如果您可以获取已添加到
rootLogger的附加程序,则可以防止再次添加附加程序。或者,如果您将SlackAppender设为object(即单例),则可以对其进行重复数据删除日志(保留最后几个events的缓冲区,如果重复则不要登录到 slack)。 -
当然,如果 notebook 跨多个 JVM 执行代码,那么这两种方法都行不通。
标签: scala log4j databricks appender