Skip to content

Commit

Permalink
split LogEntry implementations
Browse files Browse the repository at this point in the history
  • Loading branch information
xzel23 committed Nov 2, 2023
1 parent 72030a3 commit 3de4783
Show file tree
Hide file tree
Showing 7 changed files with 331 additions and 228 deletions.
172 changes: 34 additions & 138 deletions utility-logging/src/main/java/com/dua3/utility/logging/LogEntry.java
Original file line number Diff line number Diff line change
@@ -1,165 +1,61 @@
package com.dua3.utility.logging;

import com.dua3.cabe.annotations.Nullable;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.util.Objects;
import java.util.function.Consumer;

/**
* Represents a log entry with information about the log message, time, level, logger, and optional marker and throwable.
*/
public final class LogEntry {
private static final char[] NEW_LINE = String.format("%n").toCharArray();
private final String loggerName;
private final Instant time;
private final LogLevel level;
private final String marker;
private Consumer<StringBuilder> messageFormatter;
private String formattedMessage;
private final Throwable throwable;
public interface LogEntry {
String message();

/**
* Creates a new LogEntry object.
*
* @param loggerName The name of the logger.
* @param time The time the log entry was created, represented as an Instant object.
* @param level The log level of the entry.
* @param marker The marker associated with the log entry. Can be null.
* @param messageFormatter A Supplier that provides the formatted log message.
* @param throwable The throwable associated with the log entry. Can be null.
*/
public LogEntry(String loggerName, Instant time, LogLevel level, @Nullable String marker, Consumer<StringBuilder> messageFormatter,
@Nullable Throwable throwable) {
this.loggerName = loggerName;
this.time = time;
this.level = level;
this.marker = marker;
this.messageFormatter = messageFormatter;
this.throwable = throwable;
}
String loggerName();

/**
* Formats the message using MessageFormatter.basicArrayFormat.
*
* @return the formatted message as a string.
*/
public String formatMessage() {
if (messageFormatter != null) {
StringBuilder sb = new StringBuilder(100);
messageFormatter.accept(sb);
formattedMessage = sb.toString();
messageFormatter = null;
}
return formattedMessage;
}
Instant time();

/**
* Formats the throwable object by printing its stack trace.
* If the throwable object is null, it returns an empty string.
*
* @return the formatted stack trace as a string.
*/
private void appendThrowable(StringBuilder sb) {
if (throwable == null) {
sb.append("null");
} else {
try (StringWriter sw = new StringWriter(200); PrintWriter pw = new PrintWriter(sw)) {
throwable.printStackTrace(pw);
sb.append(sw);
} catch (IOException e) {
sb.append(throwable);
}
}
}
LogLevel level();

@Override
public String toString() {
return format("", "");
}
String marker();

Throwable throwable();

default String format(String prefix, String suffix) {
class Constants {
private static final char[] NEW_LINE = String.format("%n").toCharArray();
};

String format(String prefix, String suffix) {
StringBuilder sb = new StringBuilder(100);
sb.append(prefix);
sb.append('[').append(level).append(']');
sb.append('[').append(level()).append(']');
sb.append(' ');
sb.append(DateTimeFormatter.ISO_INSTANT.format(time));
sb.append(DateTimeFormatter.ISO_INSTANT.format(time()));
sb.append(' ');
messageFormatter.accept(sb);
sb.append(suffix);
sb.append(loggerName());
sb.append(' ');
sb.append(message());
if (throwable() != null) {
sb.append(NEW_LINE);
sb.append(Constants.NEW_LINE);
appendThrowable(sb);
}
sb.append(loggerName);
sb.append(suffix);
return sb.toString();
}

/**
* Returns the logger name.
*
* @return the name of the logger.
*/
public String loggerName() {
return loggerName;
}

/**
* Returns the time when the log entry was created.
*
* @return the time when the log entry was created.
*/
public Instant time() {
return time;
}

/**
* Returns the log level of the log entry.
*
* @return the log level of the log entry.
*/
public LogLevel level() {
return level;
}

/**
* Returns the marker of the log entry.
*
* @return the marker of the log entry.
*/
public String marker() {
return marker;
}

/**
* Returns the throwable associated with this log entry.
*
* @return the throwable associated with this log entry.
* Appends the throwable object to the supplied StringBuilder instance by printing its stack trace.
* @param sb the StringBuilder to append to
*/
public Throwable throwable() {
return throwable;
}

@Override
public boolean equals(@Nullable Object obj) {
if (obj == this) return true;
if (obj == null || obj.getClass() != this.getClass()) return false;
var that = (LogEntry) obj;
return Objects.equals(this.loggerName, that.loggerName) &&
Objects.equals(this.time, that.time) &&
Objects.equals(this.level, that.level) &&
Objects.equals(this.marker, that.marker) &&
Objects.equals(this.messageFormatter, that.messageFormatter) &&
Objects.equals(this.throwable, that.throwable);
}

@Override
public int hashCode() {
return Objects.hash(loggerName, time, level, marker, messageFormatter, throwable);
private void appendThrowable(StringBuilder sb) {
Throwable t = throwable();
if (t == null) {
sb.append("null");
} else {
try (StringWriter sw = new StringWriter(200); PrintWriter pw = new PrintWriter(sw)) {
t.printStackTrace(pw);
sb.append(sw);
} catch (IOException e) {
sb.append(t);
}
}
}

}
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
package com.dua3.utility.logging.log4j;

import com.dua3.cabe.annotations.Nullable;
import com.dua3.utility.logging.LogEntry;
import com.dua3.utility.logging.LogEntryDispatcher;
import com.dua3.utility.logging.LogEntryHandler;
import com.dua3.utility.logging.LogLevel;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
Expand All @@ -15,16 +11,11 @@
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.layout.PatternLayout;
import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.message.ReusableMessage;
import org.apache.logging.log4j.spi.StandardLevel;

import java.io.Serializable;
import java.lang.ref.WeakReference;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

/**
* This class is an implementation of the Log4j Appender and LogEntryHandlerPool interfaces.
Expand Down Expand Up @@ -102,60 +93,14 @@ public void append(LogEvent event) {
if (handler==null) {
cleanup = true;
} else {
Marker marker = event.getMarker();
Consumer<StringBuilder> messageAppender;
Message message = event.getMessage();
if (message instanceof ReusableMessage rm) {
// for resusable messages, the message must be formatted instantly
StringBuilder sbMsg = new StringBuilder(80);
rm.formatTo(sbMsg);
String formattedMessage = sbMsg.toString();
messageAppender = sb -> sb.append(formattedMessage);
} else {
messageAppender = sb -> sb.append(message.getFormattedMessage());
}
Throwable thrown = event.getThrown();
handler.handleEntry(new LogEntry(
event.getLoggerName(),
Instant.ofEpochMilli(event.getTimeMillis()),
translate(event.getLevel()),
marker == null ? null : marker.getName(),
messageAppender,
thrown
));
handler.handleEntry(new LogEntryLog4J(event));
}
}
if (cleanup) {
handlers.removeIf(ref -> ref.get()==null);
}
}

/**
* Translates a Log4J Level object to a custom LogLevel object.
*
* This method takes a Log4J Level object as parameter and returns the corresponding custom LogLevel object.
* The translation is based on the integer level value of the Log4J Level object.
*
* @param level the Log4J Level object to be translated
* @return the custom LogLevel object that corresponds to the given Log4J Level object
*/
private static LogLevel translate(Level level) {
int levelInt = level.intLevel();
if (levelInt > StandardLevel.DEBUG.intLevel()) {
return LogLevel.TRACE;
}
if (levelInt > StandardLevel.INFO.intLevel()) {
return LogLevel.DEBUG;
}
if (levelInt > StandardLevel.WARN.intLevel()) {
return LogLevel.INFO;
}
if (levelInt > StandardLevel.ERROR.intLevel()) {
return LogLevel.WARN;
}
return LogLevel.ERROR;
}

@Override
public void addLogEntryHandler(LogEntryHandler handler) {
handlers.add(new WeakReference<>(handler));
Expand Down
Loading

0 comments on commit 3de4783

Please sign in to comment.