/*
* ALMA - Atacama Large Millimiter Array
* (c) European Southern Observatory, 2002
* Copyright by ESO (in the framework of the ALMA collaboration),
* All rights reserved
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
package alma.acs.logging.formatters;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Date;
import java.util.Map;
import java.util.logging.LogRecord;
import org.omg.CORBA.Any;
import alma.acs.logging.AcsLogLevel;
import alma.acs.logging.AcsLogRecord;
import alma.acs.logging.LogParameterUtil;
import alma.acs.logging.level.AcsLogLevelDefinition;
import alma.acs.util.IsoDateFormat;
import alma.acs.util.XmlNormalizer;
/**
* @author rgeorgie
*
* Class that is responsible for formatting the log records/elements of different levels
* as well as assigning the right values to their attributes.
*/
public class AcsXMLLogFormatter extends AcsLogFormatter
{
public Any formatAny( Any anyLogRecord, LogRecord logRecord){
String xmlLogRecord = format(logRecord);
anyLogRecord.insert_string(xmlLogRecord);
return anyLogRecord;
}
/**
* Constructs the XML log message that can be sent to the ACS logging service.
* @see java.util.logging.Formatter#format(java.util.logging.LogRecord)
*/
public String format(LogRecord logRecord) {
// log level
AcsLogLevel acsLevel = AcsLogLevel.getNativeLevel(logRecord.getLevel());
if (acsLevel == null) {
return "";
}
final AcsLogLevelDefinition acsCoreLevel = acsLevel.getAcsLevel();
final String levelName = acsLevel.getEntryName();
// get date
String TimeStamp = IsoDateFormat.formatDate(new Date(logRecord.getMillis()));
LogParameterUtil logParamUtil = new LogParameterUtil(logRecord);
StringBuffer sb = new StringBuffer("");
sb.append("<");
sb.append(levelName);
sb.append(" ");
sb.append("TimeStamp=\"" + TimeStamp + "\" ");
String file = logRecord.getSourceClassName();
if (file == null) {
if (acsCoreLevel == AcsLogLevelDefinition.DEBUG)
sb.append("File=\"unknown\" ");
}
else {
sb.append("File=\"" + file + "\" ");
}
long line = logParamUtil.extractLongProperty(LogParameterUtil.PARAM_LINE, -1);
if (line < 0) {
if (acsCoreLevel == AcsLogLevelDefinition.TRACE || acsCoreLevel == AcsLogLevelDefinition.DEBUG)
sb.append("Line=\"0\" ");
}
else {
sb.append("Line=\"" + line + "\" ");
}
String Routine = logRecord.getSourceMethodName();
if (Routine == null) {
if (acsCoreLevel == AcsLogLevelDefinition.TRACE)
sb.append("Routine=\"unknown\" ");
}
else {
sb.append("Routine=\"" + maskAttribute(Routine) + "\" ");
}
// host name: may be different from local host if ErrorTrace gets logged
String hostName = logParamUtil.extractStringProperty(LogParameterUtil.PARAM_HOSTNAME, null);
if (hostName == null || hostName.length() == 0) {
hostName = this.getLocalHostName();
}
//hostName = getLocalHostName();
sb.append("Host=\"" + hostName + "\" ");
String process = logParamUtil.extractStringProperty(LogParameterUtil.PARAM_PROCESSNAME, null);
if (process != null) {
sb.append("Process=\"" + process + "\" ");
}
else {
process = logRecord.getLoggerName();
if (process != null) {
sb.append("Process=\"" + process + "\" ");
}
}
String sourceObject = logParamUtil.extractStringProperty(LogParameterUtil.PARAM_SOURCEOBJECT, null);
if (sourceObject != null) {
sb.append("SourceObject=\"" + sourceObject + "\" ");
}
else {
sourceObject = logRecord.getLoggerName();
if (sourceObject != null) {
sb.append("SourceObject=\"" + sourceObject + "\" ");
}
}
// add thread ID, or name if given
String threadName = logParamUtil.extractStringProperty(LogParameterUtil.PARAM_THREAD_NAME, null);
if (threadName != null && threadName.length() > 0) {
sb.append("Thread=\"" + threadName + "\" ");
}
else if (logRecord.getThreadID() >= 0) {
sb.append("Thread=\"" + logRecord.getThreadID() + "\" ");
}
// add context
String context = logParamUtil.extractStringProperty("Context", null);
if (context != null) {
sb.append("Context=\"" + context + "\" ");
}
// add stack info
if (acsCoreLevel.compareTo(AcsLogLevelDefinition.WARNING) >= 0) {
// add stack id
String stackId = logParamUtil.extractStringProperty(LogParameterUtil.PARAM_STACK_ID, null);
if (stackId == null)
sb.append("StackId=\"unknown\" ");
else
sb.append("StackId=\"" + stackId + "\" ");
// add stack idlevel
long stackLevel = logParamUtil.extractLongProperty(LogParameterUtil.PARAM_STACK_LEVEL, -1);
if (stackLevel < 0)
sb.append("StackLevel=\"0\" ");
else
sb.append("StackLevel=\"" + stackLevel + "\" ");
}
// add log id
long logId = logRecord.getSequenceNumber();
if (logId >= 0) {
sb.append("LogId=\"" + logId + "\" ");
}
// add URI
String uri = logParamUtil.extractStringProperty(LogParameterUtil.PARAM_URI, null);
if (uri != null) {
sb.append("Uri=\"" + uri + "\" ");
}
// add priority
// to be written only different as entry priority
long priority = logParamUtil.extractLongProperty(LogParameterUtil.PARAM_PRIORITY, acsCoreLevel.value);
if (priority != acsCoreLevel.value) {
sb.append("Priority=\"" + priority + "\" ");
}
//add Audience, if applicable(for typeSafeLogs/Operator logs)
if (logRecord instanceof AcsLogRecord) {
String audience = ((AcsLogRecord) logRecord).getAudience();
if(!audience.equals(""))
sb.append("Audience=\"" + audience + "\" ");
//add Array
String array = ((AcsLogRecord) logRecord).getArray();
if(!array.equals(""))
sb.append("Array=\"" + array + "\" ");
//add Antenna
String antenna = ((AcsLogRecord) logRecord).getAntenna();
if(!antenna.equals(""))
sb.append("Antenna=\"" + antenna + "\" ");
}
sb.setCharAt(sb.lastIndexOf("") - 1, '>');
// the log message becomes the text in our XML record
if (logRecord.getMessage() != null) {
sb.append(maskMessage(logRecord.getMessage()));
}
// <Data> elements: logged exception or error trace, and log parameters
try {
// logged exception
Throwable loggedThrowable = logRecord.getThrown();
if (loggedThrowable != null) {
StringWriter exWriter = new StringWriter();
loggedThrowable.printStackTrace(new PrintWriter(exWriter));
sb.append("<Data Name=\"LoggedException\">" + maskMessage(exWriter.toString()) + "</Data>");
}
// log parameters (except for the special properties which were used already to set specific fields)
for (Object param : logParamUtil.getNonSpecialPropertiesMapParameters()) {
if (param instanceof Map) {
// any map that is not the special properties map we interpret as name-value pairs.
Map propertiesMap = (Map) param;
for (Object keyName : propertiesMap.keySet()) {
String value = maskEmptyDataContent(propertiesMap.get(keyName).toString());
sb.append("<Data Name=\"" + keyName.toString() + "\">" + maskMessage(value) + "</Data>");
}
}
else {
// a single parameter was logged, but we have to fit it into our name-value scheme using a fake name
String value = maskEmptyDataContent(param.toString());
sb.append("<Data Name=\"LoggedParameter\">" + maskMessage(value) + "</Data>");
}
}
}
catch (Exception e) {
// expected not to happen often at all, thus no try blocks inside every loop, so we may lose some <Data>
sb.append("<Data Name=\"DataConstructionError\">" + maskMessage(e.toString()) + "</Data>");
}
// end tag of XML record
sb.append("</" + levelName + ">");
String Log = sb.toString();
// System.out.println("Logging XML log entry " + Log);
return Log;
}
/**
* Escapes characters in the log message which would make the surrounding XML invalid.
* Embeds the message text in a <code><![CDATA[..]]></code> block.
* @return the masked message
*/
private String maskMessage(String message) {
String maskedMessage = "<![CDATA[" + message + "]]>";
return maskedMessage;
}
/**
* Escapes characters in a log record attribute which would make the surrounding XML invalid.
* Since XML attributes can't use <code><![CDATA[..]]></code>, illegal characters in <code>attributeValue</code>
* are replaced with the corresponding masked XML notation.
* @return the masked attribute value
* @see XmlNormalizer#normalize(java.lang.String)
*/
private String maskAttribute(String attributeValue) {
return XmlNormalizer.normalize(attributeValue);
}
/**
* If a Data element has empty content (resulting in <Data></Data>),
* then the xerces parser would throw an exception.
* As a workaround, we replace the empty string with "N/A".
*/
private String maskEmptyDataContent(String content) {
if (content == null || content.length() == 0) {
return "N/A";
}
return content;
}
}