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.VariableNameUtil; 038 import com.squareup.javapoet.*; 039 040 import javax.lang.model.element.Modifier; 041 042 import java.util.*; 043 044 import static ch.qos.logback.tyler.base.TylerConstants.*; 045 046 public class TylerModelInterpretationContext extends ModelInterpretationContext { 047 048 final public TypeSpec.Builder tylerConfiguratorTSB; 049 final public MethodSpec.Builder configureMethodSpecBuilder; 050 final public Map<String, MethodSpec.Builder> mapOfMethodSpecBuilders = new LinkedHashMap<>(); 051 052 final FieldSpec contextFieldSpec = FieldSpec.builder(LoggerContext.class, CONTEXT_FIELD_NAME, Modifier.PRIVATE).build(); 053 final ParameterSpec contextParameterSpec = ParameterSpec.builder(LoggerContext.class, LOGGER_CONTEXT_PARAMETER_NAME).build(); 054 final ParameterSpec levelParameterSpec = ParameterSpec.builder(Level.class, LEVEL_FIELD_NAME).build(); 055 056 final List<FieldSpec> appenderFieldSpecs = new ArrayList<>(); 057 058 final List<StaticImportData> staticImportsList = new ArrayList<>(); 059 060 public TylerModelInterpretationContext(Context context) { 061 super(context); 062 this.configureMethodSpecBuilder = initializeConfigureMethodSpecBuilder(); 063 this.tylerConfiguratorTSB = initializeTylerConfiguratorTSB(); 064 } 065 066 public void addStaticImport(StaticImportData sid) { 067 if (!staticImportsList.contains(sid)) 068 staticImportsList.add(sid); 069 } 070 071 TypeSpec.Builder initializeTylerConfiguratorTSB() { 072 //MethodSpec setupLoggerMS = makeSetupLoggerMethodSpec(); 073 074 TypeSpec.Builder tsb = TypeSpec.classBuilder(TylerConstants.TYLER_CONFIGURATOR).addJavadoc(""" 075 076 <p>This file was generated by logback-tyler version %s</p> 077 078 <p>Eventual errors and warnings are appended at the end.</p> 079 080 <p>You may experiment with logback.xml to Java translation, i.e. 081 TylerConfigurator generation, at the following URL:</p> 082 083 <p> https://logback.qos.ch/translator/services/xml2Java.html </p> 084 085 <p>This generated TylerConfigurator class is intended to be copied and integrated 086 into the user's project as a custom configurator. It will configure logback 087 without XML. You are free to rename TylerConfigurator as you wish.</p> 088 089 <p>It requires logback-classic version %s or later at runtime.</p> 090 091 <p>Custom configurators are looked up via Java's service-provide facility. If a 092 custom provider is found, it takes precedence over logback's own configurators, 093 e.g. DefaultJoranConfigurator.</p> 094 095 <p>To install your custom configurator to your project, add a 096 provider-configuration file to the following path:</p> 097 098 <pre> META-INF/services/ch.qos.logback.classic.spi.Configurator</pre> 099 100 <p>This provider-configuration file should contain a line with the fully 101 qualified class name of your tyler configurator.</p> 102 103 <p>See also item 1 of 'Configuration at initialization' section at </p> 104 105 <p> https://logback.qos.ch/manual/configuration.html#auto_configuration</p> 106 107 <p>With recent versions of logback and logback-tyler you can still 108 configure logger levels dynamically using properties files. Note that 109 configuration files in properties format can be watched for 110 changes. See the documentation on PropertiesConfigurator for more details.</p> 111 112 <p>https://logback.qos.ch/manual/configuration.html#propertiesConfigurator</p> 113 114 <p>Keep in mind that by integrating a .properties configuration file info 115 your tyler configurator, you can still change logger levels dynamically, without 116 redeploying your application.</p> 117 118 """.formatted(TYLER_VERSION, REQUIRED_LOGBACK_VERSION)) 119 .addSuperinterface(Configurator.class) 120 .superclass(TylerConfiguratorBase.class) 121 .addModifiers(Modifier.PUBLIC); 122 return tsb; 123 } 124 125 private MethodSpec.Builder initializeConfigureMethodSpecBuilder() { 126 MethodSpec.Builder msb = MethodSpec.methodBuilder(CONFIGURE_METHOD_NAME).addJavadoc(""" 127 <p>This method performs configuration per {@link $T} interface.</p> 128 129 <p>If <code>TylerConfigurator</code> is installed as a configurator service, this 130 method will be called by logback-classic during initialization.</p> 131 """, Configurator.class).addAnnotation(Override.class).addModifiers(Modifier.PUBLIC).addParameter(contextParameterSpec) 132 .returns(Configurator.ExecutionStatus.class) 133 .addStatement("$N($N)", TylerConfiguratorBase.SET_CONTEXT_METHOD_NAME, contextParameterSpec); 134 135 return msb; 136 } 137 138 public FieldSpec getContextFieldSpec() { 139 return contextFieldSpec; 140 } 141 142// Shows how to build fields with generic types 143// 144// public FieldSpec createAppenderBagSpec() { 145// ParameterizedTypeName mapTypeName = ParameterizedTypeName.get(Map.class, String.class, Appender.class); 146// FieldSpec.Builder fieldSpecBuilder = FieldSpec.builder(mapTypeName, TYLER_APPENDER_BAG_FIELD_NAME).addModifiers(Modifier.PROTECTED, Modifier.FINAL); 147// fieldSpecBuilder.initializer("new $T<>()", HashMap.class); 148// fieldSpecBuilder.addJavadoc("A map used to reference appenders during configuration."); 149// 150// return fieldSpecBuilder.build(); 151// } 152 153 public FieldSpec createAppenderFieldSpec(ClassName desiredAppenderCN, String appenderName) { 154 String appenderVariableName = VariableNameUtil.appenderNameToVariableName(appenderName); 155 FieldSpec.Builder fieldSpecBuilder = FieldSpec.builder(desiredAppenderCN, appenderVariableName).addModifiers(Modifier.PROTECTED); 156 fieldSpecBuilder.addJavadoc("Appender variable referencing the appender named $S.", appenderName); 157 return fieldSpecBuilder.build(); 158 } 159 160 public Collection<FieldSpec> getAppenderFieldSpecs() { 161 return appenderFieldSpecs; 162 } 163 }