【问题标题】:Duplicate logs from databricks notebook with custom log4j appender使用自定义 log4j appender 从 databricks 笔记本中复制日志
【发布时间】: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


【解决方案1】:

根据上述 Levi Ramsey 的评论,更改:

Logger.getRootLogger().addAppender(new SlackAppender());

到:

new SlackAppender().initializeAppender()

initializeAppender() 在哪里:

  def initializeAppender() {
    val rootLogger = Logger.getRootLogger

    val en: util.Enumeration[_] = rootLogger.getAllAppenders()
    while ( {en.hasMoreElements}) {
      val appender: Any = en.nextElement
      if (appender.isInstanceOf[SlackAppender]) {
        val slackAppender: Appender = appender.asInstanceOf[Appender]
        rootLogger.removeAppender(slackAppender)
      }
    }
    rootLogger.addAppender(new SlackAppender)

  }

仍在测试,但这似乎是一个可能的答案。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-10-10
    • 1970-01-01
    • 1970-01-01
    • 2020-08-19
    • 1970-01-01
    • 1970-01-01
    • 2014-05-20
    • 1970-01-01
    相关资源
    最近更新 更多