Showing posts with label java. Show all posts
Showing posts with label java. Show all posts

2009-03-13

在 Java 服务端使用 syslog 作为日志系统

1. 为什么要用 syslog?


使用 syslog 的最大理由就是可以集中处理日志,通常一个中小型的服务端10来台机器,如果日志集中到一台服务器处理起来就非常方便。

其次,你可以很方便地利用 syslog-ng 这一类的服务器来对日志进行分流,在只需要跟踪某一种类型的日志时就比较方便。

除此之外,用文件做日志在部署时总会出现目录不存在,目录没有权限的情况,而用 syslog 就没有这个问题。

2. 配置 log4j


把如下的一段加入 log4j.properties 即可把日志重定向到 syslog(同时保留了终端,方便调试)

log4j.appender.SYSLOG=org.apache.log4j.net.SyslogAppender
log4j.appender.SYSLOG.syslogHost=192.168.101.101
log4j.appender.SYSLOG.facility=local3
log4j.appender.SYSLOG.facilityPrinting=false
log4j.appender.SYSLOG.layout=org.apache.log4j.PatternLayout
log4j.appender.SYSLOG.layout.ConversionPattern=%d{dd-MM-yyyy HH:mm:ss} %-5p (%C:%M:%L) - %m%n

log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{dd-MM-yyyy HH:mm:ss} %-5p (%C:%M:%L) - %m%n

log4j.rootLogger=info, CONSOLE, SYSLOG


syslog 的每一条记录有两个属性,一个是 facility(可用于日志分类), 另外一个是 priority.

facility 就在上面的 log4j.appender.SYSLOG.facility 属性中配置,缺省值是 user, 对于服务器,我觉得在 local0 到 local7 之间选一个比较好,便于将日志分类。所有可用的 facility 可以参考log4j的文档

priority 在我们用 log.error, log.warn 之类地命令进行日志操作时就已经指定了,syslog 有 8 个优先级,log4j 用了其中的 5 个,对应关系如下(syslog的 ALERT, CRIT, NOTICE 3个等级未被使用)

log4j.FATAL = 0 = syslog.EMERG
log4j.ERROR = 3 = syslog.ERR
log4j.WARN = 4 = syslog.WARNING
log4j.INFO = 6 = syslog.INFO
log4j.DEBUG = 7 = syslog.DEBUG


log4j.appender.SYSLOG.syslogHost 这个配置选项用于配置 syslog 机的 IP, log4j.appender.SYSLOG.facilityPrinting 设为 true 时会在每条日志前打印 "local3:", 在日志没有分流时打开此选项可以用 tail -f /var/log/syslog | grep local3 来只跟踪从你的服务器传来的日志。

syslog 服务端


我一般使用 syslog-ng 来作为 syslog 服务器,安装后打开 /etc/syslog-ng/syslog-ng.conf,把 "udp()" 这一行取消掉注释后重启 syslog-ng 服务即可正常使用,所有的日志会被输出到 /var/log/syslog。(注意,如果日志服务器在外网,建议修改 /etc/hosts.deny 和 /etc/hosts.allow, 防止被人灌数据灌着玩)

一般我们会把服务器的日志定向一个单独的文件来进一步处理,最简单的情况就是把如下的一段追加到 /etc/syslog-ng/syslog-ng.conf, 然后重启 syslog-ng 服务。

filter f_foo { facility(local3); };
destination df_foo { file("/var/log/foo.log" owner("foo")); };
log {
source(s_all);
filter(f_foo);
filter(f_at_least_warn);
destination(df_foo);
};

syslog-ng 也支持把日志的输出重定向到你的程序,具体可以参见 syslog-ng 的文档。

最后你可能需要把你的日志文件加入 logrotate 的配置, 这样方便日志滚动。

2008-12-14



1. 通过数据层访问数据库


最简单的模型,采用一个数据访问层,来隔离应用与数据库。
public class MyDA {
//...
}

public class MyApp {
public MyApp() {
da = new MyDA();
}
private MyDA da;
//...
}

2. 面向界面编程,同时方便使用 Mock 方法测试

public interface IMyDA {
//...
}


public class MyDA implements IMyDA {

//...
}

public class MyApp {
public MyApp() {
setDA(new MyDA());
}
protected void setDA(IMyDA da) {
this.da = da;
}
private IMyDA da;
//...
}



关于 Mock 测试,参见 http://www.ibm.com/developerworks/library/j-mocktest.html

3. 引入工厂模式,隔离数据访问与业务逻辑




public interface IMyDA {
//...
}

public class MyDA implements IMyDA {
//...
}

public class MyDAFactory {
public IMyDA createMyDA(String name) {
if(name.equals("foo")) {
return new MyDA();
} else {
throw new Exception();
}
}
}


public class MyApp {
public MyApp() {
setDA(MyDAFactory.createMyDA("foo"));
}
protected void setDA(IMyDA da) {
this.da = da;
}
private IMyDA da;
//...
}



这种情况下 MyApp 仍然通过 MyDAFactory 来依赖与 MyDA, 甚至各种 IMyDA 的实现。另外,工厂模式不方便添加新的实现。



4. 引入 IoC 容器


Java 源码部分



public interface IMyDA {
//...
}

public class MyDA implements IMyDA {
//...
}


public class MyApp {
public MyApp() {
BeanFactory factory = new XmlBeanFactory(
new FileInputStream("myapp.xml"));

da = (IMyDA) factory.getBean("da");
}

private IMyDA da;

//...
}

用到的 myapp.xml 文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id="da" class="MyDA"/>
</beans>

5. 一个复杂点的例子


上面的例子有点小题大做了,在真实的环境中,数据访问一般需要通过 MySQL库, 连接池, cache, 甚至更多的层,每一层都需要可以配置,最好也支持替换(方便测试),设置还需要支持删除某些中间层,工厂模式用于这样的情形则非常麻烦,但是 IoC 处理这样的情形则比较简单,比如把如下的文件替换掉上面的myapp.xml,那么你的应用的数据访问层就变成了用于数据库连接池和cache功能的 SuperMyDA。



更重要的是,隔离非常彻底,在你编写编译 MyApp 的时候,你根本就用不着 proxool, memcache 这类组件。



注意: 下面文件里面的内容都是乱写的,我还没去查这些组件的接口, :-)



<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">

<!-- 配置 mysql 连接 -->
<bean id="db" class="com.mysql.Connection">
<property name="uri"
value="mysql://userName:secret@192.168.12.23/dbname"/>
</bean>

<!-- 配置 proxool -->
<bean id="proxool" class="com.proxool.Pool">
<property name="db" ref="db"/>
<property name="maxConnection" value="200"/>
</bean>

<!-- 配置 memcache -->
<bean id="memcache" class="com.memcache.Foo">
<property name="config" value="192.168.23.34:12345">
</bean>

<!-- 配置 SuperMyDA -->
<bean id="da" class="SuperMyDA">
<property name="proxool" ref="proxool"/>
<property name="memcache" ref="memcache"/>
</bean>

</beans>