【问题标题】:JavaFX application freeze when i append log4j to textarea当我将 log4j 附加到 textarea 时,JavaFX 应用程序冻结
【发布时间】:2015-10-12 09:55:25
【问题描述】:

我已经设法将我的 log4j 消息附加到 javafx textarea 组件,但是如果后台应用程序任务运行,GUI 会冻结。

所以实现中的某些内容丢失或配置错误。

这是我的自定义 log4j 附加程序:

public class TextAreaAppender extends WriterAppender {

    static Logger log = Logger.getLogger(TextAreaAppender.class.getName());

    private static volatile TextArea textArea = null;

    public static void setTextArea(final TextArea textArea) {
        TextAreaAppender.textArea = textArea;
    }

    @Override
    public void append(final LoggingEvent loggingEvent) {

        final String message = this.layout.format(loggingEvent);

        try {
            Platform.runLater(() -> {
                try {
                    if (textArea != null) {
                        if (textArea.getText().length() == 0) {
                            textArea.setText(message);
                        } else {
                            textArea.selectEnd();
                            textArea.insertText(textArea.getText().length(),
                                message);
                        }
                    }
                } catch (final Throwable t) {
                    System.out.println("Unable to append log to text area: "
                        + t.getMessage());
                }
            });
        } catch (final IllegalStateException e) {
            // ignore case when the platform hasn't yet been iniitialized
        }
    }

这是我如何将 textarea 组件插入到我的 fxml 控制器中:

public class FXMLDocumentController implements Initializable {
    ...
    @FXML
    private TextArea logText;
    ...
    @Override
    @FXML
    public void initialize(URL url, ResourceBundle rb) {
        ...
        logText.setEditable(false);
        TextAreaAppender.setTextArea(logText);
    }

最后是我的 log4j 配置:

log4j.rootLogger=INFO, file, textarea, stdout
# Direct log messages to stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n

# Append the logs to the GUI
log4j.appender.textarea = com.npap.fxutils.TextAreaAppender
log4j.appender.textarea.Target=System.out
log4j.appender.textarea.layout=org.apache.log4j.PatternLayout
log4j.appender.textarea.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n

GUI 冻结的原因可能是什么?

在后台应用程序运行多个任务/进程,应用程序冻结的操作是调用以下类及其方法时:

class StorageSCP extends StorageService  implements AssociationListener {
    ...
    private final DcmRcv dcmrcv;

     public StorageSCP(DcmRcv dcmrcv, String[] sopClasses) {
         super(sopClasses);
         this.dcmrcv = dcmrcv;
     }
     ...
    /** Overwrite {@link StorageService#cstore} to send delayed C-STORE RSP 
    * by separate Thread, so reading of following received C-STORE RQs from
    * the open association is not blocked.
    */
    @Override
    public void cstore(final Association as, final int pcid, DicomObject rq,
        PDVInputStream dataStream, String tsuid)
        throws DicomServiceException, IOException {

        final DicomObject rsp = CommandUtils.mkRSP(rq, CommandUtils.SUCCESS);
        onCStoreRQ(as, pcid, rq, dataStream, tsuid, rsp);

        if (dcmrcv.getDimseRspDelay() > 0) {
            dcmrcv.executor().execute(new Runnable() {
                public void run() {
                    try {
                        Thread.sleep(dcmrcv.getDimseRspDelay());
                        as.writeDimseRSP(pcid, rsp);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
        } else {
            as.writeDimseRSP(pcid, rsp);
        }

        onCStoreRSP(as, pcid, rq, dataStream, tsuid, rsp);
    }
     ...
     @Override
     protected void onCStoreRQ(Association association, int pcid, DicomObject dcmReqObj,
                                PDVInputStream dataStream, String transferSyntaxUID,
                                DicomObject dcmRspObj)
            throws DicomServiceException, IOException {
         final DicomOutputStream outStream = new DicomOutputStream(new BufferedOutputStream(new FileOutputStream(dicomFile), 600000));

        try {
            outStream.writeFileMetaInformation(fileMetaDcmObj);
            dataStream.copyTo(outStream);            
        } catch (DicomServiceException e) {
        } finally {
            outStream.close();  
        }
        ...
    }
    ...
    @Override
    public void associationAccepted(final AssociationAcceptEvent associationAcceptEvent) {
        final UUID assocUUID = UUID.randomUUID();
        final Association association = associationAcceptEvent.getAssociation();
        associationDataMap.put(association, assocUUID);
    }

    @Override
    public void associationClosed(final AssociationCloseEvent associationCloseEvent) {
        final Association association = associationCloseEvent.getAssociation();
        associationDataMap.remove(association);
        final Integer assocInstanceCnt = associationCounterMap.get(association);
    removeAssociationCounter(association);
    }

    private final Map<Association, UUID> associationDataMap = new HashMap<Association, UUID>();
    private final Map<Association, Integer> associationCounterMap = new HashMap<Association, Integer>();

当侦听器(侦听传入的 dicom 图像关联请求收到此类请求)时,在后台调用此类及其方法。

我不知道这段代码是否有帮助,但是应用程序行为如下:

  1. 应用程序启动正常,GUI 工作正常(可以切换选项卡、编辑 字段等)。我还可以在我的 log4j textarea 中看到它的日志
  2. 当我收到一些传入的 dicom 图像进行解析(关联 请求)然后整个应用程序冻结。
  3. 在后台应用程序正常运行,没有异常 抛出并且任务运行没有任何问题
  4. 即使所有关联请求都结束(接受并处理) GUI 保持冻结状态。

我希望以上所有信息对您有所帮助...

【问题讨论】:

  • 多久调用一次append()
  • 我使用了此处发布的 log4j appender 代码:rshingleton.com/javafx-log4j-textarea-log-appender。我假设每次触发日志事件时都会调用 append()。
  • 你多久触发一次日志事件?
  • 我问这个问题的原因是Platform.runLater() 将 Runnable 添加到事件队列中。因此,如果您使用太多待处理的 Runnable 淹没 JavaFX,应用程序可能会变得无响应。
  • 谢谢@ItachiUchiha ...确实有道理。检查我的 log4j 配置(添加到问题中)。由于我的日志级别是信息,每秒有几个日志记录事件......所以我猜这可能会挂起应用程序的 GUI......你能想到任何替代方案吗?

标签: java multithreading javafx javafx-2


【解决方案1】:

TextArea 不适合大量文本,因为它由一个大文本节点支持。您最好使用其中一个虚拟控件,例如 ListView,如果它不可编辑或 StyledText 控件之一可用。

最常用的是:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多