[Major] I18nMessage and LogMessage refactorings, added state, JSON parsers, etc.

This commit is contained in:
Robert von Burg 2020-05-26 17:37:57 +02:00
parent 006508ff2e
commit 76aff683ee
28 changed files with 608 additions and 104 deletions

View File

@ -29,6 +29,7 @@ import java.util.*;
import li.strolch.agent.api.*; import li.strolch.agent.api.*;
import li.strolch.exception.StrolchException; import li.strolch.exception.StrolchException;
import li.strolch.handler.operationslog.LogMessage; import li.strolch.handler.operationslog.LogMessage;
import li.strolch.handler.operationslog.LogMessageState;
import li.strolch.handler.operationslog.LogSeverity; import li.strolch.handler.operationslog.LogSeverity;
import li.strolch.handler.operationslog.OperationsLog; import li.strolch.handler.operationslog.OperationsLog;
import li.strolch.model.Locator; import li.strolch.model.Locator;
@ -287,7 +288,7 @@ public class ComponentContainerImpl implements ComponentContainer {
for (String realmName : getRealmNames()) { for (String realmName : getRealmNames()) {
getComponent(OperationsLog.class).addMessage(new LogMessage(realmName, SYSTEM_USER_AGENT, getComponent(OperationsLog.class).addMessage(new LogMessage(realmName, SYSTEM_USER_AGENT,
Locator.valueOf(AGENT, "strolch-agent", StrolchAgent.getUniqueId()), LogSeverity.Info, Locator.valueOf(AGENT, "strolch-agent", StrolchAgent.getUniqueId()), LogSeverity.Info,
ResourceBundle.getBundle("strolch-agent"), "agent.started") // LogMessageState.Information, ResourceBundle.getBundle("strolch-agent"), "agent.started") //
.value("applicationName", applicationName) // .value("applicationName", applicationName) //
.value("environment", environment) // .value("environment", environment) //
.value("components", "" + this.controllerMap.size()) // .value("components", "" + this.controllerMap.size()) //
@ -311,7 +312,7 @@ public class ComponentContainerImpl implements ComponentContainer {
for (String realmName : getRealmNames()) { for (String realmName : getRealmNames()) {
getComponent(OperationsLog.class).addMessage(new LogMessage(realmName, SYSTEM_USER_AGENT, getComponent(OperationsLog.class).addMessage(new LogMessage(realmName, SYSTEM_USER_AGENT,
Locator.valueOf(AGENT, "strolch-agent", StrolchAgent.getUniqueId()), LogSeverity.Info, Locator.valueOf(AGENT, "strolch-agent", StrolchAgent.getUniqueId()), LogSeverity.Info,
ResourceBundle.getBundle("strolch-agent"), "agent.stopping") // LogMessageState.Information, ResourceBundle.getBundle("strolch-agent"), "agent.stopping") //
.value("applicationName", applicationName) // .value("applicationName", applicationName) //
.value("environment", environment) // .value("environment", environment) //
.value("components", "" + this.controllerMap.size())); .value("components", "" + this.controllerMap.size()));

View File

@ -26,6 +26,7 @@ import java.util.concurrent.ScheduledExecutorService;
import li.strolch.agent.api.*; import li.strolch.agent.api.*;
import li.strolch.handler.operationslog.LogMessage; import li.strolch.handler.operationslog.LogMessage;
import li.strolch.handler.operationslog.LogMessageState;
import li.strolch.handler.operationslog.LogSeverity; import li.strolch.handler.operationslog.LogSeverity;
import li.strolch.handler.operationslog.OperationsLog; import li.strolch.handler.operationslog.OperationsLog;
import li.strolch.model.Locator; import li.strolch.model.Locator;
@ -162,7 +163,7 @@ public class DefaultObserverHandler implements ObserverHandler {
OperationsLog operationsLog = container.getComponent(OperationsLog.class); OperationsLog operationsLog = container.getComponent(OperationsLog.class);
operationsLog.addMessage(new LogMessage(this.realm.getRealm(), SYSTEM_USER_AGENT, operationsLog.addMessage(new LogMessage(this.realm.getRealm(), SYSTEM_USER_AGENT,
Locator.valueOf(AGENT, ObserverHandler.class.getName(), type, StrolchAgent.getUniqueId()), Locator.valueOf(AGENT, ObserverHandler.class.getName(), type, StrolchAgent.getUniqueId()),
LogSeverity.Exception, ResourceBundle.getBundle("strolch-agent"), "agent.observers.update.failed") LogSeverity.Exception, LogMessageState.Information, ResourceBundle.getBundle("strolch-agent"), "agent.observers.update.failed")
.withException(e).value("type", type).value("reason", e)); .withException(e).value("type", type).value("reason", e));
} }
} }

View File

@ -27,6 +27,7 @@ import java.util.concurrent.TimeUnit;
import li.strolch.agent.api.*; import li.strolch.agent.api.*;
import li.strolch.handler.operationslog.LogMessage; import li.strolch.handler.operationslog.LogMessage;
import li.strolch.handler.operationslog.LogMessageState;
import li.strolch.handler.operationslog.LogSeverity; import li.strolch.handler.operationslog.LogSeverity;
import li.strolch.handler.operationslog.OperationsLog; import li.strolch.handler.operationslog.OperationsLog;
import li.strolch.model.Locator; import li.strolch.model.Locator;
@ -193,7 +194,7 @@ public class EventCollectingObserverHandler implements ObserverHandler {
OperationsLog operationsLog = container.getComponent(OperationsLog.class); OperationsLog operationsLog = container.getComponent(OperationsLog.class);
operationsLog.addMessage(new LogMessage(this.realm.getRealm(), SYSTEM_USER_AGENT, operationsLog.addMessage(new LogMessage(this.realm.getRealm(), SYSTEM_USER_AGENT,
Locator.valueOf(AGENT, ObserverHandler.class.getName(), type, StrolchAgent.getUniqueId()), Locator.valueOf(AGENT, ObserverHandler.class.getName(), type, StrolchAgent.getUniqueId()),
LogSeverity.Exception, ResourceBundle.getBundle("strolch-agent"), "agent.observers.update.failed") LogSeverity.Exception, LogMessageState.Information, ResourceBundle.getBundle("strolch-agent"), "agent.observers.update.failed")
.withException(e).value("type", type).value("reason", e)); .withException(e).value("type", type).value("reason", e));
} }
} }

View File

@ -2,6 +2,7 @@ package li.strolch.handler.operationslog;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.Objects;
import java.util.Properties; import java.util.Properties;
import java.util.ResourceBundle; import java.util.ResourceBundle;
@ -11,32 +12,36 @@ import li.strolch.model.Locator;
import li.strolch.model.Tags.Json; import li.strolch.model.Tags.Json;
import li.strolch.utils.I18nMessage; import li.strolch.utils.I18nMessage;
import li.strolch.utils.helper.ExceptionHelper; import li.strolch.utils.helper.ExceptionHelper;
import li.strolch.utils.iso8601.ISO8601;
public class LogMessage extends I18nMessage { public class LogMessage extends I18nMessage {
private final String id; private final String id;
private final String username; private final String username;
private ZonedDateTime zonedDateTime; private final ZonedDateTime zonedDateTime;
private final String realm; private final String realm;
private final Locator locator; private final Locator locator;
private final LogSeverity severity; private final LogSeverity severity;
private LogMessageState state;
private String stackTrace; private String stackTrace;
public LogMessage(String realm, String username, Locator locator, LogSeverity severity, ResourceBundle bundle, public LogMessage(String realm, String username, Locator locator, LogSeverity severity, LogMessageState state,
String key) { ResourceBundle bundle, String key) {
super(bundle, key); super(bundle, key);
this.id = StrolchAgent.getUniqueId(); this.id = StrolchAgent.getUniqueId();
this.zonedDateTime = ZonedDateTime.now();
// persisting in the DB only handles millisecond precision, not nano precision // persisting in the DB only handles millisecond precision, not nano precision
this.zonedDateTime = this.zonedDateTime.withNano((this.zonedDateTime.getNano() / 1000000) * 1000000); ZonedDateTime now = ZonedDateTime.now();
this.zonedDateTime = now.withNano((now.getNano() / 1000000) * 1000000);
this.realm = realm; this.realm = realm;
this.username = username; this.username = username;
this.locator = locator; this.locator = locator;
this.severity = severity; this.severity = severity;
this.state = state;
} }
public LogMessage(String id, ZonedDateTime zonedDateTime, String realm, String username, Locator locator, public LogMessage(String id, ZonedDateTime zonedDateTime, String realm, String username, Locator locator,
LogSeverity severity, String key, Properties values, String message, String stackTrace) { LogSeverity severity, LogMessageState state, String key, Properties values, String message,
String stackTrace) {
super(key, values, message); super(key, values, message);
this.id = id; this.id = id;
this.zonedDateTime = zonedDateTime; this.zonedDateTime = zonedDateTime;
@ -44,6 +49,7 @@ public class LogMessage extends I18nMessage {
this.username = username; this.username = username;
this.locator = locator; this.locator = locator;
this.severity = severity; this.severity = severity;
this.state = state;
this.stackTrace = stackTrace; this.stackTrace = stackTrace;
} }
@ -71,6 +77,14 @@ public class LogMessage extends I18nMessage {
return this.severity; return this.severity;
} }
public LogMessageState getState() {
return this.state;
}
public void setState(LogMessageState state) {
this.state = state;
}
public LogMessage withException(Throwable t) { public LogMessage withException(Throwable t) {
this.stackTrace = ExceptionHelper.formatException(t); this.stackTrace = ExceptionHelper.formatException(t);
return this; return this;
@ -100,10 +114,12 @@ public class LogMessage extends I18nMessage {
jsonObject.addProperty(Json.KEY, getKey()); jsonObject.addProperty(Json.KEY, getKey());
jsonObject.addProperty(Json.MESSAGE, formatMessage()); jsonObject.addProperty(Json.MESSAGE, formatMessage());
jsonObject.addProperty(Json.SEVERITY, this.severity.name()); jsonObject.addProperty(Json.SEVERITY, this.severity.name());
jsonObject.addProperty(Json.STATE, this.state.name());
jsonObject.addProperty(Json.USERNAME, this.username); jsonObject.addProperty(Json.USERNAME, this.username);
jsonObject.addProperty(Json.REALM, this.realm); jsonObject.addProperty(Json.REALM, this.realm);
jsonObject.addProperty(Json.LOCATOR, this.locator.toString()); jsonObject.addProperty(Json.LOCATOR, this.locator.toString());
jsonObject.addProperty(Json.EXCEPTION, this.stackTrace); if (this.stackTrace != null)
jsonObject.addProperty(Json.EXCEPTION, this.stackTrace);
JsonObject values = new JsonObject(); JsonObject values = new JsonObject();
for (String key : getValues().stringPropertyNames()) { for (String key : getValues().stringPropertyNames()) {
values.addProperty(key, getValues().getProperty(key)); values.addProperty(key, getValues().getProperty(key));
@ -113,37 +129,48 @@ public class LogMessage extends I18nMessage {
return jsonObject; return jsonObject;
} }
@Override public static LogMessage fromJson(JsonObject messageJ) {
public int hashCode() {
final int prime = 31; String id = messageJ.get(Json.ID).getAsString();
int result = super.hashCode(); ZonedDateTime zonedDateTime = ISO8601.parseToZdt(messageJ.get(Json.DATE).getAsString());
result = prime * result + ((this.locator == null) ? 0 : this.locator.hashCode()); String realm = messageJ.get(Json.REALM).getAsString();
result = prime * result + ((this.realm == null) ? 0 : this.realm.hashCode()); String username = messageJ.get(Json.USERNAME).getAsString();
result = prime * result + ((this.severity == null) ? 0 : this.severity.hashCode()); Locator locator = Locator.valueOf(messageJ.get(Json.LOCATOR).getAsString());
return result; LogSeverity severity = LogSeverity.valueOf(messageJ.get(Json.SEVERITY).getAsString());
LogMessageState state = LogMessageState.valueOf(messageJ.get(Json.STATE).getAsString());
String key = messageJ.get(Json.KEY).getAsString();
String message = messageJ.get(Json.MESSAGE).getAsString();
String stackTrace = messageJ.has(Json.EXCEPTION) ? messageJ.get(Json.EXCEPTION).getAsString() : "";
Properties properties = new Properties();
if (messageJ.has(Json.VALUES)) {
JsonObject valuesJ = messageJ.getAsJsonObject(Json.VALUES);
for (String propertyName : valuesJ.keySet()) {
properties.setProperty(propertyName, valuesJ.get(propertyName).getAsString());
}
}
return new LogMessage(id, zonedDateTime, realm, username, locator, severity, state, key, properties, message,
stackTrace);
} }
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object o) {
if (this == obj) if (this == o)
return true; return true;
if (!super.equals(obj)) if (o == null || getClass() != o.getClass())
return false; return false;
if (getClass() != obj.getClass()) if (!super.equals(o))
return false; return false;
LogMessage other = (LogMessage) obj;
if (this.locator == null) { LogMessage that = (LogMessage) o;
if (other.locator != null) return Objects.equals(id, that.id);
return false; }
} else if (!this.locator.equals(other.locator))
return false; @Override
if (this.realm == null) { public int hashCode() {
if (other.realm != null) int result = super.hashCode();
return false; result = 31 * result + (id != null ? id.hashCode() : 0);
} else if (!this.realm.equals(other.realm)) return result;
return false;
if (this.severity != other.severity)
return false;
return true;
} }
} }

View File

@ -0,0 +1,7 @@
package li.strolch.handler.operationslog;
public enum LogMessageState {
Active,
Inactive,
Information
}

View File

@ -5,6 +5,7 @@ import static li.strolch.runtime.StrolchConstants.SYSTEM_USER_AGENT;
import java.util.*; import java.util.*;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;
import li.strolch.agent.api.ComponentContainer; import li.strolch.agent.api.ComponentContainer;
import li.strolch.agent.api.StrolchAgent; import li.strolch.agent.api.StrolchAgent;
@ -94,6 +95,72 @@ public class OperationsLog extends StrolchComponent {
this.executorService.submit(() -> persist(realm, logMessage, messagesToRemove)); this.executorService.submit(() -> persist(realm, logMessage, messagesToRemove));
} }
public void removeMessage(LogMessage message) {
String realmName = message.getRealm();
LinkedHashMap<Locator, LinkedHashSet<LogMessage>> byLocator = this.logMessagesByLocator.get(realmName);
if (byLocator != null)
byLocator.remove(message.getLocator());
List<LogMessage> messages = this.logMessagesByRealmAndId.get(realmName);
if (messages != null) {
messages.remove(message);
// persist changes for non-transient realms
StrolchRealm realm = getContainer().getRealm(realmName);
if (!realm.getMode().isTransient())
this.executorService.submit(() -> persist(realm, null, Collections.singletonList(message)));
}
}
public synchronized void removeMessages(Collection<LogMessage> logMessages) {
Map<String, List<LogMessage>> messagesByRealm = logMessages.stream()
.collect(Collectors.groupingBy(LogMessage::getRealm));
messagesByRealm.forEach((realmName, messages) -> {
LinkedHashMap<Locator, LinkedHashSet<LogMessage>> byLocator = this.logMessagesByLocator.get(realmName);
if (byLocator != null)
messages.forEach(logMessage -> byLocator.remove(logMessage.getLocator()));
List<LogMessage> byRealm = this.logMessagesByRealmAndId.get(realmName);
if (byRealm != null) {
messages.removeIf(logMessage -> !byRealm.remove(logMessage));
// persist changes for non-transient realms
StrolchRealm realm = getContainer().getRealm(realmName);
if (!realm.getMode().isTransient())
this.executorService.submit(() -> persist(realm, null, messages));
}
});
}
public synchronized void updateState(String realmName, Locator locator, LogMessageState state) {
getMessagesFor(realmName, locator).ifPresent(logMessages -> {
logMessages.forEach(logMessage -> logMessage.setState(state));
StrolchRealm realm = getContainer().getRealm(realmName);
if (!realm.getMode().isTransient()) {
persist(realm, logMessages);
}
});
}
public synchronized void updateState(String realmName, String id, LogMessageState state) {
List<LogMessage> logMessages = this.logMessagesByRealmAndId.get(realmName);
for (LogMessage logMessage : logMessages) {
if (logMessage.getId().equals(id)) {
logMessage.setState(state);
StrolchRealm realm = getContainer().getRealm(realmName);
if (!realm.getMode().isTransient()) {
persist(realm, logMessages);
}
}
}
}
private List<LogMessage> pruneMessages(List<LogMessage> logMessages) { private List<LogMessage> pruneMessages(List<LogMessage> logMessages) {
if (logMessages.size() < this.maxMessages) if (logMessages.size() < this.maxMessages)
return Collections.emptyList(); return Collections.emptyList();
@ -124,7 +191,8 @@ public class OperationsLog extends StrolchComponent {
LogMessageDao logMessageDao = tx.getPersistenceHandler().getLogMessageDao(tx); LogMessageDao logMessageDao = tx.getPersistenceHandler().getLogMessageDao(tx);
if (messagesToRemove != null && !messagesToRemove.isEmpty()) if (messagesToRemove != null && !messagesToRemove.isEmpty())
logMessageDao.removeAll(messagesToRemove); logMessageDao.removeAll(messagesToRemove);
logMessageDao.save(logMessage); if (logMessage != null)
logMessageDao.save(logMessage);
tx.commitOnClose(); tx.commitOnClose();
} }
}); });
@ -134,7 +202,31 @@ public class OperationsLog extends StrolchComponent {
this.logMessagesByRealmAndId.computeIfAbsent(realm.getRealm(), r -> new ArrayList<>()) this.logMessagesByRealmAndId.computeIfAbsent(realm.getRealm(), r -> new ArrayList<>())
.add(new LogMessage(realm.getRealm(), SYSTEM_USER_AGENT, .add(new LogMessage(realm.getRealm(), SYSTEM_USER_AGENT,
Locator.valueOf(AGENT, "strolch-agent", StrolchAgent.getUniqueId()), LogSeverity.Info, Locator.valueOf(AGENT, "strolch-agent", StrolchAgent.getUniqueId()), LogSeverity.Info,
ResourceBundle.getBundle("strolch-agent"), "operationsLog.persist.failed") // LogMessageState.Information, ResourceBundle.getBundle("strolch-agent"),
"operationsLog.persist.failed") //
.value("reason", e.getMessage()) //
.withException(e));
}
}
}
private void persist(StrolchRealm realm, Collection<LogMessage> logMessages) {
try {
runAsAgent(ctx -> {
try (StrolchTransaction tx = realm.openTx(ctx.getCertificate(), getClass(), false)) {
LogMessageDao logMessageDao = tx.getPersistenceHandler().getLogMessageDao(tx);
logMessageDao.updateStates(logMessages);
tx.commitOnClose();
}
});
} catch (Exception e) {
logger.error("Failed to persist operations logs!", e);
synchronized (this) {
this.logMessagesByRealmAndId.computeIfAbsent(realm.getRealm(), r -> new ArrayList<>())
.add(new LogMessage(realm.getRealm(), SYSTEM_USER_AGENT,
Locator.valueOf(AGENT, "strolch-agent", StrolchAgent.getUniqueId()), LogSeverity.Info,
LogMessageState.Information, ResourceBundle.getBundle("strolch-agent"),
"operationsLog.persist.failed") //
.value("reason", e.getMessage()) // .value("reason", e.getMessage()) //
.withException(e)); .withException(e));
} }
@ -170,9 +262,4 @@ public class OperationsLog extends StrolchComponent {
} }
}; };
} }
public void removeMessage(String realm, LogMessage message) {
List<LogMessage> messages = this.logMessagesByRealmAndId.get(realm);
messages.remove(message);
}
} }

