From 042582813f3e1189ad0fd070c67b7cf32e0224a0 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Tue, 28 May 2024 10:14:48 +1000 Subject: [PATCH] Fix #9177 dump JVM info (#11845) Fix #9177 dump info from Runtime, Jetty.VERSION and System.getProperties --- .../java/org/eclipse/jetty/server/Server.java | 2 + .../org/eclipse/jetty/server/ServerTest.java | 22 ++++++++ .../util/component/ContainerLifeCycle.java | 6 +-- .../jetty/util/component/Dumpable.java | 51 ++++++++++++++++--- .../jetty/util/component/DumpableMap.java | 39 ++++++++++++++ .../jetty/util/thread/ThreadIdPool.java | 9 ++-- .../util/thread/QueuedThreadPoolTest.java | 3 +- .../jetty/ee10/servlet/DebugListener.java | 4 +- .../jetty/ee9/nested/DebugListener.java | 4 +- 9 files changed, 119 insertions(+), 21 deletions(-) create mode 100644 jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/component/DumpableMap.java diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java index 855d0a2be91..2372f6abe5a 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java @@ -54,6 +54,7 @@ import org.eclipse.jetty.util.component.AttributeContainerMap; import org.eclipse.jetty.util.component.ClassLoaderDump; import org.eclipse.jetty.util.component.DumpableAttributes; import org.eclipse.jetty.util.component.DumpableCollection; +import org.eclipse.jetty.util.component.DumpableMap; import org.eclipse.jetty.util.component.Environment; import org.eclipse.jetty.util.component.Graceful; import org.eclipse.jetty.util.component.LifeCycle; @@ -137,6 +138,7 @@ public class Server extends Handler.Wrapper implements Attributes public Server(@Name("threadPool") ThreadPool pool) { this(pool, null, null); + installBean(new DumpableMap("System Properties", System.getProperties())); } public Server(@Name("threadPool") ThreadPool threadPool, @Name("scheduler") Scheduler scheduler, @Name("bufferPool") ByteBufferPool bufferPool) diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ServerTest.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ServerTest.java index 2fd15118a15..dcca3eb0375 100644 --- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ServerTest.java +++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ServerTest.java @@ -32,8 +32,10 @@ import org.eclipse.jetty.server.internal.HttpChannelState; import org.eclipse.jetty.server.internal.HttpConnection; import org.eclipse.jetty.util.Blocker; import org.eclipse.jetty.util.Callback; +import org.eclipse.jetty.util.Jetty; import org.eclipse.jetty.util.component.LifeCycle; import org.eclipse.jetty.util.thread.Invocable; +import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -143,6 +145,26 @@ public class ServerTest assertThat(response.getContent(), is("Hello")); } + @Test + public void testDump() throws Exception + { + testSimpleGET(); + ((QueuedThreadPool)(_server.getThreadPool())).tryExecute(() -> {}); + String dump = _server.dump(); + assertThat(dump, containsString("oejs.Server@")); + assertThat(dump, containsString("QueuedThreadPool")); + assertThat(dump, containsString("+= ReservedThreadExecutor@")); + assertThat(dump, containsString(".ArrayByteBufferPool@")); + assertThat(dump, containsString("+- System Properties size=")); + assertThat(dump, containsString("+> java.home: ")); + assertThat(dump, containsString("+> java.runtime.version: ")); + assertThat(dump, containsString("+= oejsh.ContextHandler@")); + assertThat(dump, containsString("+= LocalConnector@")); + assertThat(dump, containsString("key: +-")); + assertThat(dump, containsString("JVM: ")); + assertThat(dump, containsString(Jetty.VERSION)); + } + public static Stream completionScenarios() { List arguments = new ArrayList<>(); diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/component/ContainerLifeCycle.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/component/ContainerLifeCycle.java index 0e4a0ae37bc..36fad2ee2e3 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/component/ContainerLifeCycle.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/component/ContainerLifeCycle.java @@ -745,10 +745,10 @@ public class ContainerLifeCycle extends AbstractLifeCycle implements Container, { try { - dump(System.err, ""); - System.err.println(Dumpable.KEY); + Dumpable.dump(this, System.err); + System.err.println(); } - catch (IOException e) + catch (Throwable e) { LOG.warn("Unable to dump", e); } diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/component/Dumpable.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/component/Dumpable.java index 7c728f5a36f..17e003b28f1 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/component/Dumpable.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/component/Dumpable.java @@ -14,8 +14,13 @@ package org.eclipse.jetty.util.component; import java.io.IOException; +import java.io.UncheckedIOException; import java.lang.reflect.Array; import java.nio.file.Path; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; @@ -23,6 +28,7 @@ import java.util.Map; import java.util.Objects; import java.util.stream.Stream; +import org.eclipse.jetty.util.Jetty; import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.annotation.ManagedObject; @@ -31,7 +37,7 @@ import org.eclipse.jetty.util.annotation.ManagedOperation; @ManagedObject("Dumpable Object") public interface Dumpable { - String KEY = "key: +- bean, += managed, +~ unmanaged, +? auto, +: iterable, +] array, +@ map, +> undefined"; + String KEY = "key: +- bean, += managed, +~ unmanaged, +? auto, +: iterable, +] array, +@ map, +> undefined\n"; @ManagedOperation(value = "Dump the nested Object state as a String", impact = "INFO") default String dump() @@ -50,24 +56,55 @@ public interface Dumpable void dump(Appendable out, String indent) throws IOException; /** - * Utility method to implement {@link #dump()} by calling {@link #dump(Appendable, String)} + * Utility method to dump to a {@link String} * * @param dumpable The dumpable to dump * @return The dumped string + * @see #dump(Appendable, String) */ static String dump(Dumpable dumpable) { StringBuilder b = new StringBuilder(); + dump(dumpable, b); + return b.toString(); + } + + /** + * Utility method to dump to an {@link Appendable} + * + * @param dumpable The dumpable to dump + * @param out The destination of the dump + */ + static void dump(Dumpable dumpable, Appendable out) + { try { - dumpable.dump(b, ""); + dumpable.dump(out, ""); + + out.append(KEY); + Runtime runtime = Runtime.getRuntime(); + Instant now = Instant.now(); + String zone = System.getProperty("user.timezone"); + out.append("JVM: %s %s %s; OS: %s %s %s; Jetty: %s; CPUs: %d; mem(free/total/max): %,d/%,d/%,d MiB\nUTC: %s; %s: %s".formatted( + System.getProperty("java.vm.vendor"), + System.getProperty("java.vm.name"), + System.getProperty("java.vm.version"), + System.getProperty("os.name"), + System.getProperty("os.arch"), + System.getProperty("os.version"), + Jetty.VERSION, + runtime.availableProcessors(), + runtime.freeMemory() / (1024 * 1024), + runtime.totalMemory() / (1024 * 1024), + runtime.maxMemory() / (1024 * 1024), + DateTimeFormatter.ISO_DATE_TIME.format(now.atOffset(ZoneOffset.UTC)), + zone, + DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(now.atZone(ZoneId.of(zone))))); } catch (IOException e) { - b.append(e.toString()); + throw new UncheckedIOException(e); } - b.append(KEY); - return b.toString(); } /** @@ -301,7 +338,7 @@ public interface Dumpable * interface to allow it to refine which of its beans can be * dumped. */ - public interface DumpableContainer extends Dumpable + interface DumpableContainer extends Dumpable { default boolean isDumpable(Object o) { diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/component/DumpableMap.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/component/DumpableMap.java new file mode 100644 index 00000000000..d56a2aa821b --- /dev/null +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/component/DumpableMap.java @@ -0,0 +1,39 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.util.component; + +import java.io.IOException; +import java.util.Comparator; +import java.util.Map; + +public class DumpableMap implements Dumpable +{ + private final String _name; + private final Map _map; + + public DumpableMap(String name, Map map) + { + _name = name; + _map = map; + } + + @Override + public void dump(Appendable out, String indent) throws IOException + { + Object[] array = _map.entrySet().stream() + .sorted(Map.Entry.comparingByKey(Comparator.comparing(String::valueOf))) + .map(e -> Dumpable.named(String.valueOf(e.getKey()), e.getValue())).toArray(Object[]::new); + Dumpable.dumpObjects(out, indent, _name + " size=" + array.length, array); + } +} diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ThreadIdPool.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ThreadIdPool.java index 84f092567bf..46b0756f18a 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ThreadIdPool.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ThreadIdPool.java @@ -25,7 +25,6 @@ import org.eclipse.jetty.util.MemoryUtils; import org.eclipse.jetty.util.ProcessorUtils; import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.component.Dumpable; -import org.eclipse.jetty.util.component.DumpableCollection; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -236,8 +235,12 @@ public class ThreadIdPool implements Dumpable int capacity = capacity(); List slots = new ArrayList<>(capacity); for (int i = 0; i < capacity; i++) - slots.add(_items.get(toSlot(i))); - Dumpable.dumpObjects(out, indent, this, new DumpableCollection("items", slots)); + { + E slot = _items.get(toSlot(i)); + if (slot != null) + slots.add(Dumpable.named(Integer.toString(i), slot)); + } + Dumpable.dumpObjects(out, indent, this, slots.toArray()); } @Override diff --git a/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/thread/QueuedThreadPoolTest.java b/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/thread/QueuedThreadPoolTest.java index f790d547223..0b8583bb61b 100644 --- a/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/thread/QueuedThreadPoolTest.java +++ b/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/thread/QueuedThreadPoolTest.java @@ -833,8 +833,7 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest assertThat(count(dump, " - STARTED"), is(3)); assertThat(dump, containsString(",3<=3<=4,i=1,r=2,")); assertThat(dump, containsString("[ReservedThreadExecutor@")); - assertThat(count(dump, "> ReservedThread@"), is(1)); - assertThat(count(dump, "> null"), is(1)); + assertThat(count(dump, "+> 0: ReservedThread@"), is(1)); assertThat(count(dump, "QueuedThreadPoolTest.lambda$testDump$"), is(0)); pool.setDetailedDump(true); diff --git a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/DebugListener.java b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/DebugListener.java index 637a0f51346..6b9b169d66a 100644 --- a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/DebugListener.java +++ b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/DebugListener.java @@ -125,14 +125,12 @@ public class DebugListener extends AbstractLifeCycle implements ServletContextLi if (_out == null) { handler.dumpStdErr(); - System.err.println(Dumpable.KEY); } else { try { - handler.dump(_out); - _out.println(Dumpable.KEY); + Dumpable.dump(handler, _out); } catch (Exception e) { diff --git a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/DebugListener.java b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/DebugListener.java index 8dcbe8ff5c1..29c5e76d90d 100644 --- a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/DebugListener.java +++ b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/DebugListener.java @@ -121,14 +121,12 @@ public class DebugListener extends AbstractLifeCycle implements ServletContextLi if (_out == null) { handler.dumpStdErr(); - System.err.println(Dumpable.KEY); } else { try { - handler.dump(_out); - _out.println(Dumpable.KEY); + Dumpable.dump(handler, _out); } catch (Exception e) {