Hi, I would like to update the navigation event of the “Report Issue” button. This button is located in the marketplace entry. We would like to update this to point to a website where customers can log the issue, instead of an email address.
Thanks,
Luis
Hi, you may want to register and implement a custom ErrorReportSubmitter
.
See IDE Infrastructure | IntelliJ Platform Plugin SDK and How to implement error reporting in an IntelliJ plugin | plugin-dev.com.
Per example, some plugins use it to submit and prefill issues to their GitHub project.
1 Like
I was using this implementation (highly inspired from the fantastic work of other plugin developers):
package lermitage.intellij.extra.icons.utils;
import com.intellij.ide.BrowserUtil;
import com.intellij.ide.plugins.IdeaPluginDescriptor;
import com.intellij.ide.plugins.PluginManagerCore;
import com.intellij.openapi.application.ApplicationInfo;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.ErrorReportSubmitter;
import com.intellij.openapi.diagnostic.IdeaLoggingEvent;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.diagnostic.SubmittedReportInfo;
import com.intellij.openapi.extensions.PluginId;
import com.intellij.openapi.util.NlsActions;
import com.intellij.util.Consumer;
import com.intellij.util.ModalityUiUtil;
import org.apache.commons.lang3.SystemUtils;
import org.apache.http.client.utils.URIBuilder;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.Component;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Error reporter which prefills an issue on Extra Icons' GitHub repository.
*/
public class ExtraIconsErrorReportSubmitter extends ErrorReportSubmitter {
private static final @NonNls Logger LOGGER = Logger.getInstance(IconUtils.class);
private static final ResourceBundle i18n = I18nUtils.getResourceBundle();
private static final int MAX_GITHUB_URI_LENGTH = 8192;
@Override
public @NlsActions.ActionText @NotNull String getReportActionText() {
return i18n.getString("error.report.btn.title");
}
@Override
public boolean submit(IdeaLoggingEvent @NotNull [] events,
@Nullable String additionalInfo,
@NotNull Component parentComponent,
@NotNull Consumer<? super SubmittedReportInfo> consumer) {
try {
URI uri = constructNewGitHubIssueUri(events, additionalInfo);
ModalityUiUtil.invokeLaterIfNeeded(ModalityState.NON_MODAL, () -> BrowserUtil.browse(uri));
} catch (Exception e) {
LOGGER.error("Failed to prepare Extra Icons error reporter", e);
return false;
}
return true;
}
@SuppressWarnings("HardCodedStringLiteral")
URI constructNewGitHubIssueUri(IdeaLoggingEvent[] events, @Nullable String additionalInfo) throws URISyntaxException {
URIBuilder uriBuilder;
uriBuilder = new URIBuilder("https://github.com/jonathanlermitage/intellij-extra-icons-plugin/issues/new");
String title = Stream.of(events)
.map(event -> {
Throwable throwable = event.getThrowable();
String exceptionMessage = event.getThrowableText().lines().findFirst().orElse("");
return (throwable != null ? exceptionMessage : event.getMessage()).stripTrailing();
})
.collect(Collectors.joining("; "));
uriBuilder.setParameter("title", title);
uriBuilder.setParameter("labels", "bug");
URI uri;
List<String> reportBodyLines = getReportBody(events, additionalInfo).lines().collect(Collectors.toList());
do {
// Let's cut the body gradually line-by-line until the resulting URI fits into the GitHub limits.
// It's hard to predict the perfect exact cut in advance due to URL encoding.
reportBodyLines = reportBodyLines.subList(0, reportBodyLines.size() - 1);
uriBuilder.setParameter("body", reportBodyLines.stream().collect(Collectors.joining(System.lineSeparator())));
uri = uriBuilder.build();
} while (uri.toString().length() > MAX_GITHUB_URI_LENGTH);
return uri;
}
private String getReportBody(
IdeaLoggingEvent[] events,
@Nullable String additionalInfo) {
String reportBody = getBugTemplate();
for (java.util.Map.Entry<String, String> entry : getTemplateVariables(events, additionalInfo).entrySet()) {
reportBody = reportBody.replace("%" + entry.getKey() + "%", entry.getValue());
}
return reportBody;
}
@SuppressWarnings("HardCodedStringLiteral")
private java.util.Map<String, String> getTemplateVariables(
IdeaLoggingEvent[] events,
@Nullable String additionalInfo) {
Map<String, String> templateVariables = new HashMap<>();
templateVariables.put("ide", ApplicationInfo.getInstance().getFullApplicationName());
IdeaPluginDescriptor pluginDescriptor = PluginManagerCore.getPlugin(PluginId.getId("lermitage.intellij.extra.icons"));
String pluginVersion = pluginDescriptor == null ? "<unknown>" : pluginDescriptor.getVersion();
templateVariables.put("extraiconsPluginVersion", pluginVersion);
String osName = SystemUtils.OS_NAME;
String osVersion = SystemUtils.OS_VERSION;
templateVariables.put("os", osName + " " + osVersion);
templateVariables.put("additionalInfo", additionalInfo == null ? "N/A" : additionalInfo);
String nl = System.lineSeparator();
String stacktraces = Stream.of(events)
.map(event -> {
// This message is distinct from the throwable's message:
// in `LOG.error(message, throwable)`, it's the first parameter.
String messagePart = event.getMessage() != null ? (event.getMessage() + nl + nl) : "";
String throwablePart = shortenExceptionsStack(event.getThrowableText().stripTrailing());
return nl + messagePart + throwablePart + nl;
})
.collect(Collectors.joining(nl + nl));
templateVariables.put("stacktraces", stacktraces);
return templateVariables;
}
@SuppressWarnings("HardCodedStringLiteral")
private String shortenExceptionsStack(String stackTrace) {
String nl = System.lineSeparator();
int rootCauseIndex = Math.max(
stackTrace.lastIndexOf("Caused by:"),
stackTrace.lastIndexOf("\tSuppressed:"));
if (rootCauseIndex != -1) {
String rootCauseStackTrace = stackTrace.substring(rootCauseIndex);
String[] lines = stackTrace.substring(0, rootCauseIndex).split(nl);
StringBuilder resultString = new StringBuilder();
for (int i = 0; i < lines.length; i++) {
if (lines[i].contains("Caused by:") || lines[i].contains("Suppressed:") || i == 0) {
resultString.append(lines[i]).append(nl);
if (i + 1 < lines.length) {
resultString.append(lines[i + 1]).append("...").append(nl);
}
}
}
return resultString.append(rootCauseStackTrace).toString();
}
return stackTrace;
}
@SuppressWarnings("HardCodedStringLiteral")
private String getBugTemplate() {
return "## Running environment\n" +
"- Extra Icons plugin version - %extraiconsPluginVersion%\n" +
"- IDE - %ide%\n" +
"- OS - %os%\n" +
"\n" +
"## Bug description\n" +
"Please include steps to reproduce (like `go to...`/`click on...` etc.) + expected and actual behaviour. \n" +
"Please attach **IDE logs**. Open your IDE and go to <kbd>Help</kbd>, <kbd>Collect Logs and Diagnostic Data</kbd>.\n" +
"\n" +
"## IDE - additional info\n" +
"%additionalInfo%\n" +
"\n" +
"## IDE - stack trace\n" +
"```%stacktraces%\n";
}
}
This prefills a GitHub issue with the plugin version, information on the IDE and the OS, and the stracktrace. There is a 8192 characters limit because everything is sent via the url, and some browsers do not accept more than 8192 characters.
I switched to a com.intellij.diagnostic.JetBrainsMarketplaceErrorReportSubmitter
, but this was a bad idea (there are no notifications). I’ll revert to my old ErrorReportSubmitter
.
Thanks @jonathanlermitage.1 I’ll give this a try!
Would you be interested in a library that simplifies extending this extension point?