/** * 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 * * 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 net.logstash.logback.encoder; import static org.apache.commons.io.IOUtils.LINE_SEPARATOR; import static org.apache.commons.io.IOUtils.closeQuietly; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.io.ByteArrayOutputStream; import java.util.HashMap; import java.util.Map; import net.logstash.logback.Logback11Support; import org.apache.commons.lang.time.FastDateFormat; import org.junit.Test; import org.junit.runner.RunWith; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import ch.qos.logback.access.spi.IAccessEvent; import ch.qos.logback.core.Context; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @RunWith(PowerMockRunner.class) @PrepareForTest(Logback11Support.class) public class LogstashAccessEncoderTest { private static final ObjectMapper MAPPER = new ObjectMapper(); private LogstashAccessEncoder encoder = new LogstashAccessEncoder(); private ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); @Test public void basicsAreIncluded_logback11() throws Exception { PowerMockito.mockStatic(Logback11Support.class); when(Logback11Support.isLogback11OrBefore()).thenReturn(true); encoder.init(outputStream); final long timestamp = System.currentTimeMillis(); IAccessEvent event = mockBasicILoggingEvent(); when(event.getTimeStamp()).thenReturn(timestamp); encoder.getFieldNames().setTimestamp("timestamp"); encoder.start(); encoder.doEncode(event); closeQuietly(outputStream); JsonNode node = MAPPER.readTree(outputStream.toByteArray()); verifyBasics(timestamp, event, node); } @Test public void basicsAreIncluded_logback12OrLater() throws Exception { final long timestamp = System.currentTimeMillis(); IAccessEvent event = mockBasicILoggingEvent(); when(event.getTimeStamp()).thenReturn(timestamp); encoder.getFieldNames().setTimestamp("timestamp"); encoder.start(); byte[] encoded = encoder.encode(event); JsonNode node = MAPPER.readTree(encoded); verifyBasics(timestamp, event, node); } protected void verifyBasics(final long timestamp, IAccessEvent event, JsonNode node) { assertThat(node.get("timestamp").textValue()).isEqualTo(FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss.SSSZZ").format (timestamp)); assertThat(node.get("@version").intValue()).isEqualTo(1); assertThat(node.get("@message").textValue()).isEqualTo(String.format("%s - %s [%s] \"%s\" %s %s", event.getRemoteHost(), event.getRemoteUser(), FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss.SSSZZ").format (event.getTimeStamp()), event.getRequestURL(), event.getStatusCode(), event.getContentLength())); assertThat(node.get("@fields.method").textValue()).isEqualTo(event.getMethod()); assertThat(node.get("@fields.protocol").textValue()).isEqualTo(event.getProtocol()); assertThat(node.get("@fields.status_code").asInt()).isEqualTo(event.getStatusCode()); assertThat(node.get("@fields.requested_url").textValue()).isEqualTo(event.getRequestURL()); assertThat(node.get("@fields.requested_uri").textValue()).isEqualTo(event.getRequestURI()); assertThat(node.get("@fields.remote_host").textValue()).isEqualTo(event.getRemoteHost()); assertThat(node.get("@fields.HOSTNAME").textValue()).isEqualTo(event.getRemoteHost()); assertThat(node.get("@fields.remote_user").textValue()).isEqualTo(event.getRemoteUser()); assertThat(node.get("@fields.content_length").asLong()).isEqualTo(event.getContentLength()); assertThat(node.get("@fields.elapsed_time").asLong()).isEqualTo(event.getElapsedTime()); assertThat(node.get("@fields.request_headers")).isNull(); assertThat(node.get("@fields.response_headers")).isNull(); } @Test public void closePutsSeparatorAtTheEnd() throws Exception { PowerMockito.mockStatic(Logback11Support.class); when(Logback11Support.isLogback11OrBefore()).thenReturn(true); encoder.init(outputStream); IAccessEvent event = mockBasicILoggingEvent(); encoder.start(); encoder.doEncode(event); encoder.close(); closeQuietly(outputStream); assertThat(outputStream.toString()).endsWith(LINE_SEPARATOR); } @Test public void propertiesInContextAreIncluded() throws Exception { Map<String, String> propertyMap = new HashMap<String, String>(); propertyMap.put("thing_one", "One"); propertyMap.put("thing_two", "Three"); final Context context = mock(Context.class); when(context.getCopyOfPropertyMap()).thenReturn(propertyMap); IAccessEvent event = mockBasicILoggingEvent(); encoder.setContext(context); encoder.start(); byte[] encoded = encoder.encode(event); JsonNode node = MAPPER.readTree(encoded); assertThat(node.get("thing_one").textValue()).isEqualTo("One"); assertThat(node.get("thing_two").textValue()).isEqualTo("Three"); } @Test public void requestAndResponseHeadersAreIncluded() throws Exception { IAccessEvent event = mockBasicILoggingEvent(); encoder.getFieldNames().setFieldsRequestHeaders("@fields.request_headers"); encoder.getFieldNames().setFieldsResponseHeaders("@fields.response_headers"); encoder.start(); byte[] encoded = encoder.encode(event); JsonNode node = MAPPER.readTree(encoded); assertThat(node.get("@fields.request_headers").size()).isEqualTo(2); assertThat(node.get("@fields.request_headers").get("User-Agent").textValue()).isEqualTo("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36"); assertThat(node.get("@fields.request_headers").get("Accept").textValue()).isEqualTo("text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"); assertThat(node.get("@fields.request_headers").get("Unknown")).isNull(); assertThat(node.get("@fields.response_headers").size()).isEqualTo(2); assertThat(node.get("@fields.response_headers").get("Content-Type").textValue()).isEqualTo("text/html; charset=UTF-8"); assertThat(node.get("@fields.response_headers").get("Content-Length").textValue()).isEqualTo("42"); assertThat(node.get("@fields.response_headers").get("Unknown")).isNull(); } @Test public void immediateFlushIsSane() { encoder.setImmediateFlush(true); assertThat(encoder.isImmediateFlush()).isEqualTo(true); encoder.setImmediateFlush(false); assertThat(encoder.isImmediateFlush()).isEqualTo(false); } @Test public void testCustomFields() throws Exception { String customFields = "{\"foo\":\"bar\"}"; encoder.setCustomFields(customFields); assertThat(encoder.getCustomFields()).isEqualTo(customFields); } private IAccessEvent mockBasicILoggingEvent() { IAccessEvent event = mock(IAccessEvent.class); when(event.getContentLength()).thenReturn(123L); when(event.getMethod()).thenReturn("GET"); when(event.getProtocol()).thenReturn("HTTPS"); when(event.getRemoteHost()).thenReturn("123.123.123.123"); when(event.getRemoteUser()).thenReturn("remote-user"); when(event.getRequestURI()).thenReturn("/my/uri"); when(event.getRequestURL()).thenReturn("https://123.123.123.123/my/uri"); when(event.getStatusCode()).thenReturn(200); when(event.getTimeStamp()).thenReturn(System.currentTimeMillis()); when(event.getElapsedTime()).thenReturn(246L); when(event.getRequestHeaderMap()).thenReturn(new HashMap<String, String>() {{ put("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36"); put("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"); }}); when(event.getResponseHeaderMap()).thenReturn(new HashMap<String, String>() {{ put("Content-Type", "text/html; charset=UTF-8"); put("Content-Length", "42"); }}); return event; } }