001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017
018package org.apache.logging.log4j.jul;
019
020import java.io.Serializable;
021import java.util.ArrayList;
022import java.util.Collections;
023import java.util.Comparator;
024import java.util.IdentityHashMap;
025import java.util.List;
026import java.util.Map;
027import java.util.concurrent.ConcurrentHashMap;
028import java.util.concurrent.ConcurrentMap;
029
030import org.apache.logging.log4j.Level;
031
032/**
033 * Default implementation of LevelConverter strategy.
034 * <p>
035 * Since 2.4, supports custom JUL levels by mapping them to their closest mapped neighbour.
036 * </p>
037 *
038 * @since 2.1
039 */
040public class DefaultLevelConverter implements LevelConverter {
041
042    static final class JulLevelComparator implements Comparator<java.util.logging.Level>, Serializable {
043        private static final long serialVersionUID = 1L;
044        @Override
045        public int compare(final java.util.logging.Level level1, final java.util.logging.Level level2) {
046            return Integer.compare(level1.intValue(), level2.intValue());
047        }
048    }
049
050    private final ConcurrentMap<java.util.logging.Level, Level> julToLog4j = new ConcurrentHashMap<>(9);
051    private final Map<Level, java.util.logging.Level> log4jToJul = new IdentityHashMap<>(10);
052    private final List<java.util.logging.Level> sortedJulLevels = new ArrayList<>(9);
053
054    public DefaultLevelConverter() {
055        // Map JUL to Log4j
056        mapJulToLog4j(java.util.logging.Level.ALL, Level.ALL);
057        mapJulToLog4j(java.util.logging.Level.FINEST, LevelTranslator.FINEST);
058        mapJulToLog4j(java.util.logging.Level.FINER, Level.TRACE);
059        mapJulToLog4j(java.util.logging.Level.FINE, Level.DEBUG);
060        mapJulToLog4j(java.util.logging.Level.CONFIG, LevelTranslator.CONFIG);
061        mapJulToLog4j(java.util.logging.Level.INFO, Level.INFO);
062        mapJulToLog4j(java.util.logging.Level.WARNING, Level.WARN);
063        mapJulToLog4j(java.util.logging.Level.SEVERE, Level.ERROR);
064        mapJulToLog4j(java.util.logging.Level.OFF, Level.OFF);
065        // Map Log4j to JUL
066        mapLog4jToJul(Level.ALL, java.util.logging.Level.ALL);
067        mapLog4jToJul(LevelTranslator.FINEST, java.util.logging.Level.FINEST);
068        mapLog4jToJul(Level.TRACE, java.util.logging.Level.FINER);
069        mapLog4jToJul(Level.DEBUG, java.util.logging.Level.FINE);
070        mapLog4jToJul(LevelTranslator.CONFIG, java.util.logging.Level.CONFIG);
071        mapLog4jToJul(Level.INFO, java.util.logging.Level.INFO);
072        mapLog4jToJul(Level.WARN, java.util.logging.Level.WARNING);
073        mapLog4jToJul(Level.ERROR, java.util.logging.Level.SEVERE);
074        mapLog4jToJul(Level.FATAL, java.util.logging.Level.SEVERE);
075        mapLog4jToJul(Level.OFF, java.util.logging.Level.OFF);
076        // Sorted Java levels
077        sortedJulLevels.addAll(julToLog4j.keySet());
078        Collections.sort(sortedJulLevels, new JulLevelComparator());
079
080    }
081
082    private long distance(final java.util.logging.Level javaLevel, final java.util.logging.Level customJavaLevel) {
083        return Math.abs((long) customJavaLevel.intValue() - (long) javaLevel.intValue());
084    }
085
086    /*
087     * TODO consider making public for advanced configuration.
088     */
089    private void mapJulToLog4j(final java.util.logging.Level julLevel, final Level level) {
090        julToLog4j.put(julLevel, level);
091    }
092
093    /*
094     * TODO consider making public for advanced configuration.
095     */
096    private void mapLog4jToJul(final Level level, final java.util.logging.Level julLevel) {
097        log4jToJul.put(level, julLevel);
098    }
099
100    private Level nearestLevel(final java.util.logging.Level customJavaLevel) {
101        long prevDist = Long.MAX_VALUE;
102        java.util.logging.Level prevLevel = null;
103        for (final java.util.logging.Level mappedJavaLevel : sortedJulLevels) {
104            final long distance = distance(customJavaLevel, mappedJavaLevel);
105            if (distance > prevDist) {
106                return julToLog4j.get(prevLevel);
107            }
108            prevDist = distance;
109            prevLevel = mappedJavaLevel;
110        }
111        return julToLog4j.get(prevLevel);
112    }
113
114    @Override
115    public java.util.logging.Level toJavaLevel(final Level level) {
116        return log4jToJul.get(level);
117    }
118
119    @Override
120    public Level toLevel(final java.util.logging.Level javaLevel) {
121        if (javaLevel == null) {
122            return null;
123        }
124        final Level level = julToLog4j.get(javaLevel);
125        if (level != null) {
126            return level;
127        }
128        final Level nearestLevel = nearestLevel(javaLevel);
129        julToLog4j.put(javaLevel, nearestLevel);
130        return nearestLevel;
131    }
132}