From 94c72ed21327409402d520ad0fcfc7df553bdcdf Mon Sep 17 00:00:00 2001 From: PradnyaC11 Date: Thu, 17 Apr 2025 11:52:13 -0700 Subject: [PATCH 1/3] [CITE-219] Added InMemoryAppender for Javers logs --- .../config/core/InMemoryAppender.java | 65 +++++++++++++++++++ .../citesphere/config/core/LoggingConfig.java | 42 ++++++++++++ .../web/user/SyncInfoController.java | 12 +++- .../WEB-INF/views/auth/group/items.html | 29 ++++++++- 4 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 citesphere/src/main/java/edu/asu/diging/citesphere/config/core/InMemoryAppender.java create mode 100644 citesphere/src/main/java/edu/asu/diging/citesphere/config/core/LoggingConfig.java diff --git a/citesphere/src/main/java/edu/asu/diging/citesphere/config/core/InMemoryAppender.java b/citesphere/src/main/java/edu/asu/diging/citesphere/config/core/InMemoryAppender.java new file mode 100644 index 000000000..282635fd0 --- /dev/null +++ b/citesphere/src/main/java/edu/asu/diging/citesphere/config/core/InMemoryAppender.java @@ -0,0 +1,65 @@ +package edu.asu.diging.citesphere.config.core; + + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.apache.logging.log4j.core.Appender; +import org.apache.logging.log4j.core.Core; +import org.apache.logging.log4j.core.Filter; +import org.apache.logging.log4j.core.Layout; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.appender.AbstractAppender; +import org.apache.logging.log4j.core.config.plugins.Plugin; +import org.apache.logging.log4j.core.config.plugins.PluginAttribute; +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; + +@Plugin(name = "InMemoryAppender", category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE) +public class InMemoryAppender extends AbstractAppender { + + // Thread‑safe buffer to hold events + private static final List BUFFER = Collections.synchronizedList(new ArrayList<>()); + + // Maximum number of events to keep + private static final int MAX_BUFFER_SIZE = 1000; + + protected InMemoryAppender(String name, Filter filter, Layout layout) { + super(name, filter, layout, false); + } + + @PluginFactory + public static InMemoryAppender createAppender( + @PluginAttribute("name") String name, + @PluginElement("Filter") Filter filter + ) { + if (name == null) { + LOGGER.error("No name provided for InMemoryAppender"); + return null; + } + // Default pattern layout if none provided + PatternLayout layout = PatternLayout.newBuilder() + .withPattern("%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n") + .build(); + return new InMemoryAppender(name, filter, layout); + } + + @Override + public void append(LogEvent event) { + // make the event immutable (optional but safer) + LogEvent immutable = event.toImmutable(); + BUFFER.add(immutable); + if (BUFFER.size() > MAX_BUFFER_SIZE) { + BUFFER.remove(0); + } + } + + public static List getEvents() { + synchronized (BUFFER) { + return new ArrayList<>(BUFFER); + } + } +} \ No newline at end of file diff --git a/citesphere/src/main/java/edu/asu/diging/citesphere/config/core/LoggingConfig.java b/citesphere/src/main/java/edu/asu/diging/citesphere/config/core/LoggingConfig.java new file mode 100644 index 000000000..8caed26f7 --- /dev/null +++ b/citesphere/src/main/java/edu/asu/diging/citesphere/config/core/LoggingConfig.java @@ -0,0 +1,42 @@ +package edu.asu.diging.citesphere.config.core; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.core.Appender; +import org.apache.logging.log4j.core.Filter; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.config.LoggerConfig; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +//import org.apache.logging.log4j.core.filter.LoggerNameFilter; + +@Configuration +public class LoggingConfig { + + @Bean + public Appender inMemoryAppender() { + LoggerContext ctx = (LoggerContext) LogManager.getContext(false); + org.apache.logging.log4j.core.config.Configuration config = ctx.getConfiguration(); + +// Filter javersFilter = LoggerNameFilter.createFilter( +// "org.javers.core.Javers", +// Filter.Result.ACCEPT, +// Filter.Result.DENY +// ); + + LoggerConfig javersLogger = config.getLoggerConfig("org.javers.core.Javers"); + if (!"org.javers.core.Javers".equals(javersLogger.getName())) { + javersLogger = new LoggerConfig("org.javers.core.Javers", Level.INFO, false); + config.addLogger("org.javers.core.Javers", javersLogger); + } + + InMemoryAppender appender = InMemoryAppender.createAppender("InMemoryAppender", null); + appender.start(); + config.addAppender(appender); + // attach to root logger at INFO level + config.getRootLogger().addAppender(appender, Level.INFO, null); + ctx.updateLoggers(); + + return appender; + } +} \ No newline at end of file diff --git a/citesphere/src/main/java/edu/asu/diging/citesphere/web/user/SyncInfoController.java b/citesphere/src/main/java/edu/asu/diging/citesphere/web/user/SyncInfoController.java index bae630b93..40d439b63 100644 --- a/citesphere/src/main/java/edu/asu/diging/citesphere/web/user/SyncInfoController.java +++ b/citesphere/src/main/java/edu/asu/diging/citesphere/web/user/SyncInfoController.java @@ -1,10 +1,14 @@ package edu.asu.diging.citesphere.web.user; +import java.util.List; +import java.util.stream.Collectors; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import edu.asu.diging.citesphere.config.core.InMemoryAppender; import edu.asu.diging.citesphere.core.model.jobs.impl.GroupSyncJob; import edu.asu.diging.citesphere.core.service.jobs.ISyncJobManager; @@ -25,7 +29,12 @@ public SyncInfo getSyncInfo(@PathVariable("zoteroGroupId") String groupId) { info.total = job.getTotal(); info.current = job.getCurrent(); info.status = job.getStatus() != null ? job.getStatus().name() : ""; - } + info.logs = InMemoryAppender.getEvents().stream() + .map(ev -> ev.getTimeMillis() + " [" + ev.getThreadName() + "] " + + ev.getLevel() + " " + ev.getLoggerName() + + " - " + ev.getMessage().getFormattedMessage()) + .collect(Collectors.toList()); + } return info; } @@ -35,5 +44,6 @@ class SyncInfo { public long total; public long current; public String status; + List logs; } } diff --git a/citesphere/src/main/webapp/WEB-INF/views/auth/group/items.html b/citesphere/src/main/webapp/WEB-INF/views/auth/group/items.html index 3965aeff8..938720862 100644 --- a/citesphere/src/main/webapp/WEB-INF/views/auth/group/items.html +++ b/citesphere/src/main/webapp/WEB-INF/views/auth/group/items.html @@ -337,7 +337,7 @@

-
+
Up to date
@@ -360,6 +360,7 @@

function pollStatus(){ $.get(syncUrl, function(data) { + console.log(data); if(data['status'] == 'PREPARED' || data['status'] == 'STARTED') { $("#table-spinner").show(); if (data['total'] == 0) { @@ -388,6 +389,15 @@

}); } +function getlogs() { + if($("#syncText").text() != "Up to date") { + console.log("logs....") + $('#sync-logs').modal("show"); + } + console.log("up to date") + $('#sync-logs').modal("show"); +} + function getCurrentItemsData(parameter){ let pageNumber = $(".page-item.active").text(); $.ajax({ @@ -825,6 +835,23 @@

+ +