/* * ALMA - Atacama Large Millimiter Array * (c) European Southern Observatory, 2002 * 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.io.PrintStream; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.TreeSet; import java.util.logging.Level; import alma.acs.logging.level.AcsLogLevelDefinition; /** * Defines ACS specific logging levels. * Also provides non-ACS level, which includes * set of standard logging and possibly other vendor levels, * to ACS level mapping. Maps Java-specific levels to ACS specific levels. <p> * Although an OFF level is not mentioned in the Acs documentation, it is included * for the purpose of dealing with bad levels as well as of blocking logging. * * @author Matej Sekoranja (matej.sekoranja@cosylab.com) * @version @@VERSION@@ */ public class AcsLogLevel extends Level implements Comparable<AcsLogLevel> { /** * The resource bundle name to be used in localizing ACS level name. */ private static final String ACS_BUNDLE_NAME = AcsLogLevel.class.getPackage().getName(); /** * List of all ACS levels, sorted automatically with lowest log level first. */ private static final TreeSet<AcsLogLevel> known = new TreeSet<AcsLogLevel>(); /** * Fast lookup table mapping. */ private static final Map<Level, AcsLogLevel> lookup = new HashMap<Level, AcsLogLevel>(); /******************** Java API ACS Levels ********************/ // Currently we try to do without a level definition for ALL, see module loggingidl // /** // * Messages indicating function-calling sequence. // */ // public static final AcsLogLevel ALL = new AcsLogLevel("ALL", Level.ALL.intValue(), ACS_LEVEL_ALL); /** * Messages indicating function-calling sequence. */ public static final AcsLogLevel TRACE = new AcsLogLevel("TRACE", Level.FINEST.intValue(), AcsLogLevelDefinition.TRACE); /** * A lower level than DEBUG, just to allow finer tuning. Generally same meaning. See COMP-3749. */ public static final AcsLogLevel DELOUSE = new AcsLogLevel("DELOUSE", Level.FINER.intValue(), AcsLogLevelDefinition.DELOUSE); /** * Messages that contain information normally of use only when * debugging a program. * Java levels FINE and CONFIG map to this DEBUG level. */ public static final AcsLogLevel DEBUG = new AcsLogLevel("DEBUG", Level.CONFIG.intValue(), AcsLogLevelDefinition.DEBUG); /** * Informational messages. */ public static final AcsLogLevel INFO = new AcsLogLevel("INFO", Level.INFO.intValue(), AcsLogLevelDefinition.INFO); /** * Conditions that are not error conditions, but that may require * special handling. * TODO: use something like 850 instead of 801 to allow other levels between INFO and NOTICE */ public static final AcsLogLevel NOTICE = new AcsLogLevel("NOTICE", 801, AcsLogLevelDefinition.NOTICE); /** * Warning messages. */ public static final AcsLogLevel WARNING = new AcsLogLevel("WARNING", Level.WARNING.intValue(), AcsLogLevelDefinition.WARNING); /** * Error messages. * TODO: use something like 930 instead of 901 to allow other levels between ERROR and WARNING */ public static final AcsLogLevel ERROR = new AcsLogLevel("ERROR", 901, AcsLogLevelDefinition.ERROR); /** * Critical conditions, such as hard device errors. * TODO: use something like 960 instead of 902 to allow other levels between CRITICAL and ERROR */ public static final AcsLogLevel CRITICAL = new AcsLogLevel("CRITICAL", 902, AcsLogLevelDefinition.CRITICAL); /** * A condition that should be corrected immediately, such as a * corrupted system database. * TODO: use something like 980 instead of 903 to allow other levels between ERROR and ALERT */ public static final AcsLogLevel ALERT = new AcsLogLevel("ALERT", 903, AcsLogLevelDefinition.ALERT); /** * A panic condition. This is normally broadcast to all users. */ public static final AcsLogLevel EMERGENCY = new AcsLogLevel("EMERGENCY", Level.SEVERE.intValue(), AcsLogLevelDefinition.EMERGENCY); /** * Level not to be used for actual logging, but to set log levels for filtering. * Overwrites/hides {@link Level.OFF}. */ public static final AcsLogLevel OFF = new AcsLogLevel(Level.OFF.getName(), Level.OFF.intValue(), AcsLogLevelDefinition.OFF); /** * The ACS error system defined level (small integer) which this JDK-style level maps to */ private final AcsLogLevelDefinition acsCoreLevel; /** * XML Entry name. */ private String entryName = null; /** * Create a named Level with a given integer value. * * @param name the name of the Level, for example "INFO". * @param value an integer value for the level. */ public AcsLogLevel(String name, int value, AcsLogLevelDefinition acsCoreLevel) { super(name, value, ACS_BUNDLE_NAME); // create entry name, so that is computed only once entryName = name.substring(0, 1).toUpperCase() + name.substring(1).toLowerCase(); this.acsCoreLevel = acsCoreLevel; // add to tree of known lists synchronized (known) { known.add(this); } // save for fast lookups synchronized (lookup) { lookup.put(this, this); } } /** * Converts an ACS core log level (small integer as defined in ACS IDL) * to the largest matching AcsLogLevel. */ public static AcsLogLevel fromAcsCoreLevel(AcsLogLevelDefinition acsCoreLevel) { AcsLogLevel ret = null; for (AcsLogLevel acsLogLevel : known) { ret = acsLogLevel; if (!(ret.getAcsLevel().compareTo(acsCoreLevel) < 0)) { break; } } return ret; } /** * Converts an ACS core log level (small integer as defined in ACS IDL) * to the lowest matching AcsLogLevel or JDK Level. * <p> * Note the difference to {@link #fromAcsCoreLevel(AcsLogLevelDefinition)}: * <ul> * <li>If more than one JDK-style log level gets mapped to the given core level, * then the <em>lowest</em> of these levels gets returned. * <em>This method is therefore suitable to compute JDK log levels for filtering</em> * when logs with all matching JDK levels should be allowed.</li> * <li>Currently this method is implemented with hard-wired levels, which means that * user-defined subclasses of AcsLogLevel are not considered. * (As of ACS 8.x, there is no known usage of custom log levels. * Supporting them for this method would require to keep a list similar to {@link #known} * that includes all JDK Level and AcsLogLevel objects.) * </ul> */ public static Level getLowestMatchingJdkLevel(AcsLogLevelDefinition acsCoreLevel) { switch (acsCoreLevel) { case TRACE: // numerically the same as Level.FINEST return AcsLogLevel.TRACE; case DELOUSE: // numerically the same as Level.FINER return AcsLogLevel.DELOUSE; case DEBUG: // Both JDK levels FINE and CONFIG map to AcsLogLevel.DEBUG. // AcsLogLevel.DEBUG is numerically the same as CONFIG, which is higher than FINE (reasons related to logic in getNativeLevel()). return Level.FINE; case INFO: // numerically the same as Level.INFO return AcsLogLevel.INFO; case NOTICE: return AcsLogLevel.NOTICE; case WARNING: // numerically the same as Level.WARNING return AcsLogLevel.WARNING; case ERROR: return AcsLogLevel.ERROR; case CRITICAL: return AcsLogLevel.CRITICAL; case ALERT: return AcsLogLevel.ALERT; case EMERGENCY: // numerically the same as Level.SEVERE return AcsLogLevel.EMERGENCY; case OFF: // numerically the same as Level.OFF return AcsLogLevel.OFF; default: throw new IllegalArgumentException("Unexpected enum literal AcsLogLevelDefinition." + acsCoreLevel.name); } } /** * Returns the corresponding ACS core level, which is a small positive integer * defined as "priority" in the ACS logging and archiving architecture document * and coded in IDL (see {@link alma.AcsLogging.alma.LogLevels.WARNING_NAME} and similar constants). * <p> * This level is different from the JDK-style level, which can be any integer. * @return ACS core level */ public AcsLogLevelDefinition getAcsLevel() { return acsCoreLevel; } /** * Returns the ACS XML level entryName. * @return ACS XML level entryName */ public String getEntryName() { return entryName; } /** * Compares the level with the specified level for order. * * @see java.lang.Comparable#compareTo(Object) */ public int compareTo(AcsLogLevel l) { if (intValue() < l.intValue()) return -1; else if (intValue() == l.intValue()) return 0; else return 1; } /** * Maps any (JDK or ACS) level to an ACS native level. * <p> * Note that for some strange historical reason, the ACS log level is called "native" here, * to confuse those who think that JDK levels would be native... * * @param level any level * @return native level, can be <code>null</code> if no native level is found or if level==Level.OFF */ public static AcsLogLevel getNativeLevel(Level level) { // try fast lookup AcsLogLevel luLevel = null; synchronized (lookup) { luLevel = lookup.get(level); } if (luLevel != null) { return luLevel; } // check if there is any native level of OFF if (known.size() == 0 || level.intValue() == Level.OFF.intValue()) return null; // if (level.intValue() == Level.ALL.intValue()) // return AcsLogLevel.ALL; // search through iterator and find the most appropriate. Relies on "known" being a sorted set with low levels first. synchronized (known) { Iterator<AcsLogLevel> iter = known.iterator(); AcsLogLevel acsLevel = iter.next(); while (level.intValue() > acsLevel.intValue() && iter.hasNext()) { acsLevel = iter.next(); } // save for lookup synchronized (lookup) { lookup.put(level, acsLevel); } return acsLevel; } } /** * This method should only be used to generate documentation about the various level mappings in use. * @param ps The PrintStream to print to, e.g. System.out */ static void printMappings(PrintStream ps) { final String delim = "\t"; synchronized (known) { for (AcsLogLevel level : known) { String acsLevelName = level.getEntryName(); int levelValue = level.intValue(); int coreLevelValue = level.getAcsLevel().value; ps.println(acsLevelName + delim + levelValue + delim + coreLevelValue); } } } }