001package org.cache2k.impl.util;
002
003/*
004 * #%L
005 * cache2k core package
006 * %%
007 * Copyright (C) 2000 - 2015 headissue GmbH, Munich
008 * %%
009 * This program is free software: you can redistribute it and/or modify
010 * it under the terms of the GNU General Public License as
011 * published by the Free Software Foundation, either version 3 of the 
012 * License, or (at your option) any later version.
013 * 
014 * This program is distributed in the hope that it will be useful,
015 * but WITHOUT ANY WARRANTY; without even the implied warranty of
016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
017 * GNU General Public License for more details.
018 * 
019 * You should have received a copy of the GNU General Public 
020 * License along with this program.  If not, see
021 * <http://www.gnu.org/licenses/gpl-3.0.html>.
022 * #L%
023 */
024
025import java.util.ServiceLoader;
026import java.util.WeakHashMap;
027import java.util.concurrent.atomic.AtomicInteger;
028import java.util.logging.Level;
029import java.util.logging.Logger;
030
031/**
032 * cache2k has only sparse logging. The direct use of java.util.logging was
033 * examined. However, a thread name is missing within the output and it
034 * is not properly recorded. To have the chance to redirect any
035 * logging to the framework of choice, the log output is channeled
036 * through this class.
037 *
038 * <p/>To hook in another log framework provide another LogFactory
039 * implementation via the service loader.
040 *
041 * @author Jens Wilke; created: 2014-04-27
042 * @see ServiceLoader
043 */
044public abstract class Log {
045
046  static WeakHashMap<String, Log> loggers = new WeakHashMap<String, Log>();
047
048  static LogFactory logFactory;
049
050  public static Log getLog(Class<?> _class) {
051    return getLog(_class.getName());
052  }
053
054  public static synchronized Log getLog(String s) {
055    Log l = loggers.get(s);
056    if (l != null) {
057      return l;
058    }
059    if (logFactory != null) {
060      l = logFactory.getLog(s);
061      loggers.put(s, l);
062      return l;
063    }
064    ServiceLoader<LogFactory> loader = ServiceLoader.load(LogFactory.class);
065    for (LogFactory lf : loader) {
066      logFactory = lf;
067      getLog(Log.class.getName()).debug("New instance, using: " + logFactory);
068      return getLog(s);
069    }
070    try {
071      final org.apache.commons.logging.LogFactory cl =
072        org.apache.commons.logging.LogFactory.getFactory();
073      logFactory = new LogFactory() {
074        @Override
075        public Log getLog(String s) {
076          return new CommonsLogger(cl.getInstance(s));
077        }
078      };
079      getLog(Log.class.getName()).debug("New instance, using: " + logFactory);
080      return getLog(s);
081    } catch (NoClassDefFoundError ignore) {
082    }
083    logFactory = new LogFactory() {
084      @Override
085      public Log getLog(String s) {
086        return new JdkLogger(Logger.getLogger(s));
087      }
088    };
089    getLog(Log.class.getName()).debug("New instance, using: " + logFactory);
090    return getLog(s);
091  }
092
093  /**
094   * Redirects log output, this is used for testing purposes.
095   */
096  public static synchronized void registerSuppression(String s, Log l) {
097    loggers.put(s, l);
098  }
099
100  public static synchronized void unregisterSuppression(String s) {
101    loggers.remove(s);
102  }
103
104  public abstract boolean isDebugEnabled();
105
106  public abstract boolean isInfoEnabled();
107
108  public abstract void debug(String s);
109
110  public abstract void debug(String s, Throwable ex);
111
112  public abstract void info(String s);
113
114  public abstract void info(String s, Throwable ex);
115
116  public abstract void warn(String s);
117
118  public abstract void warn(String s, Throwable ex);
119
120  private static class CommonsLogger extends Log {
121
122    org.apache.commons.logging.Log cLog;
123
124    private CommonsLogger(org.apache.commons.logging.Log cLog) {
125      this.cLog = cLog;
126    }
127
128    @Override
129    public boolean isDebugEnabled() {
130      return cLog.isDebugEnabled();
131    }
132
133    @Override
134    public boolean isInfoEnabled() {
135      return cLog.isInfoEnabled();
136    }
137
138    @Override
139    public void debug(String s) {
140      cLog.debug(s);
141    }
142
143    @Override
144    public void debug(String s, Throwable ex) {
145      cLog.debug(s, ex);
146    }
147
148    @Override
149    public void info(String s, Throwable ex) {
150      cLog.info(s);
151    }
152
153    @Override
154    public void info(String s) {
155      cLog.info(s);
156    }
157
158    @Override
159    public void warn(String s) {
160      cLog.warn(s);
161    }
162
163    @Override
164    public void warn(String s, Throwable ex) {
165      cLog.warn(s, ex);
166    }
167  }
168
169  private static class JdkLogger extends Log {
170
171    Logger logger;
172
173    private JdkLogger(Logger logger) {
174      this.logger = logger;
175    }
176
177    @Override
178    public boolean isDebugEnabled() {
179      return logger.isLoggable(Level.FINE);
180    }
181
182    @Override
183    public boolean isInfoEnabled() {
184      return logger.isLoggable(Level.INFO);
185    }
186
187    @Override
188    public void debug(String s) {
189      logger.log(Level.FINE, s);
190    }
191
192    @Override
193    public void debug(String s, Throwable ex) {
194      logger.log(Level.FINE, s, ex);
195    }
196
197    @Override
198    public void info(String s) {
199      logger.log(Level.INFO, s);
200    }
201
202    @Override
203    public void info(String s, Throwable ex) {
204      logger.log(Level.INFO, s, ex);
205    }
206
207    @Override
208    public void warn(String s) {
209      logger.log(Level.WARNING, s);
210    }
211
212    @Override
213    public void warn(String s, Throwable ex) {
214      logger.log(Level.WARNING, s, ex);
215    }
216  }
217
218  /**
219   * Log implementation that can be used to count suppressed log outputs.
220   */
221  public static class SuppressionCounter extends Log {
222
223    AtomicInteger debugCount = new AtomicInteger();
224    AtomicInteger infoCount = new AtomicInteger();
225    AtomicInteger warnCount = new AtomicInteger();
226
227    @Override
228    public boolean isDebugEnabled() {
229      return true;
230    }
231
232    @Override
233    public boolean isInfoEnabled() {
234      return true;
235    }
236
237    @Override
238    public void debug(String s) {
239      debugCount.incrementAndGet();
240    }
241
242    @Override
243    public void debug(String s, Throwable ex) {
244      debugCount.incrementAndGet();
245    }
246
247    @Override
248    public void info(String s, Throwable ex) {
249      infoCount.incrementAndGet();
250    }
251
252    @Override
253    public void info(String s) {
254      infoCount.incrementAndGet();
255    }
256
257    @Override
258    public void warn(String s) {
259      warnCount.incrementAndGet();
260    }
261
262    @Override
263    public void warn(String s, Throwable ex) {
264      warnCount.incrementAndGet();
265    }
266
267    public int getDebugCount() {
268      return debugCount.get();
269    }
270
271    public int getInfoCount() {
272      return infoCount.get();
273    }
274
275    public int getWarnCount() {
276      return warnCount.get();
277    }
278  }
279
280}