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 caterForLegacy_DefaultConverterMap(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); 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 caterForLegacy_DefaultConverterMap(Map<String, Supplier<DynamicConverter>> effectiveMap) { 108 // this transformation is for backward compatibility of existing code 109 Map<String, String> defaultConverterMap = getDefaultConverterMap(); 110 if(defaultConverterMap != null) { 111 for(Map.Entry<String, String> entry: defaultConverterMap.entrySet()) { 112 String key = entry.getKey().toString(); 113 String converterClassName = entry.getValue(); 114 ConverterSupplierByClassName converterSupplierByClassName = new ConverterSupplierByClassName(key, converterClassName); 115 converterSupplierByClassName.setContext(getContext()); 116 effectiveMap.put(key, converterSupplierByClassName); 117 } 118 } 119 } 120 121 public void start() { 122 if (pattern == null || pattern.length() == 0) { 123 addError("Empty or null pattern."); 124 return; 125 } 126 try { 127 Parser<E> p = new Parser<E>(pattern); 128 if (getContext() != null) { 129 p.setContext(getContext()); 130 } 131 Node t = p.parse(); 132 this.head = p.compile(t, getEffectiveConverterMap()); 133 if (postCompileProcessor != null) { 134 postCompileProcessor.process(context, head); 135 } 136 ConverterUtil.setContextForConverters(getContext(), head); 137 ConverterUtil.startConverters(this.head); 138 super.start(); 139 } catch (ScanException sce) { 140 StatusManager sm = getContext().getStatusManager(); 141 sm.add(new ErrorStatus("Failed to parse pattern \"" + getPattern() + "\".", this, sce)); 142 } 143 } 144 145 public void setPostCompileProcessor(PostCompileProcessor<E> postCompileProcessor) { 146 this.postCompileProcessor = postCompileProcessor; 147 } 148 149 /** 150 * 151 * @param head 152 * @deprecated Use {@link ConverterUtil#setContextForConverters} instead. This 153 * method will be removed in future releases. 154 */ 155 protected void setContextForConverters(Converter<E> head) { 156 ConverterUtil.setContextForConverters(getContext(), head); 157 } 158 159 protected String writeLoopOnConverters(E event) { 160 StringBuilder strBuilder = new StringBuilder(INTIAL_STRING_BUILDER_SIZE); 161 Converter<E> c = head; 162 while (c != null) { 163 c.write(strBuilder, event); 164 c = c.getNext(); 165 } 166 return strBuilder.toString(); 167 } 168 169 public String getPattern() { 170 return pattern; 171 } 172 173 public void setPattern(String pattern) { 174 this.pattern = pattern; 175 } 176 177 public String toString() { 178 return this.getClass().getName() + "(\"" + getPattern() + "\")"; 179 } 180 181 public Map<String, Supplier<DynamicConverter>> getInstanceConverterMap() { 182 return instanceConverterMap; 183 } 184 185 protected String getPresentationHeaderPrefix() { 186 return CoreConstants.EMPTY_STRING; 187 } 188 189 public boolean isOutputPatternAsHeader() { 190 return outputPatternAsHeader; 191 } 192 193 public void setOutputPatternAsHeader(boolean outputPatternAsHeader) { 194 this.outputPatternAsHeader = outputPatternAsHeader; 195 } 196 197 @Override 198 public String getPresentationHeader() { 199 if (outputPatternAsHeader) 200 return getPresentationHeaderPrefix() + pattern; 201 else 202 return super.getPresentationHeader(); 203 } 204}