From 269b60bdc67be9289c6e5192e89c39ceb65a8b20 Mon Sep 17 00:00:00 2001 From: John Blum Date: Fri, 7 Feb 2020 16:59:58 -0800 Subject: [PATCH] Provide DelegatingAppender class to allow SLF4J logging provider Appenders to be injected [programmatically] at configuration/runtime. This is very useful in Integration Tests where the test class needs to assert log statements in infrastructure, system, framework or application code. Resolves gh-73. --- .../slf4j/logback/DelegatingAppender.java | 68 +++++++++++++++++++ .../logback/DelegatingAppenderUnitTests.java | 62 +++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 spring-geode-starter-logging/src/main/java/org/springframework/geode/logging/slf4j/logback/DelegatingAppender.java create mode 100644 spring-geode-starter-logging/src/test/java/org/springframework/geode/logging/slf4j/logback/DelegatingAppenderUnitTests.java diff --git a/spring-geode-starter-logging/src/main/java/org/springframework/geode/logging/slf4j/logback/DelegatingAppender.java b/spring-geode-starter-logging/src/main/java/org/springframework/geode/logging/slf4j/logback/DelegatingAppender.java new file mode 100644 index 00000000..32eabecf --- /dev/null +++ b/spring-geode-starter-logging/src/main/java/org/springframework/geode/logging/slf4j/logback/DelegatingAppender.java @@ -0,0 +1,68 @@ +/* + * Copyright 2019 the original author or authors. + * + * Licensed 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 + * + * https://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.springframework.geode.logging.slf4j.logback; + +import java.util.Objects; +import java.util.Optional; + +import org.slf4j.LoggerFactory; + +import ch.qos.logback.core.Appender; +import ch.qos.logback.core.AppenderBase; +import ch.qos.logback.core.Context; +import ch.qos.logback.core.helpers.NOPAppender; + +/** + * {@link DelegatingAppender} is an SLF4J {@link Appender} that delegates to the configured {@link Appender}. + * + * If no {@link Appender} was configured, then the {@link DelegatingAppender} delegates to the {@link NOPAppender}. + * + * @author John Blum + * @see ch.qos.logback.core.Appender + * @see ch.qos.logback.core.AppenderBase + * @see ch.qos.logback.core.helpers.NOPAppender + * @since 1.3.0 + */ +public class DelegatingAppender extends AppenderBase { + + @SuppressWarnings("rawtypes") + protected static final Appender defaultAppender = new NOPAppender<>(); + + public DelegatingAppender() { + + Optional.ofNullable(LoggerFactory.getILoggerFactory()) + .filter(it -> Objects.isNull(defaultAppender.getContext())) + .filter(Context.class::isInstance) + .map(Context.class::cast) + .ifPresent(defaultAppender::setContext); + } + + private volatile Appender appender; + + public void setAppender(Appender appender) { + this.appender = appender; + } + + @SuppressWarnings("unchecked") + protected Appender getAppender() { + return Optional.ofNullable(this.appender).orElse(defaultAppender); + } + + @Override + protected void append(T eventObject) { + getAppender().doAppend(eventObject); + } +} diff --git a/spring-geode-starter-logging/src/test/java/org/springframework/geode/logging/slf4j/logback/DelegatingAppenderUnitTests.java b/spring-geode-starter-logging/src/test/java/org/springframework/geode/logging/slf4j/logback/DelegatingAppenderUnitTests.java new file mode 100644 index 00000000..04263ee3 --- /dev/null +++ b/spring-geode-starter-logging/src/test/java/org/springframework/geode/logging/slf4j/logback/DelegatingAppenderUnitTests.java @@ -0,0 +1,62 @@ +/* + * Copyright 2019 the original author or authors. + * + * Licensed 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 + * + * https://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.springframework.geode.logging.slf4j.logback; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import org.junit.Test; + +import ch.qos.logback.core.Appender; +import ch.qos.logback.core.helpers.NOPAppender; + +/** + * Unit Tests for {@link DelegatingAppender}. + * + * @author John Blum + * @see org.junit.Test + * @see org.mockito.Mock + * @see org.mockito.Mockito + * @see org.springframework.geode.logging.slf4j.logback.DelegatingAppender + * @since 1.3.0 + */ +public class DelegatingAppenderUnitTests { + + @Test + public void delegatingAppenderDefaultsToNoOpAppender() { + assertThat(new DelegatingAppender<>().getAppender()).isInstanceOf(NOPAppender.class); + } + + @Test + @SuppressWarnings({ "rawtypes", "unchecked" }) + public void delegatingAppenderDelegatesToMockAppender() { + + Appender mockAppender = mock(Appender.class); + + DelegatingAppender delegatingAppender = new DelegatingAppender<>(); + + delegatingAppender.setAppender(mockAppender); + + assertThat(delegatingAppender.getAppender()).isSameAs(mockAppender); + + delegatingAppender.append("TEST"); + + verify(mockAppender, times(1)).doAppend(eq("TEST")); + } +}