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    package org.apache.logging.log4j.core.pattern;
018    
019    import java.util.HashMap;
020    import java.util.Locale;
021    import java.util.Map;
022    
023    import org.apache.logging.log4j.Level;
024    import org.apache.logging.log4j.core.LogEvent;
025    import org.apache.logging.log4j.core.config.plugins.Plugin;
026    import org.apache.logging.log4j.core.util.Patterns;
027    
028    /**
029     * Returns the event's level in a StringBuilder.
030     */
031    @Plugin(name = "LevelPatternConverter", category = PatternConverter.CATEGORY)
032    @ConverterKeys({ "p", "level" })
033    public final class LevelPatternConverter extends LogEventPatternConverter {
034        private static final String OPTION_LENGTH = "length";
035        private static final String OPTION_LOWER = "lowerCase";
036    
037        /**
038         * Singleton.
039         */
040        private static final LevelPatternConverter INSTANCE = new LevelPatternConverter(null);
041    
042        private final Map<Level, String> levelMap;
043    
044        /**
045         * Private constructor.
046         */
047        private LevelPatternConverter(final Map<Level, String> map) {
048            super("Level", "level");
049            this.levelMap = map;
050        }
051    
052        /**
053         * Obtains an instance of pattern converter.
054         *
055         * @param options
056         *            options, may be null. May contain a list of level names and The value that should be displayed for the
057         *            Level.
058         * @return instance of pattern converter.
059         */
060        public static LevelPatternConverter newInstance(final String[] options) {
061            if (options == null || options.length == 0) {
062                return INSTANCE;
063            }
064            final Map<Level, String> levelMap = new HashMap<Level, String>();
065            int length = Integer.MAX_VALUE; // More than the longest level name.
066            boolean lowerCase = false;
067            final String[] definitions = options[0].split(Patterns.COMMA_SEPARATOR);
068            for (final String def : definitions) {
069                final String[] pair = def.split("=");
070                if (pair == null || pair.length != 2) {
071                    LOGGER.error("Invalid option {}", def);
072                    continue;
073                }
074                final String key = pair[0].trim();
075                final String value = pair[1].trim();
076                if (OPTION_LENGTH.equalsIgnoreCase(key)) {
077                    length = Integer.parseInt(value);
078                } else if (OPTION_LOWER.equalsIgnoreCase(key)) {
079                    lowerCase = Boolean.parseBoolean(value);
080                } else {
081                    final Level level = Level.toLevel(key, null);
082                    if (level == null) {
083                        LOGGER.error("Invalid Level {}", key);
084                    } else {
085                        levelMap.put(level, value);
086                    }
087                }
088            }
089            if (levelMap.isEmpty() && length == Integer.MAX_VALUE && !lowerCase) {
090                return INSTANCE;
091            }
092            for (final Level level : Level.values()) {
093                if (!levelMap.containsKey(level)) {
094                    final String left = left(level, length);
095                    levelMap.put(level, lowerCase ? left.toLowerCase(Locale.US) : left);
096                }
097            }
098            return new LevelPatternConverter(levelMap);
099        }
100    
101        /**
102         * Returns the leftmost chars of the level name for the given level.
103         *
104         * @param level
105         *            The level
106         * @param length
107         *            How many chars to return
108         * @return The abbreviated level name, or the whole level name if the {@code length} is greater than the level name
109         *         length,
110         */
111        private static String left(final Level level, final int length) {
112            final String string = level.toString();
113            if (length >= string.length()) {
114                return string;
115            }
116            return string.substring(0, length);
117        }
118    
119        /**
120         * {@inheritDoc}
121         */
122        @Override
123        public void format(final LogEvent event, final StringBuilder output) {
124            output.append(levelMap == null ? event.getLevel().toString() : levelMap.get(event.getLevel()));
125        }
126    
127        /**
128         * {@inheritDoc}
129         */
130        @Override
131        public String getStyleClass(final Object e) {
132            if (e instanceof LogEvent) {
133                return "level " + ((LogEvent) e).getLevel().name().toLowerCase(Locale.ENGLISH);
134            }
135    
136            return "level";
137        }
138    }