View File

@ -21,6 +21,7 @@ import li.strolch.agent.api.StrolchAgent;
import li.strolch.agent.api.StrolchComponent; import li.strolch.agent.api.StrolchComponent;
import li.strolch.agent.api.StrolchRealm; import li.strolch.agent.api.StrolchRealm;
import li.strolch.handler.operationslog.LogMessage; import li.strolch.handler.operationslog.LogMessage;
import li.strolch.handler.operationslog.LogMessageState;
import li.strolch.handler.operationslog.LogSeverity; import li.strolch.handler.operationslog.LogSeverity;
import li.strolch.handler.operationslog.OperationsLog; import li.strolch.handler.operationslog.OperationsLog;
import li.strolch.model.Locator; import li.strolch.model.Locator;
@ -266,7 +267,7 @@ public abstract class StrolchJob implements Runnable, Restrictable {
operationsLog.addMessage( operationsLog.addMessage(
new LogMessage(this.realmName == null ? StrolchConstants.DEFAULT_REALM : this.realmName, new LogMessage(this.realmName == null ? StrolchConstants.DEFAULT_REALM : this.realmName,
SYSTEM_USER_AGENT, Locator.valueOf(AGENT, "strolch-agent", StrolchAgent.getUniqueId()), SYSTEM_USER_AGENT, Locator.valueOf(AGENT, "strolch-agent", StrolchAgent.getUniqueId()),
LogSeverity.Exception, ResourceBundle.getBundle("strolch-agent"), "strolchjob.failed") LogSeverity.Exception, LogMessageState.Information, ResourceBundle.getBundle("strolch-agent"), "strolchjob.failed")
.withException(e).value("jobName", getClass().getName()).value("reason", e)); .withException(e).value("jobName", getClass().getName()).value("reason", e));
} }
} }

