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());
-    }
-}


Reply via email to