blob: 82e0f74ec59e8bbeaac8d6287a41d1ffcab54b50 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.log4j.chainsaw.layout;
import java.util.Hashtable;
import java.util.Set;
import org.apache.log4j.EnhancedPatternLayout;
import org.apache.log4j.Layout;
import org.apache.log4j.Logger;
import org.apache.log4j.spi.LocationInfo;
import org.apache.log4j.spi.LoggingEvent;
/**
* This layout is used for formatting HTML text for use inside
* the Chainsaw Event Detail Panel, and the tooltip used
* when mouse-over on a particular log event row.
*
* It relies an an internal PatternLayout to accomplish this, but ensures HTML characters
* from any LoggingEvent are escaped first.
*
* @author Paul Smith <psmith@apache.org>
*/
public class EventDetailLayout extends Layout {
private final EnhancedPatternLayout patternLayout = new EnhancedPatternLayout();
public EventDetailLayout() {
}
public void setConversionPattern(String conversionPattern) {
patternLayout.setConversionPattern(conversionPattern);
patternLayout.activateOptions();
}
public String getConversionPattern() {
return patternLayout.getConversionPattern();
}
/* (non-Javadoc)
* @see org.apache.log4j.Layout#getFooter()
*/
public String getFooter() {
return "";
}
/* (non-Javadoc)
* @see org.apache.log4j.Layout#getHeader()
*/
public String getHeader() {
return "";
}
// /* (non-Javadoc)
// * @see org.apache.log4j.Layout#format(java.io.Writer, org.apache.log4j.spi.LoggingEvent)
// */
// public void format(Writer output, LoggingEvent event)
// throws IOException {
// boolean pastFirst = false;
// output.write("<html><body><table cellspacing=0 cellpadding=0>");
//
// List columnNames = ChainsawColumns.getColumnsNames();
//
// Vector v = ChainsawAppenderHandler.convert(event);
//
// /**
// * we need to add the ID property from the event
// */
// v.add(event.getProperty(ChainsawConstants.LOG4J_ID_KEY));
//
// // ListIterator iter = displayFilter.getDetailColumns().listIterator();
// Iterator iter = columnNames.iterator();
// String column = null;
// int index = -1;
//
// while (iter.hasNext()) {
// column = (String) iter.next();
// index = columnNames.indexOf(column);
//
// if (index > -1) {
// if (pastFirst) {
// output.write("</td></tr>");
// }
//
// output.write("<tr><td valign=\"top\"><b>");
// output.write(column);
// output.write(": </b></td><td>");
//
//
// if (index<v.size()) {
// Object o = v.get(index);
//
// if (o != null) {
// output.write(escape(o.toString()));
// } else {
// output.write("{null}");
// }
//
// }else {
//// output.write("Invalid column " + column + " (index=" + index + ")");
// }
//
// pastFirst = true;
// }
// }
//
// output.write("</table></body></html>");
// }
/**
* Escape &lt;, &gt; &amp; and &quot; as their entities. It is very
* dumb about &amp; handling.
* @param aStr the String to escape.
* @return the escaped String
*/
private static String escape(String string) {
if (string == null) {
return "";
}
final StringBuilder buf = new StringBuilder();
for (int i = 0; i < string.length(); i++) {
char c = string.charAt(i);
switch (c) {
case '<':
buf.append("&lt;");
break;
case '>':
buf.append("&gt;");
break;
case '\"':
buf.append("&quot;");
break;
case '&':
buf.append("&amp;");
break;
default:
buf.append(c);
break;
}
}
return buf.toString();
}
/**
* Takes a source event and copies it into a new LoggingEvent object
* and ensuring all the internal elements of the event are HTML safe
* @param event
* @return new LoggingEvent
*/
private static LoggingEvent copyForHTML(LoggingEvent event) {
Logger logger = Logger.getLogger(event.getLoggerName());
String threadName = event.getThreadName();
Object msg = escape(event.getMessage().toString());
String ndc = event.getNDC();
// Hashtable mdc = formatMDC(event);
LocationInfo li = null;
if (event.locationInformationExists()) {
li = formatLocationInfo(event);
}
Hashtable<String, String> properties = formatProperties(event);
LoggingEvent copy = new LoggingEvent(null,
logger, event.getTimeStamp(),
event.getLevel(),
msg,
threadName,
event.getThrowableInformation(),
ndc,
li,
properties);
return copy;
}
// /**
// * @param event
// * @return
// */
// private static Hashtable formatMDC(LoggingEvent event) {
// Set keySet = event.getMDCKeySet();
// Hashtable hashTable = new Hashtable();
//
// for (Iterator iter = keySet.iterator(); iter.hasNext();) {
// Object key = (Object) iter.next();
// Object value = event.getMDC(key.toString());
// hashTable.put(escape(key.toString()), escape(value.toString()));
// }
//
// return hashTable;
// }
/**
* @param event
* @return
*/
private static LocationInfo formatLocationInfo(LoggingEvent event) {
LocationInfo info = event.getLocationInformation();
LocationInfo newInfo =
new LocationInfo(
escape(info.getFileName()), escape(info.getClassName()),
escape(info.getMethodName()), escape(info.getLineNumber()));
return newInfo;
}
/**
* @param event
* @return
*/
private static Hashtable<String, String> formatProperties(LoggingEvent event) {
Set keySet = event.getPropertyKeySet();
Hashtable<String, String> hashTable = new Hashtable<>();
for (Object key : keySet) {
Object value = event.getProperty(key.toString());
hashTable.put(escape(key.toString()), escape(value.toString()));
}
return hashTable;
}
/* (non-Javadoc)
* @see org.apache.log4j.Layout#ignoresThrowable()
*/
public boolean ignoresThrowable() {
return false;
}
/* (non-Javadoc)
* @see org.apache.log4j.spi.OptionHandler#activateOptions()
*/
public void activateOptions() {
}
/* (non-Javadoc)
* @see org.apache.log4j.Layout#format(java.io.Writer, org.apache.log4j.spi.LoggingEvent)
*/
public String format(final LoggingEvent event) {
LoggingEvent newEvent = copyForHTML(event);
/**
* Layouts are not thread-safe, but are normally
* protected by the fact that their Appender is thread-safe.
*
* But here in Chainsaw there is no such guarantees.
*/
synchronized(patternLayout) {
return patternLayout.format(newEvent);
}
}
}