/*
* Copyright (C) 2012-2015 DataStax 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 com.datastax.driver.core;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.WriterAppender;
import org.apache.log4j.spi.LoggingEvent;
import java.io.StringWriter;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
/**
* Simple Log4J appender that captures logs to memory in order to inspect them in unit tests.
* <p/>
* There is no purging mechanism, so make sure it doesn't stay enabled for too long (this is best
* done with an {@code @After} method that removes it).
*/
public class MemoryAppender extends WriterAppender {
private final Lock appendLock = new ReentrantLock();
private final Condition append = appendLock.newCondition();
public final StringWriter writer = new StringWriter();
private int nextLogIdx = 0;
public MemoryAppender() {
setWriter(writer);
setLayout(new PatternLayout("%m%n"));
}
@Override
protected void subAppend(LoggingEvent event) {
appendLock.lock();
try {
super.subAppend(event);
append.signal();
} finally {
appendLock.unlock();
}
}
public String get() {
return writer.toString();
}
/**
* Wait until at least one log event is appended to the current appender,
* or a timeout occurs, whichever happens first,
* then return the appender contents.
* Not thread safe.
* Useful when asynchronous code needs to wait until
* the appender is actually invoked at least once.
*
* @param timeoutMillis timeout in milliseconds
* @return The appender contents. Not thread safe.
*/
public String waitAndGet(long timeoutMillis) throws InterruptedException {
long nanos = MILLISECONDS.toNanos(timeoutMillis);
appendLock.lock();
try {
while (get().isEmpty()) {
if (nanos <= 0L) break; // timeout
nanos = append.awaitNanos(nanos);
}
return get();
} finally {
appendLock.unlock();
}
}
/**
* @return The next set of logs after getNext was last called. Not thread safe.
*/
public String getNext() {
String next = get().substring(nextLogIdx);
nextLogIdx += next.length();
return next;
}
}