
com.composum.sling.core.logging.MessageContainer Maven / Gradle / Ivy
package com.composum.sling.core.logging;
import com.google.gson.annotations.JsonAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.annotation.concurrent.ThreadSafe;
import java.util.*;
import java.util.function.Consumer;
/**
* A collection of {@link Message}s for humans - also meant for transmitting them via JSON.
*/
@ThreadSafe
@JsonAdapter(MessageTypeAdapterFactory.class)
public class MessageContainer implements Iterable {
private static final Logger LOG = LoggerFactory.getLogger(MessageContainer.class);
/**
* Used to sort messages by their time.
*/
protected static final Comparator MESSAGE_TIME_COMPARATOR =
Comparator.nullsFirst(Comparator.comparing(Message::getTimestamp));
/**
* A logger where {@link #add(Message)} automatically logs to.
*/
@Nullable
protected volatile transient Logger log;
/**
* Synchronizing on this when accessing messages.
*/
protected transient final Object lockObject = new Object();
/**
* @see #getMessages()
*/
@Nullable
protected List messages;
/**
* Default constructor which means we do not log added messages - usually it's better to use
* {@link #MessageContainer(Logger)} and log things immediately.
*/
public MessageContainer() {
log = null;
}
/**
* Constructor that allows to set a logger to which {@link #add(Message)} automatically writes the messages.
*
* @param log an optional log to which added messages are logged.
*/
public MessageContainer(@Nullable Logger log) {
this.log = log;
}
/**
* A (unmodifiable) snapshot of the list of messages.
*/
@NotNull
public List getMessages() {
synchronized (lockObject) {
return messages != null ? Collections.unmodifiableList(new ArrayList<>(messages)) : Collections.emptyList();
}
}
/**
* Adds a message to the container, and logs it into the logger if one was specified for this container.
* The intended usecase is with a logger, so we log a warning if it's called with a throwable but
* we have no logger, so that Stacktraces don't disappear accidentially.
*
* @return this MessageContainer, for builder-style operation chaining.
*/
@NotNull
public MessageContainer add(@Nullable Message message, @Nullable Throwable throwable) {
if (message != null) {
synchronized (lockObject) {
if (messages == null) {
messages = new ArrayList<>();
}
boolean doSort = messages.size() > 0 && MESSAGE_TIME_COMPARATOR.compare(messages.get(messages.size() - 1), message) > 0;
messages.add(message);
if (doSort) {
messages.sort(MESSAGE_TIME_COMPARATOR);
}
}
if (log != null) {
message.logInto(log, throwable);
} else if (throwable != null) { // very likely a misuse
LOG.warn("Received throwable but have no logger: {}", message.toFormattedMessage(), throwable);
}
}
return this;
}
/**
* Adds a message to the container, and logs it into the logger if one was specified for this container.
* Like in SLF4J, if the last argument is a Throwable we use it for logging with {@link #add(Message, Throwable)}.
*
* @return this MessageContainer, for builder-style operation chaining.
*/
@NotNull
public MessageContainer add(@Nullable Message message) {
Throwable throwable = null;
List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy