As Jess has mentioned, a custom log4j logger can be used to accomplish the desired output. I have this working, and would like to share my steps to get there. As it turns out, it is pretty lengthy. But I hope that someone finds it useful. There are also probably people that are already familiar with this, and using their own log4j logging. So I welcome corrections & comments if I've published anything wrong or misleading.
The way I see it, there are 2 scenarios that could be in play here.
- The logging is going to be executed from some Execute Expression robot in a workflow template, or maybe some custom JSP page that is placed on the server. In these cases, there is no java package and class.
- The logging is going to be executed from an actual compiled java class, such as ext.company.SomeClass.
I will cover both scenarios.
Scenario 1
I have a custom JSP page that I put onto the server into codebase/netmarkets/jsp/Custom/mypage.jsp. Every time someone loads that JSP page, I want to log an event to some external file. I don't want it simply logged in the MethodServer. In the example below, it looks at user IP address and tries to update the user's Vault preference. For the logging, we're only concerned with lines 7 and 16 and 19 in the JSP code below.
<%@ page import="org.apache.log4j.Logger" %>
<%@ page import="wt.org.WTUser" %>
<%@ page import="wt.preference.PreferenceHelper" %>
<%@ page import="wt.session.SessionHelper" %>
<%
// Instantiate some variables
Logger LOGGER = Logger.getLogger("ext.company.customJspLogger");
WTUser user = (WTUser)SessionHelper.manager.getPrincipal();
String username = user.getName();
String ipAddress = request.getRemoteAddr();
String vaultPreferenceOld = (String)PreferenceHelper.service.getValue(user, "/wt/content/contentCacheSite", "WINDCHILL");
String vaultPreferenceNew = "master";
if (ipAddress.equals("192.168.242.32")) {
vaultPreferenceNew = "Value_of_the_Preference_setting__aka_master_or_Replica1";
PreferenceHelper.service.setValue("/wt/content/contentCacheSite", vaultPreferenceNew, user);
LOGGER.info(username + " " + ipAddress + " " + vaultPreferenceOld + " " + vaultPreferenceNew);
} else {
vaultPreferenceNew = "IP_NOT_RECOGNIZED_PREFERENCE_NOT_SET";
LOGGER.info(username + " " + ipAddress + " " + vaultPreferenceOld + " " + vaultPreferenceNew);
}
//String redirectURL = "../../../servlet/WindchillGW";
//response.sendRedirect(redirectURL);
%>
Hello, thank you for testing! Have a nice day.
The corresponding update that needs to be made to codebase/WEB-INF/log4jMethodServer.properties is listed below. Note "ext.enerpac.customJspLogger" in line 2 below, which matches the getLogger() callout in line 7 of the JSP code above. Also, after updating and saving the log4jMethodServer.properties file, be sure to wait for up to 3 minutes for the settings to take effect. I've noticed in the MS, a line printed such as wt.log4j.jmx.LoggerRepositoryMonitor - log4j configuration read from file:<WT_HOME>/codebase/WEB-INF/log4jMethodServer.properties, and then I know that the new configuration has been read, and ready to use.
# Define customJspLogger appender
log4j.logger.ext.company.customJspLogger=INFO, customJspLogger
log4j.additivity.customJspLogger=false
log4j.appender.customJspLogger=wt.log4j.jmx.DailyRollingFileAppender
log4j.appender.customJspLogger.File=/home/ptc/testing1.log
log4j.appender.customJspLogger.DatePattern='.'yyyy-MM-dd
log4j.appender.customJspLogger.layout=org.apache.log4j.PatternLayout
log4j.appender.customJspLogger.layout.ConversionPattern=%d{ISO8601} %-5p [%t] %c - %m%n
Then, after trying to load the page http://<server>/Windchill/netmarkets/jsp/Custom/mypage.jsp, I get the following line in the external file /home/ptc/testing1.log:
2015-09-02 04:32:38,173 INFO [ajp-bio-8010-exec-1] ext.company.customJspLogger - ben.perry 192.168.242.32 master Replica1
So to summarize, all I really did, besides the import statement, was:
- Instantiate the logger on JSP page, Logger LOGGER = Logger.getLogger("ext.enerpac.customJspLogger"); making sure to use the right callout for getLogger() function.
- Actually log information by calling LOGGER.info("whatever info message you want printed");
- Add the properties of the custom logger to codebase/WEB-INF/log4jMethodServer.properties making sure to wait some moment until the new config is loaded before testing it.
Notes to keep in mind here:
- The string "ext.company.customJspLogger" that is called out in getLogger() on the JSP page must match here also: log4j.logger.ext.company.customJspLogger=INFO, customJspLogger.
- The string "customJspLogger" at the end of this line can be anything, but must match on the following lines in the properties file, such as log4j.additivity.customJspLogger=false. I just happened to drop "ext.company" off, but you can call it anything, I believe.
- Make sure to wait and allow the new config from log4jMethodServer.properties to load before testing.
- log4j.appender.<logname>.File= specifies where the external file is going to be stored. As Jess has pointed out, if you're worried about multiple MS trying to write to the file at the same time, you can include a variable in the filename so that there are actually multiple different testing1.log files - all with unique names - and each MS will write to its respective file.
- log4j.appender.<logname>.layout.ConversionPatter= specifies the layout of the text that is printed into the log file. %d{ISO8601} is actually where I'm getting my correct timestamp, which is what I originally opened this discussion for.
Scenario 2
Another scenario is that you have an actual java file that is compiled, and part of a package. For example, you have the file <WT_HOME>/src/ext/package/CustomClass.java, and it is compiled into <WT_HOME>/codebase/ext/package/CustomClass.class. If you want to enable this type of logging in that java code, then the steps are very similar. The only real difference is that when instantiating LOGGER in the code, the getLogger() method should not be passed a string. Instead, pass it something like this: Logger LOGGER = Logger.getLogger(CustomClass.class); That will get the string of the fully qualified class name and pass it. The value in this case would be "ext.package.CustomClass". And therefore, when setting up the logger properties in log4jMethodServer.properties file, then the first line would become log4j.logger.ext.package.CustomClass=INFO, someNameHere.
Logger LOGGER = Logger.getLogger(CustomClass.class);
# Define customJspLogger appender
log4j.logger.ext.package.CustomClass=INFO, someNameHere
log4j.additivity.someNameHere=false
log4j.appender.someNameHere=wt.log4j.jmx.DailyRollingFileAppender
log4j.appender.someNameHere.File=/home/ptc/testing2.log
log4j.appender.someNameHere.DatePattern='.'yyyy-MM-dd
log4j.appender.someNameHere.layout=org.apache.log4j.PatternLayout
log4j.appender.someNameHere.layout.ConversionPattern=%d{ISO8601} %-5p [%t] %c - %m%n