2019年07月17日(星期三)  农历:己亥年六月十五

作者:三年。分类: JAVA

Log4Dom下笔者介绍了一个简单实用的日志记录模块,代码用LotusScript写成,在Lotus Notes传统的客户端和web应用程序里都可以使用。在XPages开发里,需要可在SSJS或Java里使用的日志功能。用Java开 发时,有很多现成的日志框架,比如Log4J和JDK自带的java.util.logging包。但是这些框架包含日志器层次(logger hierarchy)、过滤器(filter)、记录器(handler)和格式器(formatter)等特性,对于Lotus Notes平台上的开发来说过于复杂。Lotus Notes的环境有特殊性,现成和方便的记录日志的载体就是Notes文档,所以笔者在借鉴了OpenNTF上的Log4Dom项目后,编写了满足典型的 Lotus Notes里日志需求的Java类NotesLogger。(与Log4Dom的Java版本比较,NotesLogger放弃了Log4J的可配制记录 器的架构,直接将日志写入唯一的载体——指定Notes数据库的文档;更正了bug;简化了使用方法;减少了类的数量;调整了方法的名称和签名。)记录日 志的文档使用的表单和视图与LotusScript的Log4Dom一样。下面就是在一个XPage的按钮里分别用xp:eventHandler的 action和actionListener属性触发managed bean里的两个测试方法,分成两条日志文档。

注意两处绑定使用的都是表达式语言,并且XPages继承自JSF,分别可以使用action和 actionListener/actionListeners绑定两种签名不同的方法。action属性绑定的方法须为managed bean的公开方法,无参数且返回一个字符串;actionListener/actionListeners绑定的方法同样须为公开方法,但接受一个类 型为javax.faces.event.ActionEvent的参数且返回类型为void。

public void testActionListener(ActionEvent ae) throws NotesException{

NotesLogger logger=new NotesLogger(XSPUtil.getSession());

logger.setLogName("test actionListener from bean");

logger.info(ae);

logger.close();

}

public void testAction() throws NotesException{

NotesLogger logger=new NotesLogger(XSPUtil.getSession());

logger.setLogName("test action from bean");

logger.info("Print message from NotesLogger");

logger.close();

}

产生的日志如下:

\
\
\

我们还可以稍微再简化一下NotesLogger的调用步骤。在一个managed bean里,很多方法都可能会写日志,我们可以在bean里声明一个logger字段作为共享的日志器,在bean的构造方法里或者利用managed bean的managed

/" target="_blank" class="keylink">vcGVydHmz9cq8u69sb2dnZXKx5MG/o6zI57TL0rvAtNTaw7+49re9t6jQtMjV1r

7KsaOsvs2/ycqhyKW0tL2oyNXWvsb3tcS7t73aoaM8YnIgLz7PwsPmysfOqrTL0LS1xGZhY2VzLWNvbmZpZy54b

WyjujxiciAvPjxwcmUgY2xhc3M9"brush:java;"> logger starrow.BeanLogger application bean starrow.Test session logger #{logger}

和相应的bean的片段:

private BeanLogger logger;

public void setLogger(BeanLogger logger) {

this.logger = logger;

}

public void test() throws NotesException{

logger.setLogName("from bean logger");

logger.debug(1);

logger.info("text");

logger.warn(true);

logger.error(new Date());

String user=XSPUtil.getSession().getEffectiveUserName();

logger.info("user: " + user);

logger.flush();

logger.close();

}

产生的日志如下:

\
\

注意上面使用的日志类是BeanLogger,这是为了能将NotesLogger作为managed bean使用而继承自它的一个扩展类。

最后,我们来看看NotesLogger和BeanLogger的代码。

package starrow;

import lotus.domino.*;

import java.util.*;

import java.text.SimpleDateFormat;

/**

* Log to Notes documents

*/

public class NotesLogger {

// built in logging levels

public static final int LEVEL_DEBUG=5;

public static final int LEVEL_INFO=4;

public static final int LEVEL_WARN = 3;

public static final int LEVEL_ERROR=2;

public static final int LEVEL_FATAL=1;

public static final int LEVEL_NONE=0;

// the string version

public static final String LEVEL_DEBUG_STRING = "DEBUG";

public static final String LEVEL_INFO_STRING = "INFO";

public static final String LEVEL_WARN_STRING="WARN";

public static final String LEVEL_ERROR_STRING="ERROR";

public static final String LEVEL_FATAL_STRING="FATAL";

protected int logLevel;

private String logName;

protected String dbPath;

protected Database logDb;

private Document logDoc;

private RichTextItem logItem;

private boolean written = false;

/**

* @param db A NotesDatabase used for logging

* @throws NotesException

*/

public NotesLogger(Database db) throws NotesException{

logLevel = LEVEL_DEBUG;

logDb=db;

if (!logDb.isOpen()){

if (!logDb.open()){

throw new NotesException(NotesError.NOTES_ERR_DATABASE_NOTOPEN, "Cannot open the log database.");

}

}

}

/**

* @param s notes session

* @param dbPath path of the log database

* @throws NotesException

*/

public NotesLogger(Session s, String dbPath) throws NotesException {

//Database db=s.getDatabase(null, dbPath);

this(s.getDatabase(null, dbPath));

}

/**

* @param s notes session

* @throws NotesException

*/

public NotesLogger(Session s) throws NotesException{

this(s.getCurrentDatabase());

}

public void setLogLevel(int level) {

logLevel = level;

}

public void setLogName(String name) {

logName = name;

}

public void info(Object message) throws NotesException {

log(LEVEL_INFO, message);

}

public void warn(Object message) throws NotesException {

log(LEVEL_WARN, message);

}

public void debug(Object message) throws NotesException {

log(LEVEL_DEBUG, message);

}

public void error(Object message) throws NotesException {

log(LEVEL_ERROR, message);

}

public void fatal(Object message) throws NotesException {

log(LEVEL_FATAL, message);

}

public void log (int level, Object message) throws NotesException {

if (level>logLevel) return;

SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss aa", Locale.US);

String text = new String();

Date theCurrentDate = new Date();

String theDate = dateFormat.format(theCurrentDate);

String theLevel = new String();

theLevel = theLevel+getLevelString(level);

//System.out.println("Level :"+level+" - logLevel "+logLevel);

text = theDate+" ["+theLevel+"]: "+message;

if (logDoc == null) {

// create document and a RTF for the log

logDoc = logDb.createDocument();

logDoc.replaceItemValue("Form", "log");

logDoc.replaceItemValue("LogName", logName);

if (logDb.getParent().isOnServer()){

logDoc.replaceItemValue("ScriptRunOn", "Server");

}else{

logDoc.replaceItemValue("ScriptRunOn", "Local");

}

logItem = logDoc.createRichTextItem("logBody");

}

// write log entry

logItem.appendText(text);

logItem.addNewLine();

written = true;

}

//convert from level numbers into strings

private String getLevelString(int level) {

String levelString="";

switch (level) {

case LEVEL_DEBUG: levelString=LEVEL_DEBUG_STRING ; break;

case LEVEL_INFO: levelString=LEVEL_INFO_STRING ; break;

case LEVEL_WARN: levelString=LEVEL_WARN_STRING ; break;

case LEVEL_ERROR: levelString=LEVEL_ERROR_STRING ; break;

case LEVEL_FATAL: levelString=LEVEL_FATAL_STRING ; break;

default : levelString=levelString+"LEVEL "+level; break;

} // end switch

return levelString;

}

/**

* save the log document

* @throws NotesException

*/

public void flush() throws NotesException{

if (written) {

logDoc.save();

written=false;

}

}

/**

* close the log via recycling the underlying resource.

* @throws NotesException

*/

public void close() throws NotesException {

flush();

logDoc.recycle();

logDoc=null;

if (this.dbPath!=""){

//If the log db is not the current one, recycle it.

//Even recycling the current db doesn't cause any error.

this.logDb.recycle();

}

}

}

package starrow;

import starrow.xsp.XSPUtil;

import lotus.domino.NotesException;

public class BeanLogger extends NotesLogger implements java.io.Serializable{

private static final long serialVersionUID = 1L;

public BeanLogger(String dbPath) throws NotesException {

super(XSPUtil.getSession());

this.dbPath=dbPath;

logLevel = LEVEL_DEBUG;

}

/**

* Add a constructor with no parameter for bean creation

* @throws NotesException

*/

public BeanLogger() throws NotesException{

this("");

}

public void log(int level, Object message) throws NotesException{

//get the current db freshly

if (this.dbPath==""){

logDb=XSPUtil.getDatabase();

}else{

logDb=XSPUtil.getDatabase(this.dbPath);

}

super.log(level, message);

}

}

BeanLogger里不寻常的一点是在重载的log方法里,获取全新的日志数据库实例,这是因为XPages引擎会频繁地自动清理Java里 Lotus Domino对象用到的后端对象(参看42. Lotus Notes中的垃圾回收之Java),BeanLogger用到的日志数据库和文档很快会变成无效状态,调用它会引发以下异常:

NotesException: Object has been removed or recycled

at lotus.domino.local.NotesBase.CheckObject(Unknown Source)

at lotus.domino.local.Document.save(Unknown Source)

at lotus.domino.local.Document.save(Unknown Source)

温馨提示如有转载或引用以上内容之必要,敬请将本文链接作为出处标注,谢谢合作!

已有 0/1343 人参与

发表评论:



手Q扫描加入Java初学者群