Дублирование журналов из блокнота блоков данных с пользовательским приложением log4j

Я создал собственный log4j appender для использования в блоках данных, чтобы регистрировать ошибки заданий в 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

Вроде логи как-то сохраняются и каждый раз перезаписываются. К счастью, я не вижу того же самого, когда запускаю блокнот в качестве задания.

Кто-нибудь знает, почему это происходит и / или как я могу остановить это?


person chrismead    schedule 17.08.2020    source источник
comment
Знаете ли вы, повторяются ли какие-либо другие побочные эффекты? Ноутбуки в целом печально известны странной семантикой выполнения, которая напрямую проявляется только при наличии побочных эффектов.   -  person Levi Ramsey    schedule 18.08.2020
comment
@LeviRamsey Я не знаю о каких-либо других побочных эффектах, но я обнаружил, что каждый вызов rootLogger.addAppender (новый SlackAppender) добавлял еще один аппендер! Я не уверен, является ли это побочным эффектом или нормальным поведением ноутбука.   -  person chrismead    schedule 18.08.2020
comment
Хотя я не могу сказать, что слишком хорошо знаком с блокнотами (или log4j, если на то пошло), это звучит как странная семантика выполнения, о которой я слышал в блокнотах. Есть несколько возможностей: Если вы можете получить, какие приложения вы уже добавили в rootLogger, вы можете предотвратить добавление приложения во второй раз. В качестве альтернативы, если вы сделаете SlackAppender object (т. е. синглтоном), вы можете сделать так, чтобы он дедуплицировал журналы (сохраняйте буфер последних нескольких events и не записывайте в слабину, если это дубликат).   -  person Levi Ramsey    schedule 18.08.2020
comment
Конечно, если блокнот выполняет код на нескольких JVM, ни один из этих подходов не сработает.   -  person Levi Ramsey    schedule 18.08.2020


Ответы (1)


Основываясь на комментарии Леви Рэмси выше, изменив:

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

to:

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)

  }

Все еще тестируется, но это кажется возможным ответом.

person chrismead    schedule 18.08.2020