/* * Tigase Jabber/XMPP Server * Copyright (C) 2004-2012 "Artur Hefczyc" <artur.hefczyc@tigase.org> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, version 3 of the License. * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. Look for COPYING file in the top folder. * If not, see http://www.gnu.org/licenses/. * * $Rev$ * Last modified by $Author$ * $Date$ */ package tigase.server.sreceiver.sysmon; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.Map; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.LogRecord; import java.util.logging.Logger; import java.util.logging.MemoryHandler; import tigase.conf.ConfiguratorOld; import tigase.stats.StatisticsList; import tigase.util.LogFormatter; import tigase.xmpp.JID; /** * Created: Dec 12, 2008 8:31:38 PM * * @author <a href="mailto:artur.hefczyc@tigase.org">Artur Hefczyc</a> * @version $Rev$ */ public class LogMonitor extends AbstractMonitor { private MonitorHandler monitorHandler = null; private MemoryHandlerFlush memoryHandler = null; private Map<String, String> monitorLevels = new LinkedHashMap<String, String>(); private int loggerSize = 50; private Level levelTreshold = Level.WARNING; private int maxLogBuffer = 1000*1000; private long lastWarningSent = 0; private long logWarings = 0; private enum command { setlevel(" [package=level] - Sets logging level for specified package."), loggersize(" [N] - Sets memory logger size to specified value."), leveltreshold(" [level] - Sets level treshold to specified level."), logdump(" - retrieves all logs collected in the memory buffer and clears that buffer."); private String helpText = null; private command(String helpText) { this.helpText = helpText; } public String getHelp() { return helpText; } }; @Override public String commandsHelp() { StringBuilder sb = new StringBuilder(); for (command comm : command.values()) { sb.append("//" + comm.name() + comm.getHelp() + "\n"); } return sb.toString(); } @Override public String runCommand(String[] com) { command comm = command.valueOf(com[0].substring(2)); switch (comm) { case setlevel: if (com.length > 1) { String[] keyval = com[1].split("="); if (keyval.length > 1) { String key = (keyval[0].endsWith(".level") ? keyval[0].substring( 0, keyval[0].length() - 6) : keyval[0]); try { Level level = Level.parse(keyval[1]); Logger.getLogger(key).setLevel(level); monitorLevels.put(key + ".level", level.getName()); return "Level set successfuly: " + key + "=" + level.getName(); } catch (Exception e) { return "Incorrect level name, use: ALL, FINEST, FINER, FINE, " + "INFO, WARNING, SEVERE, OFF"; } } else { return "Incorrect level setting, use: package=level"; } } else { return "Current logging levels are:\n" + getCurrentLevels(); } case loggersize: if (com.length > 1) { try { int newLoggerSize = Integer.parseInt(com[1]); loggerSize = newLoggerSize; registerHandler(); return "New logger size successfuly set to: " + loggerSize; } catch (Exception e) { return "Incorrect logger size: " + com[1]; } } else { return "Current memory logger size is: " + loggerSize; } case leveltreshold: if (com.length > 1) { return "Setting logging treshold level is not supported yet."; } else { return "Current logging treshold level is: " + levelTreshold; } case logdump: return "Memory logging buffer content:\n" + memoryHandler.pushToString(); } return null; } @Override public boolean isMonitorCommand(String com) { if (com != null) { for (command comm: command.values()) { if (com.startsWith("//" + comm.toString())) { return true; } } } return false; } private void removeHandler() { if (memoryHandler != null) { Logger.getLogger("").removeHandler(memoryHandler); } } private void registerHandler() { removeHandler(); if (monitorHandler == null) { monitorHandler = new MonitorHandler(); monitorHandler.setLevel(Level.ALL); } memoryHandler = new MemoryHandlerFlush(monitorHandler, loggerSize, levelTreshold); memoryHandler.setLevel(Level.ALL); Logger.getLogger("").addHandler(memoryHandler); } @Override public void init(JID jid, float treshold, SystemMonitorTask smTask) { super.init(jid, treshold, smTask); registerHandler(); } @Override public void destroy() { removeHandler(); } private String getCurrentLevels() { String[] configLines = ConfiguratorOld.logManagerConfiguration.split("\n"); Map<String, String> results = new LinkedHashMap<String, String>(); for (String string : configLines) { if (string.contains(".level") && !string.contains("FileHandler") && !string.contains("ConsoleHandler")) { String[] keyval = string.split("="); results.put(keyval[0], keyval[1]); } } results.putAll(monitorLevels); StringBuilder sb = new StringBuilder(); for (Map.Entry<String, String> entry : results.entrySet()) { sb.append(entry.getKey() + "=" + entry.getValue() + "\n"); } return sb.toString(); } @Override public String getState() { StringBuilder sb = new StringBuilder("Logging levels:\n"); sb.append(getCurrentLevels()); sb.append("Monitor parameters:\n"); sb.append("loggerSize=" + loggerSize + "\n"); sb.append("levelTreshold=" + levelTreshold + "\n"); return sb.toString(); } private class MonitorHandler extends Handler { private LinkedList<String> logs = new LinkedList<String>(); private LogFormatter formatter = new LogFormatter(); @Override public synchronized void publish(LogRecord record) { String logEntry = formatter.format(record).replace('<', '[').replace('>', ']'); logs.add(logEntry); } public synchronized String logsToString() { StringBuilder sb = new StringBuilder(); String logEntry = null; while (((logEntry = logs.pollLast()) != null) && (sb.length() < maxLogBuffer)) { sb.insert(0, logEntry); } logs.clear(); String result = sb.length() <= maxLogBuffer ? sb.toString() : sb.substring(sb.length() - maxLogBuffer); return result; } @Override public synchronized void flush() { ++logWarings; if (System.currentTimeMillis() - lastWarningSent > 5*MINUTE) { String logBuff = logsToString(); // We don't want to flood the system with this in case of // some frequent error.... sendWarningOut(logBuff, null); lastWarningSent = System.currentTimeMillis(); } } @Override public void close() throws SecurityException { } } private class MemoryHandlerFlush extends MemoryHandler { MonitorHandler monHandle = null; public MemoryHandlerFlush(MonitorHandler target, int size, Level pushLevel) { super(target, size, pushLevel); monHandle = target; } public String pushToString() { super.push(); return monHandle.logsToString(); } @Override public void push() { super.push(); flush(); } } @Override public void getStatistics(StatisticsList list) { super.getStatistics(list); list.add("log-mon", "Log warings", logWarings, Level.FINE); } }