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 */
017package org.slf4j.helpers;
018
019import java.util.Map;
020import java.util.WeakHashMap;
021import java.util.concurrent.ConcurrentHashMap;
022import java.util.concurrent.ConcurrentMap;
023
024import org.apache.logging.log4j.LogManager;
025import org.apache.logging.log4j.spi.AbstractLogger;
026import org.apache.logging.log4j.spi.LoggerContext;
027import org.apache.logging.slf4j.SLF4JLoggingException;
028import org.slf4j.ILoggerFactory;
029import org.slf4j.Logger;
030import org.slf4j.LoggerFactory;
031import org.slf4j.impl.SLF4JLogger;
032
033/**
034 *
035 */
036public class Log4jLoggerFactory implements ILoggerFactory {
037
038    private static final String FQCN = Log4jLoggerFactory.class.getName();
039    private static final String PACKAGE = "org.slf4j";
040
041    private final Map<LoggerContext, ConcurrentMap<String, Logger>> contextMap =
042        new WeakHashMap<LoggerContext, ConcurrentMap<String, Logger>>();
043
044    @Override
045    public Logger getLogger(final String name) {
046        final LoggerContext context = getContext();
047        final ConcurrentMap<String, Logger> loggers = getLoggersMap(context);
048
049        if (loggers.containsKey(name)) {
050            return loggers.get(name);
051        }
052        final String key = Logger.ROOT_LOGGER_NAME.equals(name) ? LogManager.ROOT_LOGGER_NAME : name;
053        final org.apache.logging.log4j.Logger logger = context.getLogger(key);
054        if (logger instanceof AbstractLogger) {
055            loggers.putIfAbsent(name, new SLF4JLogger((AbstractLogger) logger, name));
056            return loggers.get(name);
057        }
058        throw new SLF4JLoggingException("SLF4J Adapter requires base logging system to extend Log4j AbstractLogger");
059    }
060
061    private ConcurrentMap<String, Logger> getLoggersMap(final LoggerContext context) {
062        synchronized (contextMap) {
063            ConcurrentMap<String, Logger> map = contextMap.get(context);
064            if (map == null) {
065                map = new ConcurrentHashMap<String, Logger>();
066                contextMap.put(context, map);
067            }
068            return map;
069        }
070    }
071    private LoggerContext getContext() {
072        final Throwable t = new Throwable();
073        boolean next = false;
074        boolean pkg = false;
075        String fqcn = LoggerFactory.class.getName();
076        for (final StackTraceElement element : t.getStackTrace()) {
077            if (FQCN.equals(element.getClassName())) {
078                next = true;
079                continue;
080            }
081            if (next && element.getClassName().startsWith(PACKAGE)) {
082                fqcn = element.getClassName();
083                pkg = true;
084                continue;
085            }
086            if (pkg) {
087                break;
088            }
089        }
090        return PrivateManager.getContext(fqcn);
091    }
092
093    /**
094     * The real bridge between SLF4J and Log4j.
095     */
096    private static class PrivateManager extends LogManager {
097        private static final String FQCN = LoggerFactory.class.getName();
098
099        public static LoggerContext getContext() {
100            return getContext(FQCN, false);
101        }
102
103        public static LoggerContext getContext(final String fqcn) {
104            return getContext(fqcn, false);
105        }
106
107        public static org.apache.logging.log4j.Logger getLogger(final String name) {
108            return getLogger(FQCN, name);
109        }
110    }
111
112}