java - 如何判断 java 日志记录套接字处理程序是否仍在运行?

标签 java sockets java.util.logging

我有一个应用程序,它为我的日志服务器(在不同的设备上)创建套接字处理程序并使用它。这工作得很好。

如果日志服务器终止并重新启动,应用程序不会知道这一点。应用程序应删除旧的套接字处理程序并添加一个新的。

如何判断 java 日志记录套接字处理程序是否仍在运行?

编辑:我想出了下面的代码,它似乎有效:

    public static class MySocketHandler extends SocketHandler {
        public MySocketHandler(String host,int port, Logger logger) throws IOException {
            super(host,port);
            this.logger=logger;
            setErrorManager(new ErrorManager() {
                @Override public synchronized void error(String msg,Exception ex,int code) {
                    super.error(msg,ex,code);
                    System.out.println("error: "+msg+", "+ex+", "+code);
                    removeHandler();
                    failed=true;
                }
            });
        }
        void removeHandler() {
            logger.removeHandler(MySocketHandler.this);
            System.out.println("removed my socket handler");
        }
        final Logger logger;
        Boolean failed=false;
}

最佳答案

创建一个代理处理程序来包装 SocketHandler 并安装自定义 java.util.logging.ErrorManager (作为内部类)监听异常并在出错时重新连接内部处理程序。

以下是一些示例代码,供您改进并确保在使用之前对其进行单元测试:

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayDeque;
import java.util.logging.ErrorManager;
import java.util.logging.Filter;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.SocketHandler;

public class ReconnectingSocketHandler extends Handler {

    private SocketHandler target;
    private boolean active;
    private final Watcher listener = new Watcher();
    private final ArrayDeque<LogRecord> pending = new ArrayDeque<>(1);

    public ReconnectingSocketHandler() {
        //@todo Read properties from LogManager.
    }

    @Override
    public synchronized void publish(LogRecord record) {
        if (!active) {  //Prevent reentrance.
            active = true;
            try {
                if (record != null && isLoggable(record)) {
                    pending.add(record);
                }

                //Store only the last few records only on error.
                if (pending.size() > 1000) {
                    pending.pollFirst();
                }

                //While we have retries and records.
                for (int r = 0; r < 2 && !pending.isEmpty(); ++r) {
                    if (target == null) {
                        //@todo implement time based backoff.
                        target = new SocketHandler();
                        target.setLevel(super.getLevel());
                        target.setEncoding(super.getEncoding());
                        target.setFilter(super.getFilter());
                        target.setFormatter(super.getFormatter());
                        target.setErrorManager(listener);
                    }

                    //Write the queue to the socket handler.
                    for (LogRecord lr; (lr = pending.poll()) != null;) {
                        target.publish(lr);
                        //On error, close and retry.
                        if (listener.last != null) {
                            pending.addFirst(lr);
                            reportError(null, listener.last,
                                    ErrorManager.WRITE_FAILURE);
                            listener.last = null;
                            target.close();
                            target = null;
                            break;
                        }
                    }
                }
            } catch (IOException ioe) {
                target = null; //Try again later.
                reportError(null, ioe, ErrorManager.WRITE_FAILURE);
            } finally {
                active = false;
            }
        }
    }

    @Override
    public synchronized void flush() {
        publish((LogRecord) null);
        if (target != null) {
            target.flush();
        }
    }

    @Override
    public synchronized void close() {
        super.setLevel(Level.OFF);
        flush();
        if (target != null) {
            target.close();
            target = null;
        }
    }

    @Override
    public synchronized void setLevel(Level newLevel) {
        super.setLevel(newLevel);
        if (target != null) {
            target.setLevel(newLevel);
        }
    }

    @Override
    public synchronized void setFilter(Filter newFilter) {
        super.setFilter(newFilter);
        if (target != null) {
            target.setFilter(newFilter);
        }
    }

    @Override
    public synchronized void setEncoding(String encoding) throws UnsupportedEncodingException {
        super.setEncoding(encoding);
        if (target != null) {
            target.setEncoding(encoding);
        }
    }

    @Override
    public synchronized void setFormatter(Formatter newFormatter) {
        super.setFormatter(newFormatter);
        if (target != null) {
            target.setFormatter(newFormatter);
        }
    }

    private class Watcher extends ErrorManager {

        Exception last;

        Watcher() {
        }

        @Override
        public void error(String msg, Exception ex, int code) {
            last = ex;
        }
    }
}

此代码将发布到正常路径中的套接字处理程序。出现异常时,它将关闭并重新创建套接字处理程序。如果失败,此代码会将当前记录存储在队列中,最多可达最后 1000 条记录,以便延迟发布。

删除套接字处理程序并将其添加到记录器树中是一项危险的操作,可能会导致日志记录丢失或重新连接。代理处理程序将允许您正确控制重新连接的次数,并允许您重新发布否则会丢失的错误。

How can I tell if the java logging sockethandler is still running?

您可以尝试定期调用flush检测关闭的套接字,但这太过分了。如果观察者设备已关闭并且源应用程序中没有发生错误,则套接字是否启动并不重要。您所需要做的可能就是在失败时使用react。

关于java - 如何判断 java 日志记录套接字处理程序是否仍在运行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43795377/

相关文章:

java - 打印数组、设置数组

c# - 抛出的异常似乎阻塞了其他线程

java - 如何使用 Simple Formatter 修改日志格式?

java - Logger.getGlobal() 和 Logger.getAnonymousLogger() 之间的区别?

java - 在 Websphere WebApp 中将 JUL 重定向到 Log4J2

java - 如何删除日期(dd/mm/yyyy)中的年份(yyyy)

java - 如何从 swing 代码获取任何应用程序的根安装目录?

java - 第二个捕获组不捕获

javascript - 如何从 socket.io 发出的回调中获取回调,然后将其返回给父函数

c++ - 包含消息长度的两个字节 header 的部分 recv() 怎么样?