`
kdmhh
  • 浏览: 791 次
  • 性别: Icon_minigender_2
  • 来自: 北京
最近访客 更多访客>>
文章分类
社区版块
存档分类
最新评论

log4j实现日志集中存储

 
阅读更多

先描述一下问题,多个服务器实现的负载均衡,每个服务器存储在自己的硬盘里。但是现在需要对日志做统一的分析,在多个服务器上统计就麻烦了。思路是把日志统一到一台日志服务器上,再统一做统计分析。怎么统一到一台服务器上,说实话没有特别好的思路,最后尝试了log4j的SocketAppender。查了不少网络资源,都说的有些不明了,还是得亲自尝试之后才见分晓。

1、客户端的配置

客户端的配置比较简单,只需要告诉log4j需要监听哪个远程服务器的哪个端口即可。直接在log4j.properties里直接配置就好。

log4j.appender.logs=org.apache.log4j.DailyRollingFileAppender
log4j.appender.logs.File = /data/logs/request/logs.log
log4j.appender.logs.layout = org.apache.log4j.PatternLayout
log4j.appender.logs.layout.ConversionPattern=%d [%t] - %m%n
log4j.appender.logs.DatePattern='.'yyyy-MM-dd'.log'

log4j.appender.socket=org.apache.log4j.net.SocketAppender
log4j.appender.socket.RemoteHost=172.16.2.152
log4j.appender.socket.Port=4560
log4j.appender.socket.LocationInfo=true
#下面这两句感觉没用
log4j.appender.socket.layout=org.apache.log4j.PatternLayout
log4j.appender.socket.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%t%m%n

#将日志写入本地和远程日志服务器
log4j.logger.com.test.core.filter =DEBUG,socket,logs

2、日志服务器的配置

日志服务器需要单独启动一个java进程,接收客户端给自己发送的socket请求。Log4j提供了org.apache.log4j.net.SocketServer类,直接运行其main函数就行了(当然也可以自己写啦)。

java -cp /log4jsocket/serverConfig/log4j-1.2.16.jarorg.apache.log4j.net.SocketServer 4560 /log4jsocket/log4jserver.properties /log4jsocket/clientConfig

/log4jsocket/serverConfig/log4j-1.2.16.jarlog4j jar包存放的位置,org.apache.log4j.net.SocketServer需要三个参数:

1)4560是监听的端口号

2)/log4jsocket/log4jserver.properties是记录日志服务器的日志的配置文件

3)/log4jsocket/clientConfig是客户端配置文件所在的目录(注意是目录)。

着重说一下org.apache.log4j.net.SocketServer的第三个参数,这个文件夹下配置的是各个客户端的日志的配置。配置文件以.lcf结尾,文件名可以用客户端的IP命名,log4j会自己找发送请求的客户端IP对应的那个配置文件,如172.16.2.46服务器发送的socket请求会寻找172.16.2.46.lcf配置文件,并根据配置将日志写入对应的文件。

#注意logger后面的值要与client的值相同
log4j.logger.com.test.core.filter=DEBUG,localLogs
 
log4j.appender.localLogs=org.apache.log4j.DailyRollingFileAppender
log4j.appender.localLogs.File=/data/logs/request/172.16.2.46/logs.log
log4j.appender.localLogs.layout=org.apache.log4j.PatternLayout
log4j.appender.localLogs.layout.ConversionPattern=%d [%t] - %m%n
log4j.appender.localLogs.DatePattern='.'yyyy-MM-dd'.log'


这样做的好处是可以根据不同客户端,将日志写入不同的文件夹下的。

其实,配置过程就这么简单,但是当你这么做之后,你会发现运行org.apache.log4j.net.SocketServer后,客户端向日志服务器发送请求时,会报找不到.lcf文件的错误,得不到想要的结果。原因出在org.apache.log4j.net.SocketServer代码中的一个小bug

LoggerRepository configureHierarchy(InetAddress inetAddress)
  {
    cat.info("Locating configuration file for " + inetAddress);

    String s = inetAddress.toString();
    int i = s.indexOf("/");
    if (i == -1) {
      cat.warn("Could not parse the inetAddress [" + inetAddress + "]. Using default hierarchy.");

      return genericHierarchy();
    }
    String key = s.substring(0,i);

    File configFile = new File(this.dir, key + CONFIG_FILE_EXT);
    if (configFile.exists()) {
      Hierarchy h = new Hierarchy(new RootLogger(Level.DEBUG));
      this.hierarchyMap.put(inetAddress, h);

      new PropertyConfigurator().doConfigure(configFile.getAbsolutePath(), h);

      return h;
    }
    cat.warn("Could not find config file [" + configFile + "].");
    return genericHierarchy();
  }

String key = s.substring(0, i);换成String key = s.substring(i+1);就好了。这段代码是解析IP地址,然后寻找对应IP命名的.lcf配置文件;如果找不到,则解析默认的generic.lcf。由于截取的错误,导致找不到172.16.2.46.lcf,文件夹下又没有generic.lcf,所以会抛异常。

org.apache.log4j.net.SocketServer代码中的另外一个bug是,只能接收来自一台客户端的日志请求,一旦客户端停止运行,SocketServer也将关闭。查看代码:

public static void main(String[] argv)
  {	  
	  if (argv.length == 3)
      init(argv[0], argv[1], argv[2]);
    else
      usage("Wrong number of arguments.");
    try
    {
      	cat.info("Listening on port " + port);
      	ServerSocket serverSocket = new ServerSocket(port);
      	cat.info("Waiting to accept a new client.");
	Socket socket = serverSocket.accept();
	InetAddress inetAddress = socket.getInetAddress();
	cat.info("Connected to client at " + inetAddress);
	
	LoggerRepository h = (LoggerRepository)server.hierarchyMap.get(inetAddress);
	if (h == null) {
	        h = server.configureHierarchy(inetAddress);
	}
	
	cat.info("Starting new socket node.");
	new Thread(new SocketNode(socket, h)).start();
      }
    catch (Exception e)
    {
      e.printStackTrace();
    }
  }

问题出在只建立了一个socket连接就不在accept了,加上while循环问题就解决了。

      ServerSocket serverSocket = new ServerSocket(port);
      while(true){
	      cat.info("Waiting to accept a new client.");
	      Socket socket = serverSocket.accept();
	      InetAddress inetAddress = socket.getInetAddress();
	      cat.info("Connected to client at " + inetAddress);
	
	      LoggerRepository h = (LoggerRepository)server.hierarchyMap.get(inetAddress);
	      if (h == null) {
	        h = server.configureHierarchy(inetAddress);
	      }
	
	      cat.info("Starting new socket node.");
	      new Thread(new SocketNode(socket, h)).start();
      }



好了。Log4j的配置到此结束。

最后一个问题,日志服务器是linux,需要有一个统一的start、shutdown命令来启动和关闭org.apache.log4j.net.SocketServer。那就需要些shell命令了,下面这段代码参考了http://www.cnblogs.com/baibaluo/archive/2011/08/31/2160934.html

catalina.sh

#!/bin/bash
#端口
LISTEN_PORT=4560
#服务端log4j配置文件
SERVER_CONFIG=/log4jsocket/server.properties
#客户端的配置
CLIENT_CONFIG_DIR=/log4jsocket/clientConfig

#Java程序所在的目录(classes的上一级目录)
APP_HOME=/opt/log4jsocket/serverConfig 
#需要启动的Java主程序(main方法类)
APP_MAINCLASS=org.apache.log4j.net.SocketServer
 
#拼凑完整的classpath参数,包括指定lib目录下所有的jar
CLASSPATH=$APP_HOME
for i in "$APP_HOME"/*.jar; do   
    CLASSPATH="$CLASSPATH":"$i"
done

#JDK所在路径
JAVA_HOME="/opt/jdk1.6.0_30" 
#执行程序启动所使用的系统用户,考虑到安全,推荐不使用root帐号
RUNNING_USER=root
 
#java虚拟机启动参数
JAVA_OPTS="-ms512m -mx512m -Xmn256m -Djava.awt.headless=true -XX:MaxPermSize=128m" 

#初始化psid变量(全局)
psid=0
 
checkpid() {
   javaps=`$JAVA_HOME/bin/jps -l | grep $APP_MAINCLASS`
 
   if [ -n "$javaps" ]; then
      psid=`echo $javaps | awk '{print $1}'`
   else
      psid=0
   fi
}

start() {
   checkpid
 
   if [ $psid -ne 0 ]; then
      echo "================================"
      echo "warn: $APP_MAINCLASS already started! (pid=$psid)"
      echo "================================"
   else
      echo -n "Starting $APP_MAINCLASS ..."
      JAVA_CMD="nohup $JAVA_HOME/bin/java -classpath $CLASSPATH $APP_MAINCLASS $LISTEN_PORT $SERVER_CONFIG $CLIENT_CONFIG_DIR >/dev/null 2>&1 &"
      su - $RUNNING_USER -c "$JAVA_CMD"
      checkpid
      if [ $psid -ne 0 ]; then
         echo "(pid=$psid) [OK]"
      else
         echo "[Failed]"
      fi
   fi
}

stop() {
   checkpid
 
   if [ $psid -ne 0 ]; then
      echo -n "Stopping $APP_MAINCLASS ...(pid=$psid) "
      su - $RUNNING_USER -c "kill -9 $psid"
      if [ $? -eq 0 ]; then
         echo "[OK]"
      else
         echo "[Failed]"
      fi
 
      checkpid
      if [ $psid -ne 0 ]; then
         stop
      fi
   else
      echo "================================"
      echo "warn: $APP_MAINCLASS is not running"
      echo "================================"
   fi
}

status() {
   checkpid
 
   if [ $psid -ne 0 ];  then
      echo "$APP_MAINCLASS is running! (pid=$psid)"
   else
      echo "$APP_MAINCLASS is not running"
   fi
}
info() {
   echo "System Information:"
   echo "****************************"
   echo `head -n 1 /etc/issue`
   echo `uname -a`
   echo
   echo "JAVA_HOME=$JAVA_HOME"
   echo `$JAVA_HOME/bin/java -version`
   echo
   echo "APP_HOME=$APP_HOME"
   echo "APP_MAINCLASS=$APP_MAINCLASS"
   echo "****************************"
}
case "$1" in

   'start')
      start
      ;;
   'stop')
     stop
     ;;
   'restart')
     stop
     start
     ;;
   'status')
     status
     ;;
   'info')
     info
     ;;
  *)
     echo "Usage: $0 {start|stop|restart|status|info}" 
     exit 0 
esac
startup.sh

#!/bin/sh
EXECUTABLE=/log4jsocket/catalina.sh
exec "$EXECUTABLE" start "$@"
shutdown.sh

EXECUTABLE=/log4jsocket/catalina.sh
exec "$EXECUTABLE" stop "$@"


PS: csdn博客啥时可以上传附件啊


分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics