blob: 0d0ef5877b46d39aa198c14cff6b33f6f72e7496 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache license, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the license for the specific language governing permissions and
* limitations under the license.
*/
package org.apache.logging.log4j.kotlin
import com.nhaarman.mockitokotlin2.*
import org.apache.logging.log4j.Level
import org.apache.logging.log4j.MarkerManager
import org.apache.logging.log4j.junit.LoggerContextRule
import org.apache.logging.log4j.message.DefaultFlowMessageFactory
import org.apache.logging.log4j.message.MessageFactory2
import org.apache.logging.log4j.message.ParameterizedMessage
import org.apache.logging.log4j.message.ParameterizedMessageFactory
import org.apache.logging.log4j.spi.ExtendedLogger
import org.junit.Rule
import org.junit.Test
import org.mockito.ArgumentMatchers.anyString
import kotlin.test.assertFailsWith
import kotlin.test.assertTrue
data class Custom(val i: Int)
interface Manager {
fun fetchValue(): Int
}
typealias LoggerStubbingFn = KStubbing<ExtendedLogger>.() -> Unit
class LoggerTest {
companion object {
val msg = ParameterizedMessage("msg {}", 17)
val entryMsg = DefaultFlowMessageFactory().newEntryMessage(msg)
val cseqMsg: CharSequence = StringBuilder().append("cseq msg")
val objectMsg = Custom(17)
val cause = RuntimeException("cause")
val marker = MarkerManager.getMarker("marker")
val result = "foo"
val managerValue: Int = 4711
}
@Rule @JvmField var init = LoggerContextRule("InfoLogger.xml")
class Fixture(stubbing: LoggerStubbingFn? = null) {
val mockLogger = mock<ExtendedLogger> {
on { getMessageFactory<MessageFactory2>() } doReturn ParameterizedMessageFactory()
if(stubbing != null) stubbing()
}
val manager = mock<Manager> {
on { fetchValue() } doReturn managerValue
}
}
fun withFixture(stubbing: LoggerStubbingFn?, level: Level, returning: Boolean, block: Fixture.(KotlinLogger) -> Unit): Fixture {
val f = Fixture(stubbing)
whenever(f.mockLogger.isEnabled(level)).thenReturn(returning)
val logger = KotlinLogger(f.mockLogger)
block(f, logger)
return f
}
fun withLevelFixture(level: Level, returning: Boolean, block: Fixture.(KotlinLogger) -> Unit): Fixture {
return withFixture({
on { isEnabled(level) } doReturn returning
}, level, returning, block)
}
@Test
fun `Logging works!`() {
val f = withLevelFixture(Level.ERROR, true) {
it.error(result)
}
verify(f.mockLogger).logIfEnabled(anyString(), eq(Level.ERROR), isNull(), eq<CharSequence>(result), isNull())
}
@Test
fun `Level fatal enabled with String message`() {
val f = withLevelFixture(Level.FATAL, true) {
it.fatal("string msg with value: ${manager.fetchValue()}")
}
verify(f.mockLogger).logIfEnabled(anyString(), eq(Level.FATAL), isNull(), eq<CharSequence>("string msg with value: $managerValue"), isNull())
verify(f.manager).fetchValue()
}
@Test
fun `Level fatal disabled with String message`() {
val f = withLevelFixture(Level.FATAL, false) {
it.fatal("string msg with value: ${manager.fetchValue()}")
}
verify(f.mockLogger).logIfEnabled(anyString(), eq(Level.FATAL), isNull(), eq<CharSequence>("string msg with value: $managerValue"), isNull())
// one might expect times(0), but we just delegate so times(1), we don't have any extra macro-based logic like the Scala api
// use the lambda approach to not evaluate the message if the level is not enabled
verify(f.manager, times(1)).fetchValue()
}
@Test
fun `Lambda functions are evaluated if the level is high enough`() {
var count = 0
fun lamdaFun(): String {
count++
return result
}
val log = logger()
log.info { lamdaFun() }
assertTrue { count == 1 }
}
@Test
fun `Lambda functions are not evaluated if the level is low enough`() {
var count = 0
fun lamdaFun(): String {
count++
return result
}
val log = logger()
log.debug { lamdaFun() }
assertTrue { count == 0 }
}
@Test
fun `CharSequence messages are logged`() {
val f = withLevelFixture(Level.INFO, true) {
it.info(cseqMsg)
}
verify(f.mockLogger).logIfEnabled(anyString(), eq(Level.INFO), isNull(), eq(cseqMsg), isNull())
}
@Test
fun `Object messages are logged`() {
val f = withLevelFixture(Level.INFO, true) {
it.info(objectMsg)
}
verify(f.mockLogger).logIfEnabled(anyString(), eq(Level.INFO), isNull(), eq(objectMsg), isNull())
}
@Test
fun `Markers are logged`() {
val f = withLevelFixture(Level.INFO, true) {
it.info(marker, result)
}
verify(f.mockLogger).logIfEnabled(anyString(), eq(Level.INFO), eq(marker), eq<CharSequence>(result), isNull())
}
@Test
fun `Run in trace with no result`() {
var count = 0
val f = withLevelFixture(Level.INFO, true) {
it.runInTrace(entryMsg) {
++count
Unit
}
}
assertTrue { count == 1 }
verify(f.mockLogger).traceExit(eq(entryMsg))
}
@Test
fun `Run in trace with result`() {
var count = 0
val f = withLevelFixture(Level.INFO, true) {
it.runInTrace(entryMsg) {
++count
}
}
assertTrue { count == 1 }
verify(f.mockLogger).traceExit(eq(entryMsg), eq(1))
}
@Test
fun `Run in trace with Exception`() {
var count = 0
val f = withLevelFixture(Level.INFO, true) {
assertFailsWith<RuntimeException> {
it.runInTrace(entryMsg) {
++count
throw cause
}
}
}
assertTrue { count == 1 }
verify(f.mockLogger, times(0)).traceExit(any())
verify(f.mockLogger).catching(argThat { message == "cause" })
}
}