/* * Universal Media Server, for streaming any media to DLNA * compatible renderers based on the http://www.ps3mediaserver.org. * Copyright (C) 2012 UMS developers. * * This program is a free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; version 2 * of the License only. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package net.pms.logging; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.Appender; import java.util.Iterator; import java.util.LinkedList; import org.slf4j.ILoggerFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Special LogBack cacher that administers {@link CacheAppender}. * * @author Nadahar */ public class CacheLogger { private static Logger LOGGER = LoggerFactory.getLogger(CacheLogger.class); private static LinkedList<Appender<ILoggingEvent>> appenderList = new LinkedList<>(); private static volatile CacheAppender<ILoggingEvent> cacheAppender = null; private static LoggerContext loggerContext = null; private static ch.qos.logback.classic.Logger rootLogger; private static void detachRootAppenders() { Iterator<Appender<ILoggingEvent>> it = rootLogger.iteratorForAppenders(); while (it.hasNext()) { Appender<ILoggingEvent> appender = it.next(); if (appender != cacheAppender) { appenderList.add(appender); rootLogger.detachAppender(appender); } } } private static void attachRootAppenders() { while (!appenderList.isEmpty()) { Appender<ILoggingEvent> appender = appenderList.poll(); rootLogger.addAppender(appender); } } private static void disposeOfAppenders() { appenderList.clear(); } /** * @return whether or not CacheLogger is currently running */ public static boolean isActive() { return cacheAppender != null; } /** * Sets references to the LoggerContext. Must be called whenever logging * configuration changes between {@link #startCaching()} and {@link #stopAndFlush()} */ public static synchronized void initContext() { ILoggerFactory iLoggerFactory = LoggerFactory.getILoggerFactory(); if (!(iLoggerFactory instanceof LoggerContext)) { // Not using LogBack, CacheAppender not applicable LOGGER.debug("Not using LogBack, aborting CacheLogger"); loggerContext = null; return; } else if (!isActive()) { LOGGER.error("initContext() cannot be called while isActive() is false"); return; } loggerContext = (LoggerContext) iLoggerFactory; rootLogger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); disposeOfAppenders(); detachRootAppenders(); if (!rootLogger.isAttached(cacheAppender)) { rootLogger.addAppender(cacheAppender); } cacheAppender.setContext(loggerContext); cacheAppender.setName("CacheAppender"); cacheAppender.start(); } public static synchronized void startCaching() { if (isActive()) { LOGGER.debug("StartCaching() failed: Caching already started"); } else { cacheAppender = new CacheAppender<>(); initContext(); } } public static synchronized void stopAndFlush() { if (loggerContext == null) { LOGGER.debug("Not using LogBack, aborting CacheLogger.stopAndFlush()"); return; } else if (!isActive()) { LOGGER.error("stopAndFlush() cannot be called while isActive() is false"); return; } cacheAppender.stop(); rootLogger.detachAppender(cacheAppender); attachRootAppenders(); cacheAppender.flush(rootLogger); cacheAppender = null; } public static synchronized Iterator<Appender<ILoggingEvent>> iteratorForAppenders() { return appenderList.iterator(); } public static synchronized void addAppender(Appender<ILoggingEvent> newAppender) { appenderList.add(newAppender); } public static synchronized boolean removeAppender(Appender<ILoggingEvent> appender) { return appenderList.remove(appender); } }