This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to branch issue/split-log-support
in repository 
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-mcp-server-contributions.git

commit f22460014bf1c359bf1ea92b7d2a076677be85ef
Author: Robert Munteanu <[email protected]>
AuthorDate: Tue May 12 17:57:39 2026 +0200

    chore: clearer separation of 'api' and 'impl' classes for the log support
    
    This is related to SLING-13188
---
 .../internal => contribs/log}/LogSnapshot.java     | 44 +-----------------
 .../server/contribs/log/StructuredLogBuffer.java}  | 19 ++++----
 .../server/impl/contribs/LogToolContribution.java  | 21 +++++----
 .../mcp/server/impl/contribs/log/LogLevel.java}    | 28 ++++++++----
 .../StructuredLogBufferAppender.java               | 39 +++++-----------
 .../StructuredLogBufferImpl.java}                  | 52 ++++++++++++++++++----
 .../contribs/log/StructuredLogBufferSink.java}     | 14 ++----
 .../internal => contribs/log}/LogSnapshotTest.java | 14 +++---
 .../StructuredLogBufferAppenderTest.java           | 43 ++++++++++++++----
 .../StructuredLogBufferImplTest.java}              | 36 ++++++++++-----
 10 files changed, 169 insertions(+), 141 deletions(-)

diff --git 
a/src/main/java/org/apache/sling/mcp/server/impl/contribs/internal/LogSnapshot.java
 b/src/main/java/org/apache/sling/mcp/server/contribs/log/LogSnapshot.java
similarity index 53%
rename from 
src/main/java/org/apache/sling/mcp/server/impl/contribs/internal/LogSnapshot.java
rename to 
src/main/java/org/apache/sling/mcp/server/contribs/log/LogSnapshot.java
index 6dc4cb4..c986ad8 100644
--- 
a/src/main/java/org/apache/sling/mcp/server/impl/contribs/internal/LogSnapshot.java
+++ b/src/main/java/org/apache/sling/mcp/server/contribs/log/LogSnapshot.java
@@ -16,11 +16,9 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.sling.mcp.server.impl.contribs.internal;
+package org.apache.sling.mcp.server.contribs.log;
 
-import java.util.Arrays;
 import java.util.Collections;
-import java.util.List;
 import java.util.Map;
 
 /**
@@ -29,52 +27,14 @@ import java.util.Map;
  */
 public record LogSnapshot(
         long timeMillis,
-        LogLevel level, // avoid binding to logback or a specific version of 
slf4j
+        String level,
         String loggerName,
         String threadName,
         String formattedMessage,
         String throwableText,
         Map<String, String> mdc) {
 
-    public static boolean isValidLogLevel(String logLevelName) {
-        try {
-            LogLevel.valueOf(logLevelName);
-            return true;
-        } catch (IllegalArgumentException e) {
-            return false;
-        }
-    }
-
-    public static List<String> getValidLogLevelNames() {
-        return Arrays.stream(LogLevel.values()).map(Enum::toString).toList();
-    }
-
-    public static String getHighestLogLevelName() {
-        return LogLevel.values()[LogLevel.values().length - 1].toString();
-    }
-
     public LogSnapshot {
         mdc = mdc == null ? Collections.emptyMap() : 
Collections.unmodifiableMap(mdc);
     }
-
-    enum LogLevel {
-        TRACE,
-        DEBUG,
-        INFO,
-        WARN,
-        ERROR;
-
-        boolean isGreaterOrEqual(LogLevel minLevel) {
-            return ordinal() >= minLevel.ordinal();
-        }
-
-        public boolean isValid(String logLevelName) {
-            try {
-                LogLevel.valueOf(logLevelName);
-                return true;
-            } catch (IllegalArgumentException e) {
-                return false;
-            }
-        }
-    }
 }
diff --git 
a/src/test/java/org/apache/sling/mcp/server/impl/contribs/internal/LogSnapshotTest.java
 
b/src/main/java/org/apache/sling/mcp/server/contribs/log/StructuredLogBuffer.java
similarity index 68%
copy from 
src/test/java/org/apache/sling/mcp/server/impl/contribs/internal/LogSnapshotTest.java
copy to 
src/main/java/org/apache/sling/mcp/server/contribs/log/StructuredLogBuffer.java
index 8c494b5..07e0c8d 100644
--- 
a/src/test/java/org/apache/sling/mcp/server/impl/contribs/internal/LogSnapshotTest.java
+++ 
b/src/main/java/org/apache/sling/mcp/server/contribs/log/StructuredLogBuffer.java
@@ -16,17 +16,18 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.sling.mcp.server.impl.contribs.internal;
+package org.apache.sling.mcp.server.contribs.log;
 
