Late last night I released the XPages OpenLog Logger project on OpenNTF. There have been options for logging to OpenLog from XPages for some time – Matt White’s OpenLogXPages SSJS library from TaskJam and the OpenLogItem class I included in XPages Help Application. So why did I bother with this?
Firstly, if you think this is just an aggregation of previous code, think again. The Java implementation has three main changes:
- I was frustrated with having to create an OpenLogItem in each class or in a central utils class. So all methods are now static. So now I just call OpenLogItem.logError() instead of calling a getOpenLogItem() method from some intermediate class to get an instance of OpenLogItem, and then calling logError(). Note to self: must get round to changing my Java templates in Designer’s preferences!
- The log path was hard-coded in the script, so I never went beyond the default. But a few weeks ago I was introduced (or re-introduced probably) to the code to access the xsp.properties. So the location of the log path has been extracted to xsp.properties, with a fall-back to the notes.ini, and a final fall-back to “OpenLog.nsf”. The same has been provided for debugLevel, for backup error handling if the OpenLog database is not available.
- The third change was to provide it as an OSGi plugin, so it doesn’t need copying and pasting into every database.
But the most significant reason I did this work was not specifically for me (although I’ll still use it). Through our mentoring package and at conferences I speak to a lot of newer XPages developers and I’m conscious of a number of challenges around SSJS development.
- I don’t have a problem using OpenLogXPages SSJS library – the demo video below shows I make extensive use of it. But I’m conscious it’s not Apache-licensed, which can be a problem for some companies. This SSJS implementation had to be.
- Uncaught exceptions are very frustrating, when all you get is Error while executing JavaScript computed expression. I’m frequently left asking “Which JavaScript expression?” Worst of all, if it’s in a live environment, you have nothing to help diagnose or fix it. But I know requestScope.error has lots of information. So I wanted to be able to log that information. If you have a custom error page (requestScope.error is not available until a custom error page), this code now does it.
- Custom error pages have a gotcha when triggered from a partial refresh. So for less experienced developers, this application not only explains that but also provides a sample error page.
- OpenLogXPages was an SSJS script library. They don’t perform well. Also, you don’t get all the information about the error. I wanted to provide more, both for myself when using SSJS and also for others.
- Using it from SSJS had to be quick and flexible, because code templating is not available in SSJS. So when I write SSJS, I have to type in my try/catch block, I need to remember the syntax for logging an error. I didn’t want to have to import the Java package, so I needed a bean. So this has addError() and addEvent() methods with a flexible list of parameters. The slight gotcha is that with addError() if you want to add a custom message, it is inserted as the second parameter. That’s to avoid conflicts in methods, because the last parameter is also a String.
- I wanted the logging from SSJS to avoid logging the same event / error multiple times. That occurs because of the JSF lifecycle, but can confuse (as well as clogging up OpenLog). The SSJS implementation handles all that.
- I didn’t want to provide just a JavaDoc. As a developer coming from a Domino background, they often frustrate me because they give a parameter type (e.g. String or int), but don’t document what is expected. The application has detailed documentation on calling from SSJS or Java, with the methods available and examples. As ever, there are little easter eggs, like explaining how you can use this and this.getParent() to avoid hard-coding.
- I wanted to give a video of how to implement it, whether the code resides in the NSF or in an OSGi plugin. Neither is particularly scary, but many developers may not have done this.
- I wanted to show it’s not a huge task to refactor existing applications, also showing the Search and Replace functionality in Designer, for those who are not aware of its flexibility. That’s in this video.
One aspect I’m undecided on at the moment is whether to extend the Java code to have addJavaErrors() and addJavaEvents() methods. It would require another managed bean, so you would have to call that from Java. The benefit it would give is that duplicate errors and events from Java would also be suppressed. I would welcome feedback on that.
Any other suggestions for enhancements are also welcome.
Paul, you are the man. That makes things a lot easier. Thanks a lot.
Thanks a lot Paul.
I started something similar, but I will use your version which is more complete.
One little point though, as we currently do not use the extension library and you use only basic function calls to it, it would be nice to have a basic class in your package to make it work without the extension library too.
I did one for my application and change the references to ExtLibUtil to JSFUtils (or whatever you call the class) :
package com.paulwithers.openLog;
import javax.faces.context.FacesContext;
import lotus.domino.Database;
import lotus.domino.Session;
import com.ibm.xsp.application.DesignerApplicationEx;
import com.ibm.xsp.designer.context.XSPContext;
public class JSFUtils {
public static FacesContext getFacesContext() {
return FacesContext.getCurrentInstance();
}
public static DesignerApplicationEx getApplication() {
return (DesignerApplicationEx) getFacesContext().getApplication();
}
public static Database getCurrentDatabase() {
return (Database) getVariable(“database”);
}
public static Object getVariable(String variableName) {
return getApplication().getVariableResolver().resolveVariable(FacesContext.getCurrentInstance(), variableName);
}
public static XSPContext getXspContext() {
return XSPContext.getXSPContext(getFacesContext());
}
public static Session getCurrentSession() {
return (Session) getVariable(“session”);
}
}
But thanks for your work !
Yes, in XPages Extension Library I included a similar util file that Tim Tripcony created for dPloy. Everything’s set up on GitHub, so I’ll try to create a branch for that. I’m a bit new to GitHub, so it may take a day or two!
For info, the GitHub projects are https://github.com/paulswithers/openlogjava for the NSF and https://github.com/paulswithers/openLogOSGi for the plugin.
Issues and enhancement requests can also be logged there, as well as in the OpenNTF project.
Thank your very much for your effort Paul,
I may have a stupid question here. Do we have to have extension library enabled in our project to implement this? I noticed there is an error on statement import com.ibm.xsp.extlib.util.ExtLibUtil in my case. Or I can just grab utility part of Extension Library?
Thanks in advance.
Yes, it does use Extension Library. You could just grab that package, though I would not be surprised if there are other dependencies. Or if you’re comfortable with source control, on GitHub I’ve pushed a non-extlib dependent branch https://github.com/paulswithers/openlogjava/branches
Clicking Compare will show the differences. You don’t need to worry about the database.properties. Tim Tripcony’s Java class that you’ll need to add is also there, at the bottom.