This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to branch issue/SLING-13213 in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-mcp-server-contributions.git
commit 2ded2c9693176e519239a70be5bb8391ec9df36d Author: Robert Munteanu <[email protected]> AuthorDate: Wed May 20 10:25:35 2026 +0200 wip: adapt to SLING-13188 changes --- pom.xml | 6 + .../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 | 87 ----------- .../contribs/log/StructuredLogBufferImplTest.java | 76 ---------- 11 files changed, 45 insertions(+), 667 deletions(-) diff --git a/pom.xml b/pom.xml index 6da50bb..fa5f28a 100644 --- a/pom.xml +++ b/pom.xml @@ -137,6 +137,12 @@ <version>0.1.2</version> <scope>provided</scope> </dependency> + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.commons.log</artifactId> + <version>6.0.5-SNAPSHOT</version> + <scope>provided</scope> + </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> 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 169ad35..b9a3945 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; @@ -44,7 +45,7 @@ import org.osgi.service.component.annotations.Reference; public class LogToolContribution implements McpServerContribution { @Reference - private StructuredLogBuffer structuredLogBuffer; + private LogStore logStore; @Reference private McpJsonMapperSupplier jsonMapper; @@ -78,9 +79,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(maxEntries, 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(DATE_FORMAT.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 2122531..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 ? (String) overridingMessage : 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 aa81263..0000000 --- a/src/test/java/org/apache/sling/mcp/server/impl/contribs/log/StructuredLogBufferAppenderTest.java +++ /dev/null @@ -1,87 +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); - - 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/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()); - } -}
