To demonstrate my problem, imagine we have a simple app with a top level package com.foo, and subpackages for ui (com.foo.ui) and server (com.foo.server). I have not been able to cook up a log4j properties file which will ensure the following:
* all ERROR level logging statements in packages including and descending from com.foo are
captured in a separate file under logs/main.log
* any and all messages at any levels (DEBUG,TRACE,INFO,WARN,ERROR) go to both stdout and the file appender.
Below are two log4j configurations I have tried and all the code for this toy app. The challenge I have when running the toy app is that the logs/main.log contains messages at all levels, not just messages at ERROR level.
First attempt
#Define root logger options
log4j.rootLogger=TRACE, file, stdout
#Define stdout appender
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
logrj.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%-5p %c{1} - %m%n
#Define rolling file appender
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=logs/main.log
log4j.appender.file.Append=true
log4j.appender.file.ImmediateFlush=true
log4j.appender.file.MaxFileSize=10MB
log4j.appender.file.MaxBackupIndex=5
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d %d{Z} [%t] %-5p (%F:%L) - %m%n
log4j.appender.error-log.filter.b=org.apache.log4j.varia.LevelMatchFilter
log4j.appender.error-log.filter.LevelToMatch = ERROR
log4j.appender.error-log.filter.AcceptOnMatch = true
log4j.appender.error-log.Threshold = ERROR
#Define loggers
log4j.logger.com.foo.log4j=TRACE, file, stdout
Second attempt
#Define root logger options
log4j.rootLogger=TRACE, file, stdout
#Define stdout appender
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
logrj.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%-5p %c{1} - %m%n
#Define rolling file appender
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=logs/main.log
log4j.appender.file.Append=true
log4j.appender.file.ImmediateFlush=true
log4j.appender.file.MaxFileSize=10MB
log4j.appender.file.MaxBackupIndex=5
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d %d{Z} [%t] %-5p (%F:%L) - %m%n
log4j.appender.error-log.filter.b=org.apache.log4j.varia.LevelRangeFilter
log4j.appender.error-log.filter.LevelMin = ERROR
log4j.appender.error-log.filter.LevelMax = ERROR
log4j.appender.error-log.filter.AcceptOnMatch = true
log4j.appender.error-log.Threshold = ERROR
#Define loggers
log4j.logger.com.foo.log4j=TRACE, file, stdout
Build dependencies
Here are the build dependencies i used:
plugins {
// Apply the scala plugin to add support for Scala
id 'scala'
// Apply the java-library plugin for API and implementation separation.
id 'java-library'
}
repositories {
// Use jcenter for resolving dependencies.
// You can declare any Maven/Ivy/file repository here.
jcenter()
}
dependencies {
// Use Scala 2.13 in our library project
implementation 'org.scala-lang:scala-library:2.13.2'
implementation 'org.apache.logging.log4j:log4j-api:2.17.0'
implementation 'org.apache.logging.log4j:log4j-core:2.17.0'
// Use Scalatest for testing our library
testImplementation 'junit:junit:null'
testImplementation 'org.scalatest:scalatest_2.13:3.1.2'
testImplementation 'org.scalatestplus:junit-4-12_2.13:3.1.2.0'
implementation 'log4j:log4j:1.2.17'
// Need scala-xml at test runtime
testRuntimeOnly 'org.scala-lang.modules:scala-xml_2.13:1.2.0'
}
Classes
And here are the three simple little classes in the App:
------------------------------------
package com.foo
import com.foo.server.Server
import com.foo.ui.UI
object Application extends App {
val server = Server()
val ui = new UI(server)
ui.message("hello")
}
------------------------------------
package com.foo.ui
import com.foo.server.Server
import org.apache.log4j.LogManager
class UI(server: Server) { // mine
val log = LogManager.getLogger(this.getClass)
def message(string: String): Unit = {
log.trace("ui got message " + string)
server.message(string)
}
}
------------------------------------
package com.foo.server
import org.apache.log4j.LogManager
case class Server() {
val log = LogManager.getLogger(this.getClass)
def message(string: String): Unit = {
log.info("server got message "+ "message")
log.error("server raised erorr on message "+ "message")
}
}