QuickServer开发指南(7)- 使用和定制日志


对任何一个项目来说,日志都是一个重要的工具。日志帮助我们去理解我们的项目内部发生了什么,它也会提供审核和调试信息。想要知道更多有关日志的资料可查阅Sun公司的网站
    [url]http://java.sun.com/j2se/1.4.0/docs/guide/util/logging/overview.html[/url]
    QuickServer目前只支持Java Logging API (java.util.logging)。1.4版的Java已加入logging。如果你还在使用旧的版本,你可以安装Lumberjack库提供的可选的实现( [url]http://javalogging.sourceforge.net/[/url])。
    让我们给我们的EchoServer做一个日志器。QuickServer默认已可以使用日志器,但是除了ConsoleHandler和设置INFO的 级别之外它什么也没有处理。因而无论何时我们在EchoServer关闭和客户端的连接,我们都会在控制台看见一些相似的信息
        Feb 16, 2000 10:11:25 PM ClientHandler sendSystemMsg
        INFO: Closing connection : /127.0.0.1
    上面的信息指明IP为127.0.0.1的客户端关闭了连接。这条消息的显示使用了ClientHalder类的sendSystemMsg()方法.
    让我们看看我们怎样在我们的项目中控制日志。使用java logging必须先在类中导入java.util.logging包。
    从上面的日志,我们发现某些客户端关闭了连接或掉线,但是它是怎样显示的,我们并没有写任何有关日志的命令。它会显示是因为QuickServer使用了内部的日志器ClientHandler类的sendSystemMsg()方法。
    查看QuickServer文档可以找到sendSystemMsg(),如果没有指定级别,它默认使用INFO。你可以在ClientCommandHandler或Authenticator或任何类中使用sendSystemMsg()来记录一个事件。

1. 简单的日志
   现在我们来记录每一个连接EchoServer的客户端IP地址。编辑EchoCommandHandler.java文件。在gotConnected(ClientHandler handler)后添加
    handler.sendSystemMsg("New Client : " +
                          handler.getSocket().getInetAddress().getHostAddress(),
                          Level.INFO);
   还要导入包import java.util.logging.*;
   编译并运行,连接到EchoServer,你将看到当客户端连接时控制台会显示你的地址。
   让QuickServer将日志记录到文件中(XML格式)。下面是修改的文件:
01 package echoserver;

02

03 import org.quickserver.net.*;

04 import org.quickserver.net.server.*;

05

06 import java.io.*;

07 import java.util.logging.*;

08

09 public class EchoServer {

10 public static void main(String s[]) {

11

12 String cmd = "echoserver.EchoCommandHandler" ;

13 String auth = "echoserver.EchoServerQuickAuthenticator" ;

14 String data = "echoserver.EchoServerPoolableData" ; //Poolable

15

16 QuickServer myServer = new QuickServer();

17

18 //setup logger to log to file

19 Logger logger = null ;

20 FileHandler xmlLog = null ;

21 File log = new File( "./log/" );

22 if (!log.canRead())

23 log.mkdir();

24 try {

25 logger = Logger.getLogger( "" ); //get root logger

26 logger.setLevel(Level.INFO);

27 xmlLog = new FileHandler("log/EchoServer.xml" );
28 logger.addHandler(xmlLog);

29 } catch(IOException e){

30 System.err.println("Could not create xmlLog FileHandler : "+e);

31 }

32 //set logging level to fine

33 myServer setConsoleLoggingLevel(Level INFO);

34

35

36 myServer.setClientCommandHandler(cmd);

37 myServer.setAuthenticator(auth);

38 myServer.setClientData(data);

39

40 myServer.setPort( 4123 );

41 myServer.setName( "Echo Server v 1.0" );

42

43 //store data needed to be changed by QSAdminServer

44 Object[] store = new Object[]{ "12.00" };

45 myServer.setStoreObjects(store);

46

47 //config QSAdminServer

48 myServer.setQSAdminServerPort( 4124 );

49 myServer.getQSAdminServer().getServer().setName( "EchoAdmin v 1.0" );

50 try {

51 //add command plugin

52 myServer.getQSAdminServer().setCommandPlugin(

53 "echoserver.QSAdminCommandPlugin" );

54 myServer.startQSAdminServer();

55 myServer.startServer();

56 } catch (AppException e){

57 System.out.println( "Error in server : " +e);

58 } catch (Exception e){

59 System.out.println( "Error : " +e);

60 }

61 }

62 }

    在上面的代码中,我们首先检查是否存在一个日志文件夹(21、22行),如果没有先创建它。
    在25行,我们尝试获得一个日志器,28行,添加一个新的FileHandler,这里我们没有指定格式因为默认的格式就是XML的。
    在33行,我们设置日志的控制级别为INFO。
    接下来修改EchoCommandHandler.java

01 // EchoCommandHandler.java

02 package echoserver;

03

04 import java.net.*;

05 import java.io.*;

06 import org.quickserver.net.server.ClientCommandHandler;

07 import org.quickserver.net.server.ClientHandler;

08 import java.util.logging.*;

09

10 public class EchoCommandHandler implements ClientCommandHandler {

11

12 public void gotConnected(ClientHandler handler)

13 throws SocketTimeoutException, IOException {

14 handler.sendSystemMsg( "New Client : " +

15 handler.getSocket().getInetAddress().getHostAddress(),

16 Level.INFO);

17 handler.sendClientMsg( "+++++++++++++++++++++++++++++++" );

18 handler.sendClientMsg( "| Welcome to EchoServer v 1.0 |" );

19 handler.sendClientMsg( "| Note: Password = Username |" );

20 handler.sendClientMsg( "| Send 'Quit' to exit |" );

21 handler.sendClientMsg( "+++++++++++++++++++++++++++++++" );

22 }

23 public void lostConnection(ClientHandler handler)

24 throws IOException {

25 handler.sendSystemMsg( "Connection lost : " +

26 handler.getSocket().getInetAddress());

27 }

28 public void closingConnection(ClientHandler handler)

29 throws IOException {

30 handler.sendSystemMsg( "Closing connection : " +

31 handler.getSocket().getInetAddress());

32 }

33

34 public void handleCommand(ClientHandler handler, String command)

35 throws SocketTimeoutException, IOException {

36 if (command.equals( "Quit" )) {

37 handler.sendClientMsg( "Bye ;-)" );

38 handler.closeConnection();

39 return ;

40 }

41 if (command.equals( "What's interest?" )) {

42 handler.sendClientMsg( "Interest is : " +

43 (String)handler.getServer().getStoreObjects()[ 0 ]+

44 "%" );

45 } else if (command.equalsIgnoreCase( "hello" )) {

46 EchoServerData data = (EchoServerData) handler.getClientData();

47 data.setHelloCount(data.getHelloCount()+ 1 );

48 if (data.getHelloCount()== 1 ) {

49 handler.sendClientMsg( "Hello " +data.getUsername());

50 } else {

51 handler.sendClientMsg( "You told Hello " +data.getHelloCount()+

52 " times. " );

53 }

54 } else {

55 handler.sendClientMsg( "Echo : " +command);

56 }

57 }

58 }

    编译并运行程序,现在尝试连接,你将看到它会同时在控制台和xml文件记录你的IP地址。
    让我们使用QSAdminGUI设置日志级别为FINEST,观察日志内容的变化。日志记录增加了。另外一个改变日志级别的方法是修改代码,可以使用 logger.setLevel()或者QuickServer的方法setLoggingLevel()来设置所有句柄的日志级别。

2. 日志的高级应用
    当我们设置日志级别为FINEST时,会记录很多信息。一个可能的需要是分离QuickServer和应用的日志。下面的代码允许你这么做。
01 package echoserver;

02

03 import org.quickserver.net.*;

04 import org.quickserver.net.server.*;

05

06 import java.io.*;

07 import java.util.logging.*;

08

09 public class EchoServer {

10 public static void main(String s[]) {

11

12 String cmd = "echoserver.EchoCommandHandler" ;

13 String auth = "echoserver.EchoServerQuickAuthenticator" ;

14 String data = "echoserver.EchoServerPoolableData" ; //Poolable

15

16 QuickServer myServer = new QuickServer();

17

18 //setup logger to log to file

19 Logger logger = null ;

20 FileHandler xmlLog = null ;

21 FileHandler txtLog = null ;
22 File log = new File( "./log/" );

23 if (!log.canRead())

24 log.mkdir();

25 try {

26 logger = Logger.getLogger( "org.quickserver.net" ); //get QS logger

27 logger.setLevel(Level.FINEST);

28 xmlLog = new FileHandler( "log/EchoServer.xml" );

29 logger.addHandler(xmlLog);

30

31 logger = Logger.getLogger( "echoserver" ); //get App logger

32 logger.setLevel(Level.FINEST);

33 txtLog = new FileHandler( "log/EchoServer.txt" );

34 txtLog.setFormatter( new SimpleFormatter());

35 logger.addHandler(txtLog);

36 myServer.setAppLogger(logger); //img : Sets logger to be used for app.

37 } catch (IOException e){

38 System.err.println( "Could not create xmlLog FileHandler : " +e);

39 }

40 //set logging level to fine

41 myServer.setConsoleLoggingLevel(Level.INFO);

42

43

44 myServer.setClientCommandHandler(cmd);

45 myServer.setAuthenticator(auth);

46 myServer.setClientData(data);

47

48 myServer.setPort( 4123 );

49 myServer.setName( "Echo Server v 1.0" );

50

51 //store data needed to be changed by QSAdminServer

52 Object[] store = new Object[]{ "12.00" };

53 myServer.setStoreObjects(store);

54

55 //config QSAdminServer

56 myServer.setQSAdminServerPort( 4124 );

57 myServer.getQSAdminServer().getServer().setName( "EchoAdmin v 1.0" );

58 try {

59 //add command plugin

60 myServer.getQSAdminServer().setCommandPlugin(

61 "echoserver.QSAdminCommandPlugin" );

62 myServer.startQSAdminServer();

63 myServer.startServer();

64 } catch (AppException e){

65 System.out.println( "Error in server : " +e);

66 } catch (Exception e){

67 System.out.println( "Error : " +e);

68 }

69 }

70 }

    代码中的注释说明的很清楚了。编译并运行,连接后你将看见QuickServer内部的日志记录到了xml文件,应用级别的日志记录到了我们想要的txt文件。

注意:
    要尽量减少控制台中的日志数量,使用文件记录详细的日志,并降低日志的级别。这样可以改善应用的性能。避免使用System.out.println (),用logging代替。ClientHandler中的sendSystemMsg()方法在记录日志方面很有用。