001/** 002 * Logback: the reliable, generic, fast and flexible logging framework. 003 * Copyright (C) 1999-2015, 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 */ 014package ch.qos.logback.core.pattern; 015 016import java.util.HashMap; 017import java.util.Map; 018import java.util.function.Supplier; 019 020import ch.qos.logback.core.Context; 021import ch.qos.logback.core.CoreConstants; 022import ch.qos.logback.core.LayoutBase; 023import ch.qos.logback.core.pattern.color.ConverterSupplierByClassName; 024import ch.qos.logback.core.pattern.parser.Node; 025import ch.qos.logback.core.pattern.parser.Parser; 026import ch.qos.logback.core.spi.ScanException; 027import ch.qos.logback.core.status.ErrorStatus; 028import ch.qos.logback.core.status.StatusManager; 029 030abstract public class PatternLayoutBase<E> extends LayoutBase<E> { 031 032 static final int INTIAL_STRING_BUILDER_SIZE = 256; 033 Converter<E> head; 034 String pattern; 035 protected PostCompileProcessor<E> postCompileProcessor; 036 037 /** 038 * <p>It should be noted that the default converter map is a static variable. Thus, changes made 039 * through {@link #getDefaultConverterSupplierMap()} apply to all instances of this class. 040 * </p> 041 * 042 * <p>The {@link #getInstanceConverterMap} variable allows for very specific extensions 043 * without impacting other instances</p> 044 */ 045 Map<String, Supplier<DynamicConverter>> instanceConverterMap = new HashMap<>(); 046 protected boolean outputPatternAsHeader = false; 047 048 /** 049 * Concrete implementations of this class are responsible for elaborating the 050 * mapping between pattern words and supplying converter instances. 051 * 052 * @return A map associating pattern words to the names of converter suppliers 053 * @since 1.5.13 054 */ 055 protected abstract Map<String, Supplier<DynamicConverter>> getDefaultConverterSupplierMap(); 056 057 /** 058 * <p>BEWARE: The map of type String,String for mapping conversion words is deprecated. 059 * Use {@link #getDefaultConverterSupplierMap()} instead.</p> 060 * 061 * <p>Existing code such as getDefaultMap().put("k", X.class.getName()) should be replaced by 062 * getDefaultConverterSupplierMap().put("k", X::new) </p> 063 * 064 * <p>Note that values in the map will still be taken into account and processed correctly.</p> 065 * 066 * @return a map of keys and class names 067 */ 068 @Deprecated 069 abstract public Map<String, String> getDefaultConverterMap(); 070 071 /** 072 * Returns a map where the default converter map is merged with the map 073 * contained in the context. 074 */ 075 public Map<String, Supplier<DynamicConverter>> getEffectiveConverterMap() { 076 Map<String, Supplier<DynamicConverter>> effectiveMap = new HashMap<>(); 077 078 // add the least specific map fist 079 Map<String, Supplier<DynamicConverter>> defaultConverterSupplierMap = getDefaultConverterSupplierMap(); 080 if (defaultConverterSupplierMap != null) { 081 effectiveMap.putAll(defaultConverterSupplierMap); 082 } 083 084 caterForLegacyConverterMaps(effectiveMap); 085 086 // contextMap is more specific than the default map 087 Context context = getContext(); 088 if (context != null) { 089 @SuppressWarnings("unchecked") 090 Map<String, Supplier<DynamicConverter>> contextMap = (Map<String, Supplier<DynamicConverter>>) context 091 .getObject(CoreConstants.PATTERN_RULE_REGISTRY_FOR_SUPPLIERS); 092 if (contextMap != null) { 093 effectiveMap.putAll(contextMap); 094 } 095 } 096 // set the most specific map last 097 effectiveMap.putAll(instanceConverterMap); 098 return effectiveMap; 099 } 100 101 /** 102 * Add class name values into the effective map to support external extensions 103 * and subclasses. 104 * 105 * @param effectiveMap 106 */ 107 private void caterForLegacyConverterMaps(Map<String, Supplier<DynamicConverter>> effectiveMap) { 108 Map<String, String> mapFromContext = (Map<String, String>) this.context 109 .getObject(CoreConstants.PATTERN_RULE_REGISTRY); 110 111 migrateFromStringMapToSupplierMap(mapFromContext, effectiveMap); 112 113 Map<String, String> defaultConverterMap = getDefaultConverterMap(); 114 migrateFromStringMapToSupplierMap(defaultConverterMap, effectiveMap); 115 } 116 117 private void migrateFromStringMapToSupplierMap(Map<String, String> legacyMap, Map<String, Supplier<DynamicConverter>> targetSupplierMap) { 118 if(legacyMap == null) 119 return; 120 121 // this transformation is for backward compatibility of existing code 122 for(Map.Entry<String, String> entry: legacyMap.entrySet()) { 123 String key = entry.getKey(); 124 String converterClassName = entry.getValue(); 125 ConverterSupplierByClassName converterSupplierByClassName = new ConverterSupplierByClassName(key, converterClassName); 126 converterSupplierByClassName.setContext(getContext()); 127 targetSupplierMap.put(key, converterSupplierByClassName); 128 } 129 130 } 131 132 public void start() { 133 if (pattern == null || pattern.length() == 0) { 134 addError("Empty or null pattern."); 135 return; 136 } 137 try { 138 Parser<E> p = new Parser<E>(pattern); 139 if (getContext() != null) { 140 p.setContext(getContext()); 141 } 142 Node t = p.parse(); 143 this.head = p.compile(t, getEffectiveConverterMap()); 144 if (postCompileProcessor != null) { 145 postCompileProcessor.process(context, head); 146 } 147 ConverterUtil.setContextForConverters(getContext(), head); 148 ConverterUtil.startConverters(this.head); 149 super.start(); 150 } catch (ScanException sce) { 151 StatusManager sm = getContext().getStatusManager(); 152 sm.add(new ErrorStatus("Failed to parse pattern \"" + getPattern() + "\".", this, sce)); 153 } 154 } 155 156 public void setPostCompileProcessor(PostCompileProcessor<E> postCompileProcessor) { 157 this.postCompileProcessor = postCompileProcessor; 158 } 159 160 /** 161 * 162 * @param head 163 * @deprecated Use {@link ConverterUtil#setContextForConverters} instead. This 164 * method will be removed in future releases. 165 */ 166 protected void setContextForConverters(Converter<E> head) { 167 ConverterUtil.setContextForConverters(getContext(), head); 168 } 169 170 protected String writeLoopOnConverters(E event) { 171 StringBuilder strBuilder = new StringBuilder(INTIAL_STRING_BUILDER_SIZE); 172 Converter<E> c = head; 173 while (c != null) { 174 c.write(strBuilder, event); 175 c = c.getNext(); 176 } 177 return strBuilder.toString(); 178 } 179 180 public String getPattern() { 181 return pattern; 182 } 183 184 public void setPattern(String pattern) { 185 this.pattern = pattern; 186 } 187 188 public String toString() { 189 return this.getClass().getName() + "(\"" + getPattern() + "\")"; 190 } 191 192 public Map<String, Supplier<DynamicConverter>> getInstanceConverterMap() { 193 return instanceConverterMap; 194 } 195 196 protected String getPresentationHeaderPrefix() { 197 return CoreConstants.EMPTY_STRING; 198 } 199 200 public boolean isOutputPatternAsHeader() { 201 return outputPatternAsHeader; 202 } 203 204 public void setOutputPatternAsHeader(boolean outputPatternAsHeader) { 205 this.outputPatternAsHeader = outputPatternAsHeader; 206 } 207 208 @Override 209 public String getPresentationHeader() { 210 if (outputPatternAsHeader) 211 return getPresentationHeaderPrefix() + pattern; 212 else 213 return super.getPresentationHeader(); 214 } 215}