View File

@ -32,6 +32,7 @@ import li.strolch.exception.StrolchAccessDeniedException;
import li.strolch.exception.StrolchException; import li.strolch.exception.StrolchException;
import li.strolch.exception.StrolchModelException; import li.strolch.exception.StrolchModelException;
import li.strolch.handler.operationslog.LogMessage; import li.strolch.handler.operationslog.LogMessage;
import li.strolch.handler.operationslog.LogMessageState;
import li.strolch.handler.operationslog.LogSeverity; import li.strolch.handler.operationslog.LogSeverity;
import li.strolch.handler.operationslog.OperationsLog; import li.strolch.handler.operationslog.OperationsLog;
import li.strolch.model.*; import li.strolch.model.*;
@ -1692,7 +1693,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
OperationsLog operationsLog = container.getComponent(OperationsLog.class); OperationsLog operationsLog = container.getComponent(OperationsLog.class);
operationsLog.addMessage(new LogMessage(this.realm.getRealm(), this.certificate.getUsername(), operationsLog.addMessage(new LogMessage(this.realm.getRealm(), this.certificate.getUsername(),
Locator.valueOf(AGENT, "tx", this.action, getUniqueId()), LogSeverity.Exception, Locator.valueOf(AGENT, "tx", this.action, getUniqueId()), LogSeverity.Exception,
ResourceBundle.getBundle("strolch-agent"), "agent.tx.failed").withException(e).value("reason", e)); LogMessageState.Information, ResourceBundle.getBundle("strolch-agent"), "agent.tx.failed").withException(e).value("reason", e));
} }
String msg = "Strolch Transaction for realm {0} failed due to {1}\n{2}"; //$NON-NLS-1$ String msg = "Strolch Transaction for realm {0} failed due to {1}\n{2}"; //$NON-NLS-1$

View File

