diff --git a/src/main/java/org/codelibs/fess/app/web/admin/maintenance/UpgradeForm.java b/src/main/java/org/codelibs/fess/app/web/admin/maintenance/ActionForm.java similarity index 97% rename from src/main/java/org/codelibs/fess/app/web/admin/maintenance/UpgradeForm.java rename to src/main/java/org/codelibs/fess/app/web/admin/maintenance/ActionForm.java index 638a3317f..f25a84332 100644 --- a/src/main/java/org/codelibs/fess/app/web/admin/maintenance/UpgradeForm.java +++ b/src/main/java/org/codelibs/fess/app/web/admin/maintenance/ActionForm.java @@ -20,7 +20,7 @@ import javax.validation.constraints.Size; import org.codelibs.fess.Constants; import org.codelibs.fess.util.ComponentUtil; -public class UpgradeForm { +public class ActionForm { @Size(max = 10) public String replaceAliases = Constants.ON; diff --git a/src/main/java/org/codelibs/fess/app/web/admin/maintenance/AdminMaintenanceAction.java b/src/main/java/org/codelibs/fess/app/web/admin/maintenance/AdminMaintenanceAction.java index 65cf9acc7..aa01bc3c4 100644 --- a/src/main/java/org/codelibs/fess/app/web/admin/maintenance/AdminMaintenanceAction.java +++ b/src/main/java/org/codelibs/fess/app/web/admin/maintenance/AdminMaintenanceAction.java @@ -15,15 +15,31 @@ */ package org.codelibs.fess.app.web.admin.maintenance; +import java.io.IOException; +import java.net.InetAddress; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.text.SimpleDateFormat; +import java.util.Arrays; import java.util.Date; +import java.util.Properties; +import java.util.stream.Stream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; import javax.annotation.Resource; +import org.codelibs.core.io.CopyUtil; +import org.codelibs.core.lang.StringUtil; +import org.codelibs.curl.CurlResponse; import org.codelibs.fess.app.web.base.FessAdminAction; import org.codelibs.fess.es.client.FessEsClient; +import org.codelibs.fess.mylasta.direction.FessConfig.SimpleImpl; +import org.codelibs.fess.util.ComponentUtil; import org.elasticsearch.action.ActionListener; import org.lastaflute.web.Execute; +import org.lastaflute.web.response.ActionResponse; import org.lastaflute.web.response.HtmlResponse; import org.lastaflute.web.ruts.process.ActionRuntime; import org.slf4j.Logger; @@ -36,6 +52,10 @@ public class AdminMaintenanceAction extends FessAdminAction { // private static final Logger logger = LoggerFactory.getLogger(AdminMaintenanceAction.class); + private static final String[] ES_CAT_NAMES = new String[] { "aliases", "allocation", "count", "fielddata", "health", "indices", + "master", "nodeattrs", "nodes", "pending_tasks", "plugins", "recovery", "repositories", "thread_pool", "shards", "segments", + "snapshots", "templates" }; + // =================================================================================== // Attribute // @@ -63,11 +83,11 @@ public class AdminMaintenanceAction extends FessAdminAction { } private HtmlResponse asIndexHtml() { - return asHtml(path_AdminMaintenance_AdminMaintenanceJsp).useForm(UpgradeForm.class); + return asHtml(path_AdminMaintenance_AdminMaintenanceJsp).useForm(ActionForm.class); } @Execute - public HtmlResponse reindexOnly(final UpgradeForm form) { + public HtmlResponse reindexOnly(final ActionForm form) { validate(form, messages -> {}, this::asIndexHtml); verifyToken(this::asIndexHtml); if (startReindex(isCheckboxEnabled(form.replaceAliases), form.numberOfShardsForDoc, form.autoExpandReplicasForDoc)) { @@ -77,7 +97,7 @@ public class AdminMaintenanceAction extends FessAdminAction { } @Execute - public HtmlResponse clearCrawlerIndex(final UpgradeForm form) { + public HtmlResponse clearCrawlerIndex(final ActionForm form) { validate(form, messages -> {}, this::asIndexHtml); verifyToken(this::asIndexHtml); fessEsClient @@ -94,7 +114,151 @@ public class AdminMaintenanceAction extends FessAdminAction { return redirect(getClass()); } - private boolean startReindex(final boolean replaceAliases, final String numberOfShards, final String autoExpandReplicas) { + @Execute + public ActionResponse downloadLogs(final ActionForm form) { + validate(form, messages -> {}, this::asIndexHtml); + verifyTokenKeep(this::asIndexHtml); + + final String diagnosticId = "log" + new SimpleDateFormat("yyyyMMddHHmm").format(ComponentUtil.getSystemHelper().getCurrentTime()); + return asStream(diagnosticId + ".zip").contentTypeOctetStream().stream(out -> { + try (ZipOutputStream zos = new ZipOutputStream(out.stream())) { + writeLogFiles(zos, diagnosticId); + writeSystemProperties(zos, diagnosticId); + writeFessBasicConfig(zos, diagnosticId); + writeFessConfig(zos, diagnosticId); + writeElasticsearchCat(zos, diagnosticId); + writeElasticsearchJson(zos, diagnosticId); + } + }); + } + + protected void writeElasticsearchJson(final ZipOutputStream zos, final String id) { + writeElastisearchJsonApi(zos, id, "cluster", "health"); + writeElastisearchJsonApi(zos, id, "cluster", "state"); + writeElastisearchJsonApi(zos, id, "cluster", "stats"); + writeElastisearchJsonApi(zos, id, "cluster", "pending_tasks"); + writeElastisearchJsonApi(zos, id, "nodes", "stats"); + writeElastisearchJsonApi(zos, id, "nodes", "_all"); + writeElastisearchJsonApi(zos, id, "nodes", "usage"); + writeElastisearchJsonApi(zos, id, "remote", "info"); + writeElastisearchJsonApi(zos, id, "tasks", ""); + writeElastisearchJsonApi(zos, id, "nodes", "hot_threads"); + } + + protected void writeElastisearchJsonApi(final ZipOutputStream zos, final String id, final String v1, final String v2) { + final ZipEntry entry = new ZipEntry(id + "/es_" + v1 + "_" + v2 + ".json"); + try { + zos.putNextEntry(entry); + try (CurlResponse response = ComponentUtil.getCurlHelper().get("/_" + v1 + "/" + v2).execute()) { + CopyUtil.copy(response.getContentAsStream(), zos); + } + } catch (final Exception e) { + logger.warn("Failed to access /_" + v1 + "/" + v2, e); + } + } + + protected void writeElasticsearchCat(final ZipOutputStream zos, final String id) { + Arrays.stream(ES_CAT_NAMES).forEach(name -> { + final ZipEntry entry = new ZipEntry(id + "/es_cat_" + name + ".txt"); + try { + zos.putNextEntry(entry); + try (CurlResponse response = ComponentUtil.getCurlHelper().get("/_cat/" + name).param("v", "").execute()) { + CopyUtil.copy(response.getContentAsStream(), zos); + } + } catch (final Exception e) { + logger.warn("Failed to access /_cat/" + name, e); + } + }); + } + + protected void writeFessConfig(final ZipOutputStream zos, final String id) { + if (fessConfig instanceof SimpleImpl) { + final Properties prop = new Properties(); + ((SimpleImpl) fessConfig).keySet().stream().forEach(k -> prop.setProperty(k, fessConfig.get(k))); + + final ZipEntry entry = new ZipEntry(id + "/fess_config.properties"); + try { + zos.putNextEntry(entry); + prop.store(zos, getHostInfo()); + } catch (final IOException e) { + logger.warn("Failed to access system.properties.", e); + } + } + } + + protected void writeFessBasicConfig(final ZipOutputStream zos, final String id) { + final ZipEntry entry = new ZipEntry(id + "/fess_basic_config.bulk"); + try { + zos.putNextEntry(entry); + try (CurlResponse response = + ComponentUtil.getCurlHelper().get("/.fess_basic_config/_data").param("format", "json") + .param("scroll", fessConfig.getIndexScrollSearchTimeout()).execute()) { + CopyUtil.copy(response.getContentAsStream(), zos); + } + } catch (final IOException e) { + logger.warn("Failed to access system.properties.", e); + } + } + + protected void writeSystemProperties(final ZipOutputStream zos, final String id) { + final ZipEntry entry = new ZipEntry(id + "/system.properties"); + try { + zos.putNextEntry(entry); + ComponentUtil.getSystemProperties().store(zos, getHostInfo()); + } catch (final IOException e) { + logger.warn("Failed to access system.properties.", e); + } + } + + protected void writeLogFiles(final ZipOutputStream zos, final String id) { + final String logFilePath = systemHelper.getLogFilePath(); + if (StringUtil.isNotBlank(logFilePath)) { + final Path logDirPath = Paths.get(logFilePath); + try (Stream stream = Files.list(logDirPath)) { + stream.filter(entry -> isLogFilename(entry.getFileName().toString())).forEach(filePath -> { + final ZipEntry entry = new ZipEntry(id + "/" + filePath.getFileName().toString()); + try { + zos.putNextEntry(entry); + final long len = Files.copy(filePath, zos); + if (logger.isDebugEnabled()) { + logger.debug(filePath.getFileName() + ": " + len); + } + } catch (final IOException e) { + logger.warn("Failed to access " + filePath, e); + } + }); + } catch (final Exception e) { + logger.warn("Failed to access log files.", e); + } + } + } + + protected String getHostInfo() { + final StringBuilder buf = new StringBuilder(); + try { + final InetAddress ia = InetAddress.getLocalHost(); + final String hostname = ia.getHostName(); + if (StringUtil.isNotBlank(hostname)) { + buf.append(hostname); + } + final String ip = ia.getHostAddress(); + if (StringUtil.isNotBlank(ip)) { + if (buf.length() > 0) { + buf.append(" : "); + } + buf.append(ip); + } + } catch (final Exception e) { + // ignore + } + return buf.toString(); + } + + protected boolean isLogFilename(final String name) { + return name.endsWith(".log") || name.endsWith(".log.gz"); + } + + protected boolean startReindex(final boolean replaceAliases, final String numberOfShards, final String autoExpandReplicas) { final String docIndex = "fess"; final String fromIndex = fessConfig.getIndexDocumentUpdateIndex(); final String toIndex = docIndex + "." + new SimpleDateFormat("yyyyMMddHHmm").format(new Date()); diff --git a/src/main/java/org/codelibs/fess/mylasta/action/FessLabels.java b/src/main/java/org/codelibs/fess/mylasta/action/FessLabels.java index 74cdf9bfe..6945e0919 100644 --- a/src/main/java/org/codelibs/fess/mylasta/action/FessLabels.java +++ b/src/main/java/org/codelibs/fess/mylasta/action/FessLabels.java @@ -2829,6 +2829,12 @@ public class FessLabels extends UserMessages { /** The key of the message: Clear .crawler Indices */ public static final String LABELS_clear_crawler_index_button = "{labels.clear_crawler_index_button}"; + /** The key of the message: Diagnostic */ + public static final String LABELS_diagnostic_logs = "{labels.diagnostic_logs}"; + + /** The key of the message: Download Logs */ + public static final String LABELS_download_diagnostic_logs_button = "{labels.download_diagnostic_logs_button}"; + /** * Assert the property is not null. * @param property The value of the property. (NotNull) diff --git a/src/main/resources/fess_label.properties b/src/main/resources/fess_label.properties index 9ab250c11..8dd73df50 100644 --- a/src/main/resources/fess_label.properties +++ b/src/main/resources/fess_label.properties @@ -933,3 +933,5 @@ labels.number_of_shards_for_doc=The number of shards labels.auto_expand_replicas_for_doc=Auto expand replicas labels.clear_crawler_index=Crawler Indices labels.clear_crawler_index_button=Clear .crawler Indices +labels.diagnostic_logs=Diagnostic +labels.download_diagnostic_logs_button=Download Logs diff --git a/src/main/resources/fess_label_en.properties b/src/main/resources/fess_label_en.properties index a8a9d767a..0c23f5704 100644 --- a/src/main/resources/fess_label_en.properties +++ b/src/main/resources/fess_label_en.properties @@ -933,3 +933,5 @@ labels.number_of_shards_for_doc=The number of shards labels.auto_expand_replicas_for_doc=Auto expand replicas labels.clear_crawler_index=Crawler Indices labels.clear_crawler_index_button=Clear .crawler Indices +labels.diagnostic_logs=Diagnostic +labels.download_diagnostic_logs_button=Download Logs diff --git a/src/main/resources/fess_label_ja.properties b/src/main/resources/fess_label_ja.properties index 346d9f7bd..a11f99c5f 100644 --- a/src/main/resources/fess_label_ja.properties +++ b/src/main/resources/fess_label_ja.properties @@ -935,3 +935,5 @@ labels.number_of_shards_for_doc=シャード数 labels.auto_expand_replicas_for_doc=最大レプリカ数 labels.clear_crawler_index=Crawlerインデックス labels.clear_crawler_index_button=.crawlerインデックスの削除 +labels.diagnostic_logs=診断 +labels.download_diagnostic_logs_button=ログのダウンロード diff --git a/src/main/webapp/WEB-INF/view/admin/maintenance/admin_maintenance.jsp b/src/main/webapp/WEB-INF/view/admin/maintenance/admin_maintenance.jsp index 943027d7e..728d93bb0 100644 --- a/src/main/webapp/WEB-INF/view/admin/maintenance/admin_maintenance.jsp +++ b/src/main/webapp/WEB-INF/view/admin/maintenance/admin_maintenance.jsp @@ -101,6 +101,26 @@ +
+
+
+

+ +

+
+ + + +
+ +