[Major] Refactored Operations Log

This commit is contained in:
Robert von Burg 2018-08-03 14:13:46 +02:00
parent 10ffa01dc9
commit e644b43e2c
10 changed files with 92 additions and 66 deletions

View File

@ -1,9 +1,10 @@
package li.strolch.handler.operationslog;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ResourceBundle;
import com.google.gson.JsonObject;
import li.strolch.agent.api.StrolchAgent;
import li.strolch.model.Locator;
import li.strolch.model.Tags.Json;
@ -13,6 +14,7 @@ import li.strolch.utils.helper.ExceptionHelper;
public class LogMessage extends I18nMessage {
private final String id;
private ZonedDateTime zonedDateTime;
private final String realm;
private final Locator locator;
private final LogSeverity severity;
@ -20,6 +22,7 @@ public class LogMessage extends I18nMessage {
public LogMessage(String realm, Locator locator, LogSeverity logSeverity, ResourceBundle bundle, String key) {
super(bundle, key);
this.id = StrolchAgent.getUniqueId();
this.zonedDateTime = ZonedDateTime.now();
this.realm = realm;
this.locator = locator;
this.severity = logSeverity;
@ -29,6 +32,10 @@ public class LogMessage extends I18nMessage {
return this.id;
}
public ZonedDateTime getZonedDateTime() {
return this.zonedDateTime;
}
public String getRealm() {
return this.realm;
}
@ -57,9 +64,10 @@ public class LogMessage extends I18nMessage {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty(Json.ID, this.id);
jsonObject.addProperty(Json.DATE, this.zonedDateTime.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
jsonObject.addProperty(Json.KEY, getKey());
jsonObject.addProperty(Json.MESSAGE, formatMessage());
jsonObject.addProperty(Json.SEVERITY, this.severity.getSeverity());
jsonObject.addProperty(Json.SEVERITY, this.severity.name());
jsonObject.addProperty(Json.REALM, this.realm);
jsonObject.addProperty(Json.LOCATOR, this.locator.toString());
JsonObject values = new JsonObject();

View File

@ -1,28 +1,9 @@
package li.strolch.handler.operationslog;
public enum LogSeverity {
INFO("Info"), //
NOTIFICATION("Notification"), //
WARN("Warn"), //
ERROR("Error"), //
EXCEPTION("Exception");
private String severity;
private LogSeverity(String severity) {
this.severity = severity;
}
public String getSeverity() {
return this.severity;
}
public static LogSeverity from(String value) {
for (LogSeverity type : values()) {
if (type.severity.equals(value))
return type;
}
throw new IllegalArgumentException("No severity exists for " + value);
}
Info,
Notification,
Warning,
Error,
Exception;
}

View File

@ -1,14 +1,6 @@
package li.strolch.handler.operationslog;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.*;
import li.strolch.agent.api.ComponentContainer;
import li.strolch.agent.api.StrolchComponent;
@ -36,7 +28,7 @@ public class OperationsLog extends StrolchComponent {
super.initialize(configuration);
}
public void addMessage(LogMessage logMessage) {
public synchronized void addMessage(LogMessage logMessage) {
// store in global list
LinkedHashMap<LogMessage, LogMessage> logMessages = this.logMessagesByRealmAndId
@ -46,25 +38,25 @@ public class OperationsLog extends StrolchComponent {
// store under locator
LinkedHashMap<Locator, LinkedHashSet<LogMessage>> logMessagesLocator = this.logMessagesByLocator
.computeIfAbsent(logMessage.getRealm(), this::newBoundedLocatorMap);
LinkedHashSet<LogMessage> messages = logMessagesLocator.computeIfAbsent(logMessage.getLocator(),
(l) -> new LinkedHashSet<LogMessage>());
LinkedHashSet<LogMessage> messages = logMessagesLocator
.computeIfAbsent(logMessage.getLocator(), (l) -> new LinkedHashSet<>());
messages.add(logMessage);
}
public void clearMessages(String realm, Locator locator) {
public synchronized void clearMessages(String realm, Locator locator) {
LinkedHashMap<Locator, LinkedHashSet<LogMessage>> logMessages = this.logMessagesByLocator.get(realm);
if (logMessages != null)
logMessages.remove(locator);
}
public Optional<Set<LogMessage>> getMessagesFor(String realm, Locator locator) {
public synchronized Optional<Set<LogMessage>> getMessagesFor(String realm, Locator locator) {
LinkedHashMap<Locator, LinkedHashSet<LogMessage>> logMessages = this.logMessagesByLocator.get(realm);
if (logMessages == null)
return Optional.empty();
return Optional.ofNullable(logMessages.get(locator));
}
public List<LogMessage> getMessages(String realm) {
public synchronized List<LogMessage> getMessages(String realm) {
LinkedHashMap<LogMessage, LogMessage> logMessages = this.logMessagesByRealmAndId.get(realm);
if (logMessages == null)
return Collections.emptyList();

View File

@ -1321,7 +1321,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
if (this.operationsLog != null) {
this.operationsLog.addMessage(
new LogMessage(this.realm.getRealm(), Locator.valueOf(AGENT, "tx", StrolchAgent.getUniqueId()),
LogSeverity.EXCEPTION, ResourceBundle.getBundle("strolch-agent"), "agent.tx.failed")
LogSeverity.Exception, ResourceBundle.getBundle("strolch-agent"), "agent.tx.failed")
.value("reason", e));
}

View File

@ -1,18 +1,21 @@
package li.strolch.rest.endpoint;
import java.util.List;
import static java.util.Comparator.comparing;
import static li.strolch.utils.helper.StringHelper.isNotEmpty;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import li.strolch.handler.operationslog.LogMessage;
import li.strolch.handler.operationslog.LogSeverity;
import li.strolch.handler.operationslog.OperationsLog;
import li.strolch.rest.RestfulStrolchComponent;
import li.strolch.rest.helper.ResponseUtil;
@ -24,15 +27,47 @@ public class OperationsLogResource {
@GET
@Path("{realm}")
@Produces(MediaType.APPLICATION_JSON)
public Response getOperationLog(@Context HttpServletRequest request, @QueryParam("offset") int offset,
@QueryParam("limit") int limit, @PathParam("realm") String realm) {
public Response getOperationLog(@Context HttpServletRequest request, @PathParam("realm") String realm,
@QueryParam("offset") int offset, @QueryParam("limit") int limit, @QueryParam("severity") String severityS,
@QueryParam("from") String fromS, @QueryParam("to") String toS, @QueryParam("query") String query) {
// TODO do privilege check
OperationsLog operationsLog = RestfulStrolchComponent.getInstance().getComponent(OperationsLog.class);
List<LogMessage> messages = operationsLog.getMessages(realm);
Stream<LogMessage> messages = operationsLog.getMessages(realm).stream();
Paging<LogMessage> paging = Paging.asPage(messages, offset, limit);
if (isNotEmpty(severityS)) {
LogSeverity severity = LogSeverity.valueOf(severityS);
messages = messages.filter(logMessage -> logMessage.getSeverity() == severity);
}
if (isNotEmpty(query)) {
messages = messages.filter(logMessage -> logMessage.getMessage().toLowerCase().contains(query) //
|| logMessage.getLocator().getPathElements().contains(query));
}
if (isNotEmpty(fromS) && isNotEmpty(toS)) {
ZonedDateTime from = LocalDate.parse(fromS).atStartOfDay(ZoneId.systemDefault());
ZonedDateTime to = LocalDate.parse(toS).plusDays(1).atStartOfDay(ZoneId.systemDefault());
messages = messages.filter(logMessage -> from.isBefore(logMessage.getZonedDateTime()) && to
.isAfter(logMessage.getZonedDateTime()));
} else if (isNotEmpty(fromS)) {
ZonedDateTime from = LocalDate.parse(fromS).atStartOfDay(ZoneId.systemDefault());
messages = messages.filter(logMessage -> from.isBefore(logMessage.getZonedDateTime()));
} else if (isNotEmpty(toS)) {
ZonedDateTime to = LocalDate.parse(toS).plusDays(1).atStartOfDay(ZoneId.systemDefault());
messages = messages.filter(logMessage -> to.isAfter(logMessage.getZonedDateTime()));
}
messages = messages.sorted(comparing(LogMessage::getId).reversed());
Paging<LogMessage> paging = Paging.asPage(messages.collect(Collectors.toList()), offset, limit);
return ResponseUtil.toResponse(paging, LogMessage::toJson);
}
}

View File

@ -163,7 +163,7 @@ public class EventBasedExecutionHandler extends ExecutionHandler {
logger.error("Failed to set " + locator + " to execution due to " + e.getMessage(), e);
if (getContainer().hasComponent(OperationsLog.class)) {
getComponent(OperationsLog.class).addMessage(new LogMessage(realm, locator, LogSeverity.EXCEPTION,
getComponent(OperationsLog.class).addMessage(new LogMessage(realm, locator, LogSeverity.Exception,
ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.execution")
.value("reason", e));
}
@ -186,7 +186,7 @@ public class EventBasedExecutionHandler extends ExecutionHandler {
logger.error("Failed to set " + locator + " to executed due to " + e.getMessage(), e);
if (getContainer().hasComponent(OperationsLog.class)) {
getComponent(OperationsLog.class).addMessage(new LogMessage(realm, locator, LogSeverity.EXCEPTION,
getComponent(OperationsLog.class).addMessage(new LogMessage(realm, locator, LogSeverity.Exception,
ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.executed")
.value("reason", e));
}
@ -205,7 +205,7 @@ public class EventBasedExecutionHandler extends ExecutionHandler {
logger.error("Failed to set " + locator + " to stopped due to " + e.getMessage(), e);
if (getContainer().hasComponent(OperationsLog.class)) {
getComponent(OperationsLog.class).addMessage(new LogMessage(realm, locator, LogSeverity.EXCEPTION,
getComponent(OperationsLog.class).addMessage(new LogMessage(realm, locator, LogSeverity.Exception,
ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.stopped")
.value("reason", e));
}
@ -224,7 +224,7 @@ public class EventBasedExecutionHandler extends ExecutionHandler {
logger.error("Failed to set " + locator + " to error due to " + e.getMessage(), e);
if (getContainer().hasComponent(OperationsLog.class)) {
getComponent(OperationsLog.class).addMessage(new LogMessage(realm, locator, LogSeverity.EXCEPTION,
getComponent(OperationsLog.class).addMessage(new LogMessage(realm, locator, LogSeverity.Exception,
ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.error")
.value("reason", e));
}
@ -243,7 +243,7 @@ public class EventBasedExecutionHandler extends ExecutionHandler {
logger.error("Failed to set " + locator + " to warning due to " + e.getMessage(), e);
if (getContainer().hasComponent(OperationsLog.class)) {
getComponent(OperationsLog.class).addMessage(new LogMessage(realm, locator, LogSeverity.EXCEPTION,
getComponent(OperationsLog.class).addMessage(new LogMessage(realm, locator, LogSeverity.Exception,
ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.warning")
.value("reason", e));
}
@ -283,7 +283,7 @@ public class EventBasedExecutionHandler extends ExecutionHandler {
if (getContainer().hasComponent(OperationsLog.class)) {
getComponent(OperationsLog.class).addMessage(
new LogMessage(realm, activityLoc, LogSeverity.EXCEPTION,
new LogMessage(realm, activityLoc, LogSeverity.Exception,
ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.archive")
.value("reason", e));
}

View File

@ -10,9 +10,9 @@ import li.strolch.model.activity.Action;
import li.strolch.persistence.api.StrolchTransaction;
/**
* The {@link ToErrorReservationExecution} executes same as {@link ReservationExection} with the difference that
* {@link #isExecutable(Action)} always returns true, and if the action's resource is currently reserved, the execution
* fails and the state is set to ERROR
* The {@link ToErrorReservationExecution} executes same as {@link ReservationExection} with the difference that {@link
* #isExecutable(Action)} always returns true, and if the action's resource is currently reserved, the execution fails
* and the state is set to ERROR
*
* @author Robert von Burg <eitch@eitchnet.ch>
*/
@ -44,7 +44,7 @@ public class ToErrorReservationExecution extends ReservationExection {
if (action.getType().equals(TYPE_RESERVE) && isReserved(action)) {
setActionState(action, State.EXECUTION);
toError(new LogMessage(tx().getRealmName(), action.getLocator(), LogSeverity.ERROR,
toError(new LogMessage(tx().getRealmName(), action.getLocator(), LogSeverity.Error,
ResourceBundle.getBundle("strolch-service"), "execution.policy.reservation.alreadyReserved")
.value("resourceLoc", getResource(action).getLocator().toString()));
} else {

View File

@ -145,7 +145,7 @@ public class Migrations {
List<Version> list = migrationsRan.getList(realm);
for (Version version : list) {
LogMessage logMessage = new LogMessage(realm, locator.append(StrolchAgent.getUniqueId()),
LogSeverity.INFO, ResourceBundle.getBundle("strolch-service"),
LogSeverity.Info, ResourceBundle.getBundle("strolch-service"),
"execution.handler.migrations.version").value("version", version.toString());
operationsLog.addMessage(logMessage);
}

View File

@ -202,7 +202,7 @@ public class MigrationsHandler extends StrolchComponent {
if (getContainer().hasComponent(OperationsLog.class)) {
getComponent(OperationsLog.class).addMessage(
new LogMessage(Tags.AGENT, getLocator().append(StrolchAgent.getUniqueId()),
LogSeverity.EXCEPTION, ResourceBundle.getBundle("strolch-service"),
LogSeverity.Exception, ResourceBundle.getBundle("strolch-service"),
"execution.handler.failed.executed").value("reason", e));
}
}

View File

@ -18,6 +18,7 @@ public class I18nMessage {
private ResourceBundle bundle;
private String key;
private Properties values;
private String message;
public I18nMessage(ResourceBundle bundle, String key) {
DBC.INTERIM.assertNotNull("bundle must be set!", bundle);
@ -35,6 +36,10 @@ public class I18nMessage {
return this.values;
}
public String getMessage() {
return formatMessage();
}
public I18nMessage value(String key, String value) {
DBC.INTERIM.assertNotEmpty("key must be set!", key);
this.values.setProperty(key, value == null ? "-" : value);
@ -42,13 +47,18 @@ public class I18nMessage {
}
public String formatMessage() {
if (this.message != null)
return this.message;
try {
String string = this.bundle.getString(this.key);
return StringHelper.replacePropertiesIn(this.values, EMPTY, string);
this.message = StringHelper.replacePropertiesIn(this.values, EMPTY, string);
} catch (MissingResourceException e) {
logger.error("Key " + this.key + " is missing in bundle " + this.bundle.getBaseBundleName());
return this.key;
this.message = this.key;
}
return this.message;
}
@Override