/* * ALMA - Atacama Large Millimiter Array * (c) European Southern Observatory, 2004 * Copyright by ESO (in the framework of the ALMA collaboration), * All rights reserved * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ package alma.acs.logging; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.LogRecord; import alma.acs.util.StopWatch; /** * Class that encapsulates access to log record parameters, and handling of a special Property-Map parameter. * Some conventions are necessary because we "smuggle" some extra information from the plain-JDK loggers * to the ACS-aware log dispatcher. The alternative would be to expose ACS custom loggers to applications, * which seems too ugly for the benefit it provides. * <p> * The rules are: * <ul> * <li>Parameters of type <code>Map</code> are interpreted as name-value pairs * and show up in the XML log output as <code><Data Name="key">value</Data></code> * <li>Single-valued parameters show up in the XML log output as * <code><Data Name="LoggedParameter">value</Data></code> * <li>There is a special properties <code>Map</code> parameter that contains the key <code>isAcsPropertiesMap</code>. * Those values are recognized by the log formatter and result in special fields being set, instead of * <code><Data/></code> elements being constructed. * When adding this map as a log parameter, it should be created using method {@link #createPropertiesMap()}. * </ul> * Note that this class is stateful: returned values come from the last given <code>LogRecord</code>, * which can be set in the constructor or overwritten in {@link #setCurrentLogRecord(LogRecord)}. */ public class LogParameterUtil { public static final String IS_ACS_PROPERTIES_MAP_KEYNAME = "isAcsPropertiesMap"; public static final String PARAM_THREAD_NAME = "ThreadName"; public static final String PARAM_LINE = "Line"; public static final String PARAM_HOSTNAME = "HostName"; public static final String PARAM_STACK_ID = "StackId"; public static final String PARAM_STACK_LEVEL = "StackLevel"; public static final String PARAM_PRIORITY = "Priority"; public static final String PARAM_URI = "Uri"; public static final String PARAM_PROCESSNAME = "ProcessName"; public static final String PARAM_SOURCEOBJECT = "SourceObject"; private static final String PARAM_STOPWATCH = "StopWatch"; private LogRecord currentLogRecord; /** * A <code>Map</code>that is used as an optional special log parameter. * It transports information from the application to the log formatter * which could otherwise not be transported using the JDK's <code>LogRecord</code>. */ private Map<String, Object> specialProperties; private List<Object> otherParameters; public LogParameterUtil(LogRecord currentLogRecord) { setCurrentLogRecord(currentLogRecord); } /** * Creates a new special properties map. * @return */ public static Map<String, Object> createPropertiesMap() { Map<String, Object> propMap = new HashMap<String, Object>(); propMap.put(IS_ACS_PROPERTIES_MAP_KEYNAME, Boolean.TRUE); return propMap; } /** * Tries to find the special properties map among the log parameters. * @return the existing properties map from the current log record, or <code>null</code> if none is found. */ Map<String, Object> extractSpecialPropertiesMap() { if (specialProperties != null) { return specialProperties; } Map<String, Object> propMap = null; Object[] parameters = currentLogRecord.getParameters(); if (parameters != null) { // the map could be among the parameters for (int i = 0; i < parameters.length; i++) { if (parameters[i] instanceof Map && ((Map) parameters[i]).containsKey(IS_ACS_PROPERTIES_MAP_KEYNAME)) { if (propMap == null) { propMap = (Map) parameters[i]; } else { // @todo error handling (this case of having more than one special properties map should never happen though) } } } } return propMap; } /** * Sets the log record from which the other methods can extract information. * Also parses the special properties map and other parameters. * @param logRecord */ public void setCurrentLogRecord(LogRecord logRecord) { if (currentLogRecord == logRecord) { return; } currentLogRecord = logRecord; specialProperties = extractSpecialPropertiesMap(); // might be null otherParameters = new ArrayList<Object>(); if (currentLogRecord.getParameters() != null) { otherParameters.addAll(Arrays.asList(currentLogRecord.getParameters())); if (specialProperties != null) { otherParameters.remove(specialProperties); } } } /** * Returns parameters of the current log record which are different from the special Properties <code>Map</code>. * These parameters may still be of type Map. * <p> * The returned list is "live", so don't muck with it. * @return array of parameters, possibly empty, but never null. */ public List<Object> getNonSpecialPropertiesMapParameters() { return otherParameters; } /** * Extracts property with specified name of type long from the special properties map. * @param name name of the property * @param defaultValue value returned if failed to obtain property value * @return value, <code>defaultValue</code> on failure */ public long extractLongProperty(String name, long defaultValue) { long retVal = defaultValue; if (specialProperties != null) { Object lv = specialProperties.get(name); if (lv != null && lv instanceof Long) retVal = ((Long) lv).longValue(); } return retVal; } /** * Extracts property with specified name of type String from the special properties map. * @param name name of the property * @param defaultValue value returned if failed to obtain property value * @return value, <code>defaultValue</code> on failure */ public String extractStringProperty(String name, String defaultValue) { String retVal = defaultValue; if (specialProperties != null) { Object sv = specialProperties.get(name); if (sv != null) retVal = sv.toString(); } return retVal; } /** * Tries to attach the given StopWatch object to the internal specialProperties map * so that it can later be retrieved again using {@link #getStopWatch()}. * This method can be used for profiling the logging system across ACS and JDK classes. */ void setStopWatch(StopWatch sw) { if (specialProperties != null) { specialProperties.put(PARAM_STOPWATCH, sw); } } /** * Tries to get the StopWatch previously set in {@link #setStopWatch(StopWatch)}. * Otherwise returns null. * This method can be used for profiling the logging system across ACS and JDK classes. */ StopWatch getStopWatch() { StopWatch ret = null; if (specialProperties != null) { ret = (StopWatch) specialProperties.get(PARAM_STOPWATCH); } return ret; } // /** // * Extract property with specified name of type Map from the <code>Map</code> // * contained in the object of type <code>java.util.Map</code>. // * @param name name of the property // * @param defaultValue value returned if failed to obtain property value // * @return value, <code>defaultValue</code> on failure // */ // public Map extractMapProperty(String name, Map defaultValue) // { // Map retVal = defaultValue; // if (specialProperties != null) // { // Object mv = specialProperties.get(name); // if (mv != null && mv instanceof Map) // retVal = (Map) mv; // } // // return retVal; // } }