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.core.async;
019
020import java.util.regex.Matcher;
021import java.util.regex.Pattern;
022
023import org.apache.logging.log4j.status.StatusLogger;
024import org.apache.logging.log4j.util.Constants;
025import org.apache.logging.log4j.util.PropertiesUtil;
026
027/**
028 * Strategy for deciding whether thread name should be cached or not.
029 */
030public enum ThreadNameCachingStrategy { // LOG4J2-467
031    CACHED {
032        @Override
033        public String getThreadName() {
034            String result = THREADLOCAL_NAME.get();
035            if (result == null) {
036                result = Thread.currentThread().getName();
037                THREADLOCAL_NAME.set(result);
038            }
039            return result;
040        }
041    },
042    UNCACHED {
043        @Override
044        public String getThreadName() {
045            return Thread.currentThread().getName();
046        }
047    };
048
049    private static final StatusLogger LOGGER = StatusLogger.getLogger();
050    private static final ThreadLocal<String> THREADLOCAL_NAME = new ThreadLocal<>();
051    static final ThreadNameCachingStrategy DEFAULT_STRATEGY = isAllocatingThreadGetName() ? CACHED : UNCACHED;
052
053    abstract String getThreadName();
054
055    public static ThreadNameCachingStrategy create() {
056        final String name = PropertiesUtil.getProperties().getStringProperty("AsyncLogger.ThreadNameStrategy");
057        try {
058            final ThreadNameCachingStrategy result = name != null ? ThreadNameCachingStrategy.valueOf(name) : DEFAULT_STRATEGY;
059            LOGGER.debug("AsyncLogger.ThreadNameStrategy={} (user specified {}, default is {})",
060                         result.name(), name, DEFAULT_STRATEGY.name());
061            return result;
062        } catch (final Exception ex) {
063            LOGGER.debug("Using AsyncLogger.ThreadNameStrategy.{}: '{}' not valid: {}",
064                         DEFAULT_STRATEGY.name(), name, ex.toString());
065            return DEFAULT_STRATEGY;
066        }
067    }
068
069    static boolean isAllocatingThreadGetName() {
070        // LOG4J2-2052, LOG4J2-2635 JDK 8u102 ("1.8.0_102") removed the String allocation in Thread.getName()
071        if (Constants.JAVA_MAJOR_VERSION == 8) {
072            try {
073                Pattern javaVersionPattern = Pattern.compile("(\\d+)\\.(\\d+)\\.(\\d+)_(\\d+)");
074                Matcher m = javaVersionPattern.matcher(System.getProperty("java.version"));
075                if (m.matches()) {
076                    return Integer.parseInt(m.group(3)) == 0 && Integer.parseInt(m.group(4)) < 102;
077                }
078                return true;
079            } catch (Exception e) {
080                return true;
081            }
082        } else {
083            return Constants.JAVA_MAJOR_VERSION < 8;
084        }
085    }
086}