All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.cisco.oss.foundation.logging.FoundationLoggingDispatcher Maven / Gradle / Ivy

/*
 * Copyright 2015 Cisco Systems, 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.cisco.oss.foundation.logging;

import org.apache.log4j.*;
import org.apache.log4j.helpers.AppenderAttachableImpl;
import org.apache.log4j.spi.LoggingEvent;
import org.slf4j.Marker;

import java.lang.reflect.Field;
import java.text.MessageFormat;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class FoundationLoggingDispatcher implements Runnable {
	/**
	 * Parent AsyncAppender.
	 */
	private final AsyncAppender parent;

	/**
	 * Event buffer.
	 */
	private final List buffer;

	/**
	 * Map of DiscardSummary keyed by logger name.
	 */
	private final Map discardMap;

	/**
	 * Wrapped appenders.
	 */
	private final AppenderAttachableImpl appenders;

	/**
	 * Create new instance of dispatcher.
	 * 
	 * @param parent
	 *            parent AsyncAppender, may not be null.
	 * @param buffer
	 *            event buffer, may not be null.
	 * @param discardMap
	 *            discard map, may not be null.
	 * @param appenders
	 *            appenders, may not be null.
	 */
	public FoundationLoggingDispatcher(final AsyncAppender parent, final List buffer, final Map discardMap, final AppenderAttachableImpl appenders) {

		this.parent = parent;
		this.buffer = buffer;
		this.appenders = appenders;
		this.discardMap = discardMap;
	}	

	/**
	 * {@inheritDoc}
	 */
	public void run() {
		boolean isActive = true;

		//
		// if interrupted (unlikely), end thread
		//
		try {
			//
			// loop until the AsyncAppender is closed.
			//
			while (isActive) {
				LoggingEvent[] events = null;

				//
				// extract pending events while synchronized
				// on buffer
				//
				synchronized (buffer) {
					int bufferSize = buffer.size();
					isActive = !isAsyncAppenderClosed(parent);

					while ((bufferSize == 0) && isActive) {
						buffer.wait();
						bufferSize = buffer.size();
						isActive = !isAsyncAppenderClosed(parent);
					}

					if (bufferSize > 0) {
						events = new LoggingEvent[bufferSize + discardMap.size()];
						buffer.toArray(events);

						//
						// add events due to buffer overflow
						//
						int index = bufferSize;

						for (Iterator iter = discardMap.values().iterator(); iter.hasNext();) {
							events[index++] = ((DiscardSummary) iter.next()).createEvent();
						}

						//
						// clear buffer and discard map
						//
						buffer.clear();
						discardMap.clear();

						//
						// allow blocked appends to continue
						buffer.notifyAll();
					}
				}

				//
				// process events after lock on buffer is released.
				//
				if (events != null) {
					for (int i = 0; i < events.length; i++) {
						synchronized (appenders) {
							LoggingEvent event = events[i];											
							@SuppressWarnings("unchecked")
							Enumeration allAppenders = appenders.getAllAppenders();
							
							while (allAppenders.hasMoreElements()) {
								Appender appender = allAppenders.nextElement();

								//since we may update the appender layout we must sync so other threads won't use it by mistake
								synchronized (appender) {
									Layout originalLayout = appender.getLayout();
									boolean appenderUpdated = udpateLayoutIfNeeded(appender, event);

									appender.doAppend(event);
									if (appenderUpdated) {
										appender.setLayout(originalLayout);
									}
								}

							}																										
						}
					}
				}
			}
		} catch (InterruptedException ex) {
			Thread.currentThread().interrupt();
		}
	}
	
	private boolean udpateLayoutIfNeeded(Appender appender, LoggingEvent event) {

		if (event instanceof FoundationLof4jLoggingEvent) {

			FoundationLof4jLoggingEvent foundationLof4jLoggingEvent = (FoundationLof4jLoggingEvent) event;
			Marker marker = foundationLof4jLoggingEvent.getSlf4jMarker();

			if (marker != null) {

				Map map = FoundationLogger.markerAppendersMap.get(marker.getName());

				if (map != null) {
					Layout specificPatternLayout = map.get(appender.getName());
					if (specificPatternLayout != null) {
						appender.setLayout(specificPatternLayout);
						return true;
					}
				}
			}
		}
		return false;
	}

	private boolean isAsyncAppenderClosed(Appender appender) {
		// no other way than use reflection to validate the state of the appender.
		// NOTE: this code will break if java SecurityManager is used.
		try {
			Field closedField = AppenderSkeleton.class.getDeclaredField("closed");
			closedField.setAccessible(true);
			boolean closed = (Boolean) closedField.get(appender);

			return closed;
		} catch (Exception e) {
			System.err.println("Cannot check AsyncAppender state. Error is: " + e);
			// assume closed in case of error.
			return true;
		}
	}

	/**
	 * Summary of discarded logging events for a logger.
	 */
	public static final class DiscardSummary {
		/**
		 * First event of the highest severity.
		 */
		private LoggingEvent maxEvent;

		/**
		 * Total count of messages discarded.
		 */
		private int count;

		/**
		 * Create new instance.
		 * 
		 * @param event
		 *            event, may not be null.
		 */
		public DiscardSummary(final LoggingEvent event) {
			maxEvent = event;
			count = 1;
		}

		/**
		 * Add discarded event to summary.
		 * 
		 * @param event
		 *            event, may not be null.
		 */
		public void add(final LoggingEvent event) {
			if (event.getLevel().toInt() > maxEvent.getLevel().toInt()) {
				maxEvent = event;
			}

			count++;
		}

		/**
		 * Create event with summary information.
		 * 
		 * @return new event.
		 */
		public LoggingEvent createEvent() {
			String msg = MessageFormat.format("Discarded {0} messages due to full event buffer including: {1}", new Object[] { new Integer(count), maxEvent.getMessage() });

			return new LoggingEvent("org.apache.log4j.AsyncAppender.DONT_REPORT_LOCATION", Logger.getLogger(maxEvent.getLoggerName()), maxEvent.getLevel(), msg, null);
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy