Log4j中添加自定义的日志级别


Log4j自带了trace,debug,info.warn,error,fatal几个日志级别。

由于项目需要拆分业务和系统日志,而我又想偷懒,所以决定在log4j日志框架上增加自定义的日志级别专门用于业务日志输出。需要增加的日志级别定为audit,在log4j所有自带日志级别之上。

Log4j官方推荐的增加自定义的日志级别的方法是“继承org.apache.log4j.Level类”,具体的可以参考log4j中的示例XLevel。由于系统开发时不直接使用Log4j,而是通过slf4j调用log4j,官方推荐的方法行不通,所以决定修改log4j和slf4j相关源代码。

Log4j源代码修改点如下:

org.apache.log4j.Logger添加audit方法

public void audit(Object message) {
	if (repository.isDisabled(Level.AUDIT_INT)) {
		return;
	}

	if (Level.AUDIT.isGreaterOrEqual(this.getEffectiveLevel())) {
		forcedLog(FQCN, Level.AUDIT, message, null);
	}
}

org.apache.log4j.Priority类添加AUDIT_INT属性

public final static int AUDIT_INT = 60000; //日志级别大于log4j包含的所有日志级别,便于后期通过Threshold属性过滤日志

org.apache.log4j.Level类添加AUDIT相关属性和方法

public static final Level AUDIT = new Level(AUDIT_INT, "AUDIT", 0);public static Level toLevel(int val, Level defaultLevel) {
	switch(val) {
		case ALL_INT: return ALL;
		case AUDIT_INT: return Level.AUDIT;
		case DEBUG_INT: return Level.DEBUG;
		case INFO_INT: return Level.INFO;
		case WARN_INT: return Level.WARN;
		case ERROR_INT: return Level.ERROR;
		case FATAL_INT: return Level.FATAL;
		case OFF_INT: return OFF;
		case TRACE_INT: return Level.TRACE;
		default: return defaultLevel;
	}
}

至此Log4j修改完成,下面修改slf4j相关源代码。slf4j需要修改slf4j-api和slf4j-log4j12两个包 继续阅读

Install Oracle 11gR1 x64 on CentOS


首先需要注意安装之前修改本地主机名。

  • 修改/etc/sysconfig/network文件中的HOSTNAME
  • 修改/etc/hosts中的127.0.0.1以及::1对应域名

修改完成后重启。原因是电信的DNS把localhost.localdomain指向了202.106.199.39,导致oracle em启动时不正常。

下面开始安装

创建Oracle用户及用户组

[root@localhost ~]# groupadd oinstall
[root@localhost ~]# groupadd dba
[root@localhost ~]# useradd -m -g oinstall -G dba oracle
[root@localhost ~]# passwd oracle

修改系统参数

[root@localhost ~]# vim /etc/sysctl.conf
# Oracle Install
kernel.shmmni = 4096
kernel.sem = 250 32000 100 128
fs.file-max = 6815744
fs.aio-max-nr = 1048576
net.ipv4.ip_local_port_range = 9000 65500
net.core.rmem_default = 4194304
net.core.rmem_max = 4194304
net.core.wmem_default = 262144
net.core.wmem_max = 1048576[root@localhost ~]# sysctl -p

创建Oracle目录 继续阅读

TNS-12537: TNS:connection closed 之 Linux Error: 29: Illegal seek


出现TNS-12537: TNS:connection closed错误的原因有很多种。

我这里碰到的是域名解析的问题,详细错误日志如下:

/opt/app/oracle/product/11.1.0/db_1/bin/dbstart: Starting Oracle Net Listener

LSNRCTL for Linux: Version 11.1.0.6.0 - Production on 07-JUN-2011 08:48:45

Copyright (c) 1991, 2007, Oracle.  All rights reserved.

Starting /opt/app/oracle/product/11.1.0/db_1/bin/tnslsnr: please wait...TNS-12537: TNS:connection closed
 TNS-12560: TNS:protocol adapter error
  TNS-00507: Connection closed
   Linux Error: 29: Illegal seek

已经在/etc/hosts中添加了127.0.0.1 localhost.localdomain,但就是无法启动Oracle 监听程序

后来执行nslookup localhost.localdomain,却发现localhost.localdomain并没有指向127.0.0.1,而是指向了202.106.199.35,真是令人惊奇。直接注释掉外网DNS服务器地址,然后重启监听服务,这次正常了。

安装SVNManager注意事项


按照手册一路安装下来,最后碰到两个问题,第一个问题是SVN路径中包含中文,在设置用户和组权限的时候,中文全部乱码,编程\234?\344?这样子的形式。解决这个问题之后,在用户和组的权限管理时碰到了第二个问题,选择了中文目录后,跳转页面出现错误,显示拼接的svn路径在file前多了”\代码,导致svn命令不能正确识别该路径。

第一个问题,网上很多说是编码的问题,我修改为GBK,GB2312都未解决,最后通过分析代码的方式发现,在/usr/share/pear/VersionControl/SVN.php中672行执行exec()函数获得返回结果的时候就已经乱码了,而这个函数是php的,现在本机设置的语言环境是zh_CN.UTF-8,php相应的编码也都设置为UTF-8,apache默认编码也已经设置为UTF-8。

最后通过在将672行的代码修改为exec(“LANG=zh_CN.UTF-8; {$this->prepend_cmd}$cmd 2>&1”, $out, $ret_var);问题解决。

现在不太清楚,为何本地locale已经设置为LANG=zh_CN.UTF-8的情况下,exec命令仍然需要明确指定语言环境。

第二个问题,发现是svnmanager-1.08\svnmanager\RepositoryModule.php代码的Bug,如果svn路径中包含了空格,那么在245行做替换的时候,会在头加上”\,从而导致拼接的svn path错误,这个Bug没有细查了,暂时把svn目录的空格都去掉了。

如果出现PHP Fatal error:  Class ‘PEAR_ErrorStack’ not found错误
可以降版本
pear uninstall VersionControl_SVN-0.5.0卸掉0.5.0
pear install VersionControl_SVN-0.4.0重装0.4.0就没报这个错了

以上是安装SVNManager获得的一些经验,希望对看客有用。另:SVNManager最终配置成功的环境全部是UTF-8编码环境,包括Mysql数据库。

跟踪 C3p0 连接池连接泄漏之参数优化与日志分析


上一篇:跟踪 C3p0 连接池连接泄漏 我们有谈到c3p0的debugUnreturnedConnectionStackTraces、unreturnedConnectionTimeout参数的说明,此篇日志进一步介绍应用这两个参数后的一些经验。

首先,为了跟踪问题,debugUnreturnedConnectionStackTraces参数肯定是设置为true的。下面主要说明unreturnedConnectionTimeout参数的设置,unreturnedConnectionTimeout参数是在连接被应用程序checkout后指定时间内未checkin则由连接缓冲池执行kill操作,同时打印堆栈跟踪信息。在我的应用里,maxIdleTime的设置是120秒,所以,我把unreturnedConnectionTimeout设置成150秒,如果达到最大存活时间后,连接还是不能被连接缓冲池正常关闭,那么肯定出现了连接泄漏,此时,再过30秒后,由连接缓冲池主动执行kill。

通过以上设置后,确实收获了一些成果,通过分析日志,找到了连接泄漏问题代码。异常信息如下:

2011-04-06 15:49:42,599 INFO : com.mchange.v2.resourcepool.BasicResourcePool.removeResource(BasicResourcePool.java:1395) – Logging the stack trace by which the overdue resource was checked-out.
java.lang.Exception: DEBUG ONLY: Overdue resource check-out stack trace.
at com.mchange.v2.resourcepool.BasicResourcePool.checkoutResource(BasicResourcePool.java:506)
at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutPooledConnection(C3P0PooledConnectionPool.java:525)
at com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource.getConnection(AbstractPoolBackedDataSource.java:128)
at org.springframework.orm.hibernate3.LocalDataSourceConnectionProvider.getConnection(LocalDataSourceConnectionProvider.java:82)
at org.hibernate.jdbc.ConnectionManager.openConnection(ConnectionManager.java:423)
at org.hibernate.jdbc.ConnectionManager.getConnection(ConnectionManager.java:144)
at org.hibernate.jdbc.AbstractBatcher.prepareQueryStatement(AbstractBatcher.java:139)
at org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:1547)
at org.hibernate.loader.Loader.doQuery(Loader.java:673)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:236)
at org.hibernate.loader.Loader.doList(Loader.java:2220)
at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2104)
at org.hibernate.loader.Loader.list(Loader.java:2099)
at org.hibernate.loader.custom.CustomLoader.list(CustomLoader.java:289)
at org.hibernate.impl.SessionImpl.listCustomQuery(SessionImpl.java:1695)
at org.hibernate.impl.AbstractSessionImpl.list(AbstractSessionImpl.java:142)
at org.hibernate.impl.SQLQueryImpl.list(SQLQueryImpl.java:152)
at com.leo.dao.XXXDAO.queryXXX(XXXDAO.java:20)
at jsp_servlet._keyareas._country.__taskcdb_add._jspService(__taskcdb_add.java:195)
at weblogic.servlet.jsp.JspBase.service(JspBase.java:34)
at weblogic.servlet.internal.StubSecurityHelper$ServletServiceAction.run(StubSecurityHelper.java:227)
at weblogic.servlet.internal.StubSecurityHelper.invokeServlet(StubSecurityHelper.java:125)
at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:300)
at weblogic.servlet.internal.ServletStubImpl.onAddToMapException(ServletStubImpl.java:416)
at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:326)
at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:183)
at weblogic.servlet.internal.RequestDispatcherImpl.invokeServlet(RequestDispatcherImpl.java:526)
at weblogic.servlet.internal.RequestDispatcherImpl.forward(RequestDispatcherImpl.java:253)

通过堆栈信息,很容易就确认了问题点。这个问题点是由于DAO使用不规范导致的,XXXDAO继承了HibernateDaoSupport,在方法中使用了getHibernateTemplate().getSessionFactory().openSession()获得session,然后通过session.createSQLQuery(),随后却并没有执行session.close()。

关于日志大小:在目前maxIdleTime=120,maxPoolSize=18,minPoolSize=5,idleConnectionTestPeriod=30的情况下,日志PatternLayout %d %-5p: %l – %m%n,一天的日志记录情况大约是12M,在硬盘空间允许的情况下建议按天存储日志。

java.sql.Date.valueOf 使用注意事项


最近在帮同事解决了一个日期格式化的问题,总结如下,以飨读者。

java.sql.Date.valueOf()方法提供了把yyyy-mm-dd类型字符串转换成java.sql.Date类型的功能。但是在使用的时候有以下注意事项:

  1. valueOf参数必须是一个有效日期的yyyy-mm-dd格式类型的日期。
    在jdk1.4中,valueOf的参数可以设置为0000-00-00,jdk能够正确转换,不出现异常。但是在jdk 6中,使用该参数时提示IllegalArgumentException异常。修改为0001-01-01正常。
  2. valueOf参数必须严格符合yyyy-mm-dd格式。
    修正之前的代码通过Calendar类获得年月日,这时获得的年月日都是int类型的, 当月份和日小于10的时候是一位的,此时JDK源码中判断月、日位数的检查生效,不会再进行转换,直接抛出IllegalArgumentException异常。通过Calendar类获取年月日之后,要对不足位的字段进行补位,保证能够正常转换。

题外话:关于第二点问题,同事很疑惑为什么之前可以,现在不可以了。阅读jdk源代码并结合Bug发生的时间来看,没问题的时间2010年12月下旬,此时月、日都是两位;而出问题的时间是2011年1月上旬,月、日都不足位。通过这个经验教训来看系统边界测试有时候是很有必要的。

IE 6 下javascript:void(0)阻止页面刷新


虽然微软已经停止对Winxp以及古老、漏洞百出的IE6的更新支持。但是在国内还是有很多人在用IE6,所以做项目的时候也不得不考虑这部分人的需求。于是,碰到了一个奇怪的问题。

有一个页面,通过<a>标记的onclick事件做了一个form.submit()动作,在IE7、chrome、firefox浏览器下都正常,唯独测试组反映页面不正常。调查之后,问题很快就锁定到浏览器版本,测试用的是IE6,问题现象是:form.submit()已经正常提交到服务器,并且服务器也做出了响应,但是页面没有刷新。

通过排查发现,IE6居然在onclick的submit后仍然会继续标记的href=”javascript:void(0)”导致页面没有被刷新。试着修改javascript:void(0)为#号或者在submit后return false;都能解决问题。最后决定在submit后增加return false;阻止IE6继续执行javascript:void(0),问题得到解决。

Ubuntu 10.10 maverick 安装nVidia 260.19显卡驱动


之前写过一篇Ubuntu安装nVidia显卡驱动的文章:在Ubuntu 9.10/9.04/8.10/8.04上安装190.42版NVidia显卡驱动

时隔近一年,之前使用的那个PPA源已经跟不上“步伐”了,在Ubuntu 10.04的时候我就已经开始使用X Updates PPA源来更新自己的显卡驱动,使用PPA源安装nVidia驱动,可以获得最新的nVidia官方发布的驱动,而且在更新内核的时候不用再次手动安装驱动,该PPA源中的nVidia驱动会在你更新内核的时候自动编译进内核。在终端执行以下步骤,即可顺利安装nVidia显卡驱动。前提条件是你未安装过nVidia官方网站下载的.bin二进制驱动,如果有安装,请先卸载。

  1. sudo add-apt-repository ppa:ubuntu-x-swat/x-updates
  2. sudo apt-get update
  3. sudo apt-get install nvidia-current nvidia-settings
    提示需要安装其他的软件包,输入Y即可。
  4. 安装完成后重启电脑,现在你应该可以在 System/Administration找到NVIDIA X Server Settings。如下图:

至此,nvidia显卡驱动安装完成。

后记

虽然nVidia很勤奋的更新Linux版本的驱动,但是在有些功能方面还是没有达到和Windows相同的水平。例如在双显示器输出时(TwinView),我的电脑在Windows平台下可以对不同的显示器设定不同的分辨率,并且可以控制不同的应用出现在不同的显示器。但是在Linux,就连最基本的不同的显示器设定不同的分辨率都不能实现(自己修改xorg.conf文件可能实现,不过我没有成功:-))。

常在河边走,哪能不湿鞋。如果某次不小心把xorg.conf改错了,导致屏幕显示不正常怎么办?教你一招,重启系统后按ctrl + F1直接进入终端界面,然后输入sudo nvidia-xconfig重新生成默认的xorg.conf。nvidia-xconfig还拥有很丰富的功能,可以通过man xorg.conf详细阅读。

顺利升级至Ubuntu 10.10 Maverick


昨天晚上,经过4个多小时的下载、安装,终于将系统成功升级至了Ubuntu 10.10 Maverick。

比起前几次的版本升级,这次的版本升级一次成功。

和lucid比较而言,Maverick明显有以下几点变化。

1、关机、重启、睡眠、待机按钮合并到System下面的一个子菜单中了,通过点击Shutdown按钮可以呼出这个菜单

2、界面的风格和样式变化了。按钮样式、窗口背景颜色等,相对于10.04而言,我更加喜欢现在的。

3、可操作性加强了。比如更新管理器的时候下载的时候,之前仅仅显示要下载的文件总数和已经下载的文件数,现在添加正在下载的文件大小。

4、图片浏览和管理软件变了,从f-spot变成了shotwell。相比f-spot,shotwell在照片的时间分类上更科学、更方便,而且f-spot已经N久没有更新过了,查看图片的时候总是突然崩溃——我是个追新的人,所以图片导入shotwell之后,马上就把f-spot卸载了。

5、对于Java编程人员来说,有一个比较有用的工具默认安装了,在Applications的Programming中——VisualVM。之前还以为是类似于KVM或者VirtualBox这样的虚拟机,后来一Google才发现,原来是用来调试和诊断Java程序的,赶紧试试吧。

……

还有更多的变化,以后慢慢去发现吧。

升级后碰到的小问题:

1、截图的时候发现不能使用Alt+Print Screen截取窗口,按下快捷键之后没有反应。在launchpad搜索发现已经有人报告了这个Bug。
https://bugs.launchpad.net/bugs/657817

2、属于IBus输入法的Python进程占用100%的CPU使用率。这个Bug也已经有报告了。并且得到了确认。
https://bugs.launchpad.net/bugs/637671
这个问题通常在打开了输入法的属性配置页之后产生,出现了这样的问题,可以重启输入法,暂时解决这个问题。

Ubuntu调整Swap分区大小后,修复不能挂载Swap及执行睡眠功能的问题


我的电脑物理内存是4G,之前由于磁盘空间有限,只给了1.5G的swap,最近在执行睡眠功能的时候,提示no enough swap。

于是使用gparted调整了分区的大小,但是因为Ubuntu使用UUID挂载分区,所以调整完毕后,系统并没有正常挂载swap分区。

通过free命令可以看出swap的大小是0。
执行ls -l /dev/disk/by-uuid/命令可以得到分区的UUID,通过和/etc/fstab中的UUID比对可以发现,Gparted并没有更新/etc/fstab中的UUID。
手动更新/etc/fstab中的UUID之后,重启系统,系统已经可以正常挂载swap了。

然后测试睡眠(hibernate)功能是否正常。
执行Hibernate后,系统正常关闭了。但是在启动的时候,系统还是像新开机的时候一样,并且之前没有关闭的应用程序都没有恢复。
这个问题可以通过修改/etc/initramfs-tools/conf.d/resume文件中的UUID,将其更新为最新的Swap UUID,然后执行sudo update-initramfs -u即可。