/*
* Copyright 2013 SURFnet bv, The Netherlands
*
* 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 nl.surfnet.coin.shared.log.diagnostics;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.read.CyclicBufferAppender;
/**
* <p>
* A {@link CyclicBufferAppender} that has an Appender attached to which events are dumped when requested.
* See {@link DiagnosticsLoggerFilter} for example usage.
* </p>
* <p>
* <strong>Configuration:</strong><br/>
* Create a logback configuration like this:
* <blockquote><pre>
* <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
* <encoder>
* <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
* </encoder>
* </appender>
*
* <appender name="DUMPAPPENDER" class="ch.qos.logback.core.FileAppender">
* <file>logs/dump.log</file>
* <encoder>
* <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
* </encoder>
* </appender>
* <appender name="MEMORYAPPENDER" class="nl.surfnet.coin.shared.log.diagnostics.MemoryAppender" />
* <logger name="MEMORYLOGGER">
* <appender-ref ref="MEMORYAPPENDER" />
* <appender-ref ref="DUMPAPPENDER" />
* </logger>
*
* <root level="info">
* <appender-ref ref="file"/>
* <appender-ref ref="MEMORYAPPENDER" />
* </root>
* </pre>
* </blockquote>
* </p>
* <p>
* Where MEMORY, DUMPLOGGER and DUMPAPPENDER are predefined tokens that are used as references. So you should leave those intact.
*
*
* </p>
*/
public class MemoryAppender extends CyclicBufferAppender<ILoggingEvent> {
public static final String MDC_DISCRIMINATOR_FIELD = "nl.surfnet.coin.shared.log.diagnostics.SESSION_DISCRIMINATOR";
public static final String MEMORY_LOGGER = "MEMORYLOGGER";
public static final String DEFAULT_DUMP_APPENDER = "DUMPAPPENDER";
private String dumpAppenderName = DEFAULT_DUMP_APPENDER;
/**
* Appender to use for eventual logging when requested.
* @param dumpAppenderName the name of the dumpAppender
*/
public void setDumpAppender(String dumpAppenderName) {
this.dumpAppenderName = dumpAppenderName;
}
protected Appender<ILoggingEvent> getDumpAppender() {
Logger rl = (Logger) LoggerFactory.getLogger(MEMORY_LOGGER);
Appender<ILoggingEvent> appender = rl.getAppender(dumpAppenderName);
Assert.notNull(appender, "Configured dumpAppender ('" + dumpAppenderName + "') cannot be found");
return appender;
}
/**
* Dump all stored events for the given discriminator (looked up in the MDC)
* @param discriminator the discriminator to filter on
*/
public void dump(String discriminator) {
Appender<ILoggingEvent> dumpAppender = getDumpAppender();
if (dumpAppender == null) {
return;
}
if (discriminator == null) {
throw new IllegalArgumentException("Discriminator cannot be null");
}
List<ILoggingEvent> eventsToDump = getBuffer(discriminator);
for (ILoggingEvent event : eventsToDump) {
dumpAppender.doAppend(event);
}
}
/**
* Accept an arbitrary list of events and dump them to the dump appender.
* Useful for dumping externally saved events (for example in an http session.
* @param events the events to dump
*/
public void dumpExternal(List<ILoggingEvent> events) {
if (events == null) {
return;
}
Appender<ILoggingEvent> dumpAppender = getDumpAppender();
for (ILoggingEvent event : events) {
dumpAppender.doAppend(event);
}
}
/**
* Get the events for the given discriminator from the buffer
* @param discriminator
* @return the list of ILoggingEvents
*/
public List<ILoggingEvent> getBuffer(String discriminator) {
int length = getLength();
List<ILoggingEvent> events = new ArrayList<ILoggingEvent>(length);
for(int i = 0; i < length; i++) {
ILoggingEvent loggingEvent = (ILoggingEvent) get(i);
if (discriminator.equals(loggingEvent.getMDCPropertyMap().get(MDC_DISCRIMINATOR_FIELD))) {
events.add(loggingEvent);
}
}
return events;
}
}