ch.qos.logback.classic.joran.SerializedModelConfigurator Maven / Gradle / Ivy
/*
* Logback: the reliable, generic, fast and flexible logging framework.
* Copyright (C) 1999-2023, QOS.ch. All rights reserved.
*
* This program and the accompanying materials are dual-licensed under
* either the terms of the Eclipse Public License v1.0 as published by
* the Eclipse Foundation
*
* or (per the licensee's choosing)
*
* under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation.
*/
package ch.qos.logback.classic.joran;
import ch.qos.logback.classic.ClassicConstants;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.serializedModel.HardenedModelInputStream;
import ch.qos.logback.classic.model.processor.LogbackClassicDefaultNestedComponentRules;
import ch.qos.logback.classic.spi.ConfiguratorRank;
import ch.qos.logback.core.Context;
import ch.qos.logback.core.LogbackException;
import ch.qos.logback.core.model.Model;
import ch.qos.logback.core.model.ModelUtil;
import ch.qos.logback.core.model.processor.DefaultProcessor;
import ch.qos.logback.core.model.processor.ModelInterpretationContext;
import ch.qos.logback.classic.spi.Configurator;
import ch.qos.logback.core.spi.ContextAwareBase;
import ch.qos.logback.core.status.InfoStatus;
import ch.qos.logback.core.status.StatusManager;
import ch.qos.logback.core.util.Loader;
import ch.qos.logback.core.util.OptionHelper;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.locks.ReentrantLock;
import static ch.qos.logback.core.CoreConstants.MODEL_CONFIG_FILE_EXTENSION;
/**
* @since 1.3.9/1.4.9
*/
// BEWARE: the fqcn is used in SerializedModelModelHandler
@ConfiguratorRank(value = ConfiguratorRank.SERIALIZED_MODEL)
public class SerializedModelConfigurator extends ContextAwareBase implements Configurator {
final public static String AUTOCONFIG_MODEL_FILE = "logback"+ MODEL_CONFIG_FILE_EXTENSION;
final public static String TEST_AUTOCONFIG_MODEL_FILE = "logback-test"+ MODEL_CONFIG_FILE_EXTENSION;
protected ModelInterpretationContext modelInterpretationContext;
@Override
public ExecutionStatus configure(LoggerContext loggerContext) {
URL url = performMultiStepModelFileSearch(true);
if (url != null) {
configureByResource(url);
return ExecutionStatus.DO_NOT_INVOKE_NEXT_IF_ANY;
} else {
return ExecutionStatus.INVOKE_NEXT_IF_ANY;
}
}
private void configureByResource(URL url) {
final String urlString = url.toString();
if (urlString.endsWith(MODEL_CONFIG_FILE_EXTENSION)) {
Model model = retrieveModel(url);
if(model == null) {
addWarn("Empty model. Abandoning.");
return;
}
ModelUtil.resetForReuse(model);
buildModelInterpretationContext(model);
DefaultProcessor defaultProcessor = new DefaultProcessor(context, this.modelInterpretationContext);
ModelClassToModelHandlerLinker mc2mhl = new ModelClassToModelHandlerLinker(context);
mc2mhl.link(defaultProcessor);
// disallow simultaneous configurations of the same context
ReentrantLock configurationLock = context.getConfigurationLock();
try {
configurationLock.lock();
defaultProcessor.process(model);
} finally {
configurationLock.unlock();
}
} else {
throw new LogbackException(
"Unexpected filename extension of file [" + url.toString() + "]. Should be " + MODEL_CONFIG_FILE_EXTENSION);
}
}
private void buildModelInterpretationContext(Model topModel) {
this.modelInterpretationContext = new ModelInterpretationContext(context, this);
this.modelInterpretationContext.setTopModel(topModel);
LogbackClassicDefaultNestedComponentRules.addDefaultNestedComponentRegistryRules(
modelInterpretationContext.getDefaultNestedComponentRegistry());
this.modelInterpretationContext.createAppenderBags();
}
private Model retrieveModel(URL url) {
long start = System.currentTimeMillis();
try (InputStream is = url.openStream()) {
HardenedModelInputStream hmis = new HardenedModelInputStream(is);
Model model = (Model) hmis.readObject();
long diff = System.currentTimeMillis() - start;
addInfo("Model at ["+url+"] read in "+diff + " milliseconds");
return model;
} catch(IOException e) {
addError("Failed to open "+url, e);
} catch (ClassNotFoundException e) {
addError("Failed read model object in "+ url, e);
}
return null;
}
private URL performMultiStepModelFileSearch(boolean updateState) {
ClassLoader myClassLoader = Loader.getClassLoaderOfObject(this);
URL url = findModelConfigFileURLFromSystemProperties(myClassLoader);
if (url != null) {
return url;
}
url = getResource(TEST_AUTOCONFIG_MODEL_FILE, myClassLoader, updateState);
if (url != null) {
return url;
}
url = getResource(AUTOCONFIG_MODEL_FILE, myClassLoader, updateState);
return url;
}
URL findModelConfigFileURLFromSystemProperties(ClassLoader classLoader) {
String logbackModelFile = OptionHelper.getSystemProperty(ClassicConstants.MODEL_CONFIG_FILE_PROPERTY);
if (logbackModelFile != null) {
URL result = null;
try {
result = new URL(logbackModelFile);
return result;
} catch (MalformedURLException e) {
// so, resource is not a URL:
// attempt to get the resource from the class path
result = Loader.getResource(logbackModelFile, classLoader);
if (result != null) {
return result;
}
File f = new File(logbackModelFile);
if (f.exists() && f.isFile()) {
try {
result = f.toURI().toURL();
return result;
} catch (MalformedURLException e1) {
}
}
} finally {
statusOnResourceSearch(logbackModelFile, result);
}
}
return null;
}
private URL getResource(String filename, ClassLoader classLoader, boolean updateStatus) {
URL url = Loader.getResource(filename, classLoader);
if (updateStatus) {
statusOnResourceSearch(filename, url);
}
return url;
}
private void statusOnResourceSearch(String resourceName, URL url) {
StatusManager sm = context.getStatusManager();
if (url == null) {
sm.add(new InfoStatus("Could NOT find resource [" + resourceName + "]", context));
} else {
sm.add(new InfoStatus("Found resource [" + resourceName + "] at [" + url.toString() + "]", context));
}
}
}