package org.graylog2.log4j2; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.message.Message; import org.graylog2.gelfclient.GelfMessage; import org.graylog2.gelfclient.transport.GelfTransport; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.core.IsNot.not; import static org.hamcrest.core.StringContains.containsString; import static org.junit.Assert.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; public class GelfAppenderThrowableTest { @Rule public final MockitoRule mockitoRule = MockitoJUnit.rule(); @Mock private GelfTransport mockedGelfTransport; @Before public void setUp() { when(mockedGelfTransport.trySend(any(GelfMessage.class))).thenReturn(true); } @Test public void shouldNotAddExceptionToFullMessage() throws InterruptedException { // given final GelfAppender gelfAppender = createGelfAppender(true, true); final LogEvent event = createLogEventMock(); given(event.getThrown()).willReturn(new RuntimeException("Outer Exception", new Exception("Inner Exception"))); // when gelfAppender.append(event); // then ArgumentCaptor<GelfMessage> gelfMessageCaptor = ArgumentCaptor.forClass(GelfMessage.class); verify(mockedGelfTransport).trySend(gelfMessageCaptor.capture()); final String fullMessage = gelfMessageCaptor.getValue().getFullMessage(); assertThat(fullMessage, is("Some Message")); } @Test public void shouldAppendExceptionClassAndMessage() throws InterruptedException { // given final GelfAppender gelfAppender = createGelfAppender(true, true); final LogEvent event = createLogEventMock(); given(event.getThrown()).willReturn(new RuntimeException("Outer Exception", new Exception("Inner Exception"))); // when gelfAppender.append(event); // then ArgumentCaptor<GelfMessage> gelfMessageCaptor = ArgumentCaptor.forClass(GelfMessage.class); verify(mockedGelfTransport).trySend(gelfMessageCaptor.capture()); final Object exceptionMessage = gelfMessageCaptor.getValue() .getAdditionalFields() .get("exceptionMessage"); final Object exceptionClass = gelfMessageCaptor.getValue() .getAdditionalFields() .get("exceptionClass"); assertThat(exceptionMessage, notNullValue()); assertThat(exceptionMessage.toString(), containsString("Outer Exception")); assertThat(exceptionClass, notNullValue()); assertThat(exceptionClass.toString(), containsString("java.lang.RuntimeException")); } @Test public void shouldNotAppendExceptionInformationIfNotRequested() throws InterruptedException { // given final GelfAppender gelfAppender = createGelfAppender(false, false); final LogEvent event = createLogEventMock(); given(event.getThrown()).willReturn(new RuntimeException("Outer Exception", new Exception("Inner Exception"))); // when gelfAppender.append(event); // then ArgumentCaptor<GelfMessage> gelfMessageCaptor = ArgumentCaptor.forClass(GelfMessage.class); verify(mockedGelfTransport).trySend(gelfMessageCaptor.capture()); final Object exceptionMessage = gelfMessageCaptor.getValue() .getAdditionalFields() .get("exceptionMessage"); final Object exceptionClass = gelfMessageCaptor.getValue() .getAdditionalFields() .get("exceptionClass"); final Object exceptionStackTrace = gelfMessageCaptor.getValue() .getAdditionalFields() .get("exceptionStackTrace"); assertThat(exceptionMessage, nullValue()); assertThat(exceptionClass, nullValue()); assertThat(exceptionStackTrace, nullValue()); } @Test public void shouldNotFailIfNoExceptionAvailable() throws InterruptedException { // given final GelfAppender gelfAppender = createGelfAppender(false, false); final LogEvent event = createLogEventMock(); given(event.getThrown()).willReturn(null); // when gelfAppender.append(event); // then ArgumentCaptor<GelfMessage> gelfMessageCaptor = ArgumentCaptor.forClass(GelfMessage.class); verify(mockedGelfTransport).trySend(gelfMessageCaptor.capture()); final Object exceptionMessage = gelfMessageCaptor.getValue() .getAdditionalFields() .get("exceptionMessage"); final Object exceptionClass = gelfMessageCaptor.getValue() .getAdditionalFields() .get("exceptionClass"); final Object exceptionStackTrace = gelfMessageCaptor.getValue() .getAdditionalFields() .get("exceptionStackTrace"); assertThat(exceptionMessage, nullValue()); assertThat(exceptionClass, nullValue()); assertThat(exceptionStackTrace, nullValue()); } @Test public void shouldAppendStacktraceWithCauses() throws InterruptedException { // given final GelfAppender gelfAppender = createGelfAppender(true, true); final LogEvent event = createLogEventMock(); given(event.getThrown()).willReturn(new RuntimeException("Outer Exception", new Exception("Inner Exception"))); // when gelfAppender.append(event); // then ArgumentCaptor<GelfMessage> gelfMessageCaptor = ArgumentCaptor.forClass(GelfMessage.class); verify(mockedGelfTransport).trySend(gelfMessageCaptor.capture()); final Object exceptionStackTrace = gelfMessageCaptor.getValue() .getAdditionalFields() .get("exceptionStackTrace"); assertThat(exceptionStackTrace, notNullValue()); assertThat(exceptionStackTrace.toString(), containsString("Caused by: java.lang.Exception: Inner Exception")); } @Test public void shouldAppendStacktraceWithoutCauses() throws InterruptedException { // given final GelfAppender gelfAppender = createGelfAppender(true, false); final LogEvent event = createLogEventMock(); final RuntimeException exception = new RuntimeException("Outer Exception", new Exception("Inner Exception")); given(event.getThrown()).willReturn(exception); // when gelfAppender.append(event); // then ArgumentCaptor<GelfMessage> gelfMessageCaptor = ArgumentCaptor.forClass(GelfMessage.class); verify(mockedGelfTransport).trySend(gelfMessageCaptor.capture()); final Object exceptionStackTrace = gelfMessageCaptor.getValue() .getAdditionalFields() .get("exceptionStackTrace"); assertThat(exceptionStackTrace, notNullValue()); assertThat(exceptionStackTrace.toString(), is(gelfAppender.getSimpleStacktraceAsString(exception))); assertThat(exceptionStackTrace.toString(), not(containsString("Caused by: java.lang.Exception: Inner Exception"))); } private GelfAppender createGelfAppender(final boolean includeStackTrace, final boolean includeExceptionCause) { GelfAppender gelfAppender = new GelfAppender("appender", null, null, false, null, "host", false, false, includeStackTrace, null, includeExceptionCause); gelfAppender.setClient(mockedGelfTransport); return gelfAppender; } private LogEvent createLogEventMock() { final Message message = mock(Message.class); given(message.getFormattedMessage()).willReturn("Some Message"); final LogEvent event = mock(LogEvent.class); given(event.getMessage()).willReturn(message); given(event.getLevel()).willReturn(Level.ALL); return event; } }