001/* 002 * Logback: the reliable, generic, fast and flexible logging framework. 003 * Copyright (C) 1999-2023, QOS.ch. All rights reserved. 004 * 005 * This program and the accompanying materials are dual-licensed under 006 * either the terms of the Eclipse Public License v1.0 as published by 007 * the Eclipse Foundation 008 * 009 * or (per the licensee's choosing) 010 * 011 * under the terms of the GNU Lesser General Public License version 2.1 012 * as published by the Free Software Foundation. 013 */ 014 015package ch.qos.logback.classic.joran; 016 017import ch.qos.logback.classic.ClassicConstants; 018import ch.qos.logback.classic.LoggerContext; 019import ch.qos.logback.classic.joran.serializedModel.HardenedModelInputStream; 020import ch.qos.logback.classic.model.processor.LogbackClassicDefaultNestedComponentRules; 021import ch.qos.logback.classic.spi.ConfiguratorRank; 022import ch.qos.logback.core.Context; 023import ch.qos.logback.core.LogbackException; 024import ch.qos.logback.core.model.Model; 025import ch.qos.logback.core.model.ModelUtil; 026import ch.qos.logback.core.model.processor.DefaultProcessor; 027import ch.qos.logback.core.model.processor.ModelInterpretationContext; 028import ch.qos.logback.classic.spi.Configurator; 029import ch.qos.logback.core.spi.ContextAwareBase; 030import ch.qos.logback.core.status.InfoStatus; 031import ch.qos.logback.core.status.StatusManager; 032import ch.qos.logback.core.util.Loader; 033import ch.qos.logback.core.util.OptionHelper; 034 035import java.io.File; 036import java.io.IOException; 037import java.io.InputStream; 038import java.net.MalformedURLException; 039import java.net.URL; 040 041import static ch.qos.logback.core.CoreConstants.MODEL_CONFIG_FILE_EXTENSION; 042 043/** 044 * @since 1.3.9/1.4.9 045 */ 046 047// BEWARE: the fqcn is used in SerializedModelModelHandler 048@ConfiguratorRank(value = ConfiguratorRank.SERIALIZED_MODEL) 049public class SerializedModelConfigurator extends ContextAwareBase implements Configurator { 050 051 final public static String AUTOCONFIG_MODEL_FILE = "logback"+ MODEL_CONFIG_FILE_EXTENSION; 052 053 final public static String TEST_AUTOCONFIG_MODEL_FILE = "logback-test"+ MODEL_CONFIG_FILE_EXTENSION; 054 protected ModelInterpretationContext modelInterpretationContext; 055 056 @Override 057 public ExecutionStatus configure(LoggerContext loggerContext) { 058 059 URL url = performMultiStepModelFileSearch(true); 060 if (url != null) { 061 configureByResource(url); 062 return ExecutionStatus.DO_NOT_INVOKE_NEXT_IF_ANY; 063 } else { 064 return ExecutionStatus.INVOKE_NEXT_IF_ANY; 065 } 066 } 067 068 private void configureByResource(URL url) { 069 final String urlString = url.toString(); 070 if (urlString.endsWith(MODEL_CONFIG_FILE_EXTENSION)) { 071 Model model = retrieveModel(url); 072 if(model == null) { 073 addWarn("Empty model. Abandoning."); 074 return; 075 } 076 ModelUtil.resetForReuse(model); 077 buildModelInterpretationContext(model); 078 079 DefaultProcessor defaultProcessor = new DefaultProcessor(context, this.modelInterpretationContext); 080 ModelClassToModelHandlerLinker mc2mhl = new ModelClassToModelHandlerLinker(context); 081 mc2mhl.link(defaultProcessor); 082 083 // disallow simultaneous configurations of the same context 084 synchronized (context.getConfigurationLock()) { 085 defaultProcessor.process(model); 086 } 087 } else { 088 throw new LogbackException( 089 "Unexpected filename extension of file [" + url.toString() + "]. Should be " + MODEL_CONFIG_FILE_EXTENSION); 090 } 091 } 092 093 private void buildModelInterpretationContext(Model topModel) { 094 this.modelInterpretationContext = new ModelInterpretationContext(context, this); 095 this.modelInterpretationContext.setTopModel(topModel); 096 LogbackClassicDefaultNestedComponentRules.addDefaultNestedComponentRegistryRules( 097 modelInterpretationContext.getDefaultNestedComponentRegistry()); 098 this.modelInterpretationContext.createAppenderBags(); 099 } 100 101 private Model retrieveModel(URL url) { 102 long start = System.currentTimeMillis(); 103 try (InputStream is = url.openStream()) { 104 HardenedModelInputStream hmis = new HardenedModelInputStream(is); 105 106 Model model = (Model) hmis.readObject(); 107 long diff = System.currentTimeMillis() - start; 108 addInfo("Model at ["+url+"] read in "+diff + " milliseconds"); 109 return model; 110 } catch(IOException e) { 111 addError("Failed to open "+url, e); 112 } catch (ClassNotFoundException e) { 113 addError("Failed read model object in "+ url, e); 114 } 115 return null; 116 } 117 118 private URL performMultiStepModelFileSearch(boolean updateState) { 119 ClassLoader myClassLoader = Loader.getClassLoaderOfObject(this); 120 URL url = findModelConfigFileURLFromSystemProperties(myClassLoader); 121 if (url != null) { 122 return url; 123 } 124 125 url = getResource(TEST_AUTOCONFIG_MODEL_FILE, myClassLoader, updateState); 126 if (url != null) { 127 return url; 128 } 129 130 url = getResource(AUTOCONFIG_MODEL_FILE, myClassLoader, updateState); 131 return url; 132 } 133 134 URL findModelConfigFileURLFromSystemProperties(ClassLoader classLoader) { 135 String logbackModelFile = OptionHelper.getSystemProperty(ClassicConstants.MODEL_CONFIG_FILE_PROPERTY); 136 137 if (logbackModelFile != null) { 138 URL result = null; 139 try { 140 result = new URL(logbackModelFile); 141 return result; 142 } catch (MalformedURLException e) { 143 // so, resource is not a URL: 144 // attempt to get the resource from the class path 145 result = Loader.getResource(logbackModelFile, classLoader); 146 if (result != null) { 147 return result; 148 } 149 File f = new File(logbackModelFile); 150 if (f.exists() && f.isFile()) { 151 try { 152 result = f.toURI().toURL(); 153 return result; 154 } catch (MalformedURLException e1) { 155 } 156 } 157 } finally { 158 statusOnResourceSearch(logbackModelFile, result); 159 } 160 } 161 return null; 162 } 163 164 165 private URL getResource(String filename, ClassLoader classLoader, boolean updateStatus) { 166 URL url = Loader.getResource(filename, classLoader); 167 if (updateStatus) { 168 statusOnResourceSearch(filename, url); 169 } 170 return url; 171 } 172 173 private void statusOnResourceSearch(String resourceName, URL url) { 174 StatusManager sm = context.getStatusManager(); 175 if (url == null) { 176 sm.add(new InfoStatus("Could NOT find resource [" + resourceName + "]", context)); 177 } else { 178 sm.add(new InfoStatus("Found resource [" + resourceName + "] at [" + url.toString() + "]", context)); 179 } 180 } 181}