View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.spi;
18  
19  import java.util.HashSet;
20  import java.util.Map;
21  import java.util.Set;
22  import java.util.concurrent.ConcurrentHashMap;
23  import java.util.concurrent.ConcurrentMap;
24  import java.util.concurrent.locks.ReadWriteLock;
25  import java.util.concurrent.locks.ReentrantReadWriteLock;
26  
27  import org.apache.logging.log4j.LogManager;
28  import org.apache.logging.log4j.util.LoaderUtil;
29  
30  /**
31   * Provides an abstract base class to use for implementing LoggerAdapter.
32   * 
33   * @param <L> the Logger class to adapt
34   * @since 2.1
35   */
36  public abstract class AbstractLoggerAdapter<L> implements LoggerAdapter<L>, LoggerContextShutdownAware {
37  
38      /**
39       * A map to store loggers for their given LoggerContexts.
40       */
41      protected final Map<LoggerContext, ConcurrentMap<String, L>> registry = new ConcurrentHashMap<>();
42  
43      private final ReadWriteLock lock = new ReentrantReadWriteLock (true);
44  
45      @Override
46      public L getLogger(final String name) {
47          final LoggerContext context = getContext();
48          final ConcurrentMap<String, L> loggers = getLoggersInContext(context);
49          final L logger = loggers.get(name);
50          if (logger != null) {
51              return logger;
52          }
53          loggers.putIfAbsent(name, newLogger(name, context));
54          return loggers.get(name);
55      }
56  
57      @Override
58      public void contextShutdown(LoggerContext loggerContext) {
59          registry.remove(loggerContext);
60      }
61  
62      /**
63       * Gets or creates the ConcurrentMap of named loggers for a given LoggerContext.
64       *
65       * @param context the LoggerContext to get loggers for
66       * @return the map of loggers for the given LoggerContext
67       */
68      public ConcurrentMap<String, L> getLoggersInContext(final LoggerContext context) {
69          ConcurrentMap<String, L> loggers;
70          lock.readLock ().lock ();
71          try {
72              loggers = registry.get (context);
73          } finally {
74              lock.readLock ().unlock ();
75          }
76  
77          if (loggers != null) {
78              return loggers;
79          }
80          lock.writeLock ().lock ();
81          try {
82              loggers = registry.get (context);
83              if (loggers == null) {
84                  loggers = new ConcurrentHashMap<> ();
85                  registry.put (context, loggers);
86                  if (context instanceof LoggerContextShutdownEnabled) {
87                      ((LoggerContextShutdownEnabled) context).addShutdownListener(this);
88                  }
89              }
90              return loggers;
91          } finally {
92              lock.writeLock ().unlock ();
93          }
94      }
95  
96      /**
97       * For unit testing. Consider to be private.
98       */
99      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 }