diff --git a/.gitignore b/.gitignore index f69eeb62..a34f981e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,7 @@ target .classpath .project *~ +*.tmproj +.scala_dependencies integration/bundle/ -integration/felix-cache/ \ No newline at end of file +integration/felix-cache/ diff --git a/pom.xml b/pom.xml index 3ec46bd7..13da3855 100644 --- a/pom.xml +++ b/pom.xml @@ -35,6 +35,7 @@ slf4j-api + slf4j-scala-api slf4j-simple slf4j-nop slf4j-jdk14 @@ -349,4 +350,4 @@ - \ No newline at end of file + diff --git a/slf4j-scala-api/pom.xml b/slf4j-scala-api/pom.xml new file mode 100644 index 00000000..c77a33cc --- /dev/null +++ b/slf4j-scala-api/pom.xml @@ -0,0 +1,175 @@ + + + + org.slf4j + slf4j-parent + 1.6.1 + + + 4.0.0 + + org.slf4j + slf4j-scala-api + jar + SLF4J Scala API Module + + http://www.slf4j.org + The slf4j Scala API + + + UTF-8 + 2.8.0 + + + + + org.slf4j + slf4j-api + + + org.scala-lang + scala-library + ${scala.version} + + + org.scala-tools.testing + specs_2.8.0 + 1.6.5 + test + + + org.mockito + mockito-all + 1.8.4 + test + + + junit + junit + 4.7 + test + + + org.slf4j + slf4j-simple + ${project.version} + test + + + + + + + + org.scala-tools + maven-scala-plugin + 2.14.1 + + ${scala.version} + ${project.build.sourceEncoding} + + -Xmx1024m + + + -make:changed + -deprecation + -unchecked + + + + + + compile + testCompile + + + + + + + maven-surefire-plugin + 2.4.3 + + + **/*Spec.java + **/*Test.java + + + + + + + + + + + + + + + + + org.codehaus.mojo + clirr-maven-plugin + + 1.5.6 + + + + + + \ No newline at end of file diff --git a/slf4j-scala-api/src/main/scala/Logging.scala b/slf4j-scala-api/src/main/scala/Logging.scala new file mode 100644 index 00000000..a67c7177 --- /dev/null +++ b/slf4j-scala-api/src/main/scala/Logging.scala @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2010 Weigle Wilczek GmbH + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package org.slf4j.scala + +import org.slf4j.LoggerFactory + +/** + * Mixin providing a Logger for the type mixed into. + */ +trait Logging { + + /** + * Logger for the type mixed into. + */ + protected[scala] lazy val logger = Logger(this.getClass) +} diff --git a/slf4j-scala-api/src/main/scala/logger.scala b/slf4j-scala-api/src/main/scala/logger.scala new file mode 100644 index 00000000..1503e95f --- /dev/null +++ b/slf4j-scala-api/src/main/scala/logger.scala @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2010 Weigle Wilczek GmbH + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package org.slf4j.scala + +import org.slf4j.{ Logger => SLF4JLogger, LoggerFactory } + +/** + * Factory for Loggers. + */ +object Logger { + + /** + * Creates a Logger named corresponding to the given class. + * @param clazz Class used for the Logger's name. Must not be null! + */ + def apply(clazz: Class[_]) = { + require(clazz != null, "clazz must not be null!") + new DefaultLogger(LoggerFactory getLogger clazz) + } + + /** + * Creates a Logger with the given name. + * @param name The Logger's name. Must not be null! + */ + def apply(name: String) = { + require(name != null, "loggerName must not be null!") + new DefaultLogger(LoggerFactory getLogger name) + } +} + +/** + * Thin wrapper for SLF4J making use of by-name parameters to improve performance. + */ +trait Logger { + + /** + * The name of this Logger. + */ + lazy val name = slf4jLogger.getName + + /** + * Log a message with ERROR level. + * @param msg The message to be logged + */ + def error(msg: => String) { + if (slf4jLogger.isErrorEnabled) slf4jLogger error msg + } + + /** + * Log a message with ERROR level. + * @param msg The message to be logged + * @param t The Throwable to be logged + */ + def error(msg: => String, t: Throwable) { + if (slf4jLogger.isErrorEnabled) slf4jLogger.error(msg, t) + } + + /** + * Log a message with WARN level. + * @param msg The message to be logged + */ + def warn(msg: => String) { + if (slf4jLogger.isWarnEnabled) slf4jLogger warn msg + } + + /** + * Log a message with WARN level. + * @param msg The message to be logged + * @param t The Throwable to be logged + */ + def warn(msg: => String, t: Throwable) { + if (slf4jLogger.isWarnEnabled) slf4jLogger.warn(msg, t) + } + + /** + * Log a message with INFO level. + * @param msg The message to be logged + */ + def info(msg: => String) { + if (slf4jLogger.isInfoEnabled) slf4jLogger info msg + } + + /** + * Log a message with INFO level. + * @param msg The message to be logged + * @param t The Throwable to be logged + */ + def info(msg: => String, t: Throwable) { + if (slf4jLogger.isInfoEnabled) slf4jLogger.info(msg, t) + } + + /** + * Log a message with DEBUG level. + * @param msg The message to be logged + */ + def debug(msg: => String) { + if (slf4jLogger.isDebugEnabled) slf4jLogger debug msg + } + + /** + * Log a message with DEBUG level. + * @param msg The message to be logged + * @param t The Throwable to be logged + */ + def debug(msg: => String, t: Throwable) { + if (slf4jLogger.isDebugEnabled) slf4jLogger.debug(msg, t) + } + + /** + * Log a message with TRACE level. + * @param msg The message to be logged + */ + def trace(msg: => String) { + if (slf4jLogger.isTraceEnabled) slf4jLogger trace msg + } + + /** + * Log a message with TRACE level. + * @param msg The message to be logged + * @param t The Throwable to be logged + */ + def trace(msg: => String, t: Throwable) { + if (slf4jLogger.isTraceEnabled) slf4jLogger.trace(msg, t) + } + + /** + * The wrapped SLF4J Logger. + */ + protected val slf4jLogger: SLF4JLogger +} + +private[scala] class DefaultLogger(override protected val slf4jLogger: SLF4JLogger) extends Logger diff --git a/slf4j-scala-api/src/test/scala/LoggerSpec.scala b/slf4j-scala-api/src/test/scala/LoggerSpec.scala new file mode 100644 index 00000000..e48fd132 --- /dev/null +++ b/slf4j-scala-api/src/test/scala/LoggerSpec.scala @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2010 Weigle Wilczek GmbH + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package org.slf4j.scala + +import org.slf4j.{ Logger => SLF4JLogger } +import org.specs.SpecificationWithJUnit +import org.specs.mock.Mockito + +class LoggerSpec extends SpecificationWithJUnit with Mockito { + + "Creating a Logger using Logger(clazz: Class[_])" should { + + "return a Logger namend like the given class" in { + val clazz = classOf[String] + Logger(clazz).name mustEqual clazz.getName + } + + "throw an IAE when creating a Logger with a null class" in { + Logger(null: Class[_]) must throwA[IllegalArgumentException] + } + } + + "Creating a Logger using Logger(name: String)" should { + + "return a Logger namend like the given name" in { + val name = "MyLogger" + Logger(name).name mustEqual name + } + + "throw an IAE when creating a Logger with a null String" in { + Logger(null: String) must throwA [IllegalArgumentException] + } + } + + "Calling Logger.error(msg)" should { + val (logger, slf4jLogger) = loggers + var evaluated = false + def msg = { + evaluated = true + Msg + } + + "not call SLF4JLogger.error when error not enabled" in { + slf4jLogger.isErrorEnabled returns false + logger error msg + there was no(slf4jLogger).error(Msg) + evaluated mustBe false + } + + "call SLF4JLogger.error when error enabled" in { + slf4jLogger.isErrorEnabled returns true + logger error msg + there was one(slf4jLogger).error(Msg) + evaluated mustBe true + } + } + + "Calling Logger.error(msg, t)" should { + val (logger, slf4jLogger) = loggers + var evaluated = false + def msg = { + evaluated = true + Msg + } + + "not call SLF4JLogger.error when error not enabled" in { + slf4jLogger.isErrorEnabled returns false + logger.error(msg, t) + there was no(slf4jLogger).error(Msg, t) + evaluated mustBe false + } + + "call SLF4JLogger.error when error enabled" in { + slf4jLogger.isErrorEnabled returns true + logger.error(msg, t) + there was one(slf4jLogger).error(Msg ,t) + evaluated mustBe true + } + } + + "Calling Logger.warn(msg)" should { + val (logger, slf4jLogger) = loggers + var evaluated = false + def msg = { + evaluated = true + Msg + } + + "not call SLF4JLogger.warn when warn not enabled" in { + slf4jLogger.isWarnEnabled returns false + logger warn msg + there was no(slf4jLogger).warn(Msg) + evaluated mustBe false + } + + "call SLF4JLogger.warn when warn enabled" in { + slf4jLogger.isWarnEnabled returns true + logger warn msg + there was one(slf4jLogger).warn(Msg) + evaluated mustBe true + } + } + + "Calling Logger.warn(msg, t)" should { + val (logger, slf4jLogger) = loggers + var evaluated = false + def msg = { + evaluated = true + Msg + } + + "not call SLF4JLogger.warn when warn not enabled" in { + slf4jLogger.isErrorEnabled returns false + logger.warn(msg, t) + there was no(slf4jLogger).warn(Msg, t) + evaluated mustBe false + } + + "call SLF4JLogger.warn when warn enabled" in { + slf4jLogger.isWarnEnabled returns true + logger.warn(msg, t) + there was one(slf4jLogger).warn(Msg ,t) + evaluated mustBe true + } + } + + "Calling Logger.info(msg)" should { + val (logger, slf4jLogger) = loggers + var evaluated = false + def msg = { + evaluated = true + Msg + } + + "not call SLF4JLogger.info when info not enabled" in { + slf4jLogger.isInfoEnabled returns false + logger info msg + there was no(slf4jLogger).info(Msg) + evaluated mustBe false + } + + "call SLF4JLogger.info when info enabled" in { + slf4jLogger.isInfoEnabled returns true + logger info msg + there was one(slf4jLogger).info(Msg) + evaluated mustBe true + } + } + + "Calling Logger.info(msg, t)" should { + val (logger, slf4jLogger) = loggers + var evaluated = false + def msg = { + evaluated = true + Msg + } + + "not call SLF4JLogger.info when info not enabled" in { + slf4jLogger.isInfoEnabled returns false + logger.info(msg, t) + there was no(slf4jLogger).info(Msg, t) + evaluated mustBe false + } + + "call SLF4JLogger.info when info enabled" in { + slf4jLogger.isInfoEnabled returns true + logger.info(msg, t) + there was one(slf4jLogger).info(Msg, t) + evaluated mustBe true + } + } + + "Calling Logger.debug(msg)" should { + val (logger, slf4jLogger) = loggers + var evaluated = false + def msg = { + evaluated = true + Msg + } + + "not call SLF4JLogger.debug when debug not enabled" in { + slf4jLogger.isDebugEnabled returns false + logger debug msg + there was no(slf4jLogger).debug(Msg) + evaluated mustBe false + } + + "call SLF4JLogger.debug when debug enabled" in { + slf4jLogger.isDebugEnabled returns true + logger debug msg + there was one(slf4jLogger).debug(Msg) + evaluated mustBe true + } + } + + "Calling Logger.debug(msg ,t)" should { + val (logger, slf4jLogger) = loggers + var evaluated = false + def msg = { + evaluated = true + Msg + } + + "not call SLF4JLogger.debug when debug not enabled" in { + slf4jLogger.isDebugEnabled returns false + logger.debug(msg, t) + there was no(slf4jLogger).debug(Msg, t) + evaluated mustBe false + } + + "call SLF4JLogger.debug when debug enabled" in { + slf4jLogger.isDebugEnabled returns true + logger.debug(msg, t) + there was one(slf4jLogger).debug(Msg, t) + evaluated mustBe true + } + } + + "Calling Logger.trace(msg)" should { + val (logger, slf4jLogger) = loggers + var evaluated = false + def msg = { + evaluated = true + Msg + } + + "not call SLF4JLogger.trace when trace not enabled" in { + slf4jLogger.isTraceEnabled returns false + logger trace msg + there was no(slf4jLogger).trace(Msg) + evaluated mustBe false + } + + "call SLF4JLogger.trace when trace enabled" in { + slf4jLogger.isTraceEnabled returns true + logger trace msg + there was one(slf4jLogger).trace(Msg) + evaluated mustBe true + } + } + + "Calling Logger.trace(msg, t)" should { + val (logger, slf4jLogger) = loggers + var evaluated = false + def msg = { + evaluated = true + Msg + } + + "not call SLF4JLogger.trace when trace not enabled" in { + slf4jLogger.isTraceEnabled returns false + logger.trace(msg, t) + there was no(slf4jLogger).trace(Msg, t) + evaluated mustBe false + } + + "call SLF4JLogger.trace when trace enabled" in { + slf4jLogger.isTraceEnabled returns true + logger.trace(msg, t) + there was one(slf4jLogger).trace(Msg, t) + evaluated mustBe true + } + } + + private lazy val Msg = "MESSAGE" + + private lazy val t = new Throwable + + private def loggers = { + val mockSLF4JLogger = mock[SLF4JLogger] + val logger = new Logger { + override protected val slf4jLogger = mockSLF4JLogger + } + (logger, mockSLF4JLogger) + } +} diff --git a/slf4j-scala-api/src/test/scala/LoggingSpec.scala b/slf4j-scala-api/src/test/scala/LoggingSpec.scala new file mode 100644 index 00000000..b4599e07 --- /dev/null +++ b/slf4j-scala-api/src/test/scala/LoggingSpec.scala @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2010 Weigle Wilczek GmbH + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package org.slf4j.scala + +import org.specs.SpecificationWithJUnit + +class LoggingSpec extends SpecificationWithJUnit { + + "Mixing Logging into SomeClass" should { + "yield a Logger named to SomeClass's FQCN" in { + val someClass = new SomeClass + someClass.logger.name mustEqual classOf[SomeClass].getName + } + } +} + +class SomeClass extends Logging