现在的位置: 首页 > 综合 > 正文

log4j 日志服务器

2013年10月23日 ⁄ 综合 ⁄ 共 16034字 ⁄ 字号 评论关闭
文章目录

 

功能说明

一个系统可能有多个子系统组成,这些子系统都有自己的日志,并且运行在不同的操作系统和主机上,收集这些日志对运营人员来说也比较困难。
因此决定在平台中采用日志服务器来做到集中日志管理,平台中所有子系统通过socket方式将日志信息传到日志服务器,再由日志服务器统一记录。这样既避免了一个应用日志不同实例分别打印,也可以将所有子系统日志集中管理,并能够自定义输出路径。

实现原理

Log4j提供了一个简单的基于socket的日志服务器,但直接使用这个服务器不能完全满足我们的需求,首先它自身代码存在问题,需要修改;其次即使修改正确,该服务器也只能按客户端IP配置打印appender,而我们有些子系统是运行在同一主机,直接使用该服务器只能将运行在同一主机上的子系统日志打在一起,不便于分析处理。我们要求按照不同应用输出日志。因此我们要对其进行改造。
Log4j提供的日志服务器由SocketServer.java和SocketNode.java实现,我们需要改造这两个类,以达到我们的目的。
Log4j提供的SocketServer利用一个Hashtable的变量hierarchyMap保存各个客户端的log4j配置信息,一旦侦听到某个客户端发送请求过来,则立刻New一个SocketNode来处理该请求,该SocketNode的构造参数中有一个是从hierarchyMap中获取的log4j配置信息,SocketNode将根据该配置信息直接输出客户端发送的日志请求。
改造后的日志服务器, SocketServer仍然利用hierarchyMap保存各个客户端的log4j配置信息,但这次不是基于客户端IP,而是基于应用的,当侦听到某个客户端发送请求过来,则同样New一个SocketNode来处理该请求,hierarchyMap将作为改造后的SocketNode一个构造参数,这样SocketNode自己就能够根据客户端请求内容来决定使用哪个log4j配置信息输出客户端日志请求,这里有个关键就是客户端需要上传信息表明自己是哪个应用。
分析Log4j源码,我们发现可以为SocketAppender配置一个属性application,而这个属性在服务端是可以获取的,SocketNode读取这个属性并自动选择相应的log4j配置信息来处理

更改代码