-import org.apache.sling.mcp.server.impl.contribs.internal.LogSnapshot.LogLevel;
-import org.junit.jupiter.api.Test;
+import java.util.List;
+import java.util.regex.Pattern;
 
-import static org.junit.jupiter.api.Assertions.*;
+public interface StructuredLogBuffer {
 
-class LogSnapshotTest {
+    boolean isValidLogLevel(String logLevelName);
 
-    @Test
-    void logLevelComparison() {
-        assertTrue(LogSnapshot.LogLevel.INFO.isGreaterOrEqual(LogLevel.TRACE));
-    }
+    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 bbd11c9..169ad35 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,8 @@ 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.impl.contribs.internal.LogSnapshot;
-import 
org.apache.sling.mcp.server.impl.contribs.internal.StructuredLogBufferAppender;
+import org.apache.sling.mcp.server.contribs.log.LogSnapshot;
+import org.apache.sling.mcp.server.contribs.log.StructuredLogBuffer;
 import org.apache.sling.mcp.server.spi.McpServerContribution;
 import org.osgi.service.component.annotations.Component;
 import org.osgi.service.component.annotations.Reference;
@@ -44,7 +44,7 @@ import org.osgi.service.component.annotations.Reference;
 public class LogToolContribution implements McpServerContribution {
 
     @Reference
-    private StructuredLogBufferAppender structuredLogBufferAppender;
+    private StructuredLogBuffer structuredLogBuffer;
 
     @Reference
     private McpJsonMapperSupplier jsonMapper;
@@ -79,7 +79,7 @@ public class LogToolContribution implements 
McpServerContribution {
                 }
                 """.formatted(
                         validLogLevelValuesAsCsv(),
-                        LogSnapshot.getHighestLogLevelName(),
+                        structuredLogBuffer.getHighestLogLevelName(),
                         validLogLevelValuesAsJsonSchemaEnum());
 
         return List.of(new SyncToolSpecification(
@@ -103,12 +103,12 @@ public class LogToolContribution implements 
McpServerContribution {
                         maxEntries = Math.min(maxEntries, 1000); // Cap at 1000
                     }
 
-                    String minLogLevel = LogSnapshot.getHighestLogLevelName();
+                    String minLogLevel = 
structuredLogBuffer.getHighestLogLevelName();
                     if (logLevelStr != null && !logLevelStr.isEmpty()) {
-                        if (!LogSnapshot.isValidLogLevel(logLevelStr)) {
+                        if (!structuredLogBuffer.isValidLogLevel(logLevelStr)) 
{
                             return CallToolResult.builder()
                                     .addTextContent("Invalid log level: " + 
logLevelStr + ". Valid options are: "
-                                            + String.join(", ", 
LogSnapshot.getValidLogLevelNames()))
+                                            + String.join(", ", 
structuredLogBuffer.getValidLogLevelNames()))
                                     .isError(true)
                                     .build();
                         }
@@ -128,8 +128,7 @@ public class LogToolContribution implements 
McpServerContribution {
                         }
                     }
 
-                    List<LogSnapshot> filteredLogs =
-                            
structuredLogBufferAppender.getBuffer().getRecent(pattern, minLogLevel, 
maxEntries);
+                    List<LogSnapshot> filteredLogs = 
structuredLogBuffer.getRecent(pattern, minLogLevel, maxEntries);
 
                     // Format output
                     String result = formatLogs(filteredLogs, regexPattern, 
minLogLevel, maxEntries);
@@ -216,13 +215,13 @@ public class LogToolContribution implements 
McpServerContribution {
     }
 
     private String validLogLevelValuesAsCsv() {
-        return String.join(", ", LogSnapshot.getValidLogLevelNames());
+        return String.join(", ", structuredLogBuffer.getValidLogLevelNames());
     }
 
     private String validLogLevelValuesAsJsonSchemaEnum() {
         StringBuilder result = new StringBuilder();
         result.append("[ ");
-        result.append(LogSnapshot.getValidLogLevelNames().stream()
+        result.append(structuredLogBuffer.getValidLogLevelNames().stream()
                 .map(name -> '"' + name + '"')
                 .collect(Collectors.joining(", ")));
         result.append(" ]");
diff --git 
a/src/test/java/org/apache/sling/mcp/server/impl/contribs/internal/LogSnapshotTest.java
 b/src/main/java/org/apache/sling/mcp/server/impl/contribs/log/LogLevel.java
similarity index 60%
copy from 
src/test/java/org/apache/sling/mcp/server/impl/contribs/internal/LogSnapshotTest.java
copy to 
src/main/java/org/apache/sling/mcp/server/impl/contribs/log/LogLevel.java
index 8c494b5..c0cb8fd 100644
--- 
a/src/test/java/org/apache/sling/mcp/server/impl/contribs/internal/LogSnapshotTest.java
+++ b/src/main/java/org/apache/sling/mcp/server/impl/contribs/log/LogLevel.java
@@ -16,17 +16,29 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.sling.mcp.server.impl.contribs.internal;
+package org.apache.sling.mcp.server.impl.contribs.log;
 
-import org.apache.sling.mcp.server.impl.contribs.internal.LogSnapshot.LogLevel;
-import org.junit.jupiter.api.Test;
+enum LogLevel {
+    TRACE,
+    DEBUG,
+    INFO,
+    WARN,
+    ERROR;
 
-import static org.junit.jupiter.api.Assertions.*;
+    static boolean isValid(String logLevelName) {
+        try {
+            LogLevel.valueOf(logLevelName);
+            return true;
+        } catch (IllegalArgumentException e) {
+            return false;
+        }
+    }
 
-class LogSnapshotTest {
+    static String getHighestName() {
+        return values()[values().length - 1].toString();
+    }
 
-    @Test
-    void logLevelComparison() {
-        assertTrue(LogSnapshot.LogLevel.INFO.isGreaterOrEqual(LogLevel.TRACE));
+    boolean isGreaterOrEqual(LogLevel minLevel) {
+        return ordinal() >= minLevel.ordinal();
     }
 }
diff --git 
a/src/main/java/org/apache/sling/mcp/server/impl/contribs/internal/StructuredLogBufferAppender.java
 
b/src/main/java/org/apache/sling/mcp/server/impl/contribs/log/StructuredLogBufferAppender.java
similarity index 81%
rename from 
src/main/java/org/apache/sling/mcp/server/impl/contribs/internal/StructuredLogBufferAppender.java
rename to 
src/main/java/org/apache/sling/mcp/server/impl/contribs/log/StructuredLogBufferAppender.java
index 244f297..2122531 100644
--- 
a/src/main/java/org/apache/sling/mcp/server/impl/contribs/internal/StructuredLogBufferAppender.java
+++ 
b/src/main/java/org/apache/sling/mcp/server/impl/contribs/log/StructuredLogBufferAppender.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.sling.mcp.server.impl.contribs.internal;
+package org.apache.sling.mcp.server.impl.contribs.log;
 
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
@@ -29,52 +29,37 @@ 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.impl.contribs.internal.LogSnapshot.LogLevel;
+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.metatype.annotations.AttributeDefinition;
-import org.osgi.service.metatype.annotations.Designate;
-import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+import org.osgi.service.component.annotations.Reference;
 
 @Component(
-        service = {Appender.class, StructuredLogBufferAppender.class},
-        property = {
-            "loggers=ROOT",
-            "service.description=Structured in-memory MCP log appender",
-            "service.vendor=The Apache Software Foundation"
-        })
-@Designate(ocd = StructuredLogBufferAppender.Configuration.class)
+        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();
 
-    @ObjectClassDefinition(name = "Apache Sling Structured Log Buffer")
-    public @interface Configuration {
-
-        @AttributeDefinition(name = "Max entries")
-        int maxEntries() default 10000;
-    }
-
-    private final StructuredLogBuffer buffer;
+    private final StructuredLogBufferSink buffer;
 
     @Activate
-    public StructuredLogBufferAppender(Configuration configuration) {
-        buffer = new StructuredLogBuffer(configuration.maxEntries());
+    public StructuredLogBufferAppender(@Reference StructuredLogBufferSink 
buffer) {
+        this.buffer = buffer;
         setName("structured-log-buffer");
     }
 
-    public StructuredLogBuffer getBuffer() {
-        return buffer;
-    }
-
     @Override
     protected void append(ILoggingEvent eventObject) {
         if (eventObject == null) {
             return;
         }
 
-        LogLevel logLevel = LogLevel.valueOf(eventObject.getLevel().levelStr);
+        String logLevel = eventObject.getLevel().levelStr;
+        if (!LogLevel.isValid(logLevel)) {
+            return;
+        }
 
         buffer.append(new LogSnapshot(
                 eventObject.getTimeStamp(),
diff --git 
a/src/main/java/org/apache/sling/mcp/server/impl/contribs/internal/StructuredLogBuffer.java
 
b/src/main/java/org/apache/sling/mcp/server/impl/contribs/log/StructuredLogBufferImpl.java
similarity index 65%
rename from 
src/main/java/org/apache/sling/mcp/server/impl/contribs/internal/StructuredLogBuffer.java
rename to 
src/main/java/org/apache/sling/mcp/server/impl/contribs/log/StructuredLogBufferImpl.java
index 9a9ba53..c3bfe5f 100644
--- 
a/src/main/java/org/apache/sling/mcp/server/impl/contribs/internal/StructuredLogBuffer.java
+++ 
b/src/main/java/org/apache/sling/mcp/server/impl/contribs/log/StructuredLogBufferImpl.java
@@ -16,26 +16,48 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.sling.mcp.server.impl.contribs.internal;
+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.impl.contribs.internal.LogSnapshot.LogLevel;
+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;
 
-public class StructuredLogBuffer {
+@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 StructuredLogBuffer(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);
@@ -43,9 +65,24 @@ public class StructuredLogBuffer {
         }
     }
 
+    @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 (!LogSnapshot.isValidLogLevel(minLevel)) {
+        if (!isValidLogLevel(minLevel)) {
             throw new IllegalArgumentException("Invalid log level: " + 
minLevel);
         }
 
@@ -70,12 +107,11 @@ public class StructuredLogBuffer {
 
     private boolean matches(LogSnapshot snapshot, Pattern pattern, LogLevel 
minLevel) {
 
-        if (snapshot.level().isGreaterOrEqual(minLevel)) {
+        if (LogLevel.valueOf(snapshot.level()).isGreaterOrEqual(minLevel)) {
             if (pattern == null) {
                 return true;
             }
-            return matchesField(
-                            pattern, snapshot.level() != null ? 
snapshot.level().toString() : null)
+            return matchesField(pattern, snapshot.level())
                     || matchesField(pattern, snapshot.loggerName())
                     || matchesField(pattern, snapshot.threadName())
                     || matchesField(pattern, snapshot.formattedMessage())
diff --git 
a/src/test/java/org/apache/sling/mcp/server/impl/contribs/internal/LogSnapshotTest.java
 
b/src/main/java/org/apache/sling/mcp/server/impl/contribs/log/StructuredLogBufferSink.java
similarity index 68%
copy from 
src/test/java/org/apache/sling/mcp/server/impl/contribs/internal/LogSnapshotTest.java
copy to 
src/main/java/org/apache/sling/mcp/server/impl/contribs/log/StructuredLogBufferSink.java
index 8c494b5..1524641 100644
--- 
a/src/test/java/org/apache/sling/mcp/server/impl/contribs/internal/LogSnapshotTest.java
+++ 
b/src/main/java/org/apache/sling/mcp/server/impl/contribs/log/StructuredLogBufferSink.java
@@ -16,17 +16,11 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.sling.mcp.server.impl.contribs.internal;
+package org.apache.sling.mcp.server.impl.contribs.log;
 
-import org.apache.sling.mcp.server.impl.contribs.internal.LogSnapshot.LogLevel;
-import org.junit.jupiter.api.Test;
+import org.apache.sling.mcp.server.contribs.log.LogSnapshot;
 
-import static org.junit.jupiter.api.Assertions.*;
+interface StructuredLogBufferSink {
 
-class LogSnapshotTest {
-
-    @Test
-    void logLevelComparison() {
-        assertTrue(LogSnapshot.LogLevel.INFO.isGreaterOrEqual(LogLevel.TRACE));
-    }
+    void append(LogSnapshot snapshot);
 }
diff --git 
a/src/test/java/org/apache/sling/mcp/server/impl/contribs/internal/LogSnapshotTest.java
 b/src/test/java/org/apache/sling/mcp/server/contribs/log/LogSnapshotTest.java
similarity index 69%
rename from 
src/test/java/org/apache/sling/mcp/server/impl/contribs/internal/LogSnapshotTest.java
rename to 
src/test/java/org/apache/sling/mcp/server/contribs/log/LogSnapshotTest.java
index 8c494b5..5ea3105 100644
--- 
a/src/test/java/org/apache/sling/mcp/server/impl/contribs/internal/LogSnapshotTest.java
+++ 
b/src/test/java/org/apache/sling/mcp/server/contribs/log/LogSnapshotTest.java
@@ -16,17 +16,21 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.sling.mcp.server.impl.contribs.internal;
+package org.apache.sling.mcp.server.contribs.log;
+
+import java.util.Map;
 
-import org.apache.sling.mcp.server.impl.contribs.internal.LogSnapshot.LogLevel;
 import org.junit.jupiter.api.Test;
 
-import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
 
 class LogSnapshotTest {
 
     @Test
-    void logLevelComparison() {
-        assertTrue(LogSnapshot.LogLevel.INFO.isGreaterOrEqual(LogLevel.TRACE));
+    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/internal/StructuredLogBufferAppenderTest.java
 
b/src/test/java/org/apache/sling/mcp/server/impl/contribs/log/StructuredLogBufferAppenderTest.java
similarity index 58%
rename from 
src/test/java/org/apache/sling/mcp/server/impl/contribs/internal/StructuredLogBufferAppenderTest.java
rename to 
src/test/java/org/apache/sling/mcp/server/impl/contribs/log/StructuredLogBufferAppenderTest.java
index 1d34628..aa81263 100644
--- 
a/src/test/java/org/apache/sling/mcp/server/impl/contribs/internal/StructuredLogBufferAppenderTest.java
+++ 
b/src/test/java/org/apache/sling/mcp/server/impl/contribs/log/StructuredLogBufferAppenderTest.java
@@ -16,26 +16,28 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.sling.mcp.server.impl.contribs.internal;
+package org.apache.sling.mcp.server.impl.contribs.log;
 
+import java.lang.reflect.Constructor;
 import java.util.List;
-import java.util.Map;
 
 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 org.osgi.util.converter.Converters;
 
 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() {
-        StructuredLogBufferAppender appender = new 
StructuredLogBufferAppender(configuration(5));
+        StructuredLogBufferImpl buffer = new StructuredLogBufferImpl(5);
+        StructuredLogBufferAppender appender = new 
StructuredLogBufferAppender(buffer);
 
         LoggerContext context = new LoggerContext();
         appender.setContext(context);
@@ -47,16 +49,39 @@ class StructuredLogBufferAppenderTest {
 
         appender.append(event);
 
-        List<LogSnapshot> logs = appender.getBuffer().getRecent(null, "TRACE", 
10);
+        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());
     }
 
-    private StructuredLogBufferAppender.Configuration configuration(int 
maxEntries) {
-        return Converters.standardConverter()
-                .convert(Map.of("maxEntries", maxEntries))
-                .to(StructuredLogBufferAppender.Configuration.class);
+    @Test
+    void appenderSkipsInvalidLogLevels() {
+        StructuredLogBufferImpl buffer = new StructuredLogBufferImpl(5);
+        StructuredLogBufferAppender appender = new 
StructuredLogBufferAppender(buffer);
+
+        LoggerContext context = new LoggerContext();
+        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/internal/StructuredLogBufferTest.java
 
b/src/test/java/org/apache/sling/mcp/server/impl/contribs/log/StructuredLogBufferImplTest.java
similarity index 57%
rename from 
src/test/java/org/apache/sling/mcp/server/impl/contribs/internal/StructuredLogBufferTest.java
rename to 
src/test/java/org/apache/sling/mcp/server/impl/contribs/log/StructuredLogBufferImplTest.java
index 8557801..d2e4d7d 100644
--- 
a/src/test/java/org/apache/sling/mcp/server/impl/contribs/internal/StructuredLogBufferTest.java
+++ 
b/src/test/java/org/apache/sling/mcp/server/impl/contribs/log/StructuredLogBufferImplTest.java
@@ -16,26 +16,28 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.sling.mcp.server.impl.contribs.internal;
+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.impl.contribs.internal.LogSnapshot.LogLevel;
+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 StructuredLogBufferTest {
+class StructuredLogBufferImplTest {
 
     @Test
     void keepsOnlyNewestEntriesWithinCapacity() {
-        StructuredLogBuffer buffer = new StructuredLogBuffer(2);
+        StructuredLogBufferImpl buffer = new StructuredLogBufferImpl(2);
 
-        buffer.append(snapshot(1L, LogLevel.INFO, "first"));
-        buffer.append(snapshot(2L, LogLevel.INFO, "second"));
-        buffer.append(snapshot(3L, LogLevel.INFO, "third"));
+        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(
@@ -45,11 +47,11 @@ class StructuredLogBufferTest {
 
     @Test
     void filtersByLevelAndRegex() {
-        StructuredLogBuffer buffer = new StructuredLogBuffer(10);
+        StructuredLogBufferImpl buffer = new StructuredLogBufferImpl(10);
 
-        buffer.append(snapshot(1L, LogLevel.DEBUG, "debug trace"));
-        buffer.append(snapshot(2L, LogLevel.INFO, "first user ok"));
-        buffer.append(snapshot(3L, LogLevel.ERROR, "first user failure"));
+        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);
 
@@ -58,7 +60,17 @@ class StructuredLogBufferTest {
                 logs.stream().map(LogSnapshot::formattedMessage).toList());
     }
 
-    private LogSnapshot snapshot(long timeMillis, LogLevel level, String 
message) {
+    @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