Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #9177 dump JVM info #11845

Merged
merged 7 commits into from
May 28, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
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;
Expand Down Expand Up @@ -137,6 +138,7 @@ public Server(@Name("address") InetSocketAddress addr)
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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@
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;
Expand Down Expand Up @@ -143,6 +145,27 @@ public boolean handle(Request request, Response response, Callback callback)
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("1: ReservedThread@"));
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()
{
List<Arguments> arguments = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -745,10 +745,10 @@ public void dumpStdErr()
{
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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,21 @@
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;
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;
Expand All @@ -31,7 +37,7 @@
@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()
Expand All @@ -50,24 +56,56 @@ default String dump()
void dump(Appendable out, String indent) throws IOException;

/**
* Utility method to implement {@link #dump()} by calling {@link #dump(Appendable, String)}
* Utility method to call 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();
}

DateTimeFormatter UTC_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").withZone(ZoneOffset.UTC);
DateTimeFormatter LOCAL_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS Z").withZone(ZoneId.of(System.getProperty("user.timezone")));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are now public constants that are just an implementation detail.

I would just allocate them on-the-fly for the formatting, if necessary.

Actually, I think there are predefined constants in DateTimeFormatter, but I would not even do that.
Just calling Instant.toString() formats it in UTC following the standard.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about this output:

key: +- bean, += managed, +~ unmanaged, +? auto, +: iterable, +] array, +@ map, +> undefined
JVM: Oracle Corporation OpenJDK 64-Bit Server VM 22+36-2370; OS: Linux amd64 6.5.0-35-generic; Jetty: 12.0.10-SNAPSHOT; CPUs: 20; mem(free/total/max): 4,022/4,096/6,144 MiB
UTC: 2024-05-27T21:59:02.015802402Z; Europe/Rome: 2024-05-27T23:59:02.015802402+02:00


/**
* 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();
out.append("JVM: %s %s; OS: %s %s %s; Jetty: %s; CPUs: %d; mem(free/total/max): %,d/%,d/%,d MB\nUTC: %s; %s: %s".formatted(
System.getProperty("java.runtime.name"),
System.getProperty("java.runtime.version"),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The vendor is missing, and it's important (say IBM).
I would use: java.vendor java.vm.name java.vm.version.

For example, IBM's does not have any java.runtime.* properties (at least according to their docs).

Use MiB instead of MB (powers of 1024 instead of 1000).

I would just print the UTC time, not the local one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like having UTC and local time... it is OK for you who only lives an hour or 2 off UTC, but for those of us that struggle with bigger differences, it can be nice to have them both listed :)
Having said that, my laptop still thinks it is in Italy??!?!?

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),
UTC_FORMATTER.format(now),
System.getProperty("user.timezone"),
LOCAL_FORMATTER.format(now)));
}
catch (IOException e)
{
b.append(e.toString());
throw new UncheckedIOException(e);
}
b.append(KEY);
return b.toString();
}

/**
Expand Down Expand Up @@ -301,7 +339,7 @@ public void dump(Appendable out, String indent) throws IOException
* 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)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
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;

Expand Down Expand Up @@ -236,8 +235,12 @@ public void dump(Appendable out, String indent) throws IOException
int capacity = capacity();
List<Object> 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -833,8 +833,7 @@ public void testDump() throws Exception
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,14 +125,12 @@ public void contextInitialized(ServletContextEvent sce)
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)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,14 +121,12 @@ public void contextInitialized(ServletContextEvent sce)
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)
{
Expand Down
Loading