@ -15,8 +15,8 @@
*/ */
package li.strolch.persistence.api; package li.strolch.persistence.api;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map;
import li.strolch.handler.operationslog.LogMessage; import li.strolch.handler.operationslog.LogMessage;
@ -31,6 +31,10 @@ public interface LogMessageDao {
void saveAll(List<LogMessage> logMessages); void saveAll(List<LogMessage> logMessages);
void updateState(LogMessage logMessage);
void updateStates(Collection<LogMessage> logMessages);
void remove(LogMessage logMessage); void remove(LogMessage logMessage);
void removeAll(List<LogMessage> logMessages); void removeAll(List<LogMessage> logMessages);

View File

@ -28,6 +28,7 @@ import li.strolch.agent.api.StrolchComponent;
import li.strolch.exception.StrolchAccessDeniedException; import li.strolch.exception.StrolchAccessDeniedException;
import li.strolch.exception.StrolchException; import li.strolch.exception.StrolchException;
import li.strolch.handler.operationslog.LogMessage; import li.strolch.handler.operationslog.LogMessage;
import li.strolch.handler.operationslog.LogMessageState;
import li.strolch.handler.operationslog.LogSeverity; import li.strolch.handler.operationslog.LogSeverity;
import li.strolch.handler.operationslog.OperationsLog; import li.strolch.handler.operationslog.OperationsLog;
import li.strolch.model.Locator; import li.strolch.model.Locator;
@ -219,11 +220,11 @@ public class DefaultServiceHandler extends StrolchComponent implements ServiceHa
ResourceBundle bundle = ResourceBundle.getBundle("strolch-agent"); ResourceBundle bundle = ResourceBundle.getBundle("strolch-agent");
if (throwable == null) { if (throwable == null) {
logMessage = new LogMessage(realmName, username, Locator.valueOf(AGENT, svcName, getUniqueId()), logMessage = new LogMessage(realmName, username, Locator.valueOf(AGENT, svcName, getUniqueId()),
LogSeverity.Exception, bundle, "agent.service.failed").value("service", svcName) LogSeverity.Exception, LogMessageState.Information, bundle, "agent.service.failed").value("service", svcName)
.value("reason", reason); .value("reason", reason);
} else { } else {
logMessage = new LogMessage(realmName, username, Locator.valueOf(AGENT, svcName, getUniqueId()), logMessage = new LogMessage(realmName, username, Locator.valueOf(AGENT, svcName, getUniqueId()),
LogSeverity.Exception, bundle, "agent.service.failed.ex").withException(throwable) LogSeverity.Exception, LogMessageState.Information, bundle, "agent.service.failed.ex").withException(throwable)
.value("service", svcName).value("reason", reason).value("exception", throwable); .value("service", svcName).value("reason", reason).value("exception", throwable);
} }

View File

@ -1,24 +1,34 @@
package li.strolch.model.i18n; package li.strolch.model.i18n;
import java.util.Properties;
import java.util.Set;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import li.strolch.model.Tags;
import li.strolch.utils.I18nMessage; import li.strolch.utils.I18nMessage;
public class I18nMessageJsonParser { public class I18nMessageJsonParser {
public static I18nMessage parse(JsonObject messageJ) { public I18nMessage parse(JsonObject messageJ) {
// TODO String key = messageJ.get(Tags.Json.KEY).getAsString();
String message = messageJ.get(Tags.Json.MESSAGE).getAsString();
// String key = json.addProperty("key", message.getKey()); Properties properties = new Properties();
// json.addProperty("message", message.getMessage()); if (messageJ.has(Tags.Json.VALUES)) {
// JsonArray valuesJ = messageJ.getAsJsonArray(Tags.Json.VALUES);
// Properties values = message.getValues(); for (JsonElement jsonElement : valuesJ) {
// if (!values.isEmpty()) { JsonObject valueJ = jsonElement.getAsJsonObject();
// JsonObject valuesJ = new JsonObject();
// values.stringPropertyNames().forEach(key -> valuesJ.addProperty(key, values.getProperty(key)));
// json.add("values", valuesJ);
// }
return null; Set<String> keys = valueJ.keySet();
for (String propertyName : keys) {
properties.setProperty(propertyName, valueJ.get(propertyName).getAsString());
}
}
}
return new I18nMessage(key, properties, message);
} }
} }

View File

