package cz.cuni.mff.d3s.been.hostruntime.tasklogs;
import static cz.cuni.mff.d3s.been.cluster.Names.*;
import java.util.concurrent.TimeUnit;
import cz.cuni.mff.d3s.been.logging.TaskLogMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.hazelcast.core.IMap;
import com.hazelcast.core.IQueue;
import cz.cuni.mff.d3s.been.cluster.context.ClusterContext;
import cz.cuni.mff.d3s.been.logging.LogMessage;
import cz.cuni.mff.d3s.been.core.TaskMessageType;
import cz.cuni.mff.d3s.been.core.persistence.EntityCarrier;
import cz.cuni.mff.d3s.been.core.persistence.EntityID;
import cz.cuni.mff.d3s.been.util.JSONUtils;
import cz.cuni.mff.d3s.been.util.JsonException;
import cz.cuni.mff.d3s.been.util.JsonKeyHandler;
import cz.cuni.mff.d3s.been.util.JsonStreamer;
import cz.cuni.mff.d3s.been.debugassistant.DebugAssistant;
import cz.cuni.mff.d3s.been.socketworks.oneway.ReadOnlyHandler;
/**
* A listen handler for task log messages.
*
* @author Martin Sixta
* @author darklight
*/
public class TaskLogHandler implements ReadOnlyHandler {
private static final Logger log = LoggerFactory.getLogger(TaskLogHandler.class);
private static final EntityID LOG_ENTITY_ID = new EntityID().withKind("log").withGroup("task");
private static final String PREFIX_SEPARATOR = "#";
private final ClusterContext ctx;
private final IQueue<EntityCarrier> logQueue;
private final IMap<String, String> taskLogs;
private final IMap<String, String> contextLogs;
private final IMap<String, String> benchmarkLogs;
private final JsonStreamer jsonStreamer;
private final JSONUtils jsonUtils;
private TaskLogHandler(ClusterContext ctx) {
this.ctx = ctx;
this.logQueue = ctx.getQueue(PERSISTENCE_QUEUE_NAME);
taskLogs = ctx.getMap(LOGS_TASK_MAP_NAME);
contextLogs = ctx.getMap(LOGS_CONTEXT_MAP_NAME);
benchmarkLogs = ctx.getMap(LOGS_BENCHMARK_MAP_NAME);
jsonStreamer = new JsonStreamer();
jsonUtils = JSONUtils.newInstance();
jsonStreamer.addHandler("taskId", new JsonKeyHandler() {
@Override
public void handle(String key, String value, String json) {
// value is the new key
taskLogs.put(value, json, 60, TimeUnit.SECONDS);
}
});
jsonStreamer.addHandler("contextId", new JsonKeyHandler() {
@Override
public void handle(String key, String value, String json) {
// value is the new key
contextLogs.put(value, json, 60, TimeUnit.SECONDS);
}
});
}
/**
* Create a handler that listens to task log messages within the context of a
* cluster node.
*
* @param ctx
* Cluster context
*
* @return The handler
*/
public static TaskLogHandler create(ClusterContext ctx) {
return new TaskLogHandler(ctx);
}
private void handleMessage(TaskMessageType messageType, String message) {
switch (messageType) {
case LOG_MESSAGE:
handleLogMessage(message);
break;
case TASK_RUNNING:
handleTaskRunningMessage(message);
break;
case UNKNOWN:
break;
}
}
@Override
public void handle(String message) {
TaskMessageType messageType = getType(message);
try {
String messageContent = stripPrefix(messageType, message);
handleMessage(messageType, messageContent);
} catch (Exception e) {
String msg = String.format("Cannot handle message '%s' of type %s", message, messageType.toString());
log.error(msg, e);
}
}
private void handleTaskRunningMessage(String taskId) {
DebugAssistant debugAssistant = new DebugAssistant(ctx);
debugAssistant.setSuspended(taskId, false);
}
private void handleLogMessage(String message) {
try {
logQueue.put(fabricateEntityTransport(message));
publishLog(message);
if (log.isDebugEnabled()) {
printDebuggingMessage(message);
}
} catch (InterruptedException e) {
log.error("Interrupted when trying to submit log message {} to cluster", message);
}
}
private void publishLog(String message) {
try {
jsonStreamer.process(message);
} catch (JsonException e) {
String msg = String.format("Cannot parse log message '%s", message);
log.error(msg, e);
}
}
/**
* Prints debugging message.
*
* @param message
* JSON encoded {@link LogMessage}
*/
private void printDebuggingMessage(String message) {
try {
TaskLogMessage logMessage = jsonUtils.deserialize(message, TaskLogMessage.class);
log.debug("Task {} logs {}{}", logMessage.getTaskId(), logMessage.getMessage().getMessage(), logMessage.getMessage().getErrorTrace() == null ? "" : "\n" + logMessage.getMessage().getErrorTrace());
} catch (JsonException e) {
String errorMessage = String.format("Cannot parse log message: %s", message);
log.warn(errorMessage, e);
}
}
private EntityCarrier fabricateEntityTransport(String message) {
EntityCarrier ec = new EntityCarrier();
ec.setEntityId(LOG_ENTITY_ID);
ec.setEntityJSON(message);
return ec;
}
private TaskMessageType getType(String message) {
final int separatorIndex = message.indexOf(PREFIX_SEPARATOR);
try {
return (separatorIndex >= 0) ? TaskMessageType.valueOf(message.substring(0, separatorIndex))
: TaskMessageType.UNKNOWN;
} catch (IllegalArgumentException e) {
return TaskMessageType.UNKNOWN;
}
}
/**
* Strips prefix from the message.
*
* The message must contain the prefix.
*
* @param type
* message type
* @param message
* message to strip the prefix from
* @return message without prefix
*/
private String stripPrefix(TaskMessageType type, String message) {
String prefix = type.toString() + PREFIX_SEPARATOR;
return message.substring(prefix.length());
}
}