Skip to content

Commit

Permalink
when crash logs (hs_err_pid*.log) are found, display them in the syst…
Browse files Browse the repository at this point in the history
…em actions
  • Loading branch information
evernat committed May 26, 2018
1 parent 0ea7e76 commit 5ebb28e
Show file tree
Hide file tree
Showing 16 changed files with 349 additions and 1 deletion.
Expand Up @@ -41,6 +41,7 @@ public enum HttpPart {
USAGES("usages"),
JNDI("jndi"),
MBEANS("mbeans"),
CRASHES("crashes"),
THREADS("threads"),
THREADS_DUMP("threadsDump"),
COUNTER_SUMMARY_PER_CLASS("counterSummaryPerClass"),
Expand Down
@@ -0,0 +1,123 @@
/*
* Copyright 2008-2017 by Emeric Vernat
*
* This file is part of Java Melody.
*
* 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 net.bull.javamelody.internal.model;

import java.io.File;
import java.io.FilenameFilter;
import java.io.Serializable;
import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;

/**
* HsErrPid files.
* @author Emeric Vernat
*/
public final class HsErrPid implements Serializable {
/**
* Comparateur de HsErrPid par date.
*/
static final class HsErrPidComparator implements Comparator<HsErrPid>, Serializable {
private static final long serialVersionUID = 1L;

/** {@inheritDoc} */
@Override
public int compare(HsErrPid hsErrPid1, HsErrPid hsErrPid2) {
return hsErrPid2.getDate().compareTo(hsErrPid1.getDate());
}
}

private static final String XX_ERROR_FILE = "-XX:ErrorFile=";

private static final long serialVersionUID = 1L;

private static final FilenameFilter FILENAME_FILTER = new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.startsWith("hs_err_pid") && name.endsWith(".log");
}
};

private final String file;

private final Date date;

private HsErrPid(String file, Date date) {
super();
this.file = file;
this.date = date;
}

public static List<HsErrPid> buildHsErrPidList() {
final List<File> directories = new ArrayList<File>();
// locations of fatal error log:
// http://www.oracle.com/technetwork/java/javase/felog-138657.html
directories.add(new File("./"));
// linux:
directories.add(new File("/tmp"));
// windows:
final String tmp = System.getenv("TMP");
if (tmp != null) {
directories.add(new File(tmp));
}
final List<String> args = ManagementFactory.getRuntimeMXBean().getInputArguments();
for (final String arg : args) {
if (arg.startsWith(XX_ERROR_FILE)) {
final String errorFile = arg.substring(XX_ERROR_FILE.length());
final File dir = new File(errorFile).getParentFile();
if (dir != null) {
directories.add(dir);
}
}
}

final List<HsErrPid> result = new ArrayList<HsErrPid>();
for (final File dir : directories) {
final File[] files = dir.listFiles(FILENAME_FILTER);
if (files != null) {
for (final File file : files) {
result.add(new HsErrPid(file.getAbsolutePath(), new Date(file.lastModified())));
}
}
}
return result;
}

public static List<HsErrPid> getHsErrPidList(List<JavaInformations> javaInformationsList) {
final List<HsErrPid> result = new ArrayList<HsErrPid>();
for (final JavaInformations javaInformations : javaInformationsList) {
final List<HsErrPid> hsErrPidList = javaInformations.getHsErrPidList();
if (hsErrPidList != null) {
result.addAll(hsErrPidList);
}
}
Collections.sort(result, new HsErrPidComparator());
return result;
}

public String getFile() {
return file;
}

public Date getDate() {
return date;
}
}
Expand Up @@ -41,6 +41,7 @@
import net.bull.javamelody.SessionListener;
import net.bull.javamelody.SpringContext;
import net.bull.javamelody.internal.common.Parameters;
import net.bull.javamelody.internal.model.HsErrPid.HsErrPidComparator;

