Fix #9177 dump JVM info (#11845)

Fix #9177 dump info from Runtime, Jetty.VERSION and System.getProperties
This commit is contained in:
Greg Wilkins 2024-05-28 10:14:48 +10:00 committed by GitHub
parent 7e36f3c6e9
commit 042582813f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 119 additions and 21 deletions

View File

@ -54,6 +54,7 @@ import org.eclipse.jetty.util.component.AttributeContainerMap;
import org.eclipse.jetty.util.component.ClassLoaderDump; import org.eclipse.jetty.util.component.ClassLoaderDump;
import org.eclipse.jetty.util.component.DumpableAttributes; import org.eclipse.jetty.util.component.DumpableAttributes;
import org.eclipse.jetty.util.component.DumpableCollection; 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.Environment;
import org.eclipse.jetty.util.component.Graceful; import org.eclipse.jetty.util.component.Graceful;
import org.eclipse.jetty.util.component.LifeCycle; 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) public Server(@Name("threadPool") ThreadPool pool)
{ {
this(pool, null, null); 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) public Server(@Name("threadPool") ThreadPool threadPool, @Name("scheduler") Scheduler scheduler, @Name("bufferPool") ByteBufferPool bufferPool)

View File

@ -32,8 +32,10 @@ import org.eclipse.jetty.server.internal.HttpChannelState;
import org.eclipse.jetty.server.internal.HttpConnection; import org.eclipse.jetty.server.internal.HttpConnection;
import org.eclipse.jetty.util.Blocker; import org.eclipse.jetty.util.Blocker;
import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Jetty;
import org.eclipse.jetty.util.component.LifeCycle; import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.thread.Invocable; 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.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -143,6 +145,26 @@ public class ServerTest
assertThat(response.getContent(), is("Hello")); 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<Arguments> completionScenarios() public static Stream<Arguments> completionScenarios()
{ {
List<Arguments> arguments = new ArrayList<>(); List<Arguments> arguments = new ArrayList<>();

View File

@ -745,10 +745,10 @@ public class ContainerLifeCycle extends AbstractLifeCycle implements Container,
{ {
try try
{ {
dump(System.err, ""); Dumpable.dump(this, System.err);
System.err.println(Dumpable.KEY); System.err.println();
} }
catch (IOException e) catch (Throwable e)
{ {
LOG.warn("Unable to dump", e); LOG.warn("Unable to dump", e);
} }

View File

@ -14,8 +14,13 @@
package org.eclipse.jetty.util.component; package org.eclipse.jetty.util.component;
import java.io.IOException; import java.io.IOException;
import java.io.UncheckedIOException;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.nio.file.Path; 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.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Iterator; import java.util.Iterator;
@ -23,6 +28,7 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.eclipse.jetty.util.Jetty;
import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.annotation.ManagedObject;
@ -31,7 +37,7 @@ import org.eclipse.jetty.util.annotation.ManagedOperation;
@ManagedObject("Dumpable Object") @ManagedObject("Dumpable Object")
public interface Dumpable 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") @ManagedOperation(value = "Dump the nested Object state as a String", impact = "INFO")
default String dump() default String dump()
@ -50,24 +56,55 @@ public interface Dumpable
void dump(Appendable out, String indent) throws IOException; 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 * @param dumpable The dumpable to dump
* @return The dumped string * @return The dumped string
* @see #dump(Appendable, String)
*/ */
static String dump(Dumpable dumpable) static String dump(Dumpable dumpable)
{ {
StringBuilder b = new StringBuilder(); 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 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) 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 * interface to allow it to refine which of its beans can be
* dumped. * dumped.
*/ */
public interface DumpableContainer extends Dumpable interface DumpableContainer extends Dumpable
{ {
default boolean isDumpable(Object o) default boolean isDumpable(Object o)
{ {

View File

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

View File

@ -25,7 +25,6 @@ import org.eclipse.jetty.util.MemoryUtils;
import org.eclipse.jetty.util.ProcessorUtils; import org.eclipse.jetty.util.ProcessorUtils;
import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.component.Dumpable; import org.eclipse.jetty.util.component.Dumpable;
import org.eclipse.jetty.util.component.DumpableCollection;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -236,8 +235,12 @@ public class ThreadIdPool<E> implements Dumpable
int capacity = capacity(); int capacity = capacity();
List<Object> slots = new ArrayList<>(capacity); List<Object> slots = new ArrayList<>(capacity);
for (int i = 0; i < capacity; i++) 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 @Override

View File

@ -833,8 +833,7 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest
assertThat(count(dump, " - STARTED"), is(3)); assertThat(count(dump, " - STARTED"), is(3));
assertThat(dump, containsString(",3<=3<=4,i=1,r=2,")); assertThat(dump, containsString(",3<=3<=4,i=1,r=2,"));
assertThat(dump, containsString("[ReservedThreadExecutor@")); assertThat(dump, containsString("[ReservedThreadExecutor@"));
assertThat(count(dump, "> ReservedThread@"), is(1)); assertThat(count(dump, "+> 0: ReservedThread@"), is(1));
assertThat(count(dump, "> null"), is(1));
assertThat(count(dump, "QueuedThreadPoolTest.lambda$testDump$"), is(0)); assertThat(count(dump, "QueuedThreadPoolTest.lambda$testDump$"), is(0));
pool.setDetailedDump(true); pool.setDetailedDump(true);

View File

@ -125,14 +125,12 @@ public class DebugListener extends AbstractLifeCycle implements ServletContextLi
if (_out == null) if (_out == null)
{ {
handler.dumpStdErr(); handler.dumpStdErr();
System.err.println(Dumpable.KEY);
} }
else else
{ {
try try
{ {
handler.dump(_out); Dumpable.dump(handler, _out);
_out.println(Dumpable.KEY);
} }
catch (Exception e) catch (Exception e)
{ {

View File

@ -121,14 +121,12 @@ public class DebugListener extends AbstractLifeCycle implements ServletContextLi
if (_out == null) if (_out == null)
{ {
handler.dumpStdErr(); handler.dumpStdErr();
System.err.println(Dumpable.KEY);
} }
else else
{ {
try try
{ {
handler.dump(_out); Dumpable.dump(handler, _out);
_out.println(Dumpable.KEY);
} }
catch (Exception e) catch (Exception e)
{ {