This is an automated email from the ASF dual-hosted git repository.
rombert pushed a commit to branch master
in repository
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-mcp-server-contributions.git
The following commit(s) were added to refs/heads/master by this push:
new a694fed SLING-13213 - Use the new LogStore from commons.log in the
log MCP tool (#7)
a694fed is described below
commit a694fed450c2429a398a8fd01a4f323e0725c9ef
Author: Robert Munteanu <[email protected]>
AuthorDate: Thu May 28 11:44:05 2026 +0200
SLING-13213 - Use the new LogStore from commons.log in the log MCP tool (#7)
---
pom.xml | 22 +--
src/main/features/main.json | 7 +-
.../sling/mcp/server/contribs/log/LogSnapshot.java | 40 -----
.../server/contribs/log/StructuredLogBuffer.java | 33 -----
.../server/impl/contribs/LogToolContribution.java | 58 +++++---
.../mcp/server/impl/contribs/log/LogLevel.java | 44 ------
.../contribs/log/StructuredLogBufferAppender.java | 161 ---------------------
.../impl/contribs/log/StructuredLogBufferImpl.java | 145 -------------------
.../impl/contribs/log/StructuredLogBufferSink.java | 26 ----
.../mcp/server/contribs/log/LogSnapshotTest.java | 36 -----
.../log/StructuredLogBufferAppenderTest.java | 86 -----------
.../contribs/log/StructuredLogBufferImplTest.java | 76 ----------
12 files changed, 50 insertions(+), 684 deletions(-)
diff --git a/pom.xml b/pom.xml
index 5aaeefb..813d4a8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -115,27 +115,15 @@
<scope>provided</scope>
</dependency>
<dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>1.7.32</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-core</artifactId>
- <version>1.2.13</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-classic</artifactId>
- <version>1.2.13</version>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.mcp-server</artifactId>
+ <version>0.1.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.sling</groupId>
- <artifactId>org.apache.sling.mcp-server</artifactId>
- <version>0.1.2</version>
+ <artifactId>org.apache.sling.commons.log</artifactId>
+ <version>6.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
diff --git a/src/main/features/main.json b/src/main/features/main.json
index 9c6b8cc..35222b5 100644
--- a/src/main/features/main.json
+++ b/src/main/features/main.json
@@ -4,5 +4,10 @@
"id": "${project.groupId}:${project.artifactId}:${project.version}",
"start-order": 25
}
- ]
+ ],
+ "configurations": {
+ "org.apache.sling.commons.log.LogStore": {
+ "maxEntries": 5000
+ }
+ }
}
diff --git
a/src/main/java/org/apache/sling/mcp/server/contribs/log/LogSnapshot.java
b/src/main/java/org/apache/sling/mcp/server/contribs/log/LogSnapshot.java
deleted file mode 100644
index c986ad8..0000000
--- a/src/main/java/org/apache/sling/mcp/server/contribs/log/LogSnapshot.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.mcp.server.contribs.log;
-
-import java.util.Collections;
-import java.util.Map;
-
-/**
- * Stores only the lightweight, stable parts of a log event so the in-memory
buffer
- * does not retain full logging event object graphs.
- */
-public record LogSnapshot(
- long timeMillis,
- String level,
- String loggerName,
- String threadName,
- String formattedMessage,
- String throwableText,
- Map<String, String> mdc) {
-
- public LogSnapshot {
- mdc = mdc == null ? Collections.emptyMap() :
Collections.unmodifiableMap(mdc);
- }
-}
diff --git
a/src/main/java/org/apache/sling/mcp/server/contribs/log/StructuredLogBuffer.java
b/src/main/java/org/apache/sling/mcp/server/contribs/log/StructuredLogBuffer.java
deleted file mode 100644
index 07e0c8d..0000000
---
a/src/main/java/org/apache/sling/mcp/server/contribs/log/StructuredLogBuffer.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.mcp.server.contribs.log;
-
-import java.util.List;
-import java.util.regex.Pattern;
-
-public interface StructuredLogBuffer {
-
- boolean isValidLogLevel(String logLevelName);
-
- List<String> getValidLogLevelNames();
-
- String getHighestLogLevelName();
-
- List<LogSnapshot> getRecent(Pattern pattern, String minLevel, int
maxEntries);
-}
diff --git
a/src/main/java/org/apache/sling/mcp/server/impl/contribs/LogToolContribution.java
b/src/main/java/org/apache/sling/mcp/server/impl/contribs/LogToolContribution.java
index 3ae4067..993f451 100644
---
a/src/main/java/org/apache/sling/mcp/server/impl/contribs/LogToolContribution.java
+++
b/src/main/java/org/apache/sling/mcp/server/impl/contribs/LogToolContribution.java
@@ -30,8 +30,9 @@ import io.modelcontextprotocol.json.McpJsonMapperSupplier;
import
io.modelcontextprotocol.server.McpStatelessServerFeatures.SyncToolSpecification;
import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
import io.modelcontextprotocol.spec.McpSchema.Tool;
-import org.apache.sling.mcp.server.contribs.log.LogSnapshot;
-import org.apache.sling.mcp.server.contribs.log.StructuredLogBuffer;
+import org.apache.sling.commons.log.logback.store.LogEntry;
+import org.apache.sling.commons.log.logback.store.LogLevel;
+import org.apache.sling.commons.log.logback.store.LogStore;
import org.apache.sling.mcp.server.spi.McpServerContribution;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
@@ -46,7 +47,7 @@ public class LogToolContribution implements
McpServerContribution {
private static final int DEFAULT_MAX_LOGS = 200;
@Reference
- private StructuredLogBuffer structuredLogBuffer;
+ private LogStore logStore;
@Reference
private McpJsonMapperSupplier jsonMapper;
@@ -79,9 +80,7 @@ public class LogToolContribution implements
McpServerContribution {
}
}
""".formatted(
- validLogLevelValuesAsCsv(),
- structuredLogBuffer.getHighestLogLevelName(),
- validLogLevelValuesAsJsonSchemaEnum());
+ validLogLevelValuesAsCsv(), getHighestLogLevelName(),
validLogLevelValuesAsJsonSchemaEnum());
return List.of(new SyncToolSpecification(
Tool.builder()
@@ -103,16 +102,16 @@ public class LogToolContribution implements
McpServerContribution {
maxEntries = Math.min(maxEntriesNumber.intValue(),
1000); // Cap at 1000
}
- String minLogLevel =
structuredLogBuffer.getHighestLogLevelName();
+ LogLevel minLogLevel = getHighestLogLevel();
if (logLevelStr != null && !logLevelStr.isEmpty()) {
- if (!structuredLogBuffer.isValidLogLevel(logLevelStr))
{
+ if (!isValidLogLevel(logLevelStr)) {
return CallToolResult.builder()
.addTextContent("Invalid log level: " +
logLevelStr + ". Valid options are: "
- + String.join(", ",
structuredLogBuffer.getValidLogLevelNames()))
+ + String.join(", ",
getValidLogLevelNames()))
.isError(true)
.build();
}
- minLogLevel = logLevelStr;
+ minLogLevel = LogLevel.valueOf(logLevelStr);
}
// Compile regex pattern if provided
@@ -128,16 +127,16 @@ public class LogToolContribution implements
McpServerContribution {
}
}
- List<LogSnapshot> filteredLogs =
structuredLogBuffer.getRecent(pattern, minLogLevel, maxEntries);
+ List<LogEntry> filteredLogs = logStore.getRecent(pattern,
minLogLevel, maxEntries);
// Format output
- String result = formatLogs(filteredLogs, regexPattern,
minLogLevel, maxEntries);
+ String result = formatLogs(filteredLogs, regexPattern,
minLogLevel.name(), maxEntries);
return
CallToolResult.builder().addTextContent(result).build();
}));
}
- private String formatLogs(List<LogSnapshot> logs, String regexPattern,
String minLogLevel, int maxEntries) {
+ private String formatLogs(List<LogEntry> logs, String regexPattern, String
minLogLevel, int maxEntries) {
StringBuilder result = new StringBuilder();
result.append("=== Log Entries ===\n\n");
@@ -157,7 +156,7 @@ public class LogToolContribution implements
McpServerContribution {
result.append("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n");
for (int i = 0; i < logs.size(); i++) {
- LogSnapshot entry = logs.get(i);
+ LogEntry entry = logs.get(i);
formatLogEntry(entry, i + 1, result);
if (i < logs.size() - 1) {
@@ -168,7 +167,7 @@ public class LogToolContribution implements
McpServerContribution {
return result.toString();
}
- private void formatLogEntry(LogSnapshot entry, int index, StringBuilder
result) {
+ private void formatLogEntry(LogEntry entry, int index, StringBuilder
result) {
result.append("[").append(index).append("] ");
result.append(dateFormat.format(new Date(entry.timeMillis())));
result.append(" [").append(entry.level()).append("] ");
@@ -215,16 +214,37 @@ public class LogToolContribution implements
McpServerContribution {
}
private String validLogLevelValuesAsCsv() {
- return String.join(", ", structuredLogBuffer.getValidLogLevelNames());
+ return String.join(", ", getValidLogLevelNames());
}
private String validLogLevelValuesAsJsonSchemaEnum() {
StringBuilder result = new StringBuilder();
result.append("[ ");
- result.append(structuredLogBuffer.getValidLogLevelNames().stream()
- .map(name -> '"' + name + '"')
- .collect(Collectors.joining(", ")));
+ result.append(
+ getValidLogLevelNames().stream().map(name -> '"' + name +
'"').collect(Collectors.joining(", ")));
result.append(" ]");
return result.toString();
}
+
+ private boolean isValidLogLevel(String logLevelName) {
+ try {
+ LogLevel.valueOf(logLevelName);
+ return true;
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+ }
+
+ private List<String> getValidLogLevelNames() {
+ return
java.util.Arrays.stream(LogLevel.values()).map(Enum::name).toList();
+ }
+
+ private LogLevel getHighestLogLevel() {
+ LogLevel[] levels = LogLevel.values();
+ return levels[levels.length - 1];
+ }
+
+ private String getHighestLogLevelName() {
+ return getHighestLogLevel().name();
+ }
}
diff --git
a/src/main/java/org/apache/sling/mcp/server/impl/contribs/log/LogLevel.java
b/src/main/java/org/apache/sling/mcp/server/impl/contribs/log/LogLevel.java
deleted file mode 100644
index c0cb8fd..0000000
--- a/src/main/java/org/apache/sling/mcp/server/impl/contribs/log/LogLevel.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.mcp.server.impl.contribs.log;
-
-enum LogLevel {
- TRACE,
- DEBUG,
- INFO,
- WARN,
- ERROR;
-
- static boolean isValid(String logLevelName) {
- try {
- LogLevel.valueOf(logLevelName);
- return true;
- } catch (IllegalArgumentException e) {
- return false;
- }
- }
-
- static String getHighestName() {
- return values()[values().length - 1].toString();
- }
-
- boolean isGreaterOrEqual(LogLevel minLevel) {
- return ordinal() >= minLevel.ordinal();
- }
-}
diff --git
a/src/main/java/org/apache/sling/mcp/server/impl/contribs/log/StructuredLogBufferAppender.java
b/src/main/java/org/apache/sling/mcp/server/impl/contribs/log/StructuredLogBufferAppender.java
deleted file mode 100644
index 9263d12..0000000
---
a/src/main/java/org/apache/sling/mcp/server/impl/contribs/log/StructuredLogBufferAppender.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.mcp.server.impl.contribs.log;
-
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodType;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-import ch.qos.logback.classic.spi.ILoggingEvent;
-import ch.qos.logback.classic.spi.IThrowableProxy;
-import ch.qos.logback.classic.spi.StackTraceElementProxy;
-import ch.qos.logback.core.Appender;
-import ch.qos.logback.core.AppenderBase;
-import org.apache.sling.mcp.server.contribs.log.LogSnapshot;
-import org.osgi.service.component.annotations.Activate;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.Reference;
-
-@Component(
- service = Appender.class,
- property = {"loggers=ROOT"})
-public class StructuredLogBufferAppender extends AppenderBase<ILoggingEvent> {
-
- // Forward compatibility with logback 1.5+, where IThrowableProxy may
expose getOverridingMessage().
- private static final MethodHandle GET_OVERRIDING_MESSAGE =
findGetOverridingMessage();
-
- private final StructuredLogBufferSink buffer;
-
- @Activate
- public StructuredLogBufferAppender(@Reference StructuredLogBufferSink
buffer) {
- this.buffer = buffer;
- setName("structured-log-buffer");
- }
-
- @Override
- protected void append(ILoggingEvent eventObject) {
- if (eventObject == null) {
- return;
- }
-
- String logLevel = eventObject.getLevel().levelStr;
- if (!LogLevel.isValid(logLevel)) {
- return;
- }
-
- buffer.append(new LogSnapshot(
- eventObject.getTimeStamp(),
- logLevel,
- eventObject.getLoggerName(),
- eventObject.getThreadName(),
- eventObject.getFormattedMessage(),
- getThrowableText(eventObject),
- copyMdc(eventObject)));
- }
-
- private Map<String, String> copyMdc(ILoggingEvent eventObject) {
- Map<String, String> mdc = eventObject.getMDCPropertyMap();
- if (mdc == null || mdc.isEmpty()) {
- return Map.of();
- }
- return new LinkedHashMap<>(mdc);
- }
-
- private String getThrowableText(ILoggingEvent eventObject) {
- IThrowableProxy throwableProxy = eventObject.getThrowableProxy();
- if (throwableProxy == null) {
- return null;
- }
-
- StringBuilder text = new StringBuilder();
- appendThrowable(text, throwableProxy, null);
- return text.toString();
- }
-
- private void appendThrowable(StringBuilder text, IThrowableProxy
throwableProxy, String prefix) {
- if (prefix != null) {
- text.append(prefix);
- }
- text.append(getThrowableHeader(throwableProxy)).append('\n');
-
- StackTraceElementProxy[] stackTrace =
throwableProxy.getStackTraceElementProxyArray();
- if (stackTrace != null) {
- int framesToRender = Math.max(0, stackTrace.length - Math.max(0,
throwableProxy.getCommonFrames()));
- for (int i = 0; i < framesToRender; i++) {
- text.append('\t').append(stackTrace[i]).append('\n');
- }
- if (throwableProxy.getCommonFrames() > 0) {
- text.append("\t... ")
- .append(throwableProxy.getCommonFrames())
- .append(" common frames omitted")
- .append('\n');
- }
- }
-
- IThrowableProxy[] suppressed = throwableProxy.getSuppressed();
- if (suppressed != null) {
- for (IThrowableProxy suppressedThrowable : suppressed) {
- appendThrowable(text, suppressedThrowable, "Suppressed: ");
- }
- }
-
- IThrowableProxy cause = throwableProxy.getCause();
- if (cause != null) {
- appendThrowable(text, cause, "Caused by: ");
- }
- }
-
- private String getThrowableHeader(IThrowableProxy throwableProxy) {
- String overridingMessage = getOverridingMessage(throwableProxy);
- if (overridingMessage != null && !overridingMessage.isEmpty()) {
- return overridingMessage;
- }
-
- StringBuilder header = new
StringBuilder(throwableProxy.getClassName());
- String message = throwableProxy.getMessage();
- if (message != null && !message.isEmpty()) {
- header.append(": ").append(message);
- }
- return header.toString();
- }
-
- private String getOverridingMessage(IThrowableProxy throwableProxy) {
- if (GET_OVERRIDING_MESSAGE == null) {
- return null;
- }
-
- try {
- Object overridingMessage =
GET_OVERRIDING_MESSAGE.invoke(throwableProxy);
- return overridingMessage instanceof String msg ? msg : null;
- } catch (Throwable e) {
- return null;
- }
- }
-
- private static MethodHandle findGetOverridingMessage() {
- try {
- return MethodHandles.publicLookup()
- .findVirtual(IThrowableProxy.class,
"getOverridingMessage", MethodType.methodType(String.class));
- } catch (NoSuchMethodException | IllegalAccessException e) {
- return null;
- }
- }
-}
diff --git
a/src/main/java/org/apache/sling/mcp/server/impl/contribs/log/StructuredLogBufferImpl.java
b/src/main/java/org/apache/sling/mcp/server/impl/contribs/log/StructuredLogBufferImpl.java
deleted file mode 100644
index c3bfe5f..0000000
---
a/src/main/java/org/apache/sling/mcp/server/impl/contribs/log/StructuredLogBufferImpl.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.mcp.server.impl.contribs.log;
-
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Deque;
-import java.util.List;
-import java.util.regex.Pattern;
-
-import org.apache.sling.mcp.server.contribs.log.LogSnapshot;
-import org.apache.sling.mcp.server.contribs.log.StructuredLogBuffer;
-import org.osgi.service.component.annotations.Activate;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.metatype.annotations.AttributeDefinition;
-import org.osgi.service.metatype.annotations.Designate;
-import org.osgi.service.metatype.annotations.ObjectClassDefinition;
-
-@Component(service = {StructuredLogBuffer.class,
StructuredLogBufferSink.class})
-@Designate(ocd = StructuredLogBufferImpl.Configuration.class)
-public class StructuredLogBufferImpl implements StructuredLogBuffer,
StructuredLogBufferSink {
-
- @ObjectClassDefinition(name = "Apache Sling Structured Log Buffer")
- public @interface Configuration {
-
- @AttributeDefinition(name = "Max entries")
- int maxEntries() default 10000;
- }
-
- private final Object lock = new Object();
- private final Deque<LogSnapshot> entries = new ArrayDeque<>();
- private int maxEntriesKept;
-
- public StructuredLogBufferImpl(int maxEntriesKept) {
- this.maxEntriesKept = Math.max(1, maxEntriesKept);
- }
-
- @Activate
- public StructuredLogBufferImpl(Configuration configuration) {
- this(configuration.maxEntries());
- }
-
- @Override
- public void append(LogSnapshot snapshot) {
- synchronized (lock) {
- entries.addLast(snapshot);
- trimToSize();
- }
- }
-
- @Override
- public boolean isValidLogLevel(String logLevelName) {
- return LogLevel.isValid(logLevelName);
- }
-
- @Override
- public List<String> getValidLogLevelNames() {
- return Arrays.stream(LogLevel.values()).map(Enum::toString).toList();
- }
-
- @Override
- public String getHighestLogLevelName() {
- return LogLevel.getHighestName();
- }
-
- public List<LogSnapshot> getRecent(Pattern pattern, String minLevel, int
maxEntries) {
-
- if (!isValidLogLevel(minLevel)) {
- throw new IllegalArgumentException("Invalid log level: " +
minLevel);
- }
-
- LogLevel minLogLevel = LogLevel.valueOf(minLevel);
-
- synchronized (lock) {
- List<LogSnapshot> matches = new ArrayList<>();
- int remaining = Math.max(1, maxEntries);
-
- for (var iterator = entries.descendingIterator();
iterator.hasNext() && remaining > 0; ) {
- LogSnapshot snapshot = iterator.next();
- if (!matches(snapshot, pattern, minLogLevel)) {
- continue;
- }
- matches.add(snapshot);
- remaining--;
- }
-
- return matches;
- }
- }
-
- private boolean matches(LogSnapshot snapshot, Pattern pattern, LogLevel
minLevel) {
-
- if (LogLevel.valueOf(snapshot.level()).isGreaterOrEqual(minLevel)) {
- if (pattern == null) {
- return true;
- }
- return matchesField(pattern, snapshot.level())
- || matchesField(pattern, snapshot.loggerName())
- || matchesField(pattern, snapshot.threadName())
- || matchesField(pattern, snapshot.formattedMessage())
- || matchesField(pattern, snapshot.throwableText())
- || matchesMdc(pattern, snapshot);
- }
- return false;
- }
-
- private boolean matchesMdc(Pattern pattern, LogSnapshot snapshot) {
- if (snapshot.mdc().isEmpty()) {
- return false;
- }
- for (var entry : snapshot.mdc().entrySet()) {
- if (matchesField(pattern, entry.getKey()) || matchesField(pattern,
entry.getValue())) {
- return true;
- }
- }
- return false;
- }
-
- private boolean matchesField(Pattern pattern, String value) {
- return value != null && !value.isEmpty() &&
pattern.matcher(value).find();
- }
-
- private void trimToSize() {
- while (entries.size() > maxEntriesKept) {
- entries.removeFirst();
- }
- }
-}
diff --git
a/src/main/java/org/apache/sling/mcp/server/impl/contribs/log/StructuredLogBufferSink.java
b/src/main/java/org/apache/sling/mcp/server/impl/contribs/log/StructuredLogBufferSink.java
deleted file mode 100644
index 1524641..0000000
---
a/src/main/java/org/apache/sling/mcp/server/impl/contribs/log/StructuredLogBufferSink.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.mcp.server.impl.contribs.log;
-
-import org.apache.sling.mcp.server.contribs.log.LogSnapshot;
-
-interface StructuredLogBufferSink {
-
- void append(LogSnapshot snapshot);
-}
diff --git
a/src/test/java/org/apache/sling/mcp/server/contribs/log/LogSnapshotTest.java
b/src/test/java/org/apache/sling/mcp/server/contribs/log/LogSnapshotTest.java
deleted file mode 100644
index 5ea3105..0000000
---
a/src/test/java/org/apache/sling/mcp/server/contribs/log/LogSnapshotTest.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.mcp.server.contribs.log;
-
-import java.util.Map;
-
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-class LogSnapshotTest {
-
- @Test
- void storesMdcAsUnmodifiableMap() {
- LogSnapshot snapshot = new LogSnapshot(1L, "INFO", "logger", "thread",
"message", null, Map.of("k", "v"));
-
- assertEquals("INFO", snapshot.level());
- assertEquals(Map.of("k", "v"), snapshot.mdc());
- }
-}
diff --git
a/src/test/java/org/apache/sling/mcp/server/impl/contribs/log/StructuredLogBufferAppenderTest.java
b/src/test/java/org/apache/sling/mcp/server/impl/contribs/log/StructuredLogBufferAppenderTest.java
deleted file mode 100644
index 7d05a5e..0000000
---
a/src/test/java/org/apache/sling/mcp/server/impl/contribs/log/StructuredLogBufferAppenderTest.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.mcp.server.impl.contribs.log;
-
-import java.lang.reflect.Constructor;
-import java.util.List;
-
-import ch.qos.logback.classic.Level;
-import ch.qos.logback.classic.Logger;
-import ch.qos.logback.classic.LoggerContext;
-import ch.qos.logback.classic.spi.LoggingEvent;
-import org.apache.sling.mcp.server.contribs.log.LogSnapshot;
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.fail;
-
-class StructuredLogBufferAppenderTest {
-
- @Test
- void appenderSnapshotsFormattedMessageAndThrowable() {
- StructuredLogBufferImpl buffer = new StructuredLogBufferImpl(5);
- StructuredLogBufferAppender appender = new
StructuredLogBufferAppender(buffer);
-
- LoggerContext context = new LoggerContext();
- appender.setContext(context);
- Logger logger = context.getLogger("test.logger");
- RuntimeException failure = new RuntimeException("error");
- LoggingEvent event = new LoggingEvent(getClass().getName(), logger,
Level.ERROR, "message", failure, null);
- event.setMDCPropertyMap(java.util.Map.of());
- event.setThreadName("worker-1");
-
- appender.append(event);
-
- List<LogSnapshot> logs = buffer.getRecent(null, "TRACE", 10);
- assertEquals(1, logs.size());
- assertEquals("message", logs.get(0).formattedMessage());
- assertEquals("worker-1", logs.get(0).threadName());
- assertEquals("ERROR", logs.get(0).level());
- assertNotNull(logs.get(0).throwableText());
- }
-
- @Test
- void appenderSkipsInvalidLogLevels() {
- StructuredLogBufferImpl buffer = new StructuredLogBufferImpl(5);
- StructuredLogBufferAppender appender = new
StructuredLogBufferAppender(buffer);
-
- LoggingEvent event = new LoggingEvent();
- event.setLoggerName("invalid.logger");
- event.setThreadName("invalid-thread");
- event.setMessage("ignored");
- event.setLevel(invalidLevel());
-
- appender.append(event);
-
- assertEquals(List.of(), buffer.getRecent(null, "TRACE", 10));
- }
-
- private Level invalidLevel() {
- try {
- Constructor<Level> constructor =
Level.class.getDeclaredConstructor(int.class, String.class);
- constructor.setAccessible(true);
- return constructor.newInstance(Integer.MAX_VALUE, "INVALID");
- } catch (ReflectiveOperationException e) {
- fail("Unable to construct invalid log level", e);
- return null;
- }
- }
-}
diff --git
a/src/test/java/org/apache/sling/mcp/server/impl/contribs/log/StructuredLogBufferImplTest.java
b/src/test/java/org/apache/sling/mcp/server/impl/contribs/log/StructuredLogBufferImplTest.java
deleted file mode 100644
index d2e4d7d..0000000
---
a/src/test/java/org/apache/sling/mcp/server/impl/contribs/log/StructuredLogBufferImplTest.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.mcp.server.impl.contribs.log;
-
-import java.util.List;
-import java.util.Map;
-import java.util.regex.Pattern;
-
-import org.apache.sling.mcp.server.contribs.log.LogSnapshot;
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-class StructuredLogBufferImplTest {
-
- @Test
- void keepsOnlyNewestEntriesWithinCapacity() {
- StructuredLogBufferImpl buffer = new StructuredLogBufferImpl(2);
-
- buffer.append(snapshot(1L, "INFO", "first"));
- buffer.append(snapshot(2L, "INFO", "second"));
- buffer.append(snapshot(3L, "INFO", "third"));
-
- List<LogSnapshot> logs = buffer.getRecent(null, "TRACE", 10);
- assertEquals(
- List.of("third", "second"),
- logs.stream().map(LogSnapshot::formattedMessage).toList());
- }
-
- @Test
- void filtersByLevelAndRegex() {
- StructuredLogBufferImpl buffer = new StructuredLogBufferImpl(10);
-
- buffer.append(snapshot(1L, "DEBUG", "debug trace"));
- buffer.append(snapshot(2L, "INFO", "first user ok"));
- buffer.append(snapshot(3L, "ERROR", "first user failure"));
-
- List<LogSnapshot> logs = buffer.getRecent(Pattern.compile("first",
Pattern.CASE_INSENSITIVE), "INFO", 10);
-
- assertEquals(
- List.of("first user failure", "first user ok"),
- logs.stream().map(LogSnapshot::formattedMessage).toList());
- }
-
- @Test
- void exposesSupportedLogLevels() {
- StructuredLogBufferImpl buffer = new StructuredLogBufferImpl(10);
-
- assertTrue(buffer.isValidLogLevel("INFO"));
- assertFalse(buffer.isValidLogLevel("info"));
- assertEquals(List.of("TRACE", "DEBUG", "INFO", "WARN", "ERROR"),
buffer.getValidLogLevelNames());
- assertEquals("ERROR", buffer.getHighestLogLevelName());
- }
-
- private LogSnapshot snapshot(long timeMillis, String level, String
message) {
- return new LogSnapshot(timeMillis, level, "logger", "thread", message,
null, Map.of());
- }
-}