/* * Copyright (c) 2015-2016, Christoph Engelbert (aka noctarius) and * contributors. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.noctarius.tengi.spi.logging; import com.noctarius.tengi.core.exception.LoggerException; import java.util.HashMap; import java.util.Map; import java.util.ServiceLoader; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * The <tt>LoggerManager</tt> is a utility class to create and cache * {@link com.noctarius.tengi.spi.logging.Logger} instances. It will * automatically scan the classpath for implementations of * {@link com.noctarius.tengi.spi.logging.LoggerFactory} classes using * the Java ServiceLoader API and registers each of them, however it * is recommended to have only one logger adapter avaialable at a * given time. */ public final class LoggerManager { private static final LoggerManager INSTANCE = new LoggerManager(); private static final String BINDING_PREFIX_NAME = "::name::"; private static final String BINDING_PREFIX_CLASS = "::class::"; private static final Logger NOOP_LOGGER = new NoOpLogger(); private static final Map<Class<?>, LoggerFactory> LOGGER_FACTORIES; private static final LoggerFactory SINGLE_AVAILABLE_FACTORY; static { ServiceLoader<LoggerFactory> serviceLoader = ServiceLoader.load(LoggerFactory.class); Map<Class<?>, LoggerFactory> loggerFactories = new HashMap<>(); serviceLoader.forEach((loggerFactory) -> loggerFactories.put(loggerFactory.loggerClass(), loggerFactory)); LOGGER_FACTORIES = loggerFactories; LoggerFactory singleAvailableFactory = null; if (loggerFactories.size() == 1) { singleAvailableFactory = loggerFactories.values().iterator().next(); } SINGLE_AVAILABLE_FACTORY = singleAvailableFactory; } private final ConcurrentMap<String, Logger> loggerCache = new ConcurrentHashMap<>(); private LoggerManager() { } private Logger getLogger0(Class<?> binding, LoggerFactory loggerFactory) { String loggerName = buildLoggerName(binding, loggerFactory); return loggerCache.computeIfAbsent(loggerName, (key) -> { if (loggerFactory != null) { return loggerFactory.create(binding); } return NOOP_LOGGER; }); } private Logger getLogger0(String binding, LoggerFactory loggerFactory) { String loggerName = buildLoggerName(binding, loggerFactory); return loggerCache.computeIfAbsent(loggerName, (key) -> { if (loggerFactory != null) { return loggerFactory.create(binding); } return NOOP_LOGGER; }); } /** * Retrieves a logger bound to the given <tt>binding</tt> type. If a matching binding * for another <tt>Logger</tt> instance already exists the cached instance is returned. * * @param binding the <tt>java.lang.Class</tt> to bind to * @return the new <tt>Logger</tt> instance or a cached one if the binding is already created */ public static Logger getLogger(Class<?> binding) { if (LOGGER_FACTORIES.size() > 1) { throw new LoggerException("Multiple Logger frameworks registered, please choose Logger type explicitly"); } return getLogger(binding, SINGLE_AVAILABLE_FACTORY); } /** * Retrieves a logger bound to the given <tt>binding</tt> string. If a matching binding * for another <tt>Logger</tt> instance already exists the cached instance is returned. * * @param binding the <tt>java.lang.String</tt> to bind to * @return the new <tt>Logger</tt> instance or a cached one if the binding is already created */ public static Logger getLogger(String binding) { if (LOGGER_FACTORIES.size() > 1) { throw new LoggerException("Multiple Logger frameworks registered, please choose Logger type explicitly"); } return getLogger(binding, SINGLE_AVAILABLE_FACTORY); } /** * Retrieves a logger bound to the given <tt>binding</tt> type. If a matching binding * for another <tt>Logger</tt> instance already exists the cached instance is returned. * * @param binding the <tt>java.lang.Class</tt> to bind to * @param loggerType the {@link com.noctarius.tengi.spi.logging.Logger} type if multiple adapters are registered * @return the new <tt>Logger</tt> instance or a cached one if the binding is already created */ public static Logger getLogger(Class<?> binding, Class<?> loggerType) { LoggerFactory loggerFactory = LOGGER_FACTORIES.get(loggerType); if (loggerFactory == null) { throw new LoggerException("Requested Logger frameworks is not registered"); } return getLogger(binding, loggerFactory); } /** * Retrieves a logger bound to the given <tt>binding</tt> string. If a matching binding * for another <tt>Logger</tt> instance already exists the cached instance is returned. * * @param binding the <tt>java.lang.String</tt> to bind to * @param loggerType the {@link com.noctarius.tengi.spi.logging.Logger} type if multiple adapters are registered * @return the new <tt>Logger</tt> instance or a cached one if the binding is already created */ public static Logger getLogger(String binding, Class<?> loggerType) { LoggerFactory loggerFactory = LOGGER_FACTORIES.get(loggerType); if (loggerFactory == null) { throw new LoggerException("Requested Logger frameworks is not registered"); } return getLogger(binding, loggerFactory); } private static Logger getLogger(Class<?> binding, LoggerFactory loggerFactory) { return INSTANCE.getLogger0(binding, loggerFactory); } private static Logger getLogger(String binding, LoggerFactory loggerFactory) { return INSTANCE.getLogger0(binding, loggerFactory); } private static String buildLoggerName(Class<?> binding, LoggerFactory loggerFactory) { return buildLoggerName(binding.getName(), BINDING_PREFIX_CLASS, loggerFactory); } private static String buildLoggerName(String binding, LoggerFactory loggerFactory) { return buildLoggerName(binding, BINDING_PREFIX_NAME, loggerFactory); } private static String buildLoggerName(String binding, String prefix, LoggerFactory loggerFactory) { String loggerClassName = loggerFactory == null ? "noop" : loggerFactory.loggerClass().getSimpleName(); return prefix + loggerClassName + "::" + binding; } private static final class NoOpLogger implements Logger { @Override public void log(Level level, Throwable throwable, String message) { } @Override public void log(Level level, Throwable throwable, String message, Object arg) { } @Override public void log(Level level, Throwable throwable, String message, Object arg1, Object arg2) { } @Override public void log(Level level, Throwable throwable, String message, Object arg1, Object arg2, Object arg3) { } @Override public void log(Level level, Throwable throwable, String message, Object arg1, Object arg2, Object arg3, Object... args) { } } }