1 SocketNode.java

  1. package org.apache.log4j.net;  
  2.   
  3. import java.io.BufferedInputStream;  
  4. import java.io.IOException;  
  5. import java.io.ObjectInputStream;  
  6. import java.net.Socket;  
  7. import java.util.Hashtable;  
  8.   
  9. import org.apache.log4j.Hierarchy;  
  10. import org.apache.log4j.Logger;  
  11. import org.apache.log4j.spi.LoggingEvent;  
  12.   
  13. /** 
  14.  *  
  15.  * Read {@link LoggingEvent} objects sent from a remote client using Sockets 
  16.  *  
  17.  * (TCP). These logging events are logged according to local policy, as if they 
  18.  *  
  19.  * were generated locally. 
  20.  *  
  21.  * <p> 
  22.  *  
  23.  * For example, the socket node might decide to log events to a local file and 
  24.  *  
  25.  * also resent them to a second socket node. 
  26.  *  
  27.  * @author zhoulianglg *  */  
  28.   
  29. public class SocketNode implements Runnable {  
  30.   
  31.     Socket socket;  
  32.   
  33.     ObjectInputStream ois;  
  34.   
  35.     Hashtable<String, Hierarchy> hashtable;  
  36.   
  37.     static Logger logger = Logger.getLogger(SocketNode.class);  
  38.   
  39.     public SocketNode(Socket socket, Hashtable<String, Hierarchy> hashtable) {  
  40.   
  41.         this.socket = socket;  
  42.   
  43.         this.hashtable = hashtable;  
  44.   
  45.         try {  
  46.   
  47.             ois = new ObjectInputStream(new BufferedInputStream(  
  48.                     socket.getInputStream()));  
  49.   
  50.         } catch (Exception e) {  
  51.   
  52.             logger.error("Could not open ObjectInputStream to " + socket, e);  
  53.   
  54.         }  
  55.   
  56.     }  
  57.   
  58.   
  59.     public void run() {  
  60.   
  61.         LoggingEvent event;  
  62.   
  63.         Logger remoteLogger;  
  64.   
  65.         try {  
  66.   
  67.             if (ois != null) {  
  68.   
  69.                 while (true) {  
  70.   
  71.                     // read an event from the wire
      
  72.   
  73.                     event = (LoggingEvent) ois.readObject();  
  74.   
  75.                     Object application = event.getMDC("application");  
  76.   
  77.                     if (application != null) {  
  78.   
  79.                         // get a logger from the hierarchy. The name of the
      
  80.   
  81.                         // logger
      
  82.   
  83.                         // is taken to be the name contained in the event.
      
  84.                         if(hashtable.get(application)==null || hashtable.get(application).equals("")){  
  85.                             application="default";  
  86.                             logger.info("Using the default");  
  87.                         }  
  88.                         remoteLogger = hashtable.get(application).getLogger(  
  89.                                 event.getLoggerName());  
  90.   
  91.                         // apply the logger-level filter
      
  92.   
  93.                         if (remoteLogger != null  
  94.                                 && event.getLevel().isGreaterOrEqual(  
  95.                                         remoteLogger.getEffectiveLevel())) {  
  96.   
  97.                             // finally log the event as if was generated locally
      
  98.   
  99.                             remoteLogger.callAppenders(event);  
  100.   
  101.                         }  
  102.   
  103.                     }  
  104.   
  105.                 }  
  106.   
  107.             }  
  108.   
  109.         } catch (java.io.EOFException e) {  
  110.   
  111.             logger.info("Caught java.io.EOFException closing conneciton.");  
  112.   
  113.         } catch (java.net.SocketException e) {  
  114.   
  115.             logger.info("Caught java.net.SocketException closing conneciton.");  
  116.   
  117.         } catch (IOException e) {  
  118.   
  119.             logger.info("Caught java.io.IOException: " + e);  
  120.   
  121.             logger.info("Closing connection.");  
  122.   
  123.         } catch (Exception e) {  
  124.   
  125.             logger.error("Unexpected exception. Closing conneciton.", e);  
  126.   
  127.         } finally {  
  128.   
  129.             if (ois != null) {  
  130.   
  131.                 try {  
  132.   
  133.                     ois.close();  
  134.   
  135.                 } catch (Exception e) {  
  136.   
  137.                     logger.info("Could not close connection.", e);  
  138.   
  139.                 }  
  140.   
  141.             }  
  142.   
  143.             if (socket != null) {  
  144.   
  145.                 try {  
  146.   
  147.                     socket.close();  
  148.   
  149.                 } catch (IOException ex) {  
  150.   
  151.                 }  
  152.   
  153.             }  
  154.   
  155.         }  
  156.   
  157.     }  
  158.   
  159. }  


