fix #1764 download logs on maintenance page

This commit is contained in:
Shinsuke Sugaya 2018-07-15 21:41:16 +09:00
parent 9dbe3eaaec
commit 50ba9b0b1b
7 changed files with 201 additions and 5 deletions

View file

@ -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;

View file

@ -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<Path> 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());

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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=ログのダウンロード

View file

@ -101,6 +101,26 @@
</div>
<!-- /.box -->
</div>
<div class="col-md-12">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">
<la:message key="labels.diagnostic_logs" />
</h3>
</div>
<!-- /.box-header -->
<div class="box-footer">
<button type="submit" class="btn btn-primary"
name="downloadLogs"
value="<la:message key="labels.download_diagnostic_logs_button"/>">
<i class="fa fa-arrow-circle-right"></i>
<la:message key="labels.download_diagnostic_logs_button" />
</button>
</div>
<!-- /.box-footer -->
</div>
<!-- /.box -->
</div>
</la:form>
</div>
</section>