001/* 002 * #%L 003 * Netarchivesuite - monitor 004 * %% 005 * Copyright (C) 2005 - 2014 The Royal Danish Library, the Danish State and University Library, 006 * the National Library of France and the Austrian National Library. 007 * %% 008 * This program is free software: you can redistribute it and/or modify 009 * it under the terms of the GNU Lesser General Public License as 010 * published by the Free Software Foundation, either version 2.1 of the 011 * License, or (at your option) any later version. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Lesser Public License for more details. 017 * 018 * You should have received a copy of the GNU General Lesser Public 019 * License along with this program. If not, see 020 * <http://www.gnu.org/licenses/lgpl-2.1.html>. 021 * #L% 022 */ 023 024package dk.netarkivet.monitor.logging; 025 026import java.util.ArrayList; 027import java.util.Arrays; 028import java.util.Collections; 029import java.util.Iterator; 030import java.util.List; 031 032import ch.qos.logback.classic.Level; 033import ch.qos.logback.classic.PatternLayout; 034import ch.qos.logback.classic.spi.ILoggingEvent; 035import ch.qos.logback.core.AppenderBase; 036import ch.qos.logback.core.Context; 037import dk.netarkivet.common.exceptions.ArgumentNotValid; 038import dk.netarkivet.common.utils.Settings; 039import dk.netarkivet.monitor.MonitorSettings; 040 041/** 042 * SLF4J appender that caches a certain number of log entries in a cyclic manor. 043 * "DEBUG and TRACE entries are excluded". 044 */ 045public class CachingSLF4JAppender extends AppenderBase<ILoggingEvent> { 046 047 /** Log format string pattern. */ 048 protected String pattern; 049 050 /** Pattern layouter used to format log string. */ 051 protected PatternLayout layout; 052 053 /** The size of the logging cache. */ 054 protected final int loggingHistorySize; 055 056 /** The logging cache itself, caching the last "loggingHistorySize" log entries. */ 057 protected final List<String> loggingHistory; 058 059 /** The log entries exposed as MBeans. */ 060 protected final List<CachingSLF4JLogRecord> loggingMBeans; 061 062 /** The place in the loggingHistory for the next LogRecord. */ 063 protected int currentIndex; 064 065 /** 066 * Initialize an instance of this class. 067 */ 068 public CachingSLF4JAppender() { 069 layout = new PatternLayout(); 070 loggingHistorySize = Settings.getInt(MonitorSettings.LOGGING_HISTORY_SIZE); 071 loggingHistory = Collections.synchronizedList(new ArrayList<String>(loggingHistorySize)); 072 // Fill out the list with loggingHistorySize null-records. 073 loggingHistory.addAll(Arrays.asList(new String[loggingHistorySize])); 074 loggingMBeans = new ArrayList<CachingSLF4JLogRecord>(loggingHistorySize); 075 for (int i = 0; i < loggingHistorySize; i++) { 076 loggingMBeans.add(new CachingSLF4JLogRecord(i, this)); 077 } 078 currentIndex = 0; 079 } 080 081 /** 082 * Returns the pattern used to format the log string. 083 * @return the pattern used to format the log string 084 */ 085 public String getPattern() { 086 return pattern; 087 } 088 089 /** 090 * Set the pattern used to format the log string. 091 * The method should be called before the setContext() or start() methods, most notably if used programmatically.. 092 * @param pattern log pattern 093 */ 094 public void setPattern(String pattern) { 095 this.isStarted(); 096 this.pattern = pattern; 097 layout.setPattern(pattern); 098 } 099 100 @Override 101 public void setContext(Context context) { 102 super.setContext(context); 103 layout.setContext(this.context); 104 } 105 106 @Override 107 public void start() { 108 super.start(); 109 layout.start(); 110 } 111 112 @Override 113 public void stop() { 114 super.stop(); 115 layout.stop(); 116 } 117 118 /** 119 * Close the appender and release associated resources. 120 */ 121 public void close() { 122 layout = null; 123 loggingHistory.clear(); 124 if (!loggingMBeans.isEmpty()) { 125 Iterator<CachingSLF4JLogRecord> iter = loggingMBeans.iterator(); 126 while (iter.hasNext()) { 127 iter.next().close(); 128 } 129 loggingMBeans.clear(); 130 } 131 } 132 133 @Override 134 protected void append(ILoggingEvent event) { 135 switch (event.getLevel().toInt()) { 136 case Level.TRACE_INT: 137 case Level.DEBUG_INT: 138 break; 139 case Level.INFO_INT: 140 case Level.WARN_INT: 141 case Level.ERROR_INT: 142 default: 143 loggingHistory.set(currentIndex, layout.doLayout(event)); 144 currentIndex = (currentIndex + 1) % loggingHistorySize; 145 break; 146 } 147 } 148 149 /** 150 * Returns the nth logrecord from the top. 151 * 152 * @param n The number of the log record to get 153 * @return The LogRecord which is number n from the top, or null for none. 154 */ 155 public String getNthLogRecord(int n) { 156 if ((n < 0) || (n >= loggingHistorySize)) { 157 throw new ArgumentNotValid("Argument 'int n' must be between 0 and " + loggingHistorySize + ", but was " 158 + n + "."); 159 } 160 return loggingHistory.get((currentIndex - n - 1 + loggingHistorySize) % loggingHistorySize); 161 } 162 163}