What you need to do is replace the log field generated by @Slf4j AST transformation with your layout.
However, this is not so easy to achieve, since the generated code is not very convenient for testing.
A quick look at the generated code shows that it matches something like this:
class ErrorLogger { private final static transient org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ErrorLogger) }
Since the log field is declared as private final , it is not easy to replace the value with your layout. Actually it comes down to the same problem as described here . In addition, the use of this field is verified in the isEnabled() methods, therefore, for example, every time you call log.error(msg) , it is replaced by:
if (log.isErrorEnabled()) { log.error(msg) }
So how to solve this? I would advise you to register the problem in the groovy issue questionnaire , where you ask for a more flexible implementation of the AST conversion. However, this will not help you now.
There are several work solutions you can consider.
- Set a new value for the field in your test using the "terrible hack" described in the stack overflow question mentioned above . That is, make the field available using reflection and set the value. Remember to reset the value for the original during cleaning.
- Add the
getLog() method to your ErrorLogger class and use this method to access instead of directly accessing the field. You can then control metaClass to override the implementation of getLog() . The problem with this approach is that you have to modify the production code and add a getter, which, in the first place, jeopardizes the use of @Slf4j .
I would also like to point out that there are several problems with your ErrorLoggerSpec class. They are hidden due to the problems you are facing, so you will probably think about it yourself when they show themselves.
Although this is a hack, I will only give a sample code for the first sentence, since the second sentence changes the production code.
To isolate the hack, enable simple reuse and don't forget about the reset value, I wrote it as a rule JUnit (which can also be used in Spock).
import org.junit.rules.ExternalResource import org.slf4j.Logger import java.lang.reflect.Field import java.lang.reflect.Modifier public class ReplaceSlf4jLogger extends ExternalResource { Field logField Logger logger Logger originalLogger ReplaceSlf4jLogger(Class logClass, Logger logger) { logField = logClass.getDeclaredField("log"); this.logger = logger } @Override protected void before() throws Throwable { logField.accessible = true Field modifiersField = Field.getDeclaredField("modifiers") modifiersField.accessible = true modifiersField.setInt(logField, logField.getModifiers() & ~Modifier.FINAL) originalLogger = (Logger) logField.get(null) logField.set(null, logger) } @Override protected void after() { logField.set(null, originalLogger) } }
And here is the specification, after fixing all the minor errors and adding this rule. Changes are commented in code:
import org.junit.Rule import org.slf4j.Logger import spock.lang.Specification import java.nio.channels.NotYetBoundException import static ErrorLogger.handleExceptions class ErrorLoggerSpec extends Specification {