您的位置:首页 > 其它

log4j 日志服务器配置开发部署

2011-11-21 10:03 525 查看


功能说明

一个系统可能有多个子系统组成,这些子系统都有自己的日志,并且运行在不同的操作系统和主机上,收集这些日志对运营人员来说也比较困难。

因此决定在平台中采用日志服务器来做到集中日志管理,平台中所有子系统通过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

package org.apache.log4j.net;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.Socket;
import java.util.Hashtable;

import org.apache.log4j.Hierarchy;
import org.apache.log4j.Logger;
import org.apache.log4j.spi.LoggingEvent;

/**
*
* Read {@link LoggingEvent} objects sent from a remote client using Sockets
*
* (TCP). These logging events are logged according to local policy, as if they
*
* were generated locally.
*
* <p>
*
* For example, the socket node might decide to log events to a local file and
*
* also resent them to a second socket node.
*
* @author zhoulianglg *  */

public class SocketNode implements Runnable {

Socket socket;

ObjectInputStream ois;

Hashtable<String, Hierarchy> hashtable;

static Logger logger = Logger.getLogger(SocketNode.class);

public SocketNode(Socket socket, Hashtable<String, Hierarchy> hashtable) {

this.socket = socket;

this.hashtable = hashtable;

try {

ois = new ObjectInputStream(new BufferedInputStream(
socket.getInputStream()));

} catch (Exception e) {

logger.error("Could not open ObjectInputStream to " + socket, e);

}

}

public void run() {

LoggingEvent event;

Logger remoteLogger;

try {

if (ois != null) {

while (true) {

// read an event from the wire

event = (LoggingEvent) ois.readObject();

Object application = event.getMDC("application");

if (application != null) {

// get a logger from the hierarchy. The name of the

// logger

// is taken to be the name contained in the event.
if(hashtable.get(application)==null || hashtable.get(application).equals("")){
application="default";
logger.info("Using the default");
}
remoteLogger = hashtable.get(application).getLogger(
event.getLoggerName());

// apply the logger-level filter

if (remoteLogger != null
&& event.getLevel().isGreaterOrEqual(
remoteLogger.getEffectiveLevel())) {

// finally log the event as if was generated locally

remoteLogger.callAppenders(event);

}

}

}

}

} catch (java.io.EOFException e) {

logger.info("Caught java.io.EOFException closing conneciton.");

} catch (java.net.SocketException e) {

logger.info("Caught java.net.SocketException closing conneciton.");

} catch (IOException e) {

logger.info("Caught java.io.IOException: " + e);

logger.info("Closing connection.");

} catch (Exception e) {

logger.error("Unexpected exception. Closing conneciton.", e);

} finally {

if (ois != null) {

try {

ois.close();

} catch (Exception e) {

logger.info("Could not close connection.", e);

}

}

if (socket != null) {

try {

socket.close();

} catch (IOException ex) {

}

}

}

}

}



2 SocketServer.java

package org.apache.log4j.net;

import java.io.File;

import java.net.InetAddress;

import java.net.ServerSocket;

import java.net.Socket;

import java.util.Hashtable;

import org.apache.log4j.Hierarchy;

import org.apache.log4j.Level;

import org.apache.log4j.Logger;

import org.apache.log4j.PropertyConfigurator;

import org.apache.log4j.spi.RootLogger;

public class SocketServer {

static String CLIENT_DIR = "client";

static String CONFIG_FILE_EXT = ".properties";

static Logger cat = Logger.getLogger(SocketServer.class);

static SocketServer server;

static int port;// key=application, value=hierarchy

Hashtable<String, Hierarchy> hierarchyMap;

String dir;

public static void main(String argv[]) {

if (argv.length == 2)

init(argv[0], argv[1]);

else

usage("Wrong number of arguments.");

// init("8899", "config");

try {

cat.info("Listening on port " + port);

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);

cat.info("Starting new socket node.");

new Thread(new SocketNode(socket, server.hierarchyMap)).start();

}

} catch (Exception e) {

e.printStackTrace();

}

}

static void usage(String msg) {

System.err.println(msg);

System.err.println("Usage: java " + SocketServer.class.getName()
+ " port configFile directory");

System.exit(1);

}

static void init(String srvPort, String configDir) {

try {

port = Integer.parseInt(srvPort);

} catch (java.lang.NumberFormatException e) {

e.printStackTrace();

usage("Could not interpret port number [" + srvPort + "].");

}

PropertyConfigurator.configure(configDir + File.separator
+ "socketserver.properties");

server = new SocketServer(configDir);

}

public SocketServer(String configDir) {

this.dir = configDir;

hierarchyMap = new Hashtable<String, Hierarchy>(11);

configureHierarchy();

}

// This method assumes that there is no hiearchy for inetAddress

// yet. It will configure one and return it.

void configureHierarchy() {

File configFile = new File(dir + File.separator + CLIENT_DIR);

if (configFile.exists() && configFile.isDirectory()) {

String[] clients = configFile.list();

for (int i = 0; i < clients.length; i++) {

File client = new File(dir + File.separator + CLIENT_DIR
+ File.separator + clients[i]);

if (client.isFile()) {

Hierarchy h = new Hierarchy(new RootLogger(Level.DEBUG));

String application = clients[i].substring(0,
clients[i].indexOf("."));

cat.info("Locating configuration file for " + application);

hierarchyMap.put(application, h);

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

}

}

}

}

}


服务端

运行环境
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

在日志服务器参数配置config目录下建立文件名为socketserver.properties

log4j.rootCategory=debug,lgserver

log4j.rootLogger=DEBUG,lgserver

#tool

#log4j.appender.lgserver=org.apache.log4j.lf5.LF5Appender

#log4j.appender.lgserver.MaxNumberOfRecords=700

#console

log4j.appender.lgserver=org.apache.log4j.ConsoleAppender

log4j.appender.lgserver.layout=org.apache.log4j.PatternLayout

log4j.appender.lgserver.layout.ConversionPattern=[%d{yyyy-MM-dd HH\:mm\:ss}][%5p][%5t][%l] %m%n

要在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.test.application=test

应用步骤

(以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.test.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/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服务器把客户端送过来的信息 按照现在的配置模式输出日志,日志模式跟此处配置有关,与客户端原有配置没有关系

此处要没有配置 相应的文件,就会默认 执行 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 配置的端口号
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