AWS Lambda を Scala で書く際、ロガーとして Log4j2 を使った。
AWS Lambda の公式ドキュメントにも載っていたので 。
が、実行してみようと sbt-assembly で jar をつくろうとしたら以下のエラー。
$ sbt assembly
:
[error] 1 error was encountered during merge
[error] stack trace is suppressed; run last assembly for the full output
[error] (assembly) deduplicate: different file contents found in the following:
[error] /Users/hogehoge/.coursier/cache/v1/https/repo1.maven.org/maven2/com/amazonaws/aws-lambda-java-log4j2/1.2.0/aws-lambda-java-log4j2-1.2.0.jar:META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat
[error] /Users/hogehoge/.coursier/cache/v1/https/repo1.maven.org/maven2/org/apache/logging/log4j/log4j-core/2.13.3/log4j-core-2.13.3.jar:META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat
Log4j2Plugins.dat
というファイルが重複。
このファイルが何なのかよくわからなかったので、まず安直に discard してみた。
assemblyMergeStrategy in assembly := {
case x if x.endsWith("/Log4j2Plugins.dat") => MergeStrategy.discard
:
これで jar はつくれたのだが、実行すると Log4j2 でログを出している部分で、適切なロガーが見つからないとエラーになった。
ERROR Error processing element Lambda ([Appenders: null]): CLASS_NOT_FOUND
ERROR Unable to locate appender "Lambda" for logger config "root"
ので、discard じゃない方法で解決する必要がありそうなのだが、
そもそも何が起きてるの? Log4j2Plugins.dat
ってなんなの?
ってあたりがここに載っていた。
Fix appender not found / class_not_found error for Log4j2 / SpringBoot
どうやら Log4j2 が適切に動くためには Log4j2Plugins.dat
は必要そうだ。
さらにいろいろ調べると、 org.apache.logging.log4j.core.config.plugins.processor.PluginCache
に関係してそうで、
そこらへんをうまいことやっている以下に辿り着いた。
mobile-n10n/MergeLog4j2PluginCachesStrategy.scala at master · guardian/mobile-n10n
project 直下に以下の Strategy をつくる。
import java.io.ByteArrayOutputStream
import java.nio.file.Files
import org.apache.logging.log4j.core.config.plugins.processor.PluginCache
import sbt.File
import sbtassembly.MergeStrategy
import scala.collection.JavaConverters.asJavaEnumerationConverter
class MergeLog4j2PluginCachesStrategy extends MergeStrategy {
override def name: String = "MergeLog4j2PluginCachesStrategy"
override def apply(tempDir: File, path: String, files: Seq[File]): Either[String, Seq[(File, String)]] = {
val pluginCache = new PluginCache
pluginCache.loadCacheFiles(files.map(_.toURI.toURL).toIterator.asJavaEnumeration)
val baos = new ByteArrayOutputStream // avoid worrying about closeable resources
pluginCache.writeCache(baos)
val mergeFile = MergeStrategy.createMergeTarget(tempDir, path)
Files.write(mergeFile.toPath, baos.toByteArray)
Right(Seq(mergeFile -> path))
}
}
で、
https://github.com/guardian/mobile-n10n/blob/f9c6989e360b4bba47763ba84663dadc5f7cf613/build.sbt#L246
にあるように、
build.sbt
の assemblyMergeStrategy
で META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat
に対して適用する。
assemblyMergeStrategy in assembly := {
:
case "META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat" => new MergeLog4j2PluginCachesStrategy
:
これでバッチリ動いた。