diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/UserMetrics.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/UserMetrics.java index 681b1f416c78..69095a898dcb 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/UserMetrics.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/UserMetrics.java @@ -38,6 +38,14 @@ interface ClientMetrics { long getWriteRequestsCount(); long getFilteredReadRequestsCount(); + + String getHostAddress(); + + String getUserName(); + + String getClientVersion(); + + String getServiceName(); } /** Returns the user name */ diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/UserMetricsBuilder.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/UserMetricsBuilder.java index 4a66283146d9..cf5303140145 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/UserMetricsBuilder.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/UserMetricsBuilder.java @@ -36,7 +36,9 @@ public static UserMetrics toUserMetrics(ClusterStatusProtos.UserLoad userLoad) { userLoad.getClientMetricsList().stream() .map(clientMetrics -> new ClientMetricsImpl(clientMetrics.getHostName(), clientMetrics.getReadRequestsCount(), clientMetrics.getWriteRequestsCount(), - clientMetrics.getFilteredRequestsCount())) + clientMetrics.getFilteredRequestsCount(), clientMetrics.getHostAddress(), + clientMetrics.getUserName(), clientMetrics.getServiceName(), + clientMetrics.getClientVersion())) .forEach(builder::addClientMetris); return builder.build(); } @@ -49,7 +51,10 @@ public static ClusterStatusProtos.UserLoad toUserMetrics(UserMetrics userMetrics .setHostName(clientMetrics.getHostName()) .setWriteRequestsCount(clientMetrics.getWriteRequestsCount()) .setReadRequestsCount(clientMetrics.getReadRequestsCount()) - .setFilteredRequestsCount(clientMetrics.getFilteredReadRequestsCount()).build()) + .setFilteredRequestsCount(clientMetrics.getFilteredReadRequestsCount()) + .setHostAddress(clientMetrics.getHostAddress()).setUserName(clientMetrics.getUserName()) + .setServiceName(clientMetrics.getServiceName()) + .setClientVersion(clientMetrics.getClientVersion()).build()) .forEach(builder::addClientMetrics); return builder.build(); } @@ -79,13 +84,27 @@ public static class ClientMetricsImpl implements UserMetrics.ClientMetrics { private final String hostName; private final long readRequestCount; private final long writeRequestCount; + private final String hostAddress; + private final String userName; + private final String serviceName; + private final String clientVersion; public ClientMetricsImpl(String hostName, long readRequest, long writeRequest, long filteredReadRequestsCount) { + this(hostName, readRequest, writeRequest, filteredReadRequestsCount, null, null, null, null); + } + + public ClientMetricsImpl(String hostName, long readRequest, long writeRequest, + long filteredReadRequestsCount, String hostAddress, String userName, String serviceName, + String clientVersion) { this.hostName = hostName; this.readRequestCount = readRequest; this.writeRequestCount = writeRequest; this.filteredReadRequestsCount = filteredReadRequestsCount; + this.hostAddress = hostAddress != null ? hostAddress : "Unknown"; + this.userName = userName != null ? userName : "Unknown"; + this.serviceName = serviceName != null ? serviceName : "Unknown"; + this.clientVersion = clientVersion != null ? clientVersion : "Unknown"; } @Override @@ -107,6 +126,26 @@ public long getWriteRequestsCount() { public long getFilteredReadRequestsCount() { return filteredReadRequestsCount; } + + @Override + public String getHostAddress() { + return hostAddress; + } + + @Override + public String getUserName() { + return userName; + } + + @Override + public String getServiceName() { + return serviceName; + } + + @Override + public String getClientVersion() { + return clientVersion; + } } private static class UserMetricsImpl implements UserMetrics { diff --git a/hbase-hadoop-compat/src/main/java/org/apache/hadoop/hbase/regionserver/MetricsUserSource.java b/hbase-hadoop-compat/src/main/java/org/apache/hadoop/hbase/regionserver/MetricsUserSource.java index feb173a94afd..da8ee8c39722 100644 --- a/hbase-hadoop-compat/src/main/java/org/apache/hadoop/hbase/regionserver/MetricsUserSource.java +++ b/hbase-hadoop-compat/src/main/java/org/apache/hadoop/hbase/regionserver/MetricsUserSource.java @@ -39,6 +39,14 @@ interface ClientMetrics { void incrementFilteredReadRequests(); long getFilteredReadRequests(); + + String getHostAddress(); + + String getUserName(); + + String getClientVersion(); + + String getServiceName(); } String getUser(); @@ -77,5 +85,6 @@ interface ClientMetrics { * @param hostName hostname of the client * @return Instance of ClientMetrics */ - ClientMetrics getOrCreateMetricsClient(String hostName); + ClientMetrics getOrCreateMetricsClient(String hostName, String hostAddress, String userName, + String clientVersion, String serviceName); } diff --git a/hbase-hadoop-compat/src/main/java/org/apache/hadoop/hbase/regionserver/MetricsUserSourceImpl.java b/hbase-hadoop-compat/src/main/java/org/apache/hadoop/hbase/regionserver/MetricsUserSourceImpl.java index 65dfaafe522c..41527c01f99b 100644 --- a/hbase-hadoop-compat/src/main/java/org/apache/hadoop/hbase/regionserver/MetricsUserSourceImpl.java +++ b/hbase-hadoop-compat/src/main/java/org/apache/hadoop/hbase/regionserver/MetricsUserSourceImpl.java @@ -69,9 +69,18 @@ static class ClientMetricsImpl implements ClientMetrics { final LongAdder readRequestsCount = new LongAdder(); final LongAdder writeRequestsCount = new LongAdder(); final LongAdder filteredRequestsCount = new LongAdder(); + private final String hostAddress; + private final String userName; + private final String clientVersion; + private final String serviceName; - public ClientMetricsImpl(String hostName) { + public ClientMetricsImpl(String hostName, String hostAddress, String userName, + String clientVersion, String serviceName) { this.hostName = hostName; + this.hostAddress = hostAddress != null ? hostAddress : "Unknown"; + this.userName = userName != null ? userName : "Unknown"; + this.clientVersion = clientVersion != null ? clientVersion : "Unknown"; + this.serviceName = serviceName != null ? serviceName : "Unknown"; } @Override @@ -109,6 +118,26 @@ public void incrementFilteredReadRequests() { public long getFilteredReadRequests() { return filteredRequestsCount.sum(); } + + @Override + public String getHostAddress() { + return hostAddress; + } + + @Override + public String getUserName() { + return userName; + } + + @Override + public String getClientVersion() { + return clientVersion; + } + + @Override + public String getServiceName() { + return serviceName; + } } public MetricsUserSourceImpl(String user, MetricsUserAggregateSourceImpl agg) { @@ -278,12 +307,13 @@ public Map getClientMetrics() { } @Override - public ClientMetrics getOrCreateMetricsClient(String client) { + public ClientMetrics getOrCreateMetricsClient(String client, String hostAddress, String userName, + String clientVersion, String serviceName) { ClientMetrics source = clientMetricsMap.get(client); if (source != null) { return source; } - source = new ClientMetricsImpl(client); + source = new ClientMetricsImpl(client, hostAddress, userName, clientVersion, serviceName); ClientMetrics prev = clientMetricsMap.putIfAbsent(client, source); if (prev != null) { return prev; diff --git a/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/field/Field.java b/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/field/Field.java index ab776cf03368..cf36da8dfc9c 100644 --- a/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/field/Field.java +++ b/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/field/Field.java @@ -58,7 +58,11 @@ public enum Field { MAX_HEAP_SIZE("MHEAP", "Max Heap Size", false, false, FieldValueType.SIZE), CLIENT_COUNT("#CLIENT", "Client Count", false, false, FieldValueType.INTEGER), USER_COUNT("#USER", "User Count", false, false, FieldValueType.INTEGER), - CLIENT("CLIENT", "Client Hostname", true, true, FieldValueType.STRING); + CLIENT("CLIENT", "Client Hostname", true, true, FieldValueType.STRING), + HOST_ADDRESS("HADDR", "Client Host Address", true, true, FieldValueType.STRING), + CLIENT_VERSION("CVER", "Client Version", true, true, FieldValueType.STRING), + USER_NAME("USER", "User Name", true, true, FieldValueType.STRING), + SERVICE_NAME("SVC", "Service Name", true, true, FieldValueType.STRING); private final String header; private final String description; diff --git a/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/mode/ClientModeStrategy.java b/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/mode/ClientModeStrategy.java index 8327b1425cfb..075cede17f9b 100644 --- a/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/mode/ClientModeStrategy.java +++ b/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/mode/ClientModeStrategy.java @@ -48,7 +48,9 @@ public final class ClientModeStrategy implements ModeStrategy { new FieldInfo(Field.REQUEST_COUNT_PER_SECOND, 10, true), new FieldInfo(Field.READ_REQUEST_COUNT_PER_SECOND, 10, true), new FieldInfo(Field.WRITE_REQUEST_COUNT_PER_SECOND, 10, true), - new FieldInfo(Field.FILTERED_READ_REQUEST_COUNT_PER_SECOND, 10, true)); + new FieldInfo(Field.FILTERED_READ_REQUEST_COUNT_PER_SECOND, 10, true), + new FieldInfo(Field.HOST_ADDRESS, 0, true), new FieldInfo(Field.USER_NAME, 0, true), + new FieldInfo(Field.CLIENT_VERSION, 0, true), new FieldInfo(Field.SERVICE_NAME, 0, true)); private final Map requestCountPerSecondMap = new HashMap<>(); ClientModeStrategy() { @@ -145,6 +147,10 @@ Record createRecord(String user, UserMetrics.ClientMetrics clientMetrics, requestCountPerSecond.getWriteRequestCountPerSecond()); builder.put(Field.FILTERED_READ_REQUEST_COUNT_PER_SECOND, requestCountPerSecond.getFilteredReadRequestCountPerSecond()); + builder.put(Field.HOST_ADDRESS, clientMetrics.getHostAddress()); + builder.put(Field.USER_NAME, clientMetrics.getUserName()); + builder.put(Field.CLIENT_VERSION, clientMetrics.getClientVersion()); + builder.put(Field.SERVICE_NAME, clientMetrics.getServiceName()); builder.put(Field.USER, user); return builder.build(); } diff --git a/hbase-protocol-shaded/src/main/protobuf/server/ClusterStatus.proto b/hbase-protocol-shaded/src/main/protobuf/server/ClusterStatus.proto index 58fd3c8d2a5b..2177fdddd72c 100644 --- a/hbase-protocol-shaded/src/main/protobuf/server/ClusterStatus.proto +++ b/hbase-protocol-shaded/src/main/protobuf/server/ClusterStatus.proto @@ -206,6 +206,11 @@ message ClientMetrics { /** the current total filtered requests made from a client */ optional uint64 filtered_requests_count = 4; + + optional string host_address = 5; + optional string user_name = 6; + optional string client_version = 7; + optional string service_name = 8; } /* Server-level protobufs */ diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/NettyServerRpcConnection.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/NettyServerRpcConnection.java index f63b8d2730f7..31294bc57784 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/NettyServerRpcConnection.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/NettyServerRpcConnection.java @@ -66,6 +66,11 @@ class NettyServerRpcConnection extends ServerRpcConnection { } else { this.hostAddress = inetSocketAddress.getAddress().getHostAddress(); } + + InetSocketAddress localSocketAddress = ((InetSocketAddress) channel.localAddress()); + this.localHostAddress = (localSocketAddress != null && localSocketAddress.getAddress() != null) + ? localSocketAddress.getAddress().getHostAddress() + : "*Unknown*"; this.remotePort = inetSocketAddress.getPort(); } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/RpcServer.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/RpcServer.java index 3e702541acb3..a6538259c83d 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/RpcServer.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/RpcServer.java @@ -834,6 +834,24 @@ public static Optional getRemoteAddress() { return getCurrentCall().map(RpcCall::getRemoteAddress); } + /** + * Returns the ServerRpcConnection for the current RPC request or not present if no connection is + * available. This allows access to connection-level information such as the client's IP address, + * authentication details, codec configuration, etc. + *

+ * This method should only be called from within an RPC handler thread context. + * @return the current ServerRpcConnection, or Optional.empty() if called outside of an RPC + * context + */ + public static Optional getCurrentServerRpcConnection() { + Optional call = getCurrentCall(); + if (call.isPresent() && call.get() instanceof ServerCall) { + ServerCall serverCall = (ServerCall) call.get(); + return Optional.ofNullable(serverCall.getConnection()); + } + return Optional.empty(); + } + /** * @param serviceName Some arbitrary string that represents a 'service'. * @param services Available service instances diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/ServerCall.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/ServerCall.java index ed7e67edfaf0..5b3eb7295c85 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/ServerCall.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/ServerCall.java @@ -256,6 +256,14 @@ public int getPriority() { return this.header.getPriority(); } + /** + * Get the ServerRpcConnection associated with this call. + * @return the connection object + */ + public T getConnection() { + return this.connection; + } + /* * Short string representation without param info because param itself could be huge depends on * the payload of a command diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/ServerRpcConnection.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/ServerRpcConnection.java index 6a7da430efdb..8236e0553d39 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/ServerRpcConnection.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/ServerRpcConnection.java @@ -96,7 +96,7 @@ @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "VO_VOLATILE_INCREMENT", justification = "False positive according to http://sourceforge.net/p/findbugs/bugs/1032/") @InterfaceAudience.Private -abstract class ServerRpcConnection implements Closeable { +public abstract class ServerRpcConnection implements Closeable { private static final TextMapGetter getter = new RPCTInfoGetter(); @@ -114,6 +114,9 @@ abstract class ServerRpcConnection implements Closeable { protected ConnectionHeader connectionHeader; protected Map connectionAttributes; + // The local address of this server-side connection (the RegionServer's own IP). + protected String localHostAddress; + /** * Codec the client asked use. */ @@ -160,6 +163,10 @@ public int getRemotePort() { return remotePort; } + public String getLocalHostAddress() { + return localHostAddress; + } + public VersionInfo getVersionInfo() { if (connectionHeader != null && connectionHeader.hasVersionInfo()) { return connectionHeader.getVersionInfo(); @@ -487,6 +494,10 @@ private void processConnectionHeader(ByteBuff buf) throws IOException { this.hostAddress, this.remotePort, version, this.useSasl, this.ugi, serviceName); } + public ConnectionHeader getConnectionHeader() { + return connectionHeader; + } + /** * Send the response for connection header */ diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/SimpleServerCall.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/SimpleServerCall.java index aa6e2b7b4aca..0a2fb5809342 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/SimpleServerCall.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/SimpleServerCall.java @@ -70,7 +70,8 @@ public synchronized void sendResponseIfReady() throws IOException { this.responder.doRespond(getConnection(), this); } - SimpleServerRpcConnection getConnection() { + @Override + public SimpleServerRpcConnection getConnection() { return this.connection; } } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java index eea82ca511eb..fa7d3e6fee56 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java @@ -1657,7 +1657,10 @@ private UserLoad createUserLoad(String user, MetricsUserSource userSource) { .setHostName(clientMetrics.getHostName()) .setWriteRequestsCount(clientMetrics.getWriteRequestsCount()) .setFilteredRequestsCount(clientMetrics.getFilteredReadRequests()) - .setReadRequestsCount(clientMetrics.getReadRequestsCount()).build()) + .setReadRequestsCount(clientMetrics.getReadRequestsCount()) + .setHostAddress(clientMetrics.getHostAddress()).setUserName(clientMetrics.getUserName()) + .setServiceName(clientMetrics.getServiceName()) + .setClientVersion(clientMetrics.getClientVersion()).build()) .forEach(userLoadBldr::addClientMetrics); return userLoadBldr.build(); } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/MetricsUserAggregateImpl.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/MetricsUserAggregateImpl.java index 4856105f4e8f..aadb19b5ee2b 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/MetricsUserAggregateImpl.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/MetricsUserAggregateImpl.java @@ -23,10 +23,15 @@ import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.CompatibilitySingletonFactory; import org.apache.hadoop.hbase.ipc.RpcServer; +import org.apache.hadoop.hbase.ipc.ServerRpcConnection; import org.apache.hadoop.hbase.security.User; import org.apache.hadoop.hbase.security.UserProvider; import org.apache.hadoop.hbase.util.LossyCounting; import org.apache.yetus.audience.InterfaceAudience; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.apache.hadoop.hbase.shaded.protobuf.generated.RPCProtos; @InterfaceAudience.Private public class MetricsUserAggregateImpl implements MetricsUserAggregate { @@ -83,23 +88,29 @@ private String getClient() { } private void incrementClientReadMetrics(MetricsUserSource userSource) { + ClientConnectionContext ctx = getClientConnectionContext(); String client = getClient(); if (client != null && userSource != null) { - userSource.getOrCreateMetricsClient(client).incrementReadRequest(); + userSource.getOrCreateMetricsClient(client, ctx.hostAddress, ctx.userName, ctx.clientVersion, + ctx.serviceName).incrementReadRequest(); } } private void incrementFilteredReadRequests(MetricsUserSource userSource) { + ClientConnectionContext ctx = getClientConnectionContext(); String client = getClient(); if (client != null && userSource != null) { - userSource.getOrCreateMetricsClient(client).incrementFilteredReadRequests(); + userSource.getOrCreateMetricsClient(client, ctx.hostAddress, ctx.userName, ctx.clientVersion, + ctx.serviceName).incrementFilteredReadRequests(); } } private void incrementClientWriteMetrics(MetricsUserSource userSource) { + ClientConnectionContext ctx = getClientConnectionContext(); String client = getClient(); if (client != null && userSource != null) { - userSource.getOrCreateMetricsClient(client).incrementWriteRequest(); + userSource.getOrCreateMetricsClient(client, ctx.hostAddress, ctx.userName, ctx.clientVersion, + ctx.serviceName).incrementWriteRequest(); } } @@ -193,4 +204,51 @@ private MetricsUserSource getOrCreateMetricsUser(String user) { userMetricLossyCounting.add(userSource); return userSource; } + + private ClientConnectionContext getClientConnectionContext() { + String hostAddress = null; + String userName = "Unknown"; + String clientVersion = "Unknown"; + String serviceName = "Unknown"; + + Optional rpcConnectionOptional = RpcServer.getCurrentServerRpcConnection(); + if (rpcConnectionOptional.isPresent()) { + ServerRpcConnection rpcConnection = rpcConnectionOptional.get(); + hostAddress = rpcConnection.getLocalHostAddress(); + RPCProtos.ConnectionHeader connectionHeader = rpcConnection.getConnectionHeader(); + if (connectionHeader.hasUserInfo()) { + RPCProtos.UserInformation userInfoProto = connectionHeader.getUserInfo(); + if (userInfoProto.hasEffectiveUser()) { + userName = userInfoProto.getEffectiveUser(); + } + } + if (connectionHeader.hasVersionInfo()) { + clientVersion = connectionHeader.getVersionInfo().getVersion(); + } + if (connectionHeader.hasServiceName()) { + serviceName = connectionHeader.getServiceName(); + } + } + return new ClientConnectionContext(hostAddress, userName, clientVersion, serviceName); + } + + private static class ClientConnectionContext { + final String hostAddress; + final String userName; + final String clientVersion; + final String serviceName; + + ClientConnectionContext(String hostAddress, String userName, String clientVersion, + String serviceName) { + this.hostAddress = hostAddress; + this.userName = userName; + this.clientVersion = clientVersion; + this.serviceName = serviceName; + } + + public String toString() { + return "ClientConnectionContext{hostAddress=" + hostAddress + ", userName=" + userName + + ", clientVersion=" + clientVersion + ", serviceName=" + serviceName + "}"; + } + } } diff --git a/hbase-server/src/main/resources/hbase-webapps/master/regionServerList.jsp b/hbase-server/src/main/resources/hbase-webapps/master/regionServerList.jsp index 9fc0adda00cc..17525e11979d 100644 --- a/hbase-server/src/main/resources/hbase-webapps/master/regionServerList.jsp +++ b/hbase-server/src/main/resources/hbase-webapps/master/regionServerList.jsp @@ -63,6 +63,9 @@

+
<% request.setAttribute("serverNames", serverNames); %> @@ -84,6 +87,9 @@
+
+ +
diff --git a/hbase-server/src/main/resources/hbase-webapps/master/regionServerListClientConnectionsStats.jsp b/hbase-server/src/main/resources/hbase-webapps/master/regionServerListClientConnectionsStats.jsp new file mode 100644 index 000000000000..45d2766827f2 --- /dev/null +++ b/hbase-server/src/main/resources/hbase-webapps/master/regionServerListClientConnectionsStats.jsp @@ -0,0 +1,54 @@ +<%@ page contentType="text/html;charset=UTF-8" + import="org.apache.hadoop.hbase.ServerName" + import="org.apache.hadoop.hbase.master.HMaster" + import="org.apache.hadoop.hbase.ServerMetrics" + import="org.apache.hadoop.hbase.master.ServerManager" + import="org.apache.hadoop.hbase.UserMetrics" + import="java.util.Map" %> + +<% + HMaster master = (HMaster) getServletContext().getAttribute(HMaster.MASTER); + ServerName[] serverNames = (ServerName[]) request.getAttribute("serverNames"); + ServerManager serverManager = master.getServerManager(); +%> + + + + + + + + + + + + + <% + for (ServerName serverName: serverNames) { + ServerMetrics serverMetrics = serverManager.getLoad(serverName); + if(serverMetrics != null) { + + Map userMetricsMap = serverMetrics.getUserMetrics(); + for(Map.Entry entry : userMetricsMap.entrySet()) { + UserMetrics userMetrics = entry.getValue(); + Map clientMetricsMap = userMetrics.getClientMetrics(); + + for(Map.Entry clientEntry : clientMetricsMap.entrySet()) { + UserMetrics.ClientMetrics clientConnection = clientEntry.getValue(); + + %> + + + + + + cc + + <% + } + } + } + } + %> + +
ClientIPUserNameClientVersionServiceNameServerInfo
<%= clientConnection.getHostAddress() %><%= clientConnection.getUserName() %><%= clientConnection.getClientVersion() %><%= clientConnection.getServiceName() %><%= serverName.getServerName() %>