/**
* Informations systèmes sur le serveur, sans code html de présentation.
Expand Down Expand Up @@ -98,6 +99,8 @@ public class JavaInformations implements Serializable { // NOPMD
private final List<CacheInformations> cacheInformationsList;
@SuppressWarnings("all")
private final List<JobInformations> jobInformationsList;
@SuppressWarnings("all")
private final List<HsErrPid> hsErrPidList;
private final boolean webXmlExists = localWebXmlExists;
private final boolean pomXmlExists = localPomXmlExists;
private final boolean springBeanExists;
Expand Down Expand Up @@ -186,13 +189,15 @@ public JavaInformations(ServletContext servletContext, boolean includeDetails) {
threadInformationsList = buildThreadInformationsList();
cacheInformationsList = CacheInformations.buildCacheInformationsList();
jobInformationsList = JobInformations.buildJobInformationsList();
hsErrPidList = HsErrPid.buildHsErrPidList();
pid = PID.getPID();
} else {
dataBaseVersion = null;
dataSourceDetails = null;
threadInformationsList = null;
cacheInformationsList = null;
jobInformationsList = null;
hsErrPidList = null;
pid = null;
}
}
Expand Down Expand Up @@ -667,6 +672,16 @@ public int getCurrentlyExecutingJobCount() {
return result;
}

public List<HsErrPid> getHsErrPidList() {
if (hsErrPidList != null) {
// on trie sur demande (si affichage)
final List<HsErrPid> result = new ArrayList<HsErrPid>(hsErrPidList);
Collections.sort(result, new HsErrPidComparator());
return Collections.unmodifiableList(result);
}
return null;
}

public boolean isStackTraceEnabled() {
for (final ThreadInformations threadInformations : threadInformationsList) {
final List<StackTraceElement> stackTrace = threadInformations.getStackTrace();
Expand Down
Expand Up @@ -211,6 +211,11 @@ public void copyTo(HttpServletRequest httpRequest, HttpServletResponse httpRespo
httpRequest.getHeader("Accept-Language"));
connection.connect();
httpResponse.setContentType(connection.getContentType());
// Content-Disposition pour téléchargement hs_err_pid par exemple
final String contentDisposition = connection.getHeaderField("Content-Disposition");
if (contentDisposition != null) {
httpResponse.setHeader("Content-Disposition", contentDisposition);
}
final OutputStream output = httpResponse.getOutputStream();
dataLength = pump(output, connection);
} finally {
Expand Down
Expand Up @@ -49,6 +49,7 @@
import net.bull.javamelody.internal.model.CounterRequestAggregation;
import net.bull.javamelody.internal.model.CounterRequestContext;
import net.bull.javamelody.internal.model.DatabaseInformations;
import net.bull.javamelody.internal.model.HsErrPid;
import net.bull.javamelody.internal.model.JavaInformations;
import net.bull.javamelody.internal.model.JndiBinding;
import net.bull.javamelody.internal.model.LabradorRetriever;
Expand Down Expand Up @@ -220,6 +221,10 @@ private void doPart(HttpServletRequest req, HttpServletResponse resp, String app
final URL url = getUrlsByApplication(application).get(0);
doProxy(req, resp, url, HttpPart.SOURCE.getName() + '&' + HttpParameter.CLASS + '='
+ HttpParameter.CLASS.getParameterFrom(req));
} else if (HttpPart.CRASHES.isPart(req)
&& HttpParameter.PATH.getParameterFrom(req) != null) {
noCache(resp);
doCrashDownload(req, resp, application);
} else if (HttpPart.CONNECTIONS.isPart(req)) {
doMultiHtmlProxy(req, resp, application, HttpPart.CONNECTIONS.getName(),
I18N.getString("Connexions_jdbc_ouvertes"), I18N.getString("connexions_intro"),
Expand All @@ -237,6 +242,25 @@ private void doPart(HttpServletRequest req, HttpServletResponse resp, String app
}
}

private void doCrashDownload(HttpServletRequest req, HttpServletResponse resp,
String application) throws IOException {
final String partParameter = HttpPart.CRASHES.getName() + '&' + HttpParameter.PATH + '='
+ HttpParameter.PATH.getParameterFrom(req);
boolean found = false;
for (final URL url : getUrlsByApplication(application)) {
try {
doProxy(req, resp, url, partParameter);
found = true;
break;
} catch (final IOException e) {
continue;
}
}
if (!found) {
resp.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}

private void doJmxValue(HttpServletRequest req, HttpServletResponse resp, String application,
String jmxValueParameter) throws IOException {
noCache(resp);
Expand Down Expand Up @@ -411,6 +435,12 @@ private Serializable createSerializableForSystemActions(HttpServletRequest httpR
Action.checkSystemActionsEnabled();
return new ArrayList<List<ConnectionInformations>>(
collectorServer.collectConnectionInformations(application));
} else if (HttpPart.CRASHES.isPart(httpRequest)) {
// par sécurité
Action.checkSystemActionsEnabled();
final List<JavaInformations> javaInformationsList = getJavaInformationsByApplication(
application);
return new ArrayList<HsErrPid>(HsErrPid.getHsErrPidList(javaInformationsList));
}
return null;
}
Expand Down
Expand Up @@ -306,6 +306,12 @@ void doMBeans() throws IOException {
}
}

@RequestPart(HttpPart.CRASHES)
void doCrashes() throws IOException {
Action.checkSystemActionsEnabled();
htmlReport.writeCrashes();
}

@RequestPart(HttpPart.SPRING_BEANS)
void doSpringBeans() throws IOException {
htmlReport.writeSpringContext();
Expand Down
Expand Up @@ -19,6 +19,8 @@

import java.io.BufferedInputStream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
Expand All @@ -43,6 +45,7 @@
import net.bull.javamelody.internal.model.Action;
import net.bull.javamelody.internal.model.Collector;
import net.bull.javamelody.internal.model.CollectorServer;
import net.bull.javamelody.internal.model.HsErrPid;
import net.bull.javamelody.internal.model.JRobin;
import net.bull.javamelody.internal.model.JavaInformations;
import net.bull.javamelody.internal.model.MBeans;
Expand Down Expand Up @@ -194,6 +197,10 @@ public void doReport(HttpServletRequest httpRequest, HttpServletResponse httpRes
} else if (HttpPart.JNLP.isPart(httpRequest)) {
final Range range = httpCookieManager.getRange(httpRequest, httpResponse);
doJnlp(httpRequest, httpResponse, range);
} else if (HttpPart.CRASHES.isPart(httpRequest)
&& HttpParameter.PATH.getParameterFrom(httpRequest) != null) {
final String path = HttpParameter.PATH.getParameterFrom(httpRequest);
doHsErrPid(httpResponse, javaInformationsList, path);
} else if (HttpParameter.REPORT.getParameterFrom(httpRequest) != null) {
final String reportName = URLDecoder
.decode(HttpParameter.REPORT.getParameterFrom(httpRequest), "UTF-8");
Expand Down Expand Up @@ -482,6 +489,32 @@ private void doJnlp(HttpServletRequest httpRequest, HttpServletResponse httpResp
.toJnlp();
}

private void doHsErrPid(HttpServletResponse httpResponse,
List<JavaInformations> javaInformationsList, String path) throws IOException {
for (final JavaInformations javaInformations : javaInformationsList) {
for (final HsErrPid hsErrPid : javaInformations.getHsErrPidList()) {
if (hsErrPid.getFile().replace('\\', '/').equals(path) && new File(path).exists()) {
final File file = new File(path);
final OutputStream out = httpResponse.getOutputStream();
httpResponse.setContentType("text/plain");
// attachment et non inline pour proposer le téléchargement et non l'affichage direct dans le navigateur
httpResponse.addHeader("Content-Disposition",
"attachment;filename=" + file.getName());
final InputStream in = new FileInputStream(file);
try {
TransportFormat.pump(in, out);
} finally {
in.close();
}
return;
}
}
}
// si le fichier demandé n'est pas dans la liste des fichiers hs_err_pid*.log,
// alors il n'existe plus ou alors le fichier demandé est une invention malveillante
httpResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
}

private static void doCustomReport(HttpServletRequest httpRequest,
HttpServletResponse httpResponse, String reportName)
throws ServletException, IOException {
Expand Down Expand Up @@ -517,7 +550,8 @@ public static boolean isJavaInformationsNeeded(HttpServletRequest httpRequest) {
|| HttpPart.DEFAULT_WITH_CURRENT_REQUESTS.getName().equals(part)
|| HttpPart.JVM.getName().equals(part)
|| HttpPart.THREADS.getName().equals(part)
|| HttpPart.THREADS_DUMP.getName().equals(part);
|| HttpPart.THREADS_DUMP.getName().equals(part)
|| HttpPart.CRASHES.getName().equals(part);
}
return false;
}
Expand Down
Expand Up @@ -47,6 +47,7 @@
import net.bull.javamelody.internal.model.CounterRequestAggregation;
import net.bull.javamelody.internal.model.CounterRequestContext;
import net.bull.javamelody.internal.model.DatabaseInformations;
import net.bull.javamelody.internal.model.HsErrPid;
import net.bull.javamelody.internal.model.JRobin;
import net.bull.javamelody.internal.model.JavaInformations;
import net.bull.javamelody.internal.model.JndiBinding;
Expand Down Expand Up @@ -344,6 +345,12 @@ private List<CounterRequestContext> getCurrentRequests() {
return collector.getRootCurrentContexts(newCounters);
}

@RequestPart(HttpPart.CRASHES)
Serializable createCrashesSerializable(
@RequestAttribute(JAVA_INFORMATIONS_LIST_KEY) List<JavaInformations> javaInformationsList) {
return new ArrayList<HsErrPid>(HsErrPid.getHsErrPidList(javaInformationsList));
}

private Map<String, byte[]> convertJRobinsToImages(Collection<JRobin> jrobins, Range range,
int width, int height) throws IOException {
final Map<String, byte[]> images = new LinkedHashMap<String, byte[]>(jrobins.size());
Expand Down
Expand Up @@ -39,6 +39,7 @@
import net.bull.javamelody.internal.model.CollectorServer;
import net.bull.javamelody.internal.model.Counter;
import net.bull.javamelody.internal.model.CounterRequestContext;
import net.bull.javamelody.internal.model.HsErrPid;
import net.bull.javamelody.internal.model.JRobin;
import net.bull.javamelody.internal.model.JavaInformations;
import net.bull.javamelody.internal.model.JobInformations;
Expand Down Expand Up @@ -577,6 +578,10 @@ private boolean isJobEnabled() {
return false;
}

List<HsErrPid> getHsErrPidList() {
return HsErrPid.getHsErrPidList(javaInformationsList);
}

private void writeCaches() throws IOException {
int i = 0;
for (final JavaInformations javaInformations : javaInformationsList) {
Expand Down Expand Up @@ -709,6 +714,14 @@ private void writeSystemActionsLinks() throws IOException { // NOPMD
writeln("<img src='?resource=db.png' width='20' height='20' alt=\"#database#\" /> #database#</a>");
}

final int hsErrPidCount = getHsErrPidList().size();
if (hsErrPidCount > 0) {
writeln(separator);
write("<a href='?part=crashes'>");
write("<img src='?resource=alert.png' width='20' height='20' alt=\"#Crashes# ("
+ hsErrPidCount + ")\" /> #Crashes# (" + hsErrPidCount + ")</a>");
}

writeln("<br/></div>");
}

Expand Down

0 comments on commit 5ebb28e

Please sign in to comment.