View Javadoc
1   /*
2    * Copyright 2012-2025 CodeLibs Project and the Others.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13   * either express or implied. See the License for the specific language
14   * governing permissions and limitations under the License.
15   */
16  package org.codelibs.fess.timer;
17  
18  import java.io.ByteArrayOutputStream;
19  import java.io.OutputStream;
20  import java.util.Arrays;
21  import java.util.List;
22  import java.util.stream.Collectors;
23  
24  import org.apache.commons.text.StringEscapeUtils;
25  import org.apache.logging.log4j.LogManager;
26  import org.apache.logging.log4j.Logger;
27  import org.codelibs.fess.Constants;
28  import org.codelibs.fess.opensearch.client.SearchEngineClient;
29  import org.codelibs.fess.util.ComponentUtil;
30  import org.opensearch.action.admin.cluster.node.stats.NodesStatsResponse;
31  import org.opensearch.common.xcontent.XContentFactory;
32  import org.opensearch.core.xcontent.ToXContent;
33  import org.opensearch.core.xcontent.XContentBuilder;
34  import org.opensearch.monitor.jvm.JvmStats;
35  import org.opensearch.monitor.jvm.JvmStats.BufferPool;
36  import org.opensearch.monitor.jvm.JvmStats.Classes;
37  import org.opensearch.monitor.jvm.JvmStats.GarbageCollectors;
38  import org.opensearch.monitor.jvm.JvmStats.Mem;
39  import org.opensearch.monitor.jvm.JvmStats.Threads;
40  import org.opensearch.monitor.os.OsProbe;
41  import org.opensearch.monitor.os.OsStats;
42  import org.opensearch.monitor.process.ProcessProbe;
43  
44  /**
45   * This class is a timer target for monitoring system-level statistics.
46   * It collects and logs information about the operating system, process,
47   * JVM, and search engine.
48   */
49  public class SystemMonitorTarget extends MonitorTarget {
50      private static final Logger logger = LogManager.getLogger(SystemMonitorTarget.class);
51  
52      /**
53       * Constructs a new system monitor target.
54       */
55      public SystemMonitorTarget() {
56          super();
57      }
58  
59      @Override
60      public void expired() {
61          final StringBuilder buf = new StringBuilder(1000);
62  
63          buf.append("[SYSTEM MONITOR] ");
64          buf.append('{');
65  
66          appendOsStats(buf);
67          appendProcessStats(buf);
68          appendJvmStats(buf);
69          appendFesenStats(buf);
70  
71          appendTimestamp(buf);
72          buf.append('}');
73  
74          if (logger.isInfoEnabled()) {
75              logger.info(buf.toString());
76          }
77      }
78  
79      private void appendJvmStats(final StringBuilder buf) {
80          buf.append("\"jvm\":{");
81          final JvmStats jvmStats = JvmStats.jvmStats();
82          final Mem mem = jvmStats.getMem();
83          buf.append("\"memory\":{");
84          buf.append("\"heap\":{");
85          append(buf, "used", () -> mem.getHeapUsed().getBytes()).append(',');
86          append(buf, "committed", () -> mem.getHeapCommitted().getBytes()).append(',');
87          append(buf, "max", () -> mem.getHeapMax().getBytes()).append(',');
88          append(buf, "percent", () -> mem.getHeapUsedPercent());
89          buf.append("},");
90          buf.append("\"non_heap\":{");
91          append(buf, "used", () -> mem.getNonHeapUsed().getBytes()).append(',');
92          append(buf, "committed", () -> mem.getNonHeapCommitted().getBytes());
93          buf.append('}');
94          buf.append("},");
95          final List<BufferPool> bufferPools = jvmStats.getBufferPools();
96          buf.append("\"pools\":{");
97          buf.append(bufferPools.stream().map(p -> {
98              final StringBuilder b = new StringBuilder();
99              b.append('"').append(StringEscapeUtils.escapeJson(p.getName())).append("\":{");
100             append(b, "count", () -> p.getCount()).append(',');
101             append(b, "used", () -> p.getUsed().getBytes()).append(',');
102             append(b, "capacity", () -> p.getTotalCapacity().getBytes()).append('}');
103             return b.toString();
104         }).collect(Collectors.joining(",")));
105         buf.append("},");
106         final GarbageCollectors gc = jvmStats.getGc();
107         buf.append("\"gc\":{");
108         buf.append(Arrays.stream(gc.getCollectors()).map(c -> {
109             final StringBuilder b = new StringBuilder();
110             b.append('"').append(StringEscapeUtils.escapeJson(c.getName())).append("\":{");
111             append(b, "count", () -> c.getCollectionCount()).append(',');
112             append(b, "time", () -> c.getCollectionTime().getMillis()).append('}');
113             return b.toString();
114         }).collect(Collectors.joining(",")));
115         buf.append("},");
116         final Threads threads = jvmStats.getThreads();
117         buf.append("\"threads\":{");
118         append(buf, "count", () -> threads.getCount()).append(',');
119         append(buf, "peak", () -> threads.getPeakCount());
120         buf.append("},");
121         final Classes classes = jvmStats.getClasses();
122         buf.append("\"classes\":{");
123         append(buf, "loaded", () -> classes.getLoadedClassCount()).append(',');
124         append(buf, "total_loaded", () -> classes.getTotalLoadedClassCount()).append(',');
125         append(buf, "unloaded", () -> classes.getUnloadedClassCount());
126         buf.append("},");
127         append(buf, "uptime", () -> jvmStats.getUptime().getMillis());
128         buf.append("},");
129     }
130 
131     private void appendProcessStats(final StringBuilder buf) {
132         buf.append("\"process\":{");
133         final ProcessProbe processProbe = ProcessProbe.getInstance();
134         buf.append("\"file_descriptor\":{");
135         append(buf, "open", () -> processProbe.getOpenFileDescriptorCount()).append(',');
136         append(buf, "max", () -> processProbe.getMaxFileDescriptorCount());
137         buf.append("},");
138         buf.append("\"cpu\":{");
139         append(buf, "percent", () -> processProbe.getProcessCpuPercent()).append(',');
140         append(buf, "total", () -> processProbe.getProcessCpuTotalTime());
141         buf.append("},");
142         buf.append("\"virtual_memory\":{");
143         append(buf, "total", () -> processProbe.getTotalVirtualMemorySize());
144         buf.append('}');
145         buf.append("},");
146     }
147 
148     private void appendOsStats(final StringBuilder buf) {
149         buf.append("\"os\":{");
150         final OsProbe osProbe = OsProbe.getInstance();
151         buf.append("\"memory\":{");
152         buf.append("\"physical\":{");
153         append(buf, "free", () -> osProbe.getFreePhysicalMemorySize()).append(',');
154         append(buf, "total", () -> osProbe.getTotalPhysicalMemorySize());
155         buf.append("},");
156         buf.append("\"swap_space\":{");
157         append(buf, "free", () -> osProbe.getFreeSwapSpaceSize()).append(',');
158         append(buf, "total", () -> osProbe.getTotalSwapSpaceSize());
159         buf.append('}');
160         buf.append("},");
161         buf.append("\"cpu\":{");
162         append(buf, "percent", () -> osProbe.getSystemCpuPercent());
163         final OsStats osStats = osProbe.osStats();
164         buf.append("},");
165         append(buf, "load_averages", () -> osStats.getCpu().getLoadAverage());
166         buf.append("},");
167     }
168 
169     private void appendFesenStats(final StringBuilder buf) {
170         String stats = null;
171         try {
172             final SearchEngineClient esClient = ComponentUtil.getSearchEngineClient();
173             final NodesStatsResponse response = esClient.admin().cluster().prepareNodesStats().all().execute().actionGet(10000L);
174             final XContentBuilder builder = XContentFactory.jsonBuilder();
175             builder.startObject();
176             response.toXContent(builder, ToXContent.EMPTY_PARAMS);
177             builder.endObject();
178             builder.flush();
179             try (OutputStream out = builder.getOutputStream()) {
180                 stats = ((ByteArrayOutputStream) out).toString(Constants.UTF_8);
181             }
182         } catch (final Exception e) {
183             appendException(buf, e).append(',');
184         }
185         buf.append("\"search_engine\":").append(stats).append(',');
186     }
187 }