文章目录
- 1. 日志
- 2. 如何记录日志
 
1. 日志
服务器要在无人看管的情况下运行很长时间,通常需要在很久以后对服务器中发生的情况进行调试,这很重要。由于这个原因,建议在存储服务器日志,至少要存储一段时间的日志。日志中通常希望记录的主要内容是:
- 请求
- 服务器错误
实际上,服务器一般会为这两项内容维护两个不同的日志,审计日志中,对应与服务器建立的每一个连接会分别包含一个记录,每个连接完成多个操作的服务器可能对每个操作都有一个记录。例如,dict服务器可能会为客户端查找的每个单词建立一个日志记录。错误日志则主要包含服务器运行期间发送的意外异常,主要记录程序中发生的一些异常。
2. 如何记录日志
在java1.4之前,程序基本使用的是第三方日志库,如log4j或Aache Commons Logging,之后官方提供了java.util.logging包,它能满足绝大多数需求。尽管可以根据需要来加载日志工具,不过需要最容易的办法是为每个类创建一个日志工具,如下:
private final static Logger auditLogger=Logger.getLogger("requests");
日志工具是线程安全的,所以将它们存储在共享静态字段中没有任何问题,实际上,往往需要这么做,因为即使不用在线程之间共享Logger对象,日志文件或数据块也需要共享。这在大量使用多线程的服务器中非常重要。上面的例子输出到一个名为"requests"的日志中,这个日志是什么、放在哪里取决于外部配置。它不一定是一个文件,可能是一个数据块、一个在多个服务器上运行的SOAP服务、同一个主机上运行的另一个java程序,或者是其它形式。一旦又了一个日志工具,可以使用多个方法写入这个日志。最基本的是log()。java.util.logging.Level中命名常量定义了七个级别,按严重性从高到低依次为:
- Level.OFF
- Level.SERVE
- Level.WARNING
- Level.INFO
- Level.CONFIG
- Level.FINE
- Level.FINER
- Level.FINEST
- Level.ALL
我们通常会对审计日志使用INFO级别,对错误日志使用WARNING级别或SERVER级别。较低级别用于调试,不要在生成系统中使用。可以对各个日志使用任何方便的格式。一般来讲,每个记录应该包含一个时间戳、一个客户端地址,以及所处理的请求的任何特定信息。如果日志消息表示为一个叫错误,则要抛出特定的异常。Java会自动填入记录这个消息所在的代码位置,所以这个方面不用操心。
下面代码展示了如何为daytime服务器增加日志记录
public class QuizCardBuilder {
    private final static Logger auditLogger=Logger.getLogger("requests");
    private final static Logger errorLogger= Logger.getLogger("errors");
    public static void main(String[] args) {
        ExecutorService pool=Executors.newFixedThreadPool(50);
        try(ServerSocket server=new ServerSocket(8080)){
            while(true){
                try {
                    Socket connection=server.accept();
                    Callable<Void> task=new DaytimeTask(connection);
                    pool.submit(task);
                }catch (IOException ex){
                    errorLogger.log(Level.SEVERE,"accept  error"+ex);
                }catch (RuntimeException ex){
                    errorLogger.log(Level.SEVERE,"unexcepted error"+ex.getMessage(),ex);
                }
            }
        }catch (IOException e) {
            errorLogger.log(Level.SEVERE,"Coundn`t start server"+e);
        }catch (RuntimeException e){
            errorLogger.log(Level.SEVERE,"Coundm`t start server"+e);
        }
    }
    private static class DaytimeTask implements Callable<Void>{
        private Socket connection;
        DaytimeTask(Socket connection){
            this.connection=connection;
        }
        @Override
        public Void call() {
            try{
                Date now=new Date();
                //先写入日志记录以防万一客户端端口连接
                auditLogger.info(now+" "+connection.getRemoteSocketAddress());
                Writer out=new OutputStreamWriter(connection.getOutputStream());
                out.write(now.toString()+"\r\n");
                out.flush();
            }catch (IOException e){
               if(connection!=null){
                   try {
                       connection.close();
                   } catch (IOException ex) {
                       throw new RuntimeException(ex);
                   }
               }
            }finally {
                if(connection!=null){
                    try {
                        connection.close();
                    } catch (IOException ex) {
                        throw new RuntimeException(ex);
                    }
                }
            }
            return null;
        }
    }
}
多次在终端使用Telnet与dayTime服务器通信

查看结果

 上面的日志信息是直接在控制台打印的,如果我们希望将日志文件放到更加持久的位置,虽然我们可以在代码中指定,但是更好的是在配置文件中指定。这样就能改变日志位置而无需重新编译代码。java.util.logging.config.file系统属性采用常规的属性格式指向控制日志记录的一个文件。可以在启动虚拟机时加入vm配置-Djava.util.logging.config.file=_filename_参数来设置这个属性。
handlers=java.util.logging.FileHandler
java.util.logging.FileHandler.pattern=/var/logs/daytime/requests.log
java.util.logging.FileHandler.limit=10000000
java.util.logging.FileHandler.count=2
java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter
java.util.logging.FileHandler.append=true
java.util.logging.FileHandler.format=%4$s: %5$s [%1$tc]%n
上面的配置指定来下面的内容:
- 日志要写入文件
- 请求日志应当在/var/logs/daytime/requests.log(INFO级别)
- 错误日志应当在/var/logs/daytime/requests.log(SERVER级别)
- 日志大小限制为10MB,然后轮换
- 维护两个日志:当前日志和之前的日志
- 使用基本文本格式化工具
- 日志的每一行采用消息级别时间戳