2 SocketServer.java

  1. package org.apache.log4j.net;  
  2.   
  3. import java.io.File;  
  4.   
  5. import java.net.InetAddress;  
  6.   
  7. import java.net.ServerSocket;  
  8.   
  9. import java.net.Socket;  
  10.   
  11. import java.util.Hashtable;  
  12.   
  13. import org.apache.log4j.Hierarchy;  
  14.   
  15. import org.apache.log4j.Level;  
  16.   
  17. import org.apache.log4j.Logger;  
  18.   
  19. import org.apache.log4j.PropertyConfigurator;  
  20.   
  21. import org.apache.log4j.spi.RootLogger;  
  22.   
  23. public class SocketServer {  
  24.   
  25.     static String CLIENT_DIR = "client";  
  26.   
  27.     static String CONFIG_FILE_EXT = ".properties";  
  28.   
  29.     static Logger cat = Logger.getLogger(SocketServer.class);  
  30.   
  31.     static SocketServer server;  
  32.   
  33.     static int port;// key=application, value=hierarchy
      
  34.   
  35.     Hashtable<String, Hierarchy> hierarchyMap;  
  36.   
  37.     String dir;  
  38.   
  39.     public static void main(String argv[]) {  
  40.   
  41.         if (argv.length == 2)  
  42.   
  43.             init(argv[0], argv[1]);  
  44.   
  45.         else  
  46.   
  47.             usage("Wrong number of arguments.");  
  48.   
  49.         // init("8899", "config");
      
  50.   
  51.         try {  
  52.   
  53.             cat.info("Listening on port " + port);  
  54.   
  55.             ServerSocket serverSocket = new ServerSocket(port);  
  56.   
  57.             while (true) {  
  58.   
  59.                 cat.info("Waiting to accept a new client.");  
  60.   
  61.                 Socket socket = serverSocket.accept();  
  62.   
  63.                 InetAddress inetAddress = socket.getInetAddress();  
  64.   
  65.                 cat.info("Connected to client at " + inetAddress);  
  66.   
  67.                 cat.info("Starting new socket node.");  
  68.   
  69.                 new Thread(new SocketNode(socket, server.hierarchyMap)).start();  
  70.   
  71.             }  
  72.   
  73.         } catch (Exception e) {  
  74.   
  75.             e.printStackTrace();  
  76.   
  77.         }  
  78.   
  79.     }  
  80.   
  81.     static void usage(String msg) {  
  82.   
  83.         System.err.println(msg);  
  84.   
  85.         System.err.println("Usage: java " + SocketServer.class.getName()  
  86.                 + " port configFile directory");  
  87.   
  88.         System.exit(1);  
  89.   
  90.     }  
  91.   
  92.     static void init(String srvPort, String configDir) {  
  93.   
  94.         try {  
  95.   
  96.             port = Integer.parseInt(srvPort);  
  97.   
  98.         } catch (java.lang.NumberFormatException e) {  
  99.   
  100.             e.printStackTrace();  
  101.   
  102.             usage("Could not interpret port number [" + srvPort + "].");  
  103.   
  104.         }  
  105.   
  106.         PropertyConfigurator.configure(configDir + File.separator  
  107.                 + "socketserver.properties");  
  108.   
  109.         server = new SocketServer(configDir);  
  110.   
  111.     }  
  112.   
  113.     public SocketServer(String configDir) {  
  114.   
  115.         this.dir = configDir;  
  116.   
  117.         hierarchyMap = new Hashtable<String, Hierarchy>(11);  
  118.   
  119.         configureHierarchy();  
  120.   
  121.     }  
  122.   
  123.     // This method assumes that there is no hiearchy for inetAddress
      
  124.   
  125.     // yet. It will configure one and return it.
      
  126.   
  127.     void configureHierarchy() {  
  128.   
  129.         File configFile = new File(dir + File.separator + CLIENT_DIR);  
  130.   
  131.         if (configFile.exists() && configFile.isDirectory()) {  
  132.   
  133.             String[] clients = configFile.list();  
  134.   
  135.             for (int i = 0; i < clients.length; i++) {  
  136.   
  137.                 File client = new File(dir + File.separator + CLIENT_DIR  
  138.                         + File.separator + clients[i]);  
  139.   
  140.                 if (client.isFile()) {  
  141.   
  142.                     Hierarchy h = new Hierarchy(new RootLogger(Level.DEBUG));  
  143.   
  144.                     String application = clients[i].substring(0,  
  145.                             clients[i].indexOf("."));  
  146.   
  147.                     cat.info("Locating configuration file for " + application);  
  148.   
  149.                     hierarchyMap.put(application, h);  
  150.   
  151.                     new PropertyConfigurator().doConfigure(  
  152.                             client.getAbsolutePath(), h);  
  153.   
  154.                 }  
  155.   
  156.             }  
  157.   
  158.         }  
  159.   
  160.     }  
  161.   
  162. }  

 

 

服务端

运行环境

jdk:jdk1.5以上(含1.5)

环境配置

对于运行于linux环境,我们可以用如下步骤配置运行环境。

方法一 配置系统变量
打开系统变量文件(.profile),在文件的末尾依次添加以下代码:
export JAVA_HOME=/usr/java/jdk1.5.0(以实际路径为准)
export CLASSPATH=$JAVA_HOME/lib.tools.jar:$JAVA_HOME/lib:$JAVA_HOME/jre/lib
export PATH=$HOME/bin:$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$PATH

