001 /* 002 * Copyright (c) 2024 QOS.ch Sarl (Switzerland) 003 * All rights reserved. 004 * 005 * Permission is hereby granted, free of charge, to any person obtaining 006 * a copy of this software and associated documentation files (the 007 * "Software"), to deal in the Software without restriction, including 008 * without limitation the rights to use, copy, modify, merge, publish, 009 * distribute, sublicense, and/or sell copies of the Software, and to 010 * permit persons to whom the Software is furnished to do so, subject to 011 * the following conditions: 012 * 013 * The above copyright notice and this permission notice shall be 014 * included in all copies or substantial portions of the Software. 015 * 016 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 017 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 018 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 019 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 020 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 021 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 022 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 023 * 024 * 025 * 026 */ 027 028 package ch.qos.logback.tyler.base; 029 030 import ch.qos.logback.classic.Level; 031 import ch.qos.logback.classic.LoggerContext; 032 import ch.qos.logback.classic.spi.Configurator; 033 import ch.qos.logback.classic.tyler.TylerConfiguratorBase; 034 import ch.qos.logback.core.Context; 035 import ch.qos.logback.core.model.processor.ModelInterpretationContext; 036 import ch.qos.logback.tyler.base.spi.StaticImportData; 037 import ch.qos.logback.tyler.base.util.ClassUtil; 038 import ch.qos.logback.tyler.base.util.VariableNameUtil; 039 import com.squareup.javapoet.*; 040 041 import javax.lang.model.element.Modifier; 042 043 import java.util.*; 044 045 import static ch.qos.logback.tyler.base.TylerConstants.*; 046 047 public class TylerModelInterpretationContext extends ModelInterpretationContext { 048 049 final public TypeSpec.Builder tylerConfiguratorTSB; 050 final public MethodSpec.Builder configureMethodSpecBuilder; 051 final public Map<String, MethodSpec.Builder> mapOfMethodSpecBuilders = new LinkedHashMap<>(); 052 053 final FieldSpec contextFieldSpec = FieldSpec.builder(LoggerContext.class, CONTEXT_FIELD_NAME, Modifier.PRIVATE).build(); 054 final ParameterSpec contextParameterSpec = ParameterSpec.builder(LoggerContext.class, LOGGER_CONTEXT_PARAMETER_NAME).build(); 055 final ParameterSpec levelParameterSpec = ParameterSpec.builder(Level.class, LEVEL_FIELD_NAME).build(); 056 057 final List<FieldSpec> fieldSpecs = new ArrayList<>(); 058 059 final List<StaticImportData> staticImportsList = new ArrayList<>(); 060 061 062 public TylerModelInterpretationContext(Context context) { 063 super(context); 064 this.configureMethodSpecBuilder = initializeConfigureMethodSpecBuilder(); 065 this.tylerConfiguratorTSB = initializeTylerConfiguratorTSB(); 066 } 067 068 public void addStaticImport(StaticImportData sid) { 069 if (!staticImportsList.contains(sid)) 070 staticImportsList.add(sid); 071 } 072 073 TypeSpec.Builder initializeTylerConfiguratorTSB() { 074 //MethodSpec setupLoggerMS = makeSetupLoggerMethodSpec(); 075 076 TypeSpec.Builder tsb = TypeSpec.classBuilder(TylerConstants.TYLER_CONFIGURATOR).addJavadoc(""" 077 078 <p>This file was generated by logback-tyler version %s</p> 079 080 <p>Eventual errors and warnings are appended at the end.</p> 081 082 <p>You may experiment with logback.xml to Java translation, i.e. 083 TylerConfigurator generation, at the following URL:</p> 084 085 <p> https://logback.qos.ch/translator/services/xml2Java.html </p> 086 087 <p>This generated TylerConfigurator class is intended to be copied and integrated 088 into the user's project as a custom configurator. It will configure logback 089 without XML. You are free to rename TylerConfigurator as you wish.</p> 090 091 <p>It requires logback-classic version %s or later at runtime.</p> 092 093 <p>Custom configurators are looked up via Java's service-provide facility. If a 094 custom provider is found, it takes precedence over logback's own configurators, 095 e.g. DefaultJoranConfigurator.</p> 096 097 <p>To install your custom configurator to your project, add a 098 provider-configuration file to the following path:</p> 099 100 <pre> META-INF/services/ch.qos.logback.classic.spi.Configurator</pre> 101 102 <p>This provider-configuration file should contain a line with the fully 103 qualified class name of your tyler configurator.</p> 104 105 <p>See also item 1 of 'Configuration at initialization' section at </p> 106 107 <p> https://logback.qos.ch/manual/configuration.html#auto_configuration</p> 108 109 <p>With recent versions of logback and logback-tyler you can still 110 configure logger levels dynamically using properties files. Note that 111 configuration files in properties format can be watched for 112 changes. See the documentation on PropertiesConfigurator for more details.</p> 113 114 <p>https://logback.qos.ch/manual/configuration.html#propertiesConfigurator</p> 115 116 <p>Keep in mind that by integrating a .properties configuration file info 117 your tyler configurator, you can still change logger levels dynamically, without 118 redeploying your application.</p> 119 120 """.formatted(TYLER_VERSION, REQUIRED_LOGBACK_VERSION)) 121 .addSuperinterface(Configurator.class) 122 .superclass(TylerConfiguratorBase.class) 123 .addModifiers(Modifier.PUBLIC); 124 return tsb; 125 } 126 127 private MethodSpec.Builder initializeConfigureMethodSpecBuilder() { 128 MethodSpec.Builder msb = MethodSpec.methodBuilder(CONFIGURE_METHOD_NAME).addJavadoc(""" 129 <p>This method performs configuration per {@link $T} interface.</p> 130 131 <p>If <code>TylerConfigurator</code> is installed as a configurator service, this 132 method will be called by logback-classic during initialization.</p> 133 """, Configurator.class).addAnnotation(Override.class).addModifiers(Modifier.PUBLIC).addParameter(contextParameterSpec) 134 .returns(Configurator.ExecutionStatus.class) 135 .addStatement("$N($N)", TylerConfiguratorBase.SET_CONTEXT_METHOD_NAME, contextParameterSpec); 136 137 return msb; 138 } 139 140 public FieldSpec getContextFieldSpec() { 141 return contextFieldSpec; 142 } 143 144// Shows how to build fields with generic types 145// 146// public FieldSpec createAppenderBagSpec() { 147// ParameterizedTypeName mapTypeName = ParameterizedTypeName.get(Map.class, String.class, Appender.class); 148// FieldSpec.Builder fieldSpecBuilder = FieldSpec.builder(mapTypeName, TYLER_APPENDER_BAG_FIELD_NAME).addModifiers(Modifier.PROTECTED, Modifier.FINAL); 149// fieldSpecBuilder.initializer("new $T<>()", HashMap.class); 150// fieldSpecBuilder.addJavadoc("A map used to reference appenders during configuration."); 151// 152// return fieldSpecBuilder.build(); 153// } 154 155 public FieldSpec createAppenderFieldSpec(ClassName desiredAppenderCN, String appenderName) { 156 String appenderVariableName = VariableNameUtil.appenderNameToVariableName(appenderName); 157 FieldSpec.Builder fieldSpecBuilder = FieldSpec.builder(desiredAppenderCN, appenderVariableName).addModifiers(Modifier.PROTECTED); 158 fieldSpecBuilder.addJavadoc("Appender variable referencing the appender named $S.", appenderName); 159 return fieldSpecBuilder.build(); 160 } 161 162 public FieldSpec createPropertyConditionFieldSpec(ClassName className, String variableName) { 163 FieldSpec.Builder fieldSpecBuilder = FieldSpec.builder(className, variableName).addModifiers(Modifier.PROTECTED); 164 fieldSpecBuilder.addJavadoc("Variable used in conditional processing"); 165 return fieldSpecBuilder.build(); 166 } 167 168 169 public Collection<FieldSpec> getFieldSpecs() { 170 return fieldSpecs; 171 } 172 173 public ClassName makeClassName(String fqcn) { 174 String packageName = ClassUtil.extractPackageName(fqcn); 175 String simpleName = ClassUtil.extractSimpleClassName(fqcn); 176 return ClassName.get(packageName, simpleName); 177 } 178 }