net.logstash.logback.encoder.LogstashEncoderTest.java Source code

Java tutorial

Introduction

Here is the source code for net.logstash.logback.encoder.LogstashEncoderTest.java

Source

/**
 * 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.*;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
import static org.mockito.Mockito.*;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import net.logstash.logback.LogstashFormatter;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.time.FastDateFormat;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.IThrowableProxy;
import ch.qos.logback.classic.spi.ThrowableProxy;
import ch.qos.logback.classic.spi.ThrowableProxyUtil;
import ch.qos.logback.core.Context;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

public class LogstashEncoderTest {

    private static Logger LOG = LoggerFactory.getLogger(LogstashEncoderTest.class);
    private static final ObjectMapper MAPPER = new ObjectMapper();
    private LogstashEncoder encoder;
    private ByteArrayOutputStream outputStream;

    @Before
    public void before() throws Exception {
        outputStream = new ByteArrayOutputStream();
        encoder = new LogstashEncoder();
        encoder.init(outputStream);
    }

    @Test
    public void basicsAreIncluded() throws Exception {
        final long timestamp = System.currentTimeMillis();

        ILoggingEvent event = mockBasicILoggingEvent(Level.ERROR);
        when(event.getTimeStamp()).thenReturn(timestamp);

        encoder.doEncode(event);
        closeQuietly(outputStream);

        JsonNode node = MAPPER.readTree(outputStream.toByteArray());

        assertThat(node.get("@timestamp").textValue(),
                is(FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss.SSSZZ").format(timestamp)));
        assertThat(node.get("@version").intValue(), is(1));
        assertThat(node.get("logger_name").textValue(), is("LoggerName"));
        assertThat(node.get("thread_name").textValue(), is("ThreadName"));
        assertThat(node.get("message").textValue(), is("My message"));
        assertThat(node.get("level").textValue(), is("ERROR"));
        assertThat(node.get("level_value").intValue(), is(40000));
    }

    @Test
    public void closePutsSeparatorAtTheEnd() throws Exception {
        ILoggingEvent event = mockBasicILoggingEvent(Level.ERROR);

        encoder.doEncode(event);
        encoder.close();
        closeQuietly(outputStream);

        assertThat(outputStream.toString(), Matchers.endsWith(LINE_SEPARATOR));
    }

    @Test
    public void includingThrowableProxyIncludesStackTrace() throws Exception {
        IThrowableProxy throwableProxy = new ThrowableProxy(new Exception("My goodness"));

        ILoggingEvent event = mockBasicILoggingEvent(Level.ERROR);
        when(event.getThrowableProxy()).thenReturn(throwableProxy);

        encoder.doEncode(event);
        closeQuietly(outputStream);

        JsonNode node = MAPPER.readTree(outputStream.toByteArray());

        assertThat(node.get("stack_trace").textValue(), is(ThrowableProxyUtil.asString(throwableProxy)));
    }

    @Test
    public void propertiesInMDCAreIncluded() throws Exception {
        Map<String, String> mdcMap = new HashMap<String, String>();
        mdcMap.put("thing_one", "One");
        mdcMap.put("thing_two", "Three");

        ILoggingEvent event = mockBasicILoggingEvent(Level.ERROR);
        when(event.getMDCPropertyMap()).thenReturn(mdcMap);

        encoder.doEncode(event);
        closeQuietly(outputStream);

        JsonNode node = MAPPER.readTree(outputStream.toByteArray());

        assertThat(node.get("thing_one").textValue(), is("One"));
        assertThat(node.get("thing_two").textValue(), is("Three"));
    }

    @Test
    public void nullMDCDoesNotCauseEverythingToBlowUp() throws Exception {
        ILoggingEvent event = mockBasicILoggingEvent(Level.ERROR);
        when(event.getMDCPropertyMap()).thenReturn(null);

        encoder.doEncode(event);
        closeQuietly(outputStream);
    }

    @Test
    public void callerDataIsIncluded() throws Exception {
        ILoggingEvent event = mockBasicILoggingEvent(Level.ERROR);
        when(event.getMDCPropertyMap()).thenReturn(Collections.<String, String>emptyMap());
        final StackTraceElement[] stackTraceElements = {
                new StackTraceElement("caller_class", "method_name", "file_name", 12345) };
        when(event.getCallerData()).thenReturn(stackTraceElements);

        encoder.setIncludeCallerInfo(true);

        encoder.doEncode(event);
        closeQuietly(outputStream);

        JsonNode node = MAPPER.readTree(outputStream.toByteArray());

        assertThat(node.get("caller_class_name").textValue(), is(stackTraceElements[0].getClassName()));
        assertThat(node.get("caller_method_name").textValue(), is(stackTraceElements[0].getMethodName()));
        assertThat(node.get("caller_file_name").textValue(), is(stackTraceElements[0].getFileName()));
        assertThat(node.get("caller_line_number").intValue(), is(stackTraceElements[0].getLineNumber()));
    }

    @Test
    public void callerDataIsNotIncludedIfSwitchedOff() throws Exception {
        ILoggingEvent event = mock(ILoggingEvent.class);
        when(event.getLoggerName()).thenReturn("LoggerName");
        when(event.getThreadName()).thenReturn("ThreadName");
        when(event.getFormattedMessage()).thenReturn("My message");
        when(event.getLevel()).thenReturn(Level.ERROR);
        when(event.getMDCPropertyMap()).thenReturn(Collections.<String, String>emptyMap());
        final StackTraceElement[] stackTraceElements = {
                new StackTraceElement("caller_class", "method_name", "file_name", 12345) };
        when(event.getCallerData()).thenReturn(stackTraceElements);

        encoder.setIncludeCallerInfo(false);

        encoder.doEncode(event);
        closeQuietly(outputStream);

        JsonNode node = MAPPER.readTree(outputStream.toByteArray());
        assertThat(node.get("caller_class_name"), is(nullValue()));
        assertThat(node.get("caller_method_name"), is(nullValue()));
        assertThat(node.get("caller_file_name"), is(nullValue()));
        assertThat(node.get("caller_line_number"), is(nullValue()));
    }

    @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);

        ILoggingEvent event = mockBasicILoggingEvent(Level.ERROR);

        encoder.setContext(context);
        encoder.doEncode(event);
        closeQuietly(outputStream);

        JsonNode node = MAPPER.readTree(outputStream.toByteArray());

        assertThat(node.get("thing_one").textValue(), is("One"));
        assertThat(node.get("thing_two").textValue(), is("Three"));
    }

    @Test
    public void markerIncludesItselfAsTag() throws Exception {
        Marker marker = MarkerFactory.getMarker("hoosh");
        ILoggingEvent event = mockBasicILoggingEvent(Level.INFO);
        when(event.getMarker()).thenReturn(marker);

        encoder.doEncode(event);
        closeQuietly(outputStream);

        JsonNode node = MAPPER.readTree(outputStream.toByteArray());

        assertJsonArray(node.findValue("tags"), "hoosh");
    }

    @Test
    public void markerReferencesAreIncludedAsTags() throws Exception {
        Marker marker = MarkerFactory.getMarker("bees");
        marker.add(MarkerFactory.getMarker("knees"));
        ILoggingEvent event = mockBasicILoggingEvent(Level.INFO);
        when(event.getMarker()).thenReturn(marker);

        encoder.doEncode(event);
        closeQuietly(outputStream);

        JsonNode node = MAPPER.readTree(outputStream.toByteArray());

        assertJsonArray(node.findValue("tags"), "bees", "knees");
    }

    @Test
    public void nullMarkerIsIgnored() throws Exception {
        ILoggingEvent event = mockBasicILoggingEvent(Level.INFO);
        when(event.getMarker()).thenReturn(null);

        encoder.doEncode(event);
        closeQuietly(outputStream);

        JsonNode node = MAPPER.readTree(outputStream.toByteArray());

        assertJsonArray(node.findValue("tags"));
    }

    @Test
    public void markerIsJSON() throws Exception {
        String[] argArray = {
                "{\"field1\":\"value1\",\"field2\":\"value2\",\"field3\":{\"subfield1\":\"subvalue1\"}}" };
        Marker marker = MarkerFactory.getMarker("JSON");
        ILoggingEvent event = mockBasicILoggingEvent(Level.INFO);
        when(event.getMarker()).thenReturn(marker);
        when(event.getArgumentArray()).thenReturn(argArray);

        encoder.doEncode(event);
        closeQuietly(outputStream);

        JsonNode node = MAPPER.readTree(outputStream.toByteArray());

        assertThat(MAPPER.convertValue(argArray, JsonNode.class).equals(node.get("json_message")), is(true));
    }

    @Test
    public void immediateFlushIsSane() {
        encoder.setImmediateFlush(true);
        assertThat(encoder.isImmediateFlush(), is(true));

        encoder.setImmediateFlush(false);
        assertThat(encoder.isImmediateFlush(), is(false));
    }

    @Test
    public void includeJsonChunk() throws Exception {
        String customFields = "{\"appname\":\"damnGodWebservice\",\"roles\":[\"customerorder\", \"auth\"], \"buildinfo\": { \"version\" : \"Version 0.1.0-SNAPSHOT\", \"lastcommit\" : \"75473700d5befa953c45f630c6d9105413c16fe1\"} }";
        ILoggingEvent event = mockBasicILoggingEvent(Level.INFO);

        encoder.setCustomFields(customFields);
        encoder.doEncode(event);
        closeQuietly(outputStream);

        JsonNode node = MAPPER.readTree(outputStream.toByteArray());

        assertThat(node.get("appname").textValue(), is("damnGodWebservice"));
        Assert.assertTrue(
                node.get("roles").equals(LogstashFormatter.parseCustomFields("[\"customerorder\", \"auth\"]")));
        Assert.assertTrue(node.get("buildinfo").equals(LogstashFormatter.parseCustomFields(
                "{ \"version\" : \"Version 0.1.0-SNAPSHOT\", \"lastcommit\" : \"75473700d5befa953c45f630c6d9105413c16fe1\"}")));
    }

    @Test
    public void testEncoderConfiguration() throws Exception {
        // Empty the log file
        PrintWriter writer = new PrintWriter(System.getProperty("java.io.tmpdir") + "/test.log");
        writer.print("");
        writer.close();
        LOG.info("Testing info logging.");
        InputStream is = new FileInputStream(System.getProperty("java.io.tmpdir") + "/test.log");

        List<String> lines = IOUtils.readLines(is);
        JsonNode node = MAPPER.readTree(lines.get(0).getBytes("UTF-8"));

        assertThat(node.get("appname").textValue(), is("damnGodWebservice"));
        Assert.assertTrue(
                node.get("roles").equals(LogstashFormatter.parseCustomFields("[\"customerorder\", \"auth\"]")));
        Assert.assertTrue(node.get("buildinfo").equals(LogstashFormatter.parseCustomFields(
                "{ \"version\" : \"Version 0.1.0-SNAPSHOT\", \"lastcommit\" : \"75473700d5befa953c45f630c6d9105413c16fe1\"}")));
    }

    private void assertJsonArray(JsonNode jsonNode, String... expected) {
        String[] values = new String[jsonNode.size()];
        for (int i = 0; i < values.length; i++) {
            values[i] = jsonNode.get(i).asText();
        }

        Assert.assertArrayEquals(expected, values);
    }

    private ILoggingEvent mockBasicILoggingEvent(Level level) {
        ILoggingEvent event = mock(ILoggingEvent.class);
        when(event.getLoggerName()).thenReturn("LoggerName");
        when(event.getThreadName()).thenReturn("ThreadName");
        when(event.getFormattedMessage()).thenReturn("My message");
        when(event.getLevel()).thenReturn(level);
        return event;
    }

}