@ -3,6 +3,7 @@ package li.strolch.model.i18n;
import java.util.Properties; import java.util.Properties;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import li.strolch.model.Tags;
import li.strolch.utils.I18nMessage; import li.strolch.utils.I18nMessage;
import li.strolch.utils.I18nMessageVisitor; import li.strolch.utils.I18nMessageVisitor;
@ -12,14 +13,14 @@ public class I18nMessageToJsonVisitor implements I18nMessageVisitor<JsonObject>
public JsonObject visit(I18nMessage message) { public JsonObject visit(I18nMessage message) {
JsonObject json = new JsonObject(); JsonObject json = new JsonObject();
json.addProperty("key", message.getKey()); json.addProperty(Tags.Json.KEY, message.getKey());
json.addProperty("message", message.getMessage()); json.addProperty(Tags.Json.MESSAGE, message.getMessage());
Properties values = message.getValues(); Properties values = message.getValues();
if (!values.isEmpty()) { if (!values.isEmpty()) {
JsonObject valuesJ = new JsonObject(); JsonObject valuesJ = new JsonObject();
values.stringPropertyNames().forEach(key -> valuesJ.addProperty(key, values.getProperty(key))); values.stringPropertyNames().forEach(key -> valuesJ.addProperty(key, values.getProperty(key)));
json.add("values", valuesJ); json.add(Tags.Json.VALUES, valuesJ);
} }
return json; return json;

View File

@ -12,6 +12,7 @@ import java.time.ZonedDateTime;
import java.util.*; import java.util.*;
import li.strolch.handler.operationslog.LogMessage; import li.strolch.handler.operationslog.LogMessage;
import li.strolch.handler.operationslog.LogMessageState;
import li.strolch.handler.operationslog.LogSeverity; import li.strolch.handler.operationslog.LogSeverity;
import li.strolch.model.Locator; import li.strolch.model.Locator;
import li.strolch.persistence.api.LogMessageDao; import li.strolch.persistence.api.LogMessageDao;
@ -28,18 +29,21 @@ public class PostgreSqlLogMessageDao implements LogMessageDao {
private static final String KEY = "key"; private static final String KEY = "key";
private static final String MESSAGE = "message"; private static final String MESSAGE = "message";
private static final String STACK_TRACE = "stacktrace"; private static final String STACK_TRACE = "stacktrace";
private static final String STATE = "state";
private static final String FIELDS = commaSeparated(ID, REALM, DATE_TIME, USERNAME, SEVERITY, LOCATOR, KEY, MESSAGE, private static final String FIELDS = commaSeparated(ID, REALM, DATE_TIME, USERNAME, SEVERITY, STATE, LOCATOR, KEY,
STACK_TRACE); MESSAGE, STACK_TRACE);
private static final String queryByRealmMaxSql = private static final String queryByRealmMaxSql =
"select " + FIELDS + " from operations_log where realm = ? order by id desc limit ?"; "select " + FIELDS + " from operations_log where realm = ? order by id desc limit ?";
private static final String queryValuesSql = "select key, value from operations_log_values where id = ?"; private static final String queryValuesSql = "select key, value from operations_log_values where id = ?";
private static final String insertLogMessageSql = private static final String insertLogMessageSql = "insert into operations_log (" + FIELDS
"insert into operations_log (" + FIELDS + ") values (?, ?, ?, ?, ?::log_severity_type, ?, ?, ?, ?)"; + ") values (?, ?, ?, ?, ?::log_severity_type, ?::log_state_type, ?, ?, ?, ?)";
private static final String insertValuesSql = "insert into operations_log_values (id, key, value) values (?, ?, ?)"; private static final String insertValuesSql = "insert into operations_log_values (id, key, value) values (?, ?, ?)";
private static final String updateLogMessageStateSql = "update operations_log set state = ?::log_state_type where id = ?";
private static final String removeSql = "delete from operations_log where id = ?"; private static final String removeSql = "delete from operations_log where id = ?";
private static final String removeValuesSql = "delete from operations_log_values where id = ?"; private static final String removeValuesSql = "delete from operations_log_values where id = ?";
@ -106,11 +110,36 @@ public class PostgreSqlLogMessageDao implements LogMessageDao {
@Override @Override
public void saveAll(List<LogMessage> logMessages) { public void saveAll(List<LogMessage> logMessages) {
for (LogMessage logMessage : logMessages) { logMessages.forEach(this::save);
save(logMessage); }
@Override
public void updateState(LogMessage logMessage) {
try (PreparedStatement ps = this.tx.getConnection().prepareStatement(updateLogMessageStateSql)) {
// update state
ps.setString(1, logMessage.getState().name());
ps.setString(2, logMessage.getId());
int count = ps.executeUpdate();
if (count != 1) {
throw new StrolchPersistenceException(MessageFormat
.format("Expected to update 1 log_message record, but updated {0} for LogMessage {2}", count,
logMessage.getId())); //$NON-NLS-1$
}
} catch (SQLException e) {
throw new StrolchPersistenceException(MessageFormat
.format("Failed to update LogMessage state {0} due to {1}", logMessage.getId(), //$NON-NLS-1$
e.getLocalizedMessage()), e);
} }
} }
@Override
public void updateStates(Collection<LogMessage> logMessages) {
logMessages.forEach(this::updateState);
}
@Override @Override
public void remove(LogMessage logMessage) { public void remove(LogMessage logMessage) {
try (PreparedStatement removeStatement = this.tx.getConnection().prepareStatement(removeSql); try (PreparedStatement removeStatement = this.tx.getConnection().prepareStatement(removeSql);
@ -237,10 +266,11 @@ public class PostgreSqlLogMessageDao implements LogMessageDao {
// 3 dateTime = ?, // 3 dateTime = ?,
// 4 username = ?, // 4 username = ?,
// 5 severity = ?, // 5 severity = ?,
// 6 locator = ?, // 6 state = ?
// 7 key = ?, // 7 locator = ?,
// 8 message = ?, // 8 key = ?,
// 9 stacktrace = ?, // 9 message = ?,
// 10 stacktrace = ?,
ps.setString(1, logMessage.getId()); ps.setString(1, logMessage.getId());
ps.setString(2, logMessage.getRealm()); ps.setString(2, logMessage.getRealm());
@ -248,10 +278,11 @@ public class PostgreSqlLogMessageDao implements LogMessageDao {
Calendar.getInstance()); Calendar.getInstance());
ps.setString(4, logMessage.getUsername()); ps.setString(4, logMessage.getUsername());
ps.setString(5, logMessage.getSeverity().name()); ps.setString(5, logMessage.getSeverity().name());
ps.setString(6, logMessage.getLocator().toString()); ps.setString(6, logMessage.getState().name());
ps.setString(7, logMessage.getKey()); ps.setString(7, logMessage.getLocator().toString());
ps.setString(8, logMessage.getMessage()); ps.setString(8, logMessage.getKey());
ps.setString(9, logMessage.getStackTrace()); ps.setString(9, logMessage.getMessage());
ps.setString(10, logMessage.getStackTrace());
} }
private LogMessage logMessageFrom(ResultSet resultSet, ResultSet valuesResult) throws SQLException { private LogMessage logMessageFrom(ResultSet resultSet, ResultSet valuesResult) throws SQLException {
@ -261,10 +292,11 @@ public class PostgreSqlLogMessageDao implements LogMessageDao {
ZonedDateTime dateTime = ZonedDateTime.ofInstant(resultSet.getTimestamp(3).toInstant(), ZoneId.systemDefault()); ZonedDateTime dateTime = ZonedDateTime.ofInstant(resultSet.getTimestamp(3).toInstant(), ZoneId.systemDefault());
String username = resultSet.getString(4); String username = resultSet.getString(4);
LogSeverity severity = LogSeverity.valueOf(resultSet.getString(5)); LogSeverity severity = LogSeverity.valueOf(resultSet.getString(5));
Locator locator = Locator.valueOf(resultSet.getString(6)); LogMessageState state = LogMessageState.valueOf(resultSet.getString(6));
String key = resultSet.getString(7); Locator locator = Locator.valueOf(resultSet.getString(7));
String message = resultSet.getString(8); String key = resultSet.getString(8);
String exception = resultSet.getString(9); String message = resultSet.getString(9);
String exception = resultSet.getString(10);
Properties properties = new Properties(); Properties properties = new Properties();
while (valuesResult.next()) { while (valuesResult.next()) {
@ -273,6 +305,7 @@ public class PostgreSqlLogMessageDao implements LogMessageDao {
properties.setProperty(valueK, valueV); properties.setProperty(valueK, valueV);
} }
return new LogMessage(id, dateTime, realm, username, locator, severity, key, properties, message, exception); return new LogMessage(id, dateTime, realm, username, locator, severity, state, key, properties, message,
exception);
} }
} }

View File

@ -0,0 +1,16 @@
DROP TABLE IF EXISTS resources;
DROP TABLE IF EXISTS orders;
DROP TABLE IF EXISTS activities;
DROP TABLE IF EXISTS audits;
DROP TABLE IF EXISTS operations_log;
DROP TABLE IF EXISTS operations_log_values;
DROP TABLE IF EXISTS db_version;
DROP TYPE IF EXISTS order_state;
DROP TYPE IF EXISTS access_type;
DROP TYPE IF EXISTS log_severity_type;
DROP TYPE IF EXISTS log_state_type;

View File

@ -0,0 +1,207 @@
-- DB_VERSION
CREATE TABLE IF NOT EXISTS db_version (
id serial primary key not null,
app varchar(255) not null,
version varchar(255) not null,
description varchar(255) not null,
created timestamp with time zone not null
);
-- RESOURCES
CREATE TABLE IF NOT EXISTS resources (
id varchar(255) not null,
version integer not null,
created_by varchar(255) not null,
created_at timestamp with time zone not null,
updated_at timestamp with time zone not null,
deleted boolean not null,
latest boolean not null,
name varchar(255) not null,
type varchar(255) not null,
asxml xml,
asjson json,
PRIMARY KEY (id, version)
);
-- ORDERS
CREATE TYPE order_state AS ENUM ('CREATED', 'PLANNING', 'PLANNED', 'EXECUTION', 'STOPPED', 'WARNING', 'ERROR', 'EXECUTED', 'CLOSED');
CREATE TABLE IF NOT EXISTS orders (
id varchar(255) not null,
version integer not null,
created_by varchar(255) not null,
created_at timestamp with time zone not null,
updated_at timestamp with time zone not null,
deleted boolean,
latest boolean not null,
name varchar(255),
type varchar(255),
state order_state,
date timestamp with time zone,
asxml xml,
asjson json,
PRIMARY KEY (id, version)
);
-- ACTIVITIES
CREATE TABLE IF NOT EXISTS activities (
id varchar(255) not null,
version integer not null,
created_by varchar(255) not null,
created_at timestamp with time zone not null,
updated_at timestamp with time zone not null,
deleted boolean not null,
latest boolean not null,
name varchar(255) not null,
type varchar(255) not null,
state order_state,
asxml xml,
asjson json,
PRIMARY KEY (id, version)
);
-- AUDITS
CREATE TYPE access_type AS ENUM ('READ', 'CREATE', 'UPDATE', 'DELETE');
CREATE TABLE IF NOT EXISTS audits (
id bigint PRIMARY KEY,
username varchar(255) NOT NULL,
firstname varchar(255) NOT NULL,
lastname varchar(255) NOT NULL,
date timestamp with time zone NOT NULL,
element_type varchar(255) NOT NULL,
element_sub_type varchar(255) NOT NULL,
element_accessed varchar(255) NOT NULL,
new_version timestamp with time zone,
action varchar(255) NOT NULL,
access_type access_type NOT NULL
);
-- Operations Log
CREATE TYPE log_severity_type AS ENUM ('Info', 'Notification', 'Warning', 'Error', 'Exception');
CREATE TYPE log_state_type AS ENUM ('Active', 'Inactive', 'Information');
CREATE TABLE IF NOT EXISTS operations_log (
id varchar(255) PRIMARY KEY,
realm varchar(255),
dateTime timestamp with time zone,
username varchar(255),
severity log_severity_type,
state log_state_type,
locator varchar(1024),
key varchar(255),
message text,
stacktrace text
);
CREATE TABLE IF NOT EXISTS operations_log_values (
id varchar(255),
key varchar(255),
value text
);
-- set version
INSERT INTO db_version
(version, app, description, created)
values(
'0.1.0',
'strolch',
'Initial schema version',
CURRENT_TIMESTAMP
);
INSERT INTO db_version
(version, app, description, created)
values(
'0.2.0',
'strolch',
'Added new table for audits',
CURRENT_TIMESTAMP
);
INSERT INTO db_version
(version, app, description, created)
values(
'0.2.1',
'strolch',
'Added new column app to table table version',
CURRENT_TIMESTAMP
);
INSERT INTO db_version
(version, app, description, created)
values(
'0.3.0',
'strolch',
'Added new column element_sub_type to table audits',
CURRENT_TIMESTAMP
);
INSERT INTO db_version
(version, app, description, created)
values(
'0.4.0',
'strolch',
'Added new table activities',
CURRENT_TIMESTAMP
);
INSERT INTO db_version
(version, app, description, created)
values(
'0.5.0',
'strolch',
'Added versioning to root elements',
CURRENT_TIMESTAMP
);
INSERT INTO db_version
(version, app, description, created)
values(
'0.5.1',
'strolch',
'Added state column to activity, and added new states',
CURRENT_TIMESTAMP
);
INSERT INTO db_version
(version, app, description, created)
values(
'0.6.0',
'strolch',
'Added json column to all tables',
CURRENT_TIMESTAMP
);
INSERT INTO db_version
(version, app, description, created)
values(
'0.7.0',
'strolch',
'Added persisting of operations log',
CURRENT_TIMESTAMP
);
INSERT INTO db_version
(version, app, description, created)
values(
'0.8.0',
'strolch',
'Added updated_at column to all tables',
CURRENT_TIMESTAMP
);
INSERT INTO db_version
(version, app, description, created)
values(
'0.9.0',
'strolch',
'Added log_state column to operations_log',
CURRENT_TIMESTAMP
);

View File

@ -0,0 +1,26 @@
-- add new type
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'log_state_type') THEN
CREATE TYPE log_state_type AS ENUM ('Active', 'Inactive', 'Information');
END IF;
END$$;
-- add version columns
ALTER TABLE operations_log ADD COLUMN state log_state_type;
-- set initial values for new columns
UPDATE operations_log SET state = 'Information' where state IS NULL;
-- make columns not null
ALTER TABLE operations_log ALTER COLUMN state SET NOT NULL;
INSERT INTO db_version
(version, app, description, created)
values(
'0.9.0',
'strolch',
'Added log_state column to operations_log',
CURRENT_TIMESTAMP
);

View File

@ -1,2 +1,2 @@
# Property file defining what the currently expected version is supposed to be # Property file defining what the currently expected version is supposed to be
db_version=0.8.0 db_version=0.9.0

View File

@ -1,5 +1,6 @@
package li.strolch.persistence.xml; package li.strolch.persistence.xml;
import java.util.Collection;
import java.util.List; import java.util.List;
import li.strolch.handler.operationslog.LogMessage; import li.strolch.handler.operationslog.LogMessage;
@ -38,6 +39,16 @@ public class XmlLogMessageDao implements LogMessageDao {
this.tx.getObjectDao().addAll(logMessages); this.tx.getObjectDao().addAll(logMessages);
} }
@Override
public void updateState(LogMessage logMessage) {
this.tx.getObjectDao().update(logMessage);
}
@Override
public void updateStates(Collection<LogMessage> logMessages) {
logMessages.forEach(logMessage -> this.tx.getObjectDao().update(logMessage));
}
@Override @Override
public void remove(LogMessage logMessage) { public void remove(LogMessage logMessage) {
this.tx.getObjectDao().remove(logMessage); this.tx.getObjectDao().remove(logMessage);

View File

@ -21,6 +21,7 @@ import java.util.Properties;
import java.util.function.Consumer; import java.util.function.Consumer;
import li.strolch.handler.operationslog.LogMessage; import li.strolch.handler.operationslog.LogMessage;
import li.strolch.handler.operationslog.LogMessageState;
import li.strolch.handler.operationslog.LogSeverity; import li.strolch.handler.operationslog.LogSeverity;
import li.strolch.model.Locator; import li.strolch.model.Locator;
import li.strolch.model.Tags; import li.strolch.model.Tags;
@ -42,6 +43,7 @@ public class LogMessageSaxReader extends DefaultHandler {
private String username; private String username;
private Locator locator; private Locator locator;
private LogSeverity severity; private LogSeverity severity;
private LogMessageState state;
private String key; private String key;
private Properties properties; private Properties properties;
private String message; private String message;
@ -70,6 +72,7 @@ public class LogMessageSaxReader extends DefaultHandler {
case Tags.KEY: case Tags.KEY:
case Tags.MESSAGE: case Tags.MESSAGE:
case Tags.EXCEPTION: case Tags.EXCEPTION:
case Tags.STATE:
this.sb = new StringBuilder(); this.sb = new StringBuilder();
break; break;
@ -96,8 +99,11 @@ public class LogMessageSaxReader extends DefaultHandler {
switch (qName) { switch (qName) {
case Tags.LOG_MESSAGE: case Tags.LOG_MESSAGE:
if (this.state == null)
this.state = LogMessageState.Information;
LogMessage logMessage = new LogMessage(this.id, this.dateTime, this.realm, this.username, this.locator, LogMessage logMessage = new LogMessage(this.id, this.dateTime, this.realm, this.username, this.locator,
this.severity, this.key, this.properties, this.message, this.exception); this.severity, this.state, this.key, this.properties, this.message, this.exception);
this.logMessageConsumer.accept(logMessage); this.logMessageConsumer.accept(logMessage);
break; break;
@ -116,6 +122,11 @@ public class LogMessageSaxReader extends DefaultHandler {
this.sb = null; this.sb = null;
break; break;
case Tags.STATE:
this.state = LogMessageState.valueOf(this.sb.toString());
this.sb = null;
break;
case Tags.KEY: case Tags.KEY:
this.key = this.sb.toString(); this.key = this.sb.toString();
this.sb = null; this.sb = null;

View File

@ -44,6 +44,7 @@ public class LogMessageToSaxWriterVisitor {
writeElem(Tags.MESSAGE, logMessage.getMessage()); writeElem(Tags.MESSAGE, logMessage.getMessage());
writeElem(Tags.SEVERITY, logMessage.getSeverity().name()); writeElem(Tags.SEVERITY, logMessage.getSeverity().name());
writeElem(Tags.STATE, logMessage.getState().name());
writeElem(Tags.USERNAME, logMessage.getUsername()); writeElem(Tags.USERNAME, logMessage.getUsername());
writeElem(Tags.LOCATOR, logMessage.getLocator().toString()); writeElem(Tags.LOCATOR, logMessage.getLocator().toString());
writeElem(Tags.KEY, logMessage.getKey()); writeElem(Tags.KEY, logMessage.getKey());

View File

@ -13,6 +13,7 @@ import li.strolch.agent.api.StrolchRealm;
import li.strolch.execution.command.*; import li.strolch.execution.command.*;
import li.strolch.execution.policy.ExecutionPolicy; import li.strolch.execution.policy.ExecutionPolicy;
import li.strolch.handler.operationslog.LogMessage; import li.strolch.handler.operationslog.LogMessage;
import li.strolch.handler.operationslog.LogMessageState;
import li.strolch.handler.operationslog.LogSeverity; import li.strolch.handler.operationslog.LogSeverity;
import li.strolch.handler.operationslog.OperationsLog; import li.strolch.handler.operationslog.OperationsLog;
import li.strolch.model.Locator; import li.strolch.model.Locator;
@ -300,7 +301,7 @@ public class Controller {
if (this.container.hasComponent(OperationsLog.class)) { if (this.container.hasComponent(OperationsLog.class)) {
this.container.getComponent(OperationsLog.class).addMessage( this.container.getComponent(OperationsLog.class).addMessage(
new LogMessage(realm, SYSTEM_USER_AGENT, locator, LogSeverity.Exception, new LogMessage(realm, SYSTEM_USER_AGENT, locator, LogSeverity.Exception,
ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.error") LogMessageState.Information, ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.error")
.withException(e).value("reason", e)); .withException(e).value("reason", e));
} }
} }
@ -323,7 +324,7 @@ public class Controller {
if (this.container.hasComponent(OperationsLog.class)) { if (this.container.hasComponent(OperationsLog.class)) {
this.container.getComponent(OperationsLog.class).addMessage( this.container.getComponent(OperationsLog.class).addMessage(
new LogMessage(realm, SYSTEM_USER_AGENT, locator, LogSeverity.Exception, new LogMessage(realm, SYSTEM_USER_AGENT, locator, LogSeverity.Exception,
ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.warning") LogMessageState.Information, ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.warning")
.withException(e).value("reason", e)); .withException(e).value("reason", e));
} }
} }

View File

@ -13,6 +13,7 @@ import li.strolch.agent.api.StrolchRealm;
import li.strolch.execution.command.ArchiveActivityCommand; import li.strolch.execution.command.ArchiveActivityCommand;
import li.strolch.execution.policy.ExecutionPolicy; import li.strolch.execution.policy.ExecutionPolicy;
import li.strolch.handler.operationslog.LogMessage; import li.strolch.handler.operationslog.LogMessage;
import li.strolch.handler.operationslog.LogMessageState;
import li.strolch.handler.operationslog.LogSeverity; import li.strolch.handler.operationslog.LogSeverity;
import li.strolch.handler.operationslog.OperationsLog; import li.strolch.handler.operationslog.OperationsLog;
import li.strolch.model.*; import li.strolch.model.*;
@ -293,7 +294,7 @@ public class EventBasedExecutionHandler extends ExecutionHandler {
if (getContainer().hasComponent(OperationsLog.class)) { if (getContainer().hasComponent(OperationsLog.class)) {
getComponent(OperationsLog.class).addMessage( getComponent(OperationsLog.class).addMessage(
new LogMessage(realm, SYSTEM_USER_AGENT, controller.getLocator(), LogSeverity.Exception, new LogMessage(realm, SYSTEM_USER_AGENT, controller.getLocator(), LogSeverity.Exception,
ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.execution") LogMessageState.Information, ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.execution")
.withException(e).value("reason", e)); .withException(e).value("reason", e));
} }
} }
@ -315,7 +316,7 @@ public class EventBasedExecutionHandler extends ExecutionHandler {
if (getContainer().hasComponent(OperationsLog.class)) { if (getContainer().hasComponent(OperationsLog.class)) {
getComponent(OperationsLog.class).addMessage( getComponent(OperationsLog.class).addMessage(
new LogMessage(realm, SYSTEM_USER_AGENT, locator, LogSeverity.Exception, new LogMessage(realm, SYSTEM_USER_AGENT, locator, LogSeverity.Exception,
ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.executed") LogMessageState.Information, ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.executed")
.withException(e).value("reason", e)); .withException(e).value("reason", e));
} }
} }
@ -337,7 +338,7 @@ public class EventBasedExecutionHandler extends ExecutionHandler {
if (getContainer().hasComponent(OperationsLog.class)) { if (getContainer().hasComponent(OperationsLog.class)) {
getComponent(OperationsLog.class).addMessage( getComponent(OperationsLog.class).addMessage(
new LogMessage(realm, SYSTEM_USER_AGENT, locator, LogSeverity.Exception, new LogMessage(realm, SYSTEM_USER_AGENT, locator, LogSeverity.Exception,
ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.stopped") LogMessageState.Information, ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.stopped")
.withException(e).value("reason", e)); .withException(e).value("reason", e));
} }
} }
@ -359,7 +360,7 @@ public class EventBasedExecutionHandler extends ExecutionHandler {
if (getContainer().hasComponent(OperationsLog.class)) { if (getContainer().hasComponent(OperationsLog.class)) {
getComponent(OperationsLog.class).addMessage( getComponent(OperationsLog.class).addMessage(
new LogMessage(realm, SYSTEM_USER_AGENT, locator, LogSeverity.Exception, new LogMessage(realm, SYSTEM_USER_AGENT, locator, LogSeverity.Exception,
ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.error") LogMessageState.Information, ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.error")
.withException(e).value("reason", e)); .withException(e).value("reason", e));
} }
} }
@ -381,7 +382,7 @@ public class EventBasedExecutionHandler extends ExecutionHandler {
if (getContainer().hasComponent(OperationsLog.class)) { if (getContainer().hasComponent(OperationsLog.class)) {
getComponent(OperationsLog.class).addMessage( getComponent(OperationsLog.class).addMessage(
new LogMessage(realm, SYSTEM_USER_AGENT, locator, LogSeverity.Exception, new LogMessage(realm, SYSTEM_USER_AGENT, locator, LogSeverity.Exception,
ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.warning") LogMessageState.Information, ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.warning")
.withException(e).value("reason", e)); .withException(e).value("reason", e));
} }
} }
@ -407,7 +408,7 @@ public class EventBasedExecutionHandler extends ExecutionHandler {
if (getContainer().hasComponent(OperationsLog.class)) { if (getContainer().hasComponent(OperationsLog.class)) {
getComponent(OperationsLog.class).addMessage( getComponent(OperationsLog.class).addMessage(
new LogMessage(realm, SYSTEM_USER_AGENT, activity.getLocator(), LogSeverity.Exception, new LogMessage(realm, SYSTEM_USER_AGENT, activity.getLocator(), LogSeverity.Exception,
ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.archive") LogMessageState.Information, ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.archive")
.withException(e).value("reason", e)); .withException(e).value("reason", e));
} }
} }

View File

@ -13,6 +13,7 @@ import java.util.concurrent.TimeUnit;
import li.strolch.agent.api.ComponentContainer; import li.strolch.agent.api.ComponentContainer;
import li.strolch.agent.api.StrolchAgent; import li.strolch.agent.api.StrolchAgent;
import li.strolch.handler.operationslog.LogMessage; import li.strolch.handler.operationslog.LogMessage;
import li.strolch.handler.operationslog.LogMessageState;
import li.strolch.handler.operationslog.LogSeverity; import li.strolch.handler.operationslog.LogSeverity;
import li.strolch.handler.operationslog.OperationsLog; import li.strolch.handler.operationslog.OperationsLog;
import li.strolch.model.Locator; import li.strolch.model.Locator;
@ -88,7 +89,7 @@ public class SimpleDurationExecutionTimer implements DelayedExecutionTimer {
if (this.agent.getContainer().hasComponent(OperationsLog.class)) { if (this.agent.getContainer().hasComponent(OperationsLog.class)) {
this.agent.getContainer().getComponent(OperationsLog.class).addMessage( this.agent.getContainer().getComponent(OperationsLog.class).addMessage(
new LogMessage(realm, SYSTEM_USER_AGENT, locator, LogSeverity.Exception, new LogMessage(realm, SYSTEM_USER_AGENT, locator, LogSeverity.Exception,
ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.executed") LogMessageState.Information, ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.executed")
.withException(e).value("reason", e)); .withException(e).value("reason", e));
} }
} }

View File

@ -5,6 +5,7 @@ import static li.strolch.runtime.StrolchConstants.PolicyConstants.TYPE_RESERVE;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import li.strolch.handler.operationslog.LogMessage; import li.strolch.handler.operationslog.LogMessage;
import li.strolch.handler.operationslog.LogMessageState;
import li.strolch.handler.operationslog.LogSeverity; import li.strolch.handler.operationslog.LogSeverity;
import li.strolch.model.State; import li.strolch.model.State;
import li.strolch.model.activity.Action; import li.strolch.model.activity.Action;
@ -42,7 +43,7 @@ public class ToErrorReservationExecution extends ReservationExecution {
if (action.getType().equals(TYPE_RESERVE) && isReserved(tx(), action)) { if (action.getType().equals(TYPE_RESERVE) && isReserved(tx(), action)) {
setActionState(action, State.EXECUTION); setActionState(action, State.EXECUTION);
toError(new LogMessage(tx().getRealmName(), tx().getCertificate().getUsername(), action.getLocator(), toError(new LogMessage(tx().getRealmName(), tx().getCertificate().getUsername(), action.getLocator(),
LogSeverity.Error, ResourceBundle.getBundle("strolch-service"), LogSeverity.Error, LogMessageState.Information, ResourceBundle.getBundle("strolch-service"),
"execution.policy.reservation.alreadyReserved") "execution.policy.reservation.alreadyReserved")
.value("resourceLoc", action.getResourceLocator().toString())); .value("resourceLoc", action.getResourceLocator().toString()));
} else { } else {

View File

@ -26,6 +26,7 @@ import java.util.Map.Entry;
import li.strolch.agent.api.ComponentContainer; import li.strolch.agent.api.ComponentContainer;
import li.strolch.agent.api.StrolchAgent; import li.strolch.agent.api.StrolchAgent;
import li.strolch.handler.operationslog.LogMessage; import li.strolch.handler.operationslog.LogMessage;
import li.strolch.handler.operationslog.LogMessageState;
import li.strolch.handler.operationslog.LogSeverity; import li.strolch.handler.operationslog.LogSeverity;
import li.strolch.handler.operationslog.OperationsLog; import li.strolch.handler.operationslog.OperationsLog;
import li.strolch.model.Locator; import li.strolch.model.Locator;
@ -140,7 +141,7 @@ public class Migrations {
List<Version> list = migrationsRan.getList(realm); List<Version> list = migrationsRan.getList(realm);
for (Version version : list) { for (Version version : list) {
LogMessage logMessage = new LogMessage(realm, SYSTEM_USER_AGENT, LogMessage logMessage = new LogMessage(realm, SYSTEM_USER_AGENT,
locator.append(StrolchAgent.getUniqueId()), LogSeverity.Info, locator.append(StrolchAgent.getUniqueId()), LogSeverity.Info, LogMessageState.Information,
ResourceBundle.getBundle("strolch-service"), "execution.handler.migrations.version") ResourceBundle.getBundle("strolch-service"), "execution.handler.migrations.version")
.value("version", version.toString()); .value("version", version.toString());
operationsLog.addMessage(logMessage); operationsLog.addMessage(logMessage);

View File

@ -28,6 +28,7 @@ import li.strolch.agent.api.ComponentContainer;
import li.strolch.agent.api.StrolchAgent; import li.strolch.agent.api.StrolchAgent;
import li.strolch.agent.api.StrolchComponent; import li.strolch.agent.api.StrolchComponent;
import li.strolch.handler.operationslog.LogMessage; import li.strolch.handler.operationslog.LogMessage;
import li.strolch.handler.operationslog.LogMessageState;
import li.strolch.handler.operationslog.LogSeverity; import li.strolch.handler.operationslog.LogSeverity;
import li.strolch.handler.operationslog.OperationsLog; import li.strolch.handler.operationslog.OperationsLog;
import li.strolch.model.Tags; import li.strolch.model.Tags;
@ -204,7 +205,7 @@ public class MigrationsHandler extends StrolchComponent {
if (getContainer().hasComponent(OperationsLog.class)) { if (getContainer().hasComponent(OperationsLog.class)) {
getComponent(OperationsLog.class).addMessage(new LogMessage(Tags.AGENT, SYSTEM_USER_AGENT, getComponent(OperationsLog.class).addMessage(new LogMessage(Tags.AGENT, SYSTEM_USER_AGENT,
getLocator().append(StrolchAgent.getUniqueId()), LogSeverity.Exception, getLocator().append(StrolchAgent.getUniqueId()), LogSeverity.Exception,
ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.executed") LogMessageState.Information, ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.executed")
.withException(e).value("reason", e)); .withException(e).value("reason", e));
} }
} }

View File

@ -3,8 +3,9 @@ package li.strolch.testbase.runtime;
import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toList;
import static li.strolch.model.Tags.AGENT; import static li.strolch.model.Tags.AGENT;
import static li.strolch.runtime.StrolchConstants.SYSTEM_USER_AGENT; import static li.strolch.runtime.StrolchConstants.SYSTEM_USER_AGENT;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.*;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.ResourceBundle; import java.util.ResourceBundle;
@ -12,6 +13,7 @@ import li.strolch.agent.api.ComponentContainer;
import li.strolch.agent.api.StrolchAgent; import li.strolch.agent.api.StrolchAgent;
import li.strolch.agent.api.StrolchRealm; import li.strolch.agent.api.StrolchRealm;
import li.strolch.handler.operationslog.LogMessage; import li.strolch.handler.operationslog.LogMessage;
import li.strolch.handler.operationslog.LogMessageState;
import li.strolch.handler.operationslog.LogSeverity; import li.strolch.handler.operationslog.LogSeverity;
import li.strolch.handler.operationslog.OperationsLog; import li.strolch.handler.operationslog.OperationsLog;
import li.strolch.model.Locator; import li.strolch.model.Locator;
@ -45,7 +47,7 @@ public class LogMessagesTestRunner {
ResourceBundle bundle = ResourceBundle.getBundle("li-strolch-testbase"); ResourceBundle bundle = ResourceBundle.getBundle("li-strolch-testbase");
LogMessage logMessage = new LogMessage(this.realmName, SYSTEM_USER_AGENT, LogMessage logMessage = new LogMessage(this.realmName, SYSTEM_USER_AGENT,
Locator.valueOf(AGENT, "li.strolch.testbase", StrolchAgent.getUniqueId()), LogSeverity.Exception, Locator.valueOf(AGENT, "li.strolch.testbase", StrolchAgent.getUniqueId()), LogSeverity.Exception,
bundle, "test-message").withException(ex).value("reason", ex); LogMessageState.Information, bundle, "test-message").withException(ex).value("reason", ex);
this.operationsLog.addMessage(logMessage); this.operationsLog.addMessage(logMessage);
// default is async persisting... // default is async persisting...
@ -83,7 +85,7 @@ public class LogMessagesTestRunner {
for (int i = 0; i < MAX_MESSAGES * 2; i++) { for (int i = 0; i < MAX_MESSAGES * 2; i++) {
LogMessage m = new LogMessage(this.realmName, SYSTEM_USER_AGENT, LogMessage m = new LogMessage(this.realmName, SYSTEM_USER_AGENT,
Locator.valueOf(AGENT, "li.strolch.testbase", StrolchAgent.getUniqueId()), Locator.valueOf(AGENT, "li.strolch.testbase", StrolchAgent.getUniqueId()),
LogSeverity.Exception, bundle, "test-message"); LogSeverity.Exception, LogMessageState.Information, bundle, "test-message");
this.operationsLog.addMessage(m); this.operationsLog.addMessage(m);
ids.add(m.getId()); ids.add(m.getId());
} }
@ -108,6 +110,49 @@ public class LogMessagesTestRunner {
} }
} }
// add a few more messages
LogMessage logMessage1 = new LogMessage(this.realmName, SYSTEM_USER_AGENT,
Locator.valueOf(AGENT, "test", "@1"), LogSeverity.Error, LogMessageState.Active, bundle,
"test-message");
LogMessage logMessage2 = new LogMessage(this.realmName, SYSTEM_USER_AGENT,
Locator.valueOf(AGENT, "test", "@2"), LogSeverity.Error, LogMessageState.Active, bundle,
"test-message");
LogMessage logMessage3 = new LogMessage(this.realmName, SYSTEM_USER_AGENT,
Locator.valueOf(AGENT, "test", "@3"), LogSeverity.Error, LogMessageState.Active, bundle,
"test-message");
this.operationsLog.addMessage(logMessage1);
this.operationsLog.addMessage(logMessage2);
this.operationsLog.addMessage(logMessage3);
// update state of element
this.operationsLog.updateState(this.realmName, logMessage1.getLocator(), LogMessageState.Inactive);
assertEquals(LogMessageState.Inactive, logMessage1.getState());
this.operationsLog.updateState(this.realmName, logMessage1.getId(), LogMessageState.Active);
assertEquals(LogMessageState.Active, logMessage1.getState());
// now try and remove a single element
this.operationsLog.removeMessage(logMessage1);
assertFalse(this.operationsLog.getMessagesFor(this.realmName, logMessage1.getLocator()).isPresent());
// now remove bulk
List<LogMessage> toRemove = Arrays.asList(logMessage2, logMessage3);
this.operationsLog.removeMessages(toRemove);
// default is async persisting...
Thread.sleep(300L);
// assert all are removed
try (StrolchTransaction tx = realm.openTx(this.certificate, "test", true)) {
LogMessageDao logMessageDao = tx.getPersistenceHandler().getLogMessageDao(tx);
List<String> logMessageIds = logMessageDao.queryLatest(this.realmName, Integer.MAX_VALUE).stream()
.map(LogMessage::getId).sorted().collect(toList());
assertFalse(logMessageIds.contains(logMessage1.getId()));
assertFalse(logMessageIds.contains(logMessage2.getId()));
assertFalse(logMessageIds.contains(logMessage3.getId()));
}
} catch (InterruptedException e) { } catch (InterruptedException e) {
throw new IllegalStateException("Interrupted!"); throw new IllegalStateException("Interrupted!");
} }

View File

@ -15,17 +15,17 @@ public class I18nMessage {
private static final Logger logger = LoggerFactory.getLogger(I18nMessage.class); private static final Logger logger = LoggerFactory.getLogger(I18nMessage.class);
private ResourceBundle bundle; private final String key;
private String key; private final Properties values;
private Properties values; private final ResourceBundle bundle;
private String message; private String message;
public I18nMessage(ResourceBundle bundle, String key) { public I18nMessage(ResourceBundle bundle, String key) {
DBC.INTERIM.assertNotNull("bundle must be set!", bundle); DBC.INTERIM.assertNotNull("bundle must be set!", bundle);
DBC.INTERIM.assertNotEmpty("key must be set!", key); DBC.INTERIM.assertNotEmpty("key must be set!", key);
this.bundle = bundle;
this.key = key; this.key = key;
this.values = new Properties(); this.values = new Properties();
this.bundle = bundle;
} }
public I18nMessage(String key, Properties values, String message) { public I18nMessage(String key, Properties values, String message) {
@ -34,6 +34,7 @@ public class I18nMessage {
this.key = key; this.key = key;
this.values = values == null ? new Properties() : values; this.values = values == null ? new Properties() : values;
this.message = message; this.message = message;
this.bundle = null;
} }
public String getKey() { public String getKey() {
@ -62,6 +63,11 @@ public class I18nMessage {
if (this.message != null) if (this.message != null)
return this.message; return this.message;
if (this.bundle == null) {
this.message = this.key;
return this.message;
}
try { try {
String string = this.bundle.getString(this.key); String string = this.bundle.getString(this.key);
this.message = StringHelper.replacePropertiesIn(this.values, EMPTY, string); this.message = StringHelper.replacePropertiesIn(this.values, EMPTY, string);