方法二 配置局部变量
在log4jsocket.sh中添加环境变量配置
#!/bin/sh
export JAVA_HOME=/usr/java/jdk1.5.0
export CLASSPATH=$JAVA_HOME/lib.tools.jar:$JAVA_HOME/lib:$JAVA_HOME/jre/lib
export PATH=$HOME/bin:$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$PATH
java -cp ./log4j-1.2.16.jar org.apache.log4j.net.SocketServer 8899 config
注:建议使用方法二不影响其它系统

获取安装文件

http://download.csdn.net/detail/zhoulianglg/4025803

 

获取文件后,解压到要安装的路径。

目录结构
  • logservers

    • config

      • client

        • default.properties
        • test1.properties
        • test2.properties
      • socketserver.properties
    • log4j-1.2.16.jar
    • log4jsocket.bat
    • log4jsocket.sh

要在client目录添加.propreties 文件,文件名跟客户端发动过来的参数名相同 例:test.properties
log4j.rootLogger=debug,test

log4j.appender.test=org.apache.log4j.DailyRollingFileAppender
log4j.appender.test.File=logs/test.log
log4j.appender.test.Encoding=GBK
log4j.appender.test.layout=org.apache.log4j.PatternLayout
log4j.appender.test.DatePattern='.'yyyy-MM-dd
log4j.appender.test.layout.ConversionPattern=test%d{yyyy-MM-dd HH:mm:ss}[%24F:%-3L:%-5p]%x %m%n

注:具体配置就跟客户端log4j配置方式一样,这里如何配置log服务器就会按照这里的配置生成log文件
若此处没有配置相应的配置文件 默认执行 default.properties 文件

启用服务器命令

windows 直接双击打开 log4jsocket.bat
unix运行 sh log4jsocket.sh

日志服务器部署地址

IP:192.168.0.14
目录:/home/ifxt/sibas-cs/logservers

客户端

环境要求

log4j-1.2.15以上

客户端配置

在log4j.properties中加入
log4j.rootCategory=debug,test
log4j.appender.test=org.apache.log4j.net.SocketAppender
#发送到服务器端的地址
log4j.appender.test.RemoteHost=192.168.0.189
#端口号
log4j.appender.test.Port=8899
log4j.appender.test.LocationInfo=true
#发送过去的参数
log4j.appender.sibas.application=sibas

应用步骤

(以windows系统为例)

第一步获取安装log4j日志服务器

 http://download.csdn.net/detail/zhoulianglg/4025803

获取文件后,解压到你想存放的路径想。如:d:\

第二步为客户端配置连接

打开你要配置的项目的log4j.properties 文件
在log4j.properties中加入
log4j.rootCategory=debug,test
log4j.appender.test=org.apache.log4j.net.SocketAppender
#发送到服务器端的地址
log4j.appender.test.RemoteHost=192.168.0.189

#端口号
log4j.appender.test.Port=8899
log4j.appender.test.LocationInfo=true
#发送过去的参数
log4j.appender.sibas.application=test

配置服务端

在logservers/config/client 目录下添加一个命名为 test.properties 文件 文件名称与你在客户端配置的
log4j.appender.test.application=test名称相同
具体参考配置
log4j.rootLogger=debug,test

log4j.appender.test=org.apache.log4j.DailyRollingFileAppender
log4j.appender.test.File=logs/sibas.log
log4j.appender.test.Encoding=GBK
log4j.appender.test.layout=org.apache.log4j.PatternLayout
log4j.appender.test.DatePattern='.'yyyy-MM-dd
log4j.appender.test.layout.ConversionPattern=test%d{yyyy-MM-dd HH:mm:ss}[%24F:%-3L:%-5p]%x %m%n

注:此处配置作用是log4j服务器把客户端送过来的信息 按照现在的配置模式输出日志,日志模式跟此处配置有关,与客户端原有配置没有关系
此处要没有配置 相应的文件,就会默认 执行 default.properties 文件

启动日志服务

运行命令
java -cp ./log4j-1.2.16.jar org.apache.log4j.net.SocketServer 端口号 配置目录
例:java -cp ./log4j-1.2.16.jar org.apache.log4j.net.SocketServer 8899 config

注:端口号即为客户端log4j.appender.sibas.Port=8899 配置的端口号

抱歉!评论已关闭.