blob: 1e9f2a1fbc50e27b00632d56e706f5887cb192be [file] [log] [blame]
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* 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 com.intellij.diagnostic;
import com.intellij.idea.IdeaApplication;
import com.intellij.idea.Main;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.ErrorLogger;
import com.intellij.openapi.diagnostic.ExceptionWithAttachments;
import com.intellij.openapi.diagnostic.IdeaLoggingEvent;
import com.intellij.util.ExceptionUtil;
import com.intellij.util.containers.ContainerUtil;
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.Level;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.spi.ThrowableInformation;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.TestOnly;
import javax.swing.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author Mike
*/
public class DialogAppender extends AppenderSkeleton {
private static final ErrorLogger DEFAULT_LOGGER = new DefaultIdeaErrorLogger();
private static final int MAX_ASYNC_LOGGING_EVENTS = 5;
private volatile Runnable myDialogRunnable = null;
private final AtomicInteger myPendingAppendCounts = new AtomicInteger();
@Override
protected synchronized void append(@NotNull final LoggingEvent event) {
if (!event.getLevel().isGreaterOrEqual(Level.ERROR) ||
Main.isCommandLine() ||
!IdeaApplication.isLoaded()) {
return;
}
Runnable action = new Runnable() {
@Override
public void run() {
try {
List<ErrorLogger> loggers = new ArrayList<ErrorLogger>();
loggers.add(DEFAULT_LOGGER);
Application application = ApplicationManager.getApplication();
if (application != null) {
if (application.isHeadlessEnvironment() || application.isDisposed()) return;
ContainerUtil.addAll(loggers, application.getComponents(ErrorLogger.class));
}
appendToLoggers(event, loggers.toArray(new ErrorLogger[loggers.size()]));
}
finally {
myPendingAppendCounts.decrementAndGet();
}
}
};
if (myPendingAppendCounts.addAndGet(1) > MAX_ASYNC_LOGGING_EVENTS) {
// Stop adding requests to the queue or we can get OOME on pending logging requests (IDEA-95327)
myPendingAppendCounts.decrementAndGet(); // number of pending logging events should not increase
}
else {
// Note, we MUST avoid SYNCHRONOUS invokeAndWait to prevent deadlocks
//noinspection SSBasedInspection
SwingUtilities.invokeLater(action);
}
}
void appendToLoggers(@NotNull LoggingEvent event, @NotNull ErrorLogger[] errorLoggers) {
if (myDialogRunnable != null) {
return;
}
final IdeaLoggingEvent ideaEvent;
final Object message = event.getMessage();
if (message instanceof IdeaLoggingEvent) {
ideaEvent = (IdeaLoggingEvent)message;
}
else {
ThrowableInformation info = event.getThrowableInformation();
if (info == null) {
return;
}
ideaEvent = extractLoggingEvent(message, info.getThrowable());
}
for (int i = errorLoggers.length - 1; i >= 0; i--) {
final ErrorLogger logger = errorLoggers[i];
if (!logger.canHandle(ideaEvent)) {
continue;
}
myDialogRunnable = new Runnable() {
@Override
public void run() {
try {
logger.handle(ideaEvent);
}
finally {
myDialogRunnable = null;
}
}
};
final Application app = ApplicationManager.getApplication();
if (app == null) {
new Thread(myDialogRunnable).start();
}
else {
app.executeOnPooledThread(myDialogRunnable);
}
break;
}
}
private static IdeaLoggingEvent extractLoggingEvent(Object message, Throwable throwable) {
//noinspection ThrowableResultOfMethodCallIgnored
Throwable rootCause = ExceptionUtil.getRootCause(throwable);
if (rootCause instanceof LogEventException) {
return ((LogEventException)rootCause).getLogMessage();
}
String strMessage = message == null ? "<null> " : message.toString();
ExceptionWithAttachments withAttachments = ExceptionUtil.findCause(throwable, ExceptionWithAttachments.class);
if (withAttachments != null) {
return LogMessageEx.createEvent(strMessage, ExceptionUtil.getThrowableText(throwable), withAttachments.getAttachments());
}
return new IdeaLoggingEvent(strMessage, throwable);
}
@TestOnly
Runnable getDialogRunnable() {
return myDialogRunnable;
}
@Override
public boolean requiresLayout() {
return false;
}
@Override
public void close() {
}
}