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.apache.logging.log4j.spi; 018 019import java.util.HashSet; 020import java.util.Map; 021import java.util.Set; 022import java.util.concurrent.ConcurrentHashMap; 023import java.util.concurrent.ConcurrentMap; 024import java.util.concurrent.locks.ReadWriteLock; 025import java.util.concurrent.locks.ReentrantReadWriteLock; 026 027import org.apache.logging.log4j.LogManager; 028import org.apache.logging.log4j.util.LoaderUtil; 029 030/** 031 * Provides an abstract base class to use for implementing LoggerAdapter. 032 * 033 * @param <L> the Logger class to adapt 034 * @since 2.1 035 */ 036public abstract class AbstractLoggerAdapter<L> implements LoggerAdapter<L>, LoggerContextShutdownAware { 037 038 /** 039 * A map to store loggers for their given LoggerContexts. 040 */ 041 protected final Map<LoggerContext, ConcurrentMap<String, L>> registry = new ConcurrentHashMap<>(); 042 043 private final ReadWriteLock lock = new ReentrantReadWriteLock (true); 044 045 @Override 046 public L getLogger(final String name) { 047 final LoggerContext context = getContext(); 048 final ConcurrentMap<String, L> loggers = getLoggersInContext(context); 049 final L logger = loggers.get(name); 050 if (logger != null) { 051 return logger; 052 } 053 loggers.putIfAbsent(name, newLogger(name, context)); 054 return loggers.get(name); 055 } 056 057 @Override 058 public void contextShutdown(LoggerContext loggerContext) { 059 registry.remove(loggerContext); 060 } 061 062 /** 063 * Gets or creates the ConcurrentMap of named loggers for a given LoggerContext. 064 * 065 * @param context the LoggerContext to get loggers for 066 * @return the map of loggers for the given LoggerContext 067 */ 068 public ConcurrentMap<String, L> getLoggersInContext(final LoggerContext context) { 069 ConcurrentMap<String, L> loggers; 070 lock.readLock ().lock (); 071 try { 072 loggers = registry.get (context); 073 } finally { 074 lock.readLock ().unlock (); 075 } 076 077 if (loggers != null) { 078 return loggers; 079 } 080 lock.writeLock ().lock (); 081 try { 082 loggers = registry.get (context); 083 if (loggers == null) { 084 loggers = new ConcurrentHashMap<> (); 085 registry.put (context, loggers); 086 if (context instanceof LoggerContextShutdownEnabled) { 087 ((LoggerContextShutdownEnabled) context).addShutdownListener(this); 088 } 089 } 090 return loggers; 091 } finally { 092 lock.writeLock ().unlock (); 093 } 094 } 095 096 /** 097 * For unit testing. Consider to be private. 098 */ 099 public Set<LoggerContext> getLoggerContexts() { 100 return new HashSet<>(registry.keySet()); 101 } 102 103 /** 104 * Creates a new named logger for a given {@link LoggerContext}. 105 * 106 * @param name the name of the logger to create 107 * @param context the LoggerContext this logger will be associated with 108 * @return the new named logger 109 */ 110 protected abstract L newLogger(final String name, final LoggerContext context); 111 112 /** 113 * Gets the {@link LoggerContext} that should be used to look up or create loggers. This is similar in spirit to the 114 * {@code ContextSelector} class in {@code log4j-core}. However, implementations can rely on their own framework's 115 * separation of contexts instead (or simply use a singleton). 116 * 117 * @return the LoggerContext to be used for lookup and creation purposes 118 * @see org.apache.logging.log4j.LogManager#getContext(ClassLoader, boolean) 119 * @see org.apache.logging.log4j.LogManager#getContext(String, boolean) 120 */ 121 protected abstract LoggerContext getContext(); 122 123 /** 124 * Gets the {@link LoggerContext} associated with the given caller class. 125 * 126 * @param callerClass the caller class 127 * @return the LoggerContext for the calling class 128 */ 129 protected LoggerContext getContext(final Class<?> callerClass) { 130 ClassLoader cl = null; 131 if (callerClass != null) { 132 cl = callerClass.getClassLoader(); 133 } 134 if (cl == null) { 135 cl = LoaderUtil.getThreadContextClassLoader(); 136 } 137 return LogManager.getContext(cl, false); 138 } 139 140 @Override 141 public void close() { 142 lock.writeLock ().lock (); 143 try { 144 registry.clear(); 145 } finally { 146 lock.writeLock ().unlock (); 147 } 148 } 149}