/*
* ProActive Parallel Suite(TM):
* The Open Source library for parallel and distributed
* Workflows & Scheduling, Orchestration, Cloud Automation
* and Big Data Analysis on Enterprise Grids & Clouds.
*
* Copyright (c) 2007 - 2017 ActiveEon
* Contact: contact@activeeon.com
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation: version 3 of
* the License.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* If needed, contact us to obtain a release under GPL Version 2 or 3
* or a different license than the AGPL.
*/
package org.ow2.proactive.scheduler.common.task;
import static com.google.common.base.Throwables.getStackTraceAsString;
import java.io.IOException;
import java.util.LinkedList;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import org.apache.log4j.Layout;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.spi.LoggingEvent;
import org.objectweb.proactive.annotation.PublicAPI;
import org.ow2.proactive.scheduler.common.job.JobId;
import org.ow2.proactive.scheduler.core.properties.PASchedulerProperties;
import org.ow2.proactive.utils.ObjectByteConverter;
/**
* Log4j based implementation of TaskLogs.
* @author The ProActive Team
* @since 2.2
*/
@PublicAPI
@XmlAccessorType(XmlAccessType.FIELD)
public class Log4JTaskLogs implements TaskLogs {
// fix for #2456 : Credential Data and TaskLogs contain serialVersionUID based on scheduler server version
private static final long serialVersionUID = 1L;
/** Prefix for job logger */
public static final String JOB_LOGGER_PREFIX = "logger.scheduler.";
public static final String MDC_JOB_ID = "job.id";
/** Log4j context variable name for task ids */
public static final String MDC_TASK_ID = "task.id";
public static final String MDC_TASK_NAME = "task.name";
/** Log4j context variable name for task ids */
public static final String MDC_HOST = "host";
/** Default layout for logs */
public static Layout getTaskLogLayout() {
PatternLayout patternLayout;
if (PASchedulerProperties.SCHEDULER_JOB_LOGS_PATTERN.isSet()) {
patternLayout = new PatternLayout(PASchedulerProperties.SCHEDULER_JOB_LOGS_PATTERN.getValueAsString());
} else {
patternLayout = new PatternLayout("[%X{" + Log4JTaskLogs.MDC_JOB_ID + "}t%X{" + Log4JTaskLogs.MDC_TASK_ID +
"}@%X{" + Log4JTaskLogs.MDC_HOST + "};%d{HH:mm:ss}]" + " %m %n");
}
return patternLayout;
}
public static String getLoggerName(String jobId) {
return JOB_LOGGER_PREFIX + jobId;
}
public static String getLoggerName(JobId jobId) {
return getLoggerName(jobId.toString());
}
/** Logger level in which stdout must be redirected */
public static final Level STDOUT_LEVEL = Level.INFO;
/** Logger level in which stderr must be redirected */
public static final Level STDERR_LEVEL = Level.ERROR;
/** The logs buffer */
private transient LinkedList<LoggingEvent> allEvents;
private byte[] serializedAllEvents;
private String loggerName;
/** New line **/
private static final String nl = System.lineSeparator();
/** Hibernate constructor */
public Log4JTaskLogs() {
}
/**
* Create a new Log4JTaskLogs log.
* @param all the buffer of logging events.
*/
public Log4JTaskLogs(LinkedList<LoggingEvent> all, String jobId) {
this.allEvents = all;
this.loggerName = Log4JTaskLogs.JOB_LOGGER_PREFIX + jobId;
storeEvents();
}
/**
*
*
* @see org.ow2.proactive.scheduler.common.task.TaskLogs#getAllLogs(boolean
* timeStamp)
*/
public synchronized String getAllLogs(boolean timeStamp) {
restoreEvents();
StringBuffer logs = new StringBuffer(this.allEvents.size());
Layout l = getTaskLogLayout();
for (LoggingEvent e : this.allEvents) {
appendEventToLogs(e, logs, l, timeStamp);
}
return logs.toString();
}
private void appendEventToLogs(LoggingEvent logEvent, StringBuffer logs, Layout logFormat, boolean timeStamp) {
if (timeStamp) {
logs.append(logFormat.format(logEvent));
} else {
logs.append(logEvent.getMessage());
logs.append(nl);
}
}
/**
* Restore the compressed byte array in the list of loggingEvent.
*/
@SuppressWarnings("unchecked")
private void restoreEvents() {
if (this.allEvents == null) {
// restore log4j events
try {
this.allEvents = (LinkedList<LoggingEvent>) ObjectByteConverter.byteArrayToObject(this.serializedAllEvents,
true);
} catch (Exception e) {
//store exception event in logs if we cannot convert
LoggingEvent logError = new LoggingEvent(loggerName,
Logger.getLogger(loggerName),
STDERR_LEVEL,
"Cannot restore logging event from byte array : " +
getStackTraceAsString(e),
e);
this.allEvents = new LinkedList<LoggingEvent>();
this.allEvents.add(logError);
}
//this.serializedAllEvents = null;
}
}
/**
* Store the list of loggingEvent in a compressed byte array.
*/
private void storeEvents() {
if (this.serializedAllEvents == null) {
try {
this.serializedAllEvents = ObjectByteConverter.objectToByteArray(this.allEvents, true);
} catch (IOException e) {
//create a log4j event with e inside
LoggingEvent logError = new LoggingEvent(loggerName,
Logger.getLogger(loggerName),
STDERR_LEVEL,
"Could not convert logging event to byte array : " +
getStackTraceAsString(e),
e);
LinkedList<LoggingEvent> errorEvent = new LinkedList<LoggingEvent>();
errorEvent.add(logError);
try {
this.serializedAllEvents = ObjectByteConverter.objectToByteArray(errorEvent, true);
} catch (IOException e1) {
Logger.getLogger(Log4JTaskLogs.class).error("Could not convert to serialized events", e1);
}
}
this.allEvents = null;
}
}
/**
* @see org.ow2.proactive.scheduler.common.task.TaskLogs#getStderrLogs(boolean
* timeStamp)
*/
public synchronized String getStderrLogs(boolean timeStamp) {
restoreEvents();
StringBuffer logs = new StringBuffer();
Layout l = getTaskLogLayout();
for (LoggingEvent e : this.allEvents) {
if (Log4JTaskLogs.STDERR_LEVEL.equals(e.getLevel())) {
appendEventToLogs(e, logs, l, timeStamp);
}
}
return logs.toString();
}
@Override
public String getStdoutLogs() {
return getStdoutLogs(false);
}
@Override
public String getStderrLogs() {
return getStderrLogs(false);
}
@Override
public String getAllLogs() {
return getAllLogs(false);
}
/**
* @see org.ow2.proactive.scheduler.common.task.TaskLogs#getStdoutLogs(boolean
* timeStamp)
*/
public synchronized String getStdoutLogs(boolean timeStamp) {
restoreEvents();
StringBuffer logs = new StringBuffer();
Layout l = getTaskLogLayout();
for (LoggingEvent e : this.allEvents) {
if (Log4JTaskLogs.STDOUT_LEVEL.equals(e.getLevel())) {
appendEventToLogs(e, logs, l, timeStamp);
}
}
return logs.toString();
}
/**
* Return all the currently logged events
* @return a list containing all the currently logged events
*/
@SuppressWarnings("unchecked")
public synchronized LinkedList<LoggingEvent> getAllEvents() {
restoreEvents();
return (LinkedList<LoggingEvent>) allEvents.clone();
}
private void writeObject(java.io.ObjectOutputStream out) throws IOException {
storeEvents();
out.defaultWriteObject();
}
}