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.Map;
20 import java.util.WeakHashMap;
21 import java.util.concurrent.ConcurrentHashMap;
22 import java.util.concurrent.ConcurrentMap;
23 import java.util.concurrent.locks.ReadWriteLock;
24 import java.util.concurrent.locks.ReentrantReadWriteLock;
25
26 import org.apache.logging.log4j.LogManager;
27 import org.apache.logging.log4j.util.LoaderUtil;
28
29 /**
30 * Provides an abstract base class to use for implementing LoggerAdapter.
31 *
32 * @param <L> the Logger class to adapt
33 * @since 2.1
34 */
35 public abstract class AbstractLoggerAdapter<L> implements LoggerAdapter<L> {
36
37 /**
38 * A map to store loggers for their given LoggerContexts.
39 */
40 protected final Map<LoggerContext, ConcurrentMap<String, L>> registry = new WeakHashMap<>();
41
42 private final ReadWriteLock lock = new ReentrantReadWriteLock (true);
43
44 @Override
45 public L getLogger(final String name) {
46 final LoggerContext context = getContext();
47 final ConcurrentMap<String, L> loggers = getLoggersInContext(context);
48 final L logger = loggers.get(name);
49 if (logger != null) {
50 return logger;
51 }
52 loggers.putIfAbsent(name, newLogger(name, context));
53 return loggers.get(name);
54 }
55
56 /**
57 * Gets or creates the ConcurrentMap of named loggers for a given LoggerContext.
58 *
59 * @param context the LoggerContext to get loggers for
60 * @return the map of loggers for the given LoggerContext
61 */
62 public ConcurrentMap<String, L> getLoggersInContext(final LoggerContext context) {
63 ConcurrentMap<String, L> loggers;
64 lock.readLock ().lock ();
65 try {
66 loggers = registry.get (context);
67 } finally {
68 lock.readLock ().unlock ();
69 }
70
71 if (loggers != null) {
72 return loggers;
73 }
74 lock.writeLock ().lock ();
75 try {
76 loggers = registry.get (context);
77 if (loggers == null) {
78 loggers = new ConcurrentHashMap<> ();
79 registry.put (context, loggers);
80 }
81 return loggers;
82 } finally {
83 lock.writeLock ().unlock ();
84 }
85 }
86
87 /**
88 * Creates a new named logger for a given {@link LoggerContext}.
89 *
90 * @param name the name of the logger to create
91 * @param context the LoggerContext this logger will be associated with
92 * @return the new named logger
93 */
94 protected abstract L newLogger(final String name, final LoggerContext context);
95
96 /**
97 * Gets the {@link LoggerContext} that should be used to look up or create loggers. This is similar in spirit to the
98 * {@code ContextSelector} class in {@code log4j-core}. However, implementations can rely on their own framework's
99 * separation of contexts instead (or simply use a singleton).
100 *
101 * @return the LoggerContext to be used for lookup and creation purposes
102 * @see org.apache.logging.log4j.LogManager#getContext(ClassLoader, boolean)
103 * @see org.apache.logging.log4j.LogManager#getContext(String, boolean)
104 */
105 protected abstract LoggerContext getContext();
106
107 /**
108 * Gets the {@link LoggerContext} associated with the given caller class.
109 *
110 * @param callerClass the caller class
111 * @return the LoggerContext for the calling class
112 */
113 protected LoggerContext getContext(final Class<?> callerClass) {
114 ClassLoader cl = null;
115 if (callerClass != null) {
116 cl = callerClass.getClassLoader();
117 }
118 if (cl == null) {
119 cl = LoaderUtil.getThreadContextClassLoader();
120 }
121 return LogManager.getContext(cl, false);
122 }
123
124 @Override
125 public void close() {
126 lock.writeLock ().lock ();
127 try {
128 registry.clear();
129 } finally {
130 lock.writeLock ().unlock ();
131 }
132 }
133 }