diff --git a/agent/src/main/resources/profiles/local/pinpoint.config b/agent/src/main/resources/profiles/local/pinpoint.config
index 09d71c480f73..cfa05f81bae4 100644
--- a/agent/src/main/resources/profiles/local/pinpoint.config
+++ b/agent/src/main/resources/profiles/local/pinpoint.config
@@ -1379,3 +1379,14 @@ profiler.kotlin.coroutines.record.threadName=false
#This is important information to check whether the developer's intention and the behavior of the coroutine match.
#Recommend that you use it in the development environment and not in the production environment.
profiler.kotlin.coroutines.record.cancel=false
+
+###########################################################
+# Network Metric #
+###########################################################
+profiler.network.metric.enable=false
+profiler.network.metric.enable.protocolstats=false
+profiler.network.metric.collector.ip=${profiler.collector.ip}
+profiler.network.metric.collector.port=15200
+# if you want to specify host group name (default: application name)
+#profiler.network.metric.hostgroupname=
+profiler.network.metric.collect.interval=10000
\ No newline at end of file
diff --git a/agent/src/main/resources/profiles/release/pinpoint.config b/agent/src/main/resources/profiles/release/pinpoint.config
index ee778b945d70..50e60b57d586 100644
--- a/agent/src/main/resources/profiles/release/pinpoint.config
+++ b/agent/src/main/resources/profiles/release/pinpoint.config
@@ -1402,3 +1402,14 @@ profiler.kotlin.coroutines.record.threadName=false
#This is important information to check whether the developer's intention and the behavior of the coroutine match.
#Recommend that you use it in the development environment and not in the production environment.
profiler.kotlin.coroutines.record.cancel=false
+
+###########################################################
+# Network Metric #
+###########################################################
+profiler.network.metric.enable=false
+profiler.network.metric.enable.protocolstats=false
+profiler.network.metric.collector.ip=${profiler.collector.ip}
+profiler.network.metric.collector.port=15200
+# if you want to specify host group name (default: application name)
+#profiler.network.metric.hostgroupname=
+profiler.network.metric.collect.interval=10000
\ No newline at end of file
diff --git a/metric-module/metric/src/main/resources/pinot-web/telegraf-metric.yml b/metric-module/metric/src/main/resources/pinot-web/telegraf-metric.yml
index eedf01b69b08..1a97c2a391b6 100644
--- a/metric-module/metric/src/main/resources/pinot-web/telegraf-metric.yml
+++ b/metric-module/metric/src/main/resources/pinot-web/telegraf-metric.yml
@@ -115,4 +115,76 @@ mappings:
- name: "active"
matchingRule: ANY_ONE
- name: "waiting"
- matchingRule: ANY_ONE
\ No newline at end of file
+ matchingRule: ANY_ONE
+
+ - definitionId: "networkInterfaceInfo"
+ name: "network_interface"
+ title: "network interface information"
+ grouping: "TAG"
+ unit: "count"
+ fields:
+ - name: "rx_packets"
+ matchingRule: PASSED_ALL
+ - name: "rx_errors"
+ matchingRule: PASSED_ALL
+ - name: "rx_drops"
+ matchingRule: PASSED_ALL
+ - name: "tx_packets"
+ matchingRule: PASSED_ALL
+ - name: "tx_errors"
+ matchingRule: PASSED_ALL
+ - name: "tx_collisions"
+ matchingRule: PASSED_ALL
+
+ - definitionId: "networkInterfaceBytes"
+ name: "network_interface"
+ title: "network rx & tx"
+ grouping: "TAG"
+ unit: "byte"
+ fields:
+ - name: "rx_bytes"
+ matchingRule: PASSED_ALL
+ - name: "tx_bytes"
+ matchingRule: PASSED_ALL
+
+ - definitionId: "tcpStats"
+ name: "tcp_stats"
+ title: "TCP"
+ grouping: "TAG"
+ unit: "count"
+ fields:
+ - name: "conn_established"
+ matchingRule: PASSED_ALL
+ - name: "conn_active"
+ matchingRule: PASSED_ALL
+ - name: "conn_passive"
+ matchingRule: PASSED_ALL
+ - name: "conn_failure"
+ matchingRule: PASSED_ALL
+ - name: "conn_reset"
+ matchingRule: PASSED_ALL
+ - name: "seg_sent"
+ matchingRule: PASSED_ALL
+ - name: "seg_received"
+ matchingRule: PASSED_ALL
+ - name: "seg_retransmitted"
+ matchingRule: PASSED_ALL
+ - name: "in_errors"
+ matchingRule: PASSED_ALL
+ - name: "out_resets"
+ matchingRule: PASSED_ALL
+
+ - definitionId: "udpStats"
+ name: "udp_stats"
+ title: "UDP"
+ grouping: "TAG"
+ unit: "count"
+ fields:
+ - name: "txd"
+ matchingRule: PASSED_ALL
+ - name: "rxd"
+ matchingRule: PASSED_ALL
+ - name: "noport"
+ matchingRule: PASSED_ALL
+ - name: "rx_error"
+ matchingRule: PASSED_ALL
\ No newline at end of file
diff --git a/profiler/pom.xml b/profiler/pom.xml
index 0e1a5447eed4..1fb7ab06eb9c 100644
--- a/profiler/pom.xml
+++ b/profiler/pom.xml
@@ -84,6 +84,11 @@
com.github.ben-manes.caffeine
caffeine
+
+ com.github.oshi
+ oshi-core
+ 6.4.5
+
diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/context/module/ApplicationContextModule.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/context/module/ApplicationContextModule.java
index 69fca2b1d615..8eac9b61ad58 100644
--- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/context/module/ApplicationContextModule.java
+++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/context/module/ApplicationContextModule.java
@@ -112,10 +112,7 @@
import com.navercorp.pinpoint.profiler.metadata.ApiMetaDataService;
import com.navercorp.pinpoint.profiler.metadata.SqlMetaDataService;
import com.navercorp.pinpoint.profiler.metadata.StringMetaDataService;
-import com.navercorp.pinpoint.profiler.monitor.AgentStatMonitor;
-import com.navercorp.pinpoint.profiler.monitor.DeadlockMonitor;
-import com.navercorp.pinpoint.profiler.monitor.DeadlockThreadRegistry;
-import com.navercorp.pinpoint.profiler.monitor.DefaultAgentStatMonitor;
+import com.navercorp.pinpoint.profiler.monitor.*;
import com.navercorp.pinpoint.profiler.monitor.metric.response.ResponseTimeCollector;
import com.navercorp.pinpoint.profiler.monitor.metric.response.ReuseResponseTimeCollector;
import com.navercorp.pinpoint.profiler.objectfactory.ObjectBinderFactory;
@@ -212,6 +209,7 @@ protected void configure() {
bind(DeadlockMonitor.class).toProvider(DeadlockMonitorProvider.class).in(Scopes.SINGLETON);
bind(AgentInfoSender.class).toProvider(AgentInfoSenderProvider.class).in(Scopes.SINGLETON);
bind(AgentStatMonitor.class).to(DefaultAgentStatMonitor.class).in(Scopes.SINGLETON);
+ bind(NetworkStatMonitor.class).to(DefaultNetworkStatMonitor.class).in(Scopes.SINGLETON);
}
private void bindTraceComponent() {
diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/context/module/DefaultApplicationContext.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/context/module/DefaultApplicationContext.java
index bac2c6e87e65..5aa10607764e 100644
--- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/context/module/DefaultApplicationContext.java
+++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/context/module/DefaultApplicationContext.java
@@ -46,6 +46,7 @@
import com.navercorp.pinpoint.profiler.interceptor.registry.InterceptorRegistryBinder;
import com.navercorp.pinpoint.profiler.monitor.AgentStatMonitor;
import com.navercorp.pinpoint.profiler.monitor.DeadlockMonitor;
+import com.navercorp.pinpoint.profiler.monitor.NetworkStatMonitor;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -66,6 +67,7 @@ public class DefaultApplicationContext implements ApplicationContext {
private final DeadlockMonitor deadlockMonitor;
private final AgentInfoSender agentInfoSender;
private final AgentStatMonitor agentStatMonitor;
+ private final NetworkStatMonitor hwStatMonitor;
private final TraceContext traceContext;
@@ -131,6 +133,7 @@ public DefaultApplicationContext(AgentOption agentOption, ModuleFactory moduleFa
this.deadlockMonitor = injector.getInstance(DeadlockMonitor.class);
this.agentInfoSender = injector.getInstance(AgentInfoSender.class);
this.agentStatMonitor = injector.getInstance(AgentStatMonitor.class);
+ this.hwStatMonitor = injector.getInstance(NetworkStatMonitor.class);
}
private void lambdaFactorySetup(Instrumentation instrumentation, ClassFileTransformModuleAdaptor classFileTransformer, JavaModuleFactory javaModuleFactory) {
@@ -227,6 +230,7 @@ public void start() {
this.deadlockMonitor.start();
this.agentInfoSender.start();
this.agentStatMonitor.start();
+ this.hwStatMonitor.start();
}
@Override
@@ -234,6 +238,7 @@ public void close() {
this.agentInfoSender.stop();
this.agentStatMonitor.stop();
this.deadlockMonitor.stop();
+ this.hwStatMonitor.stop();
// Need to process stop
if (rpcModuleLifeCycle != null) {
diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/context/monitor/config/DefaultMonitorConfig.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/context/monitor/config/DefaultMonitorConfig.java
index 6bcaa61a72b2..a86d19f57998 100644
--- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/context/monitor/config/DefaultMonitorConfig.java
+++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/context/monitor/config/DefaultMonitorConfig.java
@@ -22,12 +22,26 @@ public class DefaultMonitorConfig implements MonitorConfig {
public static final int DEFAULT_AGENT_STAT_COLLECTION_INTERVAL_MS = 5 * 1000;
public static final int DEFAULT_NUM_AGENT_STAT_BATCH_SEND = 6;
+ public static final int DEFAULT_NETWORK_METRIC_COLLECTION_INTERVAL_MS = 10 * 1000;
@Value("${profiler.custommetric.enable}")
private boolean customMetricEnable = false;
@Value("${profiler.custommetric.limit.size}")
private int customMetricLimitSize = 10;
+ @Value("${profiler.network.metric.enable}")
+ private boolean networkMetricEnable = false;
+ @Value("${profiler.network.metric.enable.protocolstats}")
+ private boolean protocolStatsEnable = false;
+ @Value("${profiler.network.metric.hostgroupname}")
+ private String hostGroupName;
+ @Value("${profiler.network.metric.collector.ip}")
+ private String metricCollectorIP;
+ @Value("${profiler.network.metric.collector.port:15200}")
+ private String metricCollectorPort;
+ @Value("${profiler.network.metric.collect.interval}")
+ private int networkMetricCollectIntervalMs = DEFAULT_NETWORK_METRIC_COLLECTION_INTERVAL_MS;
+
@Value("${profiler.uri.stat.enable}")
private boolean uriStatEnable = false;
@Value("${profiler.uri.stat.collect.http.method}")
@@ -70,6 +84,36 @@ public int getCustomMetricLimitSize() {
return customMetricLimitSize;
}
+ @Override
+ public boolean isNetworkMetricEnable() {
+ return networkMetricEnable;
+ }
+
+ @Override
+ public String getHostGroupName() {
+ return hostGroupName;
+ }
+
+ @Override
+ public String getMetricCollectorIP() {
+ return metricCollectorIP;
+ }
+
+ @Override
+ public String getMetricCollectorPort() {
+ return metricCollectorPort;
+ }
+
+ @Override
+ public int getNetworkMetricCollectIntervalMs() {
+ return networkMetricCollectIntervalMs;
+ }
+
+ @Override
+ public boolean isProtocolStatsEnable() {
+ return protocolStatsEnable;
+ }
+
@Override
public boolean isUriStatEnable() {
return uriStatEnable;
diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/context/monitor/config/MonitorConfig.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/context/monitor/config/MonitorConfig.java
index 6aee52a2dea3..a7d911b0fb1c 100644
--- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/context/monitor/config/MonitorConfig.java
+++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/context/monitor/config/MonitorConfig.java
@@ -27,6 +27,18 @@ public interface MonitorConfig {
int getCustomMetricLimitSize();
+ boolean isNetworkMetricEnable();
+
+ String getHostGroupName();
+
+ String getMetricCollectorIP();
+
+ String getMetricCollectorPort();
+
+ int getNetworkMetricCollectIntervalMs();
+
+ boolean isProtocolStatsEnable();
+
boolean isUriStatEnable();
boolean getUriStatCollectHttpMethod();
diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/monitor/DefaultNetworkStatMonitor.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/monitor/DefaultNetworkStatMonitor.java
new file mode 100644
index 000000000000..8ec409e32de2
--- /dev/null
+++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/monitor/DefaultNetworkStatMonitor.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2023 NAVER Corp.
+ *
+ * Licensed 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 com.navercorp.pinpoint.profiler.monitor;
+
+import com.google.inject.Inject;
+import com.navercorp.pinpoint.common.profiler.concurrent.PinpointThreadFactory;
+import com.navercorp.pinpoint.common.util.StringUtils;
+import com.navercorp.pinpoint.profiler.context.module.ApplicationName;
+import com.navercorp.pinpoint.profiler.context.monitor.config.DefaultMonitorConfig;
+import com.navercorp.pinpoint.profiler.context.monitor.config.MonitorConfig;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+public class DefaultNetworkStatMonitor implements NetworkStatMonitor {
+ private static final long MIN_COLLECTION_INTERVAL_MS = 1000 * 5;
+ private static final long MAX_COLLECTION_INTERVAL_MS = 1000 * 10;
+ private static final long DEFAULT_COLLECTION_INTERVAL_MS = DefaultMonitorConfig.DEFAULT_NETWORK_METRIC_COLLECTION_INTERVAL_MS;
+
+ private final Logger logger = LogManager.getLogger(this.getClass());
+ private final long collectionIntervalMs;
+ private final StatMonitorJob statMonitorJob;
+ private final ScheduledExecutorService executor = new ScheduledThreadPoolExecutor(1, new PinpointThreadFactory("Pinpoint-hw-stat-monitor", true));
+ private final boolean isNetworkMetricEnable;
+
+ @Inject
+ public DefaultNetworkStatMonitor(@ApplicationName String applicationName,
+ MonitorConfig monitorConfig) {
+ long collectionIntervalMs = monitorConfig.getNetworkMetricCollectIntervalMs();
+
+ if (collectionIntervalMs < MIN_COLLECTION_INTERVAL_MS) {
+ collectionIntervalMs = DEFAULT_COLLECTION_INTERVAL_MS;
+ }
+ if (collectionIntervalMs > MAX_COLLECTION_INTERVAL_MS) {
+ collectionIntervalMs = DEFAULT_COLLECTION_INTERVAL_MS;
+ }
+
+ this.collectionIntervalMs = collectionIntervalMs;
+ List runnableList = new ArrayList<>();
+ this.isNetworkMetricEnable = monitorConfig.isNetworkMetricEnable();
+
+ if (isNetworkMetricEnable && NetworkMetricCollectingJob.isSupported()) {
+ String hostGroupName = monitorConfig.getHostGroupName();
+ if (StringUtils.isEmpty(hostGroupName)) {
+ hostGroupName = applicationName;
+ }
+ Runnable oshiMetricCollectingJob = new NetworkMetricCollectingJob(hostGroupName,
+ monitorConfig.getMetricCollectorIP(), monitorConfig.getMetricCollectorPort(), monitorConfig.isProtocolStatsEnable());
+ runnableList.add(oshiMetricCollectingJob);
+ }
+ this.statMonitorJob = new StatMonitorJob(runnableList);
+
+ }
+ @Override
+ public void start() {
+ if (isNetworkMetricEnable) {
+ executor.scheduleAtFixedRate(statMonitorJob, this.collectionIntervalMs, this.collectionIntervalMs, TimeUnit.MILLISECONDS);
+ logger.info("HW stat monitor started");
+ } else {
+ logger.info("HW stat monitor disabled");
+ }
+ }
+
+ @Override
+ public void stop() {
+ if (isNetworkMetricEnable) {
+ statMonitorJob.close();
+ executor.shutdown();
+ try {
+ executor.awaitTermination(3000, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ logger.info("HW stat monitor stopped");
+ }
+ }
+}
diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/monitor/NetworkMetricCollectingJob.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/monitor/NetworkMetricCollectingJob.java
new file mode 100644
index 000000000000..6054bcaeda2d
--- /dev/null
+++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/monitor/NetworkMetricCollectingJob.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright 2023 NAVER Corp.
+ *
+ * Licensed 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 com.navercorp.pinpoint.profiler.monitor;
+
+import com.navercorp.pinpoint.common.profiler.clock.Clock;
+import com.navercorp.pinpoint.profiler.monitor.metric.NetworkMetric;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import oshi.PlatformEnum;
+import oshi.SystemInfo;
+import oshi.hardware.HardwareAbstractionLayer;
+import oshi.hardware.NetworkIF;
+import oshi.software.os.OperatingSystem;
+import oshi.software.os.InternetProtocolStats;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.ProtocolException;
+import java.net.URL;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Map;
+
+public class NetworkMetricCollectingJob implements Runnable {
+ private final Logger logger = LogManager.getLogger(this.getClass());
+ private boolean networkInterfaceSupported = true;
+ private boolean protocolStatSupported;
+ private final String collectorAddr;
+ private final Clock clock = Clock.tick(1000);
+ private String hostGroupName;
+ private String hostName;
+ private InternetProtocolStats protocolStats;
+ private NetworkInterfaceInfo currNetworkIFs = null;
+ private NetworkInterfaceInfo prevNetworkIFs = null;
+ private InternetProtocolStats.TcpStats prevTcpV4Stats = null;
+ private InternetProtocolStats.TcpStats prevTcpV6Stats = null;
+ private InternetProtocolStats.UdpStats prevUdpV4Stats = null;
+ private InternetProtocolStats.UdpStats prevUdpV6Stats = null;
+ private List metrics;
+
+ private class NetworkInterfaceInfo {
+ private Map parsed;
+
+ public NetworkInterfaceInfo(HardwareAbstractionLayer hal) {
+ List networkIFs = hal.getNetworkIFs();
+ this.parsed = new HashMap<>();
+
+ for (NetworkIF networkIF : networkIFs) {
+ this.parsed.put(networkIF.getName(), networkIF);
+ }
+ }
+
+ public NetworkIF getNetworkIF(String name) {
+ return parsed.get(name);
+ }
+
+ public Collection getCollection() {
+ return parsed.values();
+ }
+ }
+
+ public NetworkMetricCollectingJob(String applicationName, String collectorIP, String collectorPort, boolean enableProtocolStats) {
+ this.hostGroupName = applicationName;
+ this.collectorAddr = "http://" + collectorIP + ":" + collectorPort + "/telegraf";
+ this.protocolStatSupported = enableProtocolStats;
+
+ SystemInfo si = new SystemInfo();
+ OperatingSystem os = si.getOperatingSystem();
+ this.hostName = os.getNetworkParams().getHostName();
+
+ initializeNetworkInterfaceInfo(si);
+
+ if (protocolStatSupported) {
+ initializeProtocolStats(os);
+ }
+ }
+
+ private void initializeProtocolStats(OperatingSystem os) {
+ this.protocolStats = os.getInternetProtocolStats();
+ try {
+ protocolStats.getTCPv4Stats();
+ } catch (Exception e) {
+ protocolStatSupported = false;
+ if (logger.isWarnEnabled()) {
+ logger.warn("OSHI Protocol Statistics not supported. Not collecting protocol stat metrics.");
+ }
+ }
+ }
+
+ private void initializeNetworkInterfaceInfo(SystemInfo si) {
+ HardwareAbstractionLayer hal = si.getHardware();
+ try {
+ currNetworkIFs = new NetworkInterfaceInfo(hal);
+ prevNetworkIFs = new NetworkInterfaceInfo(hal);
+ } catch (Exception e) {
+ networkInterfaceSupported = false;
+ if (logger.isWarnEnabled()) {
+ logger.warn("OSHI Network Interface not supported. Not collecting network interface metrics.");
+ }
+ }
+
+ if (currNetworkIFs == null || prevNetworkIFs == null) {
+ networkInterfaceSupported = false;
+ }
+ }
+
+ public static boolean isSupported() {
+ return !SystemInfo.getCurrentPlatform().equals(PlatformEnum.UNKNOWN);
+ }
+
+ @Override
+ public void run() {
+ if (networkInterfaceSupported || protocolStatSupported) {
+ metrics = new ArrayList<>();
+ }
+
+ if (networkInterfaceSupported) {
+ addNetworkIfs();
+ }
+
+ if (protocolStatSupported) {
+ addTCPProtocolStats();
+ addUDPProtocolStats();
+ }
+
+ if (networkInterfaceSupported || protocolStatSupported) {
+ postDataToMetricCollector();
+ }
+ }
+
+ private void addNetworkIfs() {
+ long timestamp = clock.millis() / 1000L;
+
+ for (NetworkIF networkIF : currNetworkIFs.getCollection()) {
+ if (networkIF.updateAttributes()) {
+ NetworkIF prev = prevNetworkIFs.getNetworkIF(networkIF.getName());
+
+ NetworkMetric metric = getMetricData(networkIF, prev, timestamp);
+ if (metric != null) {
+ metrics.add(metric);
+ }
+ } else {
+ if (logger.isWarnEnabled()) {
+ logger.warn("Failed to update current values for network interfaces");
+ }
+ }
+ }
+
+ NetworkInterfaceInfo temp = currNetworkIFs;
+ currNetworkIFs = prevNetworkIFs;
+ prevNetworkIFs = temp;
+ }
+
+ private NetworkMetric getMetricData(NetworkIF curr, NetworkIF prev, long timestamp) {
+ if (prev == null) {
+ return null;
+ }
+
+ NetworkMetric metric = new NetworkMetric("network_interface", timestamp);
+ metric.addTag("name", "\""+ curr.getName() + "\"");
+ metric.addTag("mac_addr", "\"" + curr.getMacaddr()+ "\"");
+ metric.addTag("host", "\"" + this.hostName + "\"");
+ metric.addField("rx_packets", (curr.getPacketsRecv() - prev.getPacketsRecv()));
+ metric.addField("rx_bytes", (curr.getBytesRecv() - prev.getBytesRecv()));
+ metric.addField("rx_errors", (curr.getInErrors() - prev.getInErrors()));
+ metric.addField("rx_drops", (curr.getInDrops() - prev.getInDrops()));
+ metric.addField("tx_packets", (curr.getPacketsSent() - prev.getPacketsSent()));
+ metric.addField("tx_bytes", (curr.getBytesSent() - prev.getBytesSent()));
+ metric.addField("tx_errors", (curr.getOutErrors() - prev.getOutErrors()));
+ metric.addField("tx_collisions", (curr.getCollisions() - prev.getCollisions()));
+ return metric;
+ }
+
+ private void addTCPProtocolStats() {
+ long timestamp = clock.millis() / 1000L;
+ InternetProtocolStats.TcpStats tcpv4 = protocolStats.getTCPv4Stats();
+ InternetProtocolStats.TcpStats tcpv6 = protocolStats.getTCPv6Stats();
+
+ NetworkMetric metric = getMetricData("v4", tcpv4, prevTcpV4Stats, timestamp);
+ if (metric != null) {
+ metrics.add(metric);
+ }
+
+ metric = getMetricData("v6", tcpv6, prevTcpV6Stats, timestamp);
+ if (metric != null) {
+ metrics.add(metric);
+ }
+
+ prevTcpV4Stats = tcpv4;
+ prevTcpV6Stats = tcpv6;
+ }
+
+ private void addUDPProtocolStats() {
+ long timestamp = clock.millis() / 1000L;
+ InternetProtocolStats.UdpStats udpv4 = protocolStats.getUDPv4Stats();
+ InternetProtocolStats.UdpStats udpv6 = protocolStats.getUDPv6Stats();
+
+ NetworkMetric metric = getMetricData("v4", udpv4, prevUdpV4Stats, timestamp);
+ if (metric != null) {
+ metrics.add(metric);
+ }
+
+ metric = getMetricData("v6", udpv6, prevUdpV6Stats, timestamp);
+ if (metric != null) {
+ metrics.add(metric);
+ }
+
+ prevUdpV4Stats = protocolStats.getUDPv4Stats();
+ prevUdpV6Stats = protocolStats.getUDPv6Stats();
+ }
+
+ private NetworkMetric getMetricData(String version, InternetProtocolStats.TcpStats tcpStats, InternetProtocolStats.TcpStats prev, long timestamp) {
+ if (prev == null) {
+ return null;
+ }
+
+ NetworkMetric metric = new NetworkMetric("tcp_stats", timestamp);
+ metric.addTag("version", "\"" + version + "\"");
+ metric.addTag("host", "\"" + this.hostName + "\"");
+ metric.addField("conn_established", tcpStats.getConnectionsEstablished());
+ metric.addField("conn_active", tcpStats.getConnectionsActive());
+ metric.addField("conn_passive", tcpStats.getConnectionsPassive());
+ metric.addField("conn_failure", tcpStats.getConnectionFailures());
+ metric.addField("conn_reset", tcpStats.getConnectionsReset());
+ metric.addField("seg_sent", (tcpStats.getSegmentsSent() - prev.getSegmentsSent()));
+ metric.addField("seg_received", (tcpStats.getSegmentsReceived() - prev.getSegmentsReceived()));
+ metric.addField("seg_retransmitted", (tcpStats.getSegmentsRetransmitted() - prev.getSegmentsRetransmitted()));
+ metric.addField("in_errors", (tcpStats.getInErrors() - prev.getInErrors()));
+ metric.addField("out_resets", (tcpStats.getOutResets() - prev.getOutResets()));
+ return metric;
+ }
+
+ private NetworkMetric getMetricData(String version, InternetProtocolStats.UdpStats udpStats, InternetProtocolStats.UdpStats prev, long timestamp) {
+ if (prev == null) {
+ return null;
+ }
+
+ NetworkMetric metric = new NetworkMetric("udp_stats", timestamp);
+ metric.addTag("version", "\"" + version + "\"");
+ metric.addTag("host", "\"" + this.hostName + "\"");
+ metric.addField("txd", (udpStats.getDatagramsSent() - prev.getDatagramsSent()));
+ metric.addField("rxd", (udpStats.getDatagramsReceived() - prev.getDatagramsReceived()));
+ metric.addField("noport", (udpStats.getDatagramsNoPort() - prev.getDatagramsNoPort()));
+ metric.addField("rx_error", (udpStats.getDatagramsReceivedErrors() - prev.getDatagramsReceivedErrors()));
+ return metric;
+ }
+
+ public void postDataToMetricCollector() {
+ if (metrics.isEmpty()) {
+ return;
+ }
+
+ try {
+ URL url = new URL(collectorAddr);
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setRequestMethod("POST");
+ conn.setDoOutput(true);
+ conn.setRequestProperty("Content-Type", "application/json");
+ conn.setRequestProperty("hostGroupName", hostGroupName);
+
+ try (OutputStream os = conn.getOutputStream()) {
+ String inputString = "{\"metrics\":"+ metrics.toString() + "}";
+ byte[] input = inputString.getBytes("utf-8");
+ os.write(input, 0, input.length);
+ os.flush();
+ }
+ int response = conn.getResponseCode();
+ if (response == 200) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("HW metric sent to metric collector.");
+ }
+ } else {
+ if (logger.isWarnEnabled()) {
+ logger.warn("Failed to send to metric collector with response code {}", response);
+ }
+ }
+ } catch (MalformedURLException e) {
+ if (logger.isWarnEnabled()) {
+ logger.warn("Malformed metric collector address: {}", collectorAddr);
+ }
+ } catch (ProtocolException e) {
+ if (logger.isWarnEnabled()) {
+ logger.warn("Protocol exception when opening connection to metric collector: {}", e.getMessage());
+ }
+ } catch (IOException e) {
+ if (logger.isWarnEnabled()) {
+ logger.warn("IO exception when opening connection to metric collector: {}", e.getMessage());
+ }
+ }
+ }
+}
diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/monitor/NetworkStatMonitor.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/monitor/NetworkStatMonitor.java
new file mode 100644
index 000000000000..d792dcf6e753
--- /dev/null
+++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/monitor/NetworkStatMonitor.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2023 NAVER Corp.
+ *
+ * Licensed 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 com.navercorp.pinpoint.profiler.monitor;
+
+public interface NetworkStatMonitor {
+ void start();
+
+ void stop();
+}
diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/monitor/metric/NetworkMetric.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/monitor/metric/NetworkMetric.java
new file mode 100644
index 000000000000..dd9b30452ff2
--- /dev/null
+++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/monitor/metric/NetworkMetric.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2023 NAVER Corp.
+ *
+ * Licensed 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 com.navercorp.pinpoint.profiler.monitor.metric;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+public class NetworkMetric {
+ private final Fields fields;
+ private final String name;
+ private final Fields tags;
+ private final long timestamp;
+
+ public NetworkMetric(String name, long timestamp) {
+ this.fields = new Fields<>();
+ this.name = Objects.requireNonNull(name, "name");
+ this.tags = new Fields<>();
+ this.timestamp = timestamp;
+ }
+
+ public void addField(String name, Long value) {
+ this.fields.add(new Field<>(name, value));
+ }
+
+ public void addTag(String name, String value) {
+ this.tags.add(new Field<>(name, value));
+ }
+
+ private class Fields {
+ List> fields;
+
+ public Fields() {
+ fields = new ArrayList<>();
+ }
+
+ public void add(Field field) {
+ fields.add(field);
+ }
+
+ @Override
+ public String toString() {
+ String string = fields.toString().replace("[", "").replace("]", "");
+ return "{" + string + "}";
+ }
+ }
+
+ private class Field {
+ private final String name;
+ private final T value;
+
+ public Field(String name, T value) {
+ this.name = Objects.requireNonNull(name, "name");
+ this.value = value;
+ }
+
+ @Override
+ public String toString() {
+ return "\"" + name + "\":" + value;
+ }
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("{");
+ sb.append("\"fields\":").append(fields);
+ sb.append(",\"name\":").append("\"" + name + "\"");
+ sb.append(",\"tags\":").append(tags);
+ sb.append(",\"timestamp\":").append(timestamp);
+ sb.append("}");
+ return sb.toString();
+ }
+
+}