blob: 880463c12e8724ca95db79ccc9726a1fc95c4a66 [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 groovy.util.logging
import ch.qos.logback.classic.Level
import ch.qos.logback.classic.Logger
import ch.qos.logback.classic.LoggerContext
import ch.qos.logback.classic.spi.LoggingEvent
import ch.qos.logback.core.OutputStreamAppender
import ch.qos.logback.core.layout.EchoLayout
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.slf4j.LoggerFactory
import java.lang.reflect.Modifier
import static groovy.test.GroovyAssert.assertScript
import static groovy.test.GroovyAssert.shouldFail
/**
* Tests for {@link groovy.util.logging.Slf4j Slf4j} AST transformation.
*/
final class Slf4jTest {
private static class LogbackInterceptingAppender<E> extends OutputStreamAppender<E> {
private List<LoggingEvent> events = []
List<LoggingEvent> getEvents() {
return events
}
protected void append(E event) {
if (event instanceof LoggingEvent) {
events.add(event)
super.append(event)
} else {
throw new RuntimeException('Unable to intercept logging events - probably API has changed')
}
}
}
private LogbackInterceptingAppender appender
private Logger logger
@Before
void setUp() {
appender = new LogbackInterceptingAppender()
appender.outputStream = new ByteArrayOutputStream()
appender.context = (LoggerContext) LoggerFactory.getILoggerFactory()
appender.layout = new EchoLayout()
appender.name = 'MyAppender'
appender.start()
logger = LoggerFactory.getLogger('MyClass')
logger.addAppender(appender)
logger.level = Level.ALL
}
@After
void tearDown() {
logger.detachAppender(appender)
}
@Test
void testPrivateFinalStaticLogFieldAppears() {
Class clazz = new GroovyClassLoader().parseClass('''
@groovy.util.logging.Slf4j
class MyClass {
}
''')
clazz.getDeclaredField('log').modifiers.with { int modifiers ->
assert Modifier.isPrivate(modifiers)
assert Modifier.isStatic(modifiers)
assert Modifier.isTransient(modifiers)
assert Modifier.isFinal(modifiers)
}
}
@Test
void testExplicitPrivateFinalStaticLogFieldAppears() {
Class clazz = new GroovyClassLoader().parseClass('''
import static groovy.transform.options.Visibility.*
@groovy.transform.VisibilityOptions(value = PRIVATE)
@groovy.util.logging.Slf4j
class MyClass {
}
''')
clazz.getDeclaredField('log').modifiers.with { int modifiers ->
assert Modifier.isPrivate(modifiers)
assert Modifier.isStatic(modifiers)
assert Modifier.isTransient(modifiers)
assert Modifier.isFinal(modifiers)
}
}
@Test
void testPackagePrivateFinalStaticLogFieldAppears() {
Class clazz = new GroovyClassLoader().parseClass('''
import static groovy.transform.options.Visibility.*
@groovy.transform.VisibilityOptions(value = PACKAGE_PRIVATE)
@groovy.util.logging.Slf4j
class MyClass {
}
''')
clazz.getDeclaredField('log').modifiers.with { int modifiers ->
assert !Modifier.isPrivate(modifiers)
assert !Modifier.isProtected(modifiers)
assert !Modifier.isPublic(modifiers)
assert Modifier.isStatic(modifiers)
assert Modifier.isTransient(modifiers)
assert Modifier.isFinal(modifiers)
}
}
@Test
void testProtectedFinalStaticLogFieldAppears() {
Class clazz = new GroovyClassLoader().parseClass('''
import static groovy.transform.options.Visibility.*
@groovy.transform.VisibilityOptions(value = PROTECTED)
@groovy.util.logging.Slf4j
class MyClass {
}
''')
clazz.getDeclaredField('log').modifiers.with { int modifiers ->
assert Modifier.isProtected(modifiers)
assert Modifier.isStatic(modifiers)
assert Modifier.isTransient(modifiers)
assert Modifier.isFinal(modifiers)
}
}
@Test
void testPublicFinalStaticLogFieldAppears() {
Class clazz = new GroovyClassLoader().parseClass('''
import static groovy.transform.options.Visibility.*
@groovy.transform.VisibilityOptions(value = PUBLIC)
@groovy.util.logging.Slf4j
class MyClass {
}
''')
clazz.getDeclaredField('log').modifiers.with { int modifiers ->
assert Modifier.isPublic(modifiers)
assert Modifier.isStatic(modifiers)
assert Modifier.isTransient(modifiers)
assert Modifier.isFinal(modifiers)
}
}
@Test
void testPrivateFinalStaticNamedLogFieldAppears() {
Class clazz = new GroovyClassLoader().parseClass('''
@groovy.util.logging.Slf4j('logger')
class MyClass {
}
''')
clazz.getDeclaredField('logger').modifiers.with { int modifiers ->
assert Modifier.isPrivate(modifiers)
assert Modifier.isStatic(modifiers)
assert Modifier.isTransient(modifiers)
assert Modifier.isFinal(modifiers)
}
}
@Test
void testClassAlreadyHasLogField() {
shouldFail '''
@groovy.util.logging.Slf4j
class MyClass {
String log
}
'''
}
@Test
void testClassAlreadyHasNamedLogField() {
shouldFail '''
@groovy.util.logging.Slf4j('logger')
class MyClass {
String logger
}
'''
}
@Test
void testLogInfo() {
Class clazz = new GroovyClassLoader().parseClass('''
@groovy.util.logging.Slf4j
class MyClass {
def loggingMethod() {
log.error ('error called')
log.warn ('warn called')
log.info ('info called')
log.debug ('debug called')
log.trace ('trace called')
}
}
new MyClass().loggingMethod()
''')
Script s = (Script) clazz.newInstance()
s.run()
def events = appender.events
int ind = 0
assert events.size() == 5
assert events[ind].level == Level.ERROR
assert events[ind].message == 'error called'
assert events[++ind].level == Level.WARN
assert events[ind].message == 'warn called'
assert events[++ind].level == Level.INFO
assert events[ind].message == 'info called'
assert events[++ind].level == Level.DEBUG
assert events[ind].message == 'debug called'
assert events[++ind].level == Level.TRACE
assert events[ind].message == 'trace called'
}
@Test
void testLogFromStaticMethods() {
Class clazz = new GroovyClassLoader().parseClass('''
@groovy.util.logging.Slf4j
class MyClass {
static loggingMethod() {
log.info('(static) info called')
}
}
MyClass.loggingMethod()
''')
Script s = (Script) clazz.newInstance()
s.run()
def events = appender.events
assert events.size() == 1
assert events[0].level == Level.INFO
assert events[0].message == '(static) info called'
}
@Test
void testLogInfoWithNamedLogger() {
Class clazz = new GroovyClassLoader().parseClass('''
@groovy.util.logging.Slf4j('logger')
class MyClass {
def loggingMethod() {
logger.error ('error called')
logger.warn ('warn called')
logger.info ('info called')
logger.debug ('debug called')
logger.trace ('trace called')
}
}
new MyClass().loggingMethod() ''')
Script s = (Script) clazz.newInstance()
s.run()
def events = appender.getEvents()
int ind = 0
assert events.size() == 5
assert events[ind].level == Level.ERROR
assert events[ind].message == 'error called'
assert events[++ind].level == Level.WARN
assert events[ind].message == 'warn called'
assert events[++ind].level == Level.INFO
assert events[ind].message == 'info called'
assert events[++ind].level == Level.DEBUG
assert events[ind].message == 'debug called'
assert events[++ind].level == Level.TRACE
assert events[ind].message == 'trace called'
}
@Test // GROOVY-6373
void testLogWithInnerClasses() {
Class clazz = new GroovyClassLoader().parseClass('''
@groovy.util.logging.Slf4j('logger')
class MyClass {
def loggingMethod() {
logger.info('outer called')
}
static class MyInnerClass {
def loggingMethod() {
logger.info('inner called')
}
}
}
new MyClass().loggingMethod()
new MyClass.MyInnerClass().loggingMethod()
''')
Script s = (Script) clazz.newInstance()
s.run()
def events = appender.events
int ind = 0
assert events.size() == 2
assert events[ind].level == Level.INFO
assert events[ind].message == 'outer called'
assert events[++ind].level == Level.INFO
assert events[ind].message == 'inner called'
}
@Test // GROOVY-6834
void testLogTransformInteractionWithAnonInnerClass() {
assertScript '''
@groovy.util.logging.Slf4j
class MyClass {
static myMethod() {
String message = 'hello'
String audience = 'world'
String result
new Runnable() {
void run() {
result = "$message $audience"
}
}.run()
result
}
}
assert MyClass.myMethod() == 'hello world'
'''
}
@Test // GROOVY-6873
void testLogTransformInteractionWithAnonInnerClass2() {
Class clazz = new GroovyClassLoader().parseClass('''
@groovy.util.logging.Slf4j
class Channel {
private void someMethod(String folder) {
final includeHidden = false
new Runnable() {
@Override
public void run() {
if (includeHidden) {
}
}
}
}
void otherMethod() {
def folder // "The current scope already contains a variable of the name folder"
}
}
''')
}
@Test
void testLogGuard() {
Class clazz = new GroovyClassLoader().parseClass('''
@groovy.util.logging.Slf4j
class MyClass {
def loggingMethod() {
def isSet = false
log.setLevel(ch.qos.logback.classic.Level.ERROR)
log.trace (isSet = true)
return isSet
}
}
new MyClass().loggingMethod()
''')
Script s = (Script) clazz.newInstance()
assert s.run() == false
}
@Test
void testDefaultCategory() {
Class clazz = new GroovyClassLoader().parseClass('''
@groovy.util.logging.Slf4j
class MyClass {
static loggingMethod() {
log.info('info called')
}
}
''')
def s = clazz.newInstance()
s.loggingMethod()
assert appender.events.size() == 1
}
@Test
void testCustomCategory() {
LogbackInterceptingAppender appenderForCustomCategory = new LogbackInterceptingAppender()
appenderForCustomCategory.outputStream = new ByteArrayOutputStream()
appenderForCustomCategory.layout = new EchoLayout()
appenderForCustomCategory.start()
Logger loggerForCustomCategory = LoggerFactory.getLogger('customCategory')
loggerForCustomCategory.addAppender(appenderForCustomCategory)
Class clazz = new GroovyClassLoader().parseClass('''
@groovy.util.logging.Slf4j(category='customCategory')
class MyClass {
static loggingMethod() {
log.error('error called')
}
}
''')
def s = clazz.newInstance()
s.loggingMethod()
assert appenderForCustomCategory.events.size() == 1
assert appender.events.isEmpty()
}
}