/*
* Copyright © 2014 Cask Data, Inc.
*
* 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 co.cask.cdap.logging.appender;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.core.util.StatusPrinter;
import co.cask.cdap.common.logging.ApplicationLoggingContext;
import co.cask.cdap.common.logging.LoggingContext;
import co.cask.cdap.common.logging.LoggingContextAccessor;
import co.cask.cdap.common.logging.NamespaceLoggingContext;
import co.cask.cdap.logging.context.LoggingContextHelper;
import co.cask.cdap.logging.filter.Filter;
import co.cask.cdap.logging.read.Callback;
import co.cask.cdap.logging.read.LogEvent;
import co.cask.cdap.logging.read.LogOffset;
import co.cask.cdap.logging.read.LogReader;
import co.cask.cdap.logging.read.ReadRange;
import com.google.common.base.Function;
import com.google.common.collect.Maps;
import org.junit.Assert;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
*
*/
public class LoggingTester {
public void generateLogs(Logger logger, LoggingContext loggingContextNs1) {
Exception e1 = new Exception("Test Exception1");
Exception e2 = new Exception("Test Exception2", e1);
LoggingContext loggingContextNs2 =
replaceTag(loggingContextNs1,
new Entry(NamespaceLoggingContext.TAG_NAMESPACE_ID, getNamespace2(loggingContextNs1)));
LoggingContextAccessor.setLoggingContext(loggingContextNs2);
for (int i = 0; i < 40; ++i) {
logger.warn("NS_2 Test log message {} {} {}", i, "arg1", "arg2", e2);
}
LoggingContextAccessor.setLoggingContext(loggingContextNs1);
for (int i = 0; i < 20; ++i) {
logger.warn("Test log message {} {} {}", i, "arg1", "arg2", e2);
}
LoggingContextAccessor.setLoggingContext(loggingContextNs2);
for (int i = 40; i < 80; ++i) {
logger.warn("NS_2 Test log message {} {} {}", i, "arg1", "arg2", e2);
}
LoggingContextAccessor.setLoggingContext(loggingContextNs1);
for (int i = 20; i < 40; ++i) {
logger.warn("Test log message {} {} {}", i, "arg1", "arg2", e2);
}
LoggingContextAccessor.setLoggingContext(loggingContextNs1);
for (int i = 40; i < 60; ++i) {
logger.warn("Test log message {} {} {}", i, "arg1", "arg2", e2);
}
// Add logs with a different runid
LoggingContextAccessor.setLoggingContext(
replaceTag(loggingContextNs1, new Entry(ApplicationLoggingContext.TAG_RUNID_ID, "RUN2")));
for (int i = 40; i < 60; ++i) {
logger.warn("RUN2 Test log message {} {} {}", i, "arg1", "arg2", e2);
}
// Add logs with null runid and null instanceid
LoggingContextAccessor.setLoggingContext(
replaceTag(loggingContextNs1, new Entry(ApplicationLoggingContext.TAG_RUNID_ID, null),
new Entry(ApplicationLoggingContext.TAG_INSTANCE_ID, null)));
for (int i = 40; i < 60; ++i) {
logger.warn("NULL Test log message {} {} {}", i, "arg1", "arg2", e2);
}
// Check with null runId and null instanceId
LoggingContextAccessor.setLoggingContext(
replaceTag(loggingContextNs2, new Entry(ApplicationLoggingContext.TAG_RUNID_ID, null),
new Entry(ApplicationLoggingContext.TAG_INSTANCE_ID, null)));
for (int i = 80; i < 120; ++i) {
logger.warn("NS_2 Test log message {} {} {}", i, "arg1", "arg2", e2);
}
ByteArrayOutputStream bos = new ByteArrayOutputStream();
StatusPrinter.setPrintStream(new PrintStream(bos));
StatusPrinter.print((LoggerContext) LoggerFactory.getILoggerFactory());
System.out.println(bos.toString());
}
public void testGetNext(LogReader logReader, LoggingContext loggingContext) throws Exception {
LogCallback logCallback1 = new LogCallback();
logReader.getLogNext(loggingContext, ReadRange.LATEST, 10, Filter.EMPTY_FILTER, logCallback1);
List<LogEvent> events = logCallback1.getEvents();
Assert.assertEquals(10, events.size());
Assert.assertEquals("Test log message 50 arg1 arg2", events.get(0).getLoggingEvent().getFormattedMessage());
Assert.assertEquals("Test log message 59 arg1 arg2", events.get(9).getLoggingEvent().getFormattedMessage());
LogOffset ultimateOffset = events.get(9).getOffset();
LogOffset penultimateOffset = events.get(8).getOffset();
LogCallback logCallback2 = new LogCallback();
logReader.getLogPrev(loggingContext, ReadRange.createToRange(logCallback1.getFirstOffset()), 20,
Filter.EMPTY_FILTER, logCallback2);
events = logCallback2.getEvents();
Assert.assertEquals(20, events.size());
Assert.assertEquals("Test log message 30 arg1 arg2", events.get(0).getLoggingEvent().getFormattedMessage());
Assert.assertEquals("Test log message 49 arg1 arg2", events.get(19).getLoggingEvent().getFormattedMessage());
LogCallback logCallback3 = new LogCallback();
logReader.getLogNext(loggingContext, ReadRange.createFromRange(logCallback2.getLastOffset()), 20,
Filter.EMPTY_FILTER, logCallback3);
events = logCallback3.getEvents();
Assert.assertEquals(10, events.size());
Assert.assertEquals("Test log message 50 arg1 arg2", events.get(0).getLoggingEvent().getFormattedMessage());
Assert.assertEquals("Test log message 59 arg1 arg2", events.get(9).getLoggingEvent().getFormattedMessage());
LogCallback logCallback4 = new LogCallback();
logReader.getLogNext(loggingContext, ReadRange.createFromRange(logCallback2.getFirstOffset()), 20,
Filter.EMPTY_FILTER, logCallback4);
events = logCallback4.getEvents();
Assert.assertEquals(20, events.size());
Assert.assertEquals("Test log message 31 arg1 arg2", events.get(0).getLoggingEvent().getFormattedMessage());
Assert.assertEquals("Test log message 50 arg1 arg2", events.get(19).getLoggingEvent().getFormattedMessage());
LogCallback logCallback5 = new LogCallback();
logReader.getLogNext(loggingContext, ReadRange.createFromRange(ultimateOffset), 20, Filter.EMPTY_FILTER,
logCallback5);
events = logCallback5.getEvents();
Assert.assertEquals(0, events.size());
LogCallback logCallback6 = new LogCallback();
logReader.getLogNext(loggingContext, ReadRange.createFromRange(getNextOffset(ultimateOffset)), 20,
Filter.EMPTY_FILTER, logCallback6);
events = logCallback6.getEvents();
Assert.assertEquals(0, events.size());
LogCallback logCallback7 = new LogCallback();
logReader.getLogNext(loggingContext, ReadRange.createFromRange(penultimateOffset), 20, Filter.EMPTY_FILTER,
logCallback7);
events = logCallback7.getEvents();
Assert.assertEquals(1, events.size());
Assert.assertEquals("Test log message 59 arg1 arg2", events.get(0).getLoggingEvent().getFormattedMessage());
// Try with a different run
LogCallback logCallback10 = new LogCallback();
logReader.getLogPrev(replaceTag(loggingContext, new Entry(ApplicationLoggingContext.TAG_RUNID_ID, "RUN2")),
ReadRange.LATEST, 20, Filter.EMPTY_FILTER, logCallback10);
events = logCallback10.getEvents();
Assert.assertEquals(20, events.size());
Assert.assertEquals("RUN2 Test log message 40 arg1 arg2", events.get(0).getLoggingEvent().getFormattedMessage());
Assert.assertEquals("RUN2 Test log message 59 arg1 arg2", events.get(19).getLoggingEvent().getFormattedMessage());
// Try with a null runid, should return all events with or without runid
LogCallback logCallback11 = new LogCallback();
logReader.getLogPrev(replaceTag(loggingContext, new Entry(ApplicationLoggingContext.TAG_RUNID_ID, null)),
ReadRange.LATEST, 35, Filter.EMPTY_FILTER, logCallback11);
events = logCallback11.getEvents();
Assert.assertEquals(35, events.size());
Assert.assertEquals("RUN2 Test log message 45 arg1 arg2", events.get(0).getLoggingEvent().getFormattedMessage());
Assert.assertEquals("NULL Test log message 59 arg1 arg2", events.get(34).getLoggingEvent().getFormattedMessage());
}
public void testGetPrev(LogReader logReader, LoggingContext loggingContext) throws Exception {
LogCallback logCallback1 = new LogCallback();
logReader.getLogPrev(loggingContext, ReadRange.LATEST, 10, Filter.EMPTY_FILTER, logCallback1);
List<LogEvent> events = logCallback1.getEvents();
Assert.assertEquals(10, events.size());
Assert.assertEquals("Test log message 50 arg1 arg2", events.get(0).getLoggingEvent().getFormattedMessage());
Assert.assertEquals("Test log message 59 arg1 arg2", events.get(9).getLoggingEvent().getFormattedMessage());
LogOffset ultimateOffset = events.get(9).getOffset();
LogCallback logCallback2 = new LogCallback();
logReader.getLogPrev(loggingContext, ReadRange.createToRange(logCallback1.getFirstOffset()), 20,
Filter.EMPTY_FILTER, logCallback2);
events = logCallback2.getEvents();
Assert.assertEquals(20, events.size());
Assert.assertEquals("Test log message 30 arg1 arg2", events.get(0).getLoggingEvent().getFormattedMessage());
Assert.assertEquals("Test log message 49 arg1 arg2", events.get(19).getLoggingEvent().getFormattedMessage());
LogCallback logCallback3 = new LogCallback();
logReader.getLogNext(loggingContext, ReadRange.createFromRange(logCallback2.getLastOffset()), 20,
Filter.EMPTY_FILTER, logCallback3);
events = logCallback3.getEvents();
Assert.assertEquals(10, events.size());
Assert.assertEquals("Test log message 50 arg1 arg2", events.get(0).getLoggingEvent().getFormattedMessage());
Assert.assertEquals("Test log message 59 arg1 arg2", events.get(9).getLoggingEvent().getFormattedMessage());
LogCallback logCallback4 = new LogCallback();
logReader.getLogPrev(loggingContext, ReadRange.createToRange(logCallback2.getFirstOffset()), 15,
Filter.EMPTY_FILTER, logCallback4);
events = logCallback4.getEvents();
// In kafka mode, we'll get only 10 lines, need to run the call again.
if (events.size() < 15) {
LogCallback logCallback41 = new LogCallback();
logReader.getLogPrev(loggingContext, ReadRange.createToRange(logCallback4.getFirstOffset()), 5,
Filter.EMPTY_FILTER, logCallback41);
events.addAll(0, logCallback41.getEvents());
logCallback4 = logCallback41;
}
Assert.assertEquals(15, events.size());
Assert.assertEquals("Test log message 15 arg1 arg2", events.get(0).getLoggingEvent().getFormattedMessage());
Assert.assertEquals("Test log message 29 arg1 arg2", events.get(14).getLoggingEvent().getFormattedMessage());
LogCallback logCallback6 = new LogCallback();
logReader.getLogPrev(loggingContext, ReadRange.createToRange(logCallback4.getFirstOffset()), 25,
Filter.EMPTY_FILTER, logCallback6);
events = logCallback6.getEvents();
Assert.assertEquals(15, events.size());
Assert.assertEquals("Test log message 0 arg1 arg2", events.get(0).getLoggingEvent().getFormattedMessage());
Assert.assertEquals("Test log message 14 arg1 arg2", events.get(14).getLoggingEvent().getFormattedMessage());
LogCallback logCallback5 = new LogCallback();
logReader.getLogPrev(loggingContext, ReadRange.createToRange(logCallback6.getFirstOffset()), 15,
Filter.EMPTY_FILTER, logCallback5);
events = logCallback5.getEvents();
Assert.assertEquals(0, events.size());
LogCallback logCallback7 = new LogCallback();
logReader.getLogPrev(loggingContext, ReadRange.createToRange(logCallback4.getFirstOffset()), 15,
Filter.EMPTY_FILTER, logCallback7);
events = logCallback7.getEvents();
Assert.assertEquals(15, events.size());
Assert.assertEquals("Test log message 0 arg1 arg2", events.get(0).getLoggingEvent().getFormattedMessage());
Assert.assertEquals("Test log message 14 arg1 arg2", events.get(14).getLoggingEvent().getFormattedMessage());
LogCallback logCallback9 = new LogCallback();
logReader.getLogPrev(loggingContext, ReadRange.createToRange(getNextOffset(ultimateOffset)), 15,
Filter.EMPTY_FILTER, logCallback9);
events = logCallback9.getEvents();
Assert.assertEquals(15, events.size());
Assert.assertEquals("Test log message 45 arg1 arg2", events.get(0).getLoggingEvent().getFormattedMessage());
Assert.assertEquals("Test log message 59 arg1 arg2", events.get(14).getLoggingEvent().getFormattedMessage());
// Try with a different run
LogCallback logCallback10 = new LogCallback();
logReader.getLogPrev(replaceTag(loggingContext, new Entry(ApplicationLoggingContext.TAG_RUNID_ID, "RUN2")),
ReadRange.LATEST, 20, Filter.EMPTY_FILTER, logCallback10);
events = logCallback10.getEvents();
Assert.assertEquals(20, events.size());
Assert.assertEquals("RUN2 Test log message 40 arg1 arg2", events.get(0).getLoggingEvent().getFormattedMessage());
Assert.assertEquals("RUN2 Test log message 59 arg1 arg2", events.get(19).getLoggingEvent().getFormattedMessage());
// Try with a null runid, should return all events with or without runid
LogCallback logCallback11 = new LogCallback();
logReader.getLogPrev(replaceTag(loggingContext, new Entry(ApplicationLoggingContext.TAG_RUNID_ID, null)),
ReadRange.LATEST, 40, Filter.EMPTY_FILTER, logCallback11);
events = logCallback11.getEvents();
Assert.assertEquals(40, events.size());
Assert.assertEquals("RUN2 Test log message 40 arg1 arg2", events.get(0).getLoggingEvent().getFormattedMessage());
Assert.assertEquals("NULL Test log message 59 arg1 arg2", events.get(39).getLoggingEvent().getFormattedMessage());
}
/**
* Log Call back for testing.
*/
public static class LogCallback implements Callback {
private LogOffset firstOffset;
private LogOffset lastOffset;
private List<LogEvent> events;
@Override
public void init() {
events = Collections.synchronizedList(new ArrayList<LogEvent>());
}
@Override
public void handle(LogEvent event) {
if (firstOffset == null) {
firstOffset = event.getOffset();
}
lastOffset = event.getOffset();
events.add(event);
}
@Override
public int getCount() {
return events.size();
}
@Override
public void close() {
}
public List<LogEvent> getEvents() throws Exception {
return events;
}
public LogOffset getFirstOffset() {
return firstOffset == null ? LogOffset.LATEST_OFFSET : firstOffset;
}
public LogOffset getLastOffset() {
return lastOffset == null ? LogOffset.LATEST_OFFSET : lastOffset;
}
}
private LogOffset getNextOffset(LogOffset offset) {
return new LogOffset(offset.getKafkaOffset() + 1, offset.getTime() + 1);
}
private LoggingContext replaceTag(LoggingContext loggingContext, Entry... entries) {
Map<String, String> tagMap =
Maps.newHashMap(Maps.transformValues(loggingContext.getSystemTagsMap(), TAG_TO_STRING_FUNCTION));
for (Entry entry : entries) {
tagMap.put(entry.getKey(), entry.getValue());
}
return LoggingContextHelper.getLoggingContext(tagMap);
}
private String getNamespace2(LoggingContext loggingContext) {
String ns = loggingContext.getSystemTagsMap().get(NamespaceLoggingContext.TAG_NAMESPACE_ID).getValue();
return ns.substring(0, ns.length() - 1) + "2";
}
private static final class Entry {
private final String key;
private final String value;
public Entry(String key, String value) {
this.key = key;
this.value = value;
}
public String getKey() {
return key;
}
public String getValue() {
return value;
}
}
private static final Function<LoggingContext.SystemTag, String> TAG_TO_STRING_FUNCTION =
new Function<LoggingContext.SystemTag, String>() {
@Override
public String apply(LoggingContext.SystemTag input) {
return input.getValue();
}
};
}