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.classic.log4j; 015 016import java.util.Map; 017import java.util.Set; 018import java.util.Map.Entry; 019 020import ch.qos.logback.classic.net.SMTPAppender; 021import ch.qos.logback.classic.spi.ILoggingEvent; 022import ch.qos.logback.classic.spi.IThrowableProxy; 023import ch.qos.logback.classic.spi.StackTraceElementProxy; 024import ch.qos.logback.core.CoreConstants; 025import ch.qos.logback.core.LayoutBase; 026import ch.qos.logback.core.helpers.Transform; 027 028// Code is based on revision 309623 of org.apache.log4j.xml.XMLLayout dated "Wed 029// Jul 31 09:25:14 2002 UTC" as authored by Ceki Gulcu. 030// See also http://tinyurl.com/dch9mr 031 032/** 033 * 034 * Generates log4j.dtd compliant XML documents. 035 * 036 * 037 * @author Ceki Gülcü 038 */ 039public class XMLLayout extends LayoutBase<ILoggingEvent> { 040 041 private static final int DEFAULT_SIZE = 256; 042 043 private boolean locationInfo = false; 044 private boolean properties = false; 045 046 @Override 047 public void start() { 048 super.start(); 049 } 050 051 /** 052 * The <b>LocationInfo</b> option takes a boolean value. By default, it is set 053 * to false which means there will be no location information output by this 054 * layout. If the option is set to true, then the file name and line number 055 * of the statement at the origin of the log statement will be output. 056 * 057 * <p> 058 * If you are embedding this layout within an 059 * {@link SMTPAppender} then make sure to set the 060 * {@link SMTPAppender#setIncludeCallerData(boolean) includeCallerData} option 061 * as well. 062 * </p> 063 */ 064 public void setLocationInfo(boolean flag) { 065 locationInfo = flag; 066 } 067 068 /** 069 * Returns the current value of the <b>LocationInfo</b> option. 070 */ 071 public boolean getLocationInfo() { 072 return locationInfo; 073 } 074 075 /** 076 * Sets whether MDC key-value pairs should be output, default false. 077 * 078 * @param flag new value. 079 * @since 1.2.15 080 */ 081 public void setProperties(final boolean flag) { 082 properties = flag; 083 } 084 085 /** 086 * Gets whether MDC key-value pairs should be output. 087 * 088 * @return true if MDC key-value pairs are output. 089 * @since 1.2.15 090 */ 091 public boolean getProperties() { 092 return properties; 093 } 094 095 /** 096 * Formats a {@link ILoggingEvent} in conformity with the log4j.dtd. 097 */ 098 public String doLayout(ILoggingEvent event) { 099 100 StringBuilder buf = new StringBuilder(DEFAULT_SIZE); 101 102 // We yield to the \r\n heresy. 103 104 buf.append("<log4j:event logger=\""); 105 buf.append(Transform.escapeTags(event.getLoggerName())); 106 buf.append("\"\r\n"); 107 buf.append(" timestamp=\""); 108 buf.append(event.getTimeStamp()); 109 buf.append("\" level=\""); 110 buf.append(event.getLevel()); 111 buf.append("\" thread=\""); 112 buf.append(Transform.escapeTags(event.getThreadName())); 113 buf.append("\">\r\n"); 114 115 buf.append(" <log4j:message>"); 116 buf.append(Transform.escapeTags(event.getFormattedMessage())); 117 buf.append("</log4j:message>\r\n"); 118 119 // logback does not support NDC 120 // String ndc = event.getNDC(); 121 122 IThrowableProxy tp = event.getThrowableProxy(); 123 if (tp != null) { 124 StackTraceElementProxy[] stepArray = tp.getStackTraceElementProxyArray(); 125 buf.append(" <log4j:throwable><![CDATA["); 126 for (StackTraceElementProxy step : stepArray) { 127 buf.append(CoreConstants.TAB); 128 buf.append(step.toString()); 129 buf.append("\r\n"); 130 } 131 buf.append("]]></log4j:throwable>\r\n"); 132 } 133 134 if (locationInfo) { 135 StackTraceElement[] callerDataArray = event.getCallerData(); 136 if (callerDataArray != null && callerDataArray.length > 0) { 137 StackTraceElement immediateCallerData = callerDataArray[0]; 138 buf.append(" <log4j:locationInfo class=\""); 139 buf.append(immediateCallerData.getClassName()); 140 buf.append("\"\r\n"); 141 buf.append(" method=\""); 142 buf.append(Transform.escapeTags(immediateCallerData.getMethodName())); 143 buf.append("\" file=\""); 144 buf.append(Transform.escapeTags(immediateCallerData.getFileName())); 145 buf.append("\" line=\""); 146 buf.append(immediateCallerData.getLineNumber()); 147 buf.append("\"/>\r\n"); 148 } 149 } 150 151 /* 152 * <log4j:properties> <log4j:data name="name" value="value"/> 153 * </log4j:properties> 154 */ 155 if (this.getProperties()) { 156 Map<String, String> propertyMap = event.getMDCPropertyMap(); 157 158 if ((propertyMap != null) && (propertyMap.size() != 0)) { 159 Set<Entry<String, String>> entrySet = propertyMap.entrySet(); 160 buf.append(" <log4j:properties>"); 161 for (Entry<String, String> entry : entrySet) { 162 buf.append("\r\n <log4j:data"); 163 buf.append(" name='" + Transform.escapeTags(entry.getKey()) + "'"); 164 buf.append(" value='" + Transform.escapeTags(entry.getValue()) + "'"); 165 buf.append(" />"); 166 } 167 buf.append("\r\n </log4j:properties>"); 168 } 169 } 170 171 buf.append("\r\n</log4j:event>\r\n\r\n"); 172 173 return buf.toString(); 174 } 175 176 @Override 177 public String getContentType() { 178 return "text/xml"; 179 } 180 181}