java - 跨应用程序的多个实例维护 StringBuffer 类中的日志

标签 java node.js spring shared-data

这几天我一直在为这个问题苦苦挣扎。

要求 在我们的应用程序中,我们通过 Nodejs 开发的 UI 上传文件,然后通过亚马逊简单工作流程 (SWF) 处理文件记录。对亚马逊 SWF 的调用是通过 Spring 应用程序进行的,nodejs 应用程序将在处理文件时调用该应用程序。要求是,对于正在处理的每个文件,应用程序需要创建一个日志文件,详细说明处理记录时发生的情况。

我是如何实现的? 在触发 SWF 的 spring 应用程序中,我创建了一个 FileLogger 类,它将维护一个静态 StringBuffer 变量。此 fileLogger 类设置为工作流范围,这意味着,将为工作流的每次执行创建该类,并在工作流结束时销毁该类。在处理文件时,我会继续将日志附加到 FileLogger 类中的 StringBuffer 中,并在处理结束时写入文件并保存它。

问题描述 只要我们只运行该应用程序的一个实例,该解决方案似乎就可以正常工作。一旦我们将应用程序部署到多个亚马逊 ec-2 实例中,似乎不完整的日志就保存在文件中。进一步研究发现,应用程序的每个实例都有自己的 stringBuffer 来维护日志,当我们写入应用程序时,仅读取 stringbuffer 内容之一,因此读取不完整的日志。不用说,日志模式是随机的。我观察到,如果我们部署 N 个应用程序实例,我们将拥有 N 个 StringBuffer 实例。

这是 FileLogger 类

private static final Logger logger = LoggerFactory.getLogger(FileLogger.class);

    //private static final Logger logger = LoggerFactory.getLogger(FileLogger.class);   
    private static final SimpleDateFormat logFileDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
    private static final SimpleDateFormat fileNameDateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
    private static final String COLON = ":";
    private static StringBuffer logAppender = null;

    public synchronized void debug(Date date, String logMessage){
        if(appConfig.getLogLevel().equalsIgnoreCase(LogLevel.DEBUG.name())){
            this.log(LogLevel.DEBUG.name(), date, logMessage);
        }
    }

    public synchronized void info(Date date, String logMessage){
        if(appConfig.getLogLevel().equalsIgnoreCase(LogLevel.INFO.name()) || 
                appConfig.getLogLevel().equalsIgnoreCase(LogLevel.DEBUG.name())){
            this.log(LogLevel.INFO.name(), date, logMessage);
        }
    }

    public synchronized void error(Date date, String logMessage){
        if(appConfig.getLogLevel().equalsIgnoreCase(LogLevel.ERROR.name()) ||
                appConfig.getLogLevel().equalsIgnoreCase(LogLevel.DEBUG.name())){
            this.log(LogLevel.ERROR.name(), date, logMessage);
        }
    }

    private synchronized void log(String logLevel, Date date, String logMessage){
        logger.info("logAppender Hashcode: "+logAppender.hashCode());
        if(!logLevel.equalsIgnoreCase(LogLevel.NONE.name())){
            //StringBuffer logAppender = getLogAppender();
            getLogAppender().append(getLogAppender().hashCode());
            getLogAppender().append(COLON);
            getLogAppender().append(getFormattedDate(date, logFileDateFormat));
            getLogAppender().append(COLON);
            getLogAppender().append(logLevel);
            getLogAppender().append(COLON);
            getLogAppender().append(logMessage);
            getLogAppender().append(System.getProperty("line.separator"));
        }
    }
private synchronized StringBuffer getLogAppender(){
        logger.info("Getting logAppender .."+logAppender);
        if(logAppender == null){
            logger.info("Log appender is null");
            logAppender = new StringBuffer();
        }
        return logAppender;
    }

问题 如何确保我的应用程序的多个实例中只有一个 StringBuffer (logAppender) 实例可以继续附加日志,然后在最后读取并将内容写入文件,然后再保存?

最佳答案

首先,您在非静态方法中使用静态字段:这是不好的做法,要么使用静态方法,要么使用具有非静态字段的单例。

其次,如果您担心性能,最好在不需要时删除所有同步的内容。您只需要在 log() 方法上使用它。 Error()、warn() 和 info() 不会以需要同步(只读)的方式访问共享数据。另外,为什么在 log() 中多次调用 getLogAppender() ?

使用 equalsIgnoreCase() 测试日志级别的效率也很低,尽管不会被注意到。并且 error() 中有一个错误:如果级别为 INFO,它不会记录。

现在,谈谈你的观点:就像之前其他人所说的那样,JVM 不会在实例之间共享用户数据。并且 JVM 可能不是同一操作系统实例上的事件。没有简单的出路。如果您配置得好,记录到数据库可能不会对性能产生那么坏的影响:您根本不需要事务隔离,因为您不更新任何现有数据。您还可以尝试使用日志服务器:类 UNIX 操作系统可以提供大量解决方案。或者当您刷新缓冲区时,从所有服务器实例中刷新。

关于java - 跨应用程序的多个实例维护 StringBuffer 类中的日志,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29289292/

相关文章:

java - 用java写一个json文件

java - StreamHandler接口(interface)的onListen方法中的arguments param到底是什么

JavaFx,不在开头显示内容

node.js - 如何在 Hapi 17 中抽象路由

php - 为什么使用redis进行websocket通信?

java - Hibernate Envers 如何记录额外的审计数据,例如被审计的表名

java - 安全地获取 java.lang.reflect.Method

javascript - 如何从 GraphQL 突变访问变量值?

java - STS Tomcat Deploy 导致 IllegalArgumentException

spring - JavaEE 6 和 Spring