Variable Substitution in logback groovy - java

I was working with the logback.xml and the variable were load in string like :
<FileNamePattern>${logDirectory}/${logFileName}.%d{yyyy-MM-dd}.%i.html</FileNamePattern>
where logDirectory and logFileName were set in .bat file before calling my jar.
set logFileName=foobar
But now, I deal with groovy. It's awesome and ridiculously more readable than xml. But the variable are no longer expand.
appender("FILE", RollingFileAppender) {
file = "${logDirectory}/${logFileName}.html"
rollingPolicy(TimeBasedRollingPolicy) {
fileNamePattern = "${logDirectory}/${logFileName}.%d{yyyy-MM-dd}.%i.html"
...
}
...
}
The path is now null/null :'(
WORST : the following test throw an exception :
if ("${logConsole}" == "true") {
Anyone see how to make it work ?
-- EDIT add the full logback.groovy
/*
* The configuration use the following variables :
* logDirectory => The log folder
* logFileName => The log file name
* logConsole => true console activation.
**/
import ch.qos.logback.classic.encoder.PatternLayoutEncoder
import ch.qos.logback.classic.html.HTMLLayout
import ch.qos.logback.core.ConsoleAppender
import ch.qos.logback.core.encoder.LayoutWrappingEncoder
import ch.qos.logback.core.rolling.RollingFileAppender
import ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP
import ch.qos.logback.core.rolling.TimeBasedRollingPolicy
import static ch.qos.logback.classic.Level.INFO
import static ch.qos.logback.classic.Level.OFF
def LOG_DIRECTORY = System.getProperty("logDirectory")
def LOG_FILE_NAME = System.getProperty("logFileName")
def LOG_CONSOLE = System.getProperty("logConsole")
appender("FILE", RollingFileAppender) {
file = "${LOG_DIRECTORY}/${LOG_FILE_NAME}.html"
rollingPolicy(TimeBasedRollingPolicy) {
fileNamePattern = "${LOG_DIRECTORY}/${LOG_FILE_NAME}.%d{yyyy-MM-dd}.%i.html"
timeBasedFileNamingAndTriggeringPolicy(SizeAndTimeBasedFNATP) {
maxFileSize = "100MB"
}
maxHistory = 5
}
encoder(LayoutWrappingEncoder) {
layout(HTMLLayout) {
pattern = "%level%date%logger{36}%msg"
}
}
}
appender("STDOUT", ConsoleAppender) {
withJansi = true
encoder(PatternLayoutEncoder) {
pattern = "%d{HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{36}) - %msg%n"
}
}
logger("org", INFO)
logger("net", INFO)
logger("freemarker", INFO)
// We don't care about the bean creation at least it's more than a warn
logger("org.springframework.beans.factory", WARN)
root(OFF, ["FILE"])
if (LOG_CONSOLE == "true") {
root(OFF, ["FILE","STDOUT"]);
}

The System.getProperty() call retrieves properties set with -D on the java command line whereas in the .bat file you are setting shell/environment variables.
Try using System.getenv() in the logback.groovy file.

Related

java Gherkin parser stream does not release file locks

I am using Gherkin parser to parse feature files and returning the list of Gherkin documents see the function below:
import io.cucumber.gherkin.Gherkin;
import io.cucumber.messages.IdGenerator;
import io.cucumber.messages.Messages;
import io.cucumber.messages.Messages.Envelope;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class GherkinUtils {
private static final Logger LOG = LogManager.getLogger(GherkinUtils.class);
public static ArrayList<Messages.GherkinDocument> getGherkinDocumentsFromFiles() {
IdGenerator idGenerator = new IdGenerator.Incrementing();
ArrayList<Messages.GherkinDocument> listOfGherkinDocuments = new ArrayList<>();
String pathFolderFrameworkFeatures = SettingsUtils.getPathFolderFrameworkFeatures();
List<String> listOfPathsForFeatureFiles = FileUtils.getAllFilePathsFromFolder(pathFolderFrameworkFeatures);
try (Stream<Envelope> dataStream = Gherkin.fromPaths(listOfPathsForFeatureFiles, false, true, false, idGenerator)){
List<Envelope> envelopes = dataStream.collect(Collectors.toList());
for (Envelope env : envelopes) {
Messages.GherkinDocument gherkinDocument = env.getGherkinDocument();
listOfGherkinDocuments.add(gherkinDocument);
}
} catch (Exception e) {
LOG.error("Error occurred while trying to read the feature files", new Exception(e));
}
FileUtils.renameAllFeatureFiles("b");
return listOfGherkinDocuments;
}
}
Just before the return statement, you can see the function that will update the name for all feature files just to check if they are not locked.
The problem is that only the first file is always renamed and the rest of them are always locked.
If I will place the rename function at the top, then all the files are successfully renamed...
My understanding is that the try statement will automatically close the stream. Also, I tried to close it manually inside the try block but the results are the same.
What am I missing? How can I make it to release the file locks?
Update 1:
This exact line is making the files (except the first one to be locked):
List<Envelope> envelopes = dataStream.collect(Collectors.toList());
Here is the file name update function definition in case you want to test it:
public static void renameAllFeatureFiles(String fileName) {
String pathFeaturesFolder = SettingsUtils.getPathFolderFrameworkFeatures();
List<String> pathList = FileUtils.getAllFilePathsFromFolder(pathFeaturesFolder);
int counter = 0;
for (String path : pathList) {
counter ++;
File file = new File(path);
File newFile = new File(pathFeaturesFolder + "\\" + fileName +counter+".feature");
System.out.println("File: " + path + " locked: " + !file.renameTo(newFile));
}
}
And here is a sample feature file content:
Feature: Test
Scenario: test 1
Given User will do something
And User will do something
Update 2:
Tried with separate thread using javafx Task, still the same issue :(
Except for one file (this is really strange) all files are locked...
public static void runInNewThread() {
// define the execution task that will run in a new thread
Task<Void> newTask = new Task<>() {
#Override
protected Void call() {
ArrayList<Messages.GherkinDocument> listOfGherkinDocuments = GherkinUtils.getGherkinDocumentsFromFiles();
return null;
}
};
// run the task in a new thread
Thread th = new Thread(newTask);
th.setDaemon(true);
th.start();
}
For now, I have used workaround with creating copies of the specific files and using parser on the copies to prevent locking of the original versions...

Is there any way to log System.err to a file in Logback?

I have an old project that is doing
catch (Exception e) {
e.printStacktrace();
}
all over the place.
Now I want to log those stacktraces to a file using Logback. I couldn't find any solution online.
Is there a way in Logback, to log System.Error statements to a file? I've tried the following but doesn't work, it only writes STDOUT logs to the file.
If anyone could help, it'd be great, thanks!
def DEFAULT_PATTERN = "%d [%thread] %-5level %logger - %msg%n"
appender("STDOUT", ConsoleAppender) {
encoder(PatternLayoutEncoder) {
pattern = DEFAULT_PATTERN
charset = Charset.forName("UTF-8")
}
}
appender("SYSERR", ConsoleAppender) {
encoder(PatternLayoutEncoder) {
pattern = DEFAULT_PATTERN
charset = Charset.forName("UTF-8")
}
target = "System.err"
}
appender("FILE", RollingFileAppender) {
file = "logs/stdout.log"
append = true
encoder(PatternLayoutEncoder) {
pattern = DEFAULT_PATTERN
charset = Charset.forName("UTF-8")
}
filter(ch.qos.logback.classic.filter.ThresholdFilter) {
level = INFO
}
rollingPolicy(ch.qos.logback.core.rolling.TimeBasedRollingPolicy) {
fileNamePattern = "logs/stdout-%d{yyyy-MM-dd}.%i.log"
maxHistory = 10
timeBasedFileNamingAndTriggeringPolicy(ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP) {
maxFileSize = "1MB"
}
}
}
appender("ERROR", RollingFileAppender) {
file = "logs/errors.log"
append = true
encoder(PatternLayoutEncoder) {
pattern = DEFAULT_PATTERN
charset = Charset.forName("UTF-8")
}
filter(ch.qos.logback.classic.filter.ThresholdFilter) {
level = ERROR
}
rollingPolicy(ch.qos.logback.core.rolling.TimeBasedRollingPolicy) {
fileNamePattern = "logs/errors-%d{yyyy-MM-dd}.%i.log"
maxHistory = 10
timeBasedFileNamingAndTriggeringPolicy(ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP) {
maxFileSize = "1MB"
}
}
}
/* Make Spring less verbose. */
//logger("org.springframework", INFO)
//logger("org.springframework.web.servlet", INFO)
/* Quieten Thymeleaf. */
//logger("org.thymeleaf", INFO)
/* Jetty can be really noisy on a shaded jar. */
//logger("org.eclipse.jetty.webapp.WebAppClassLoader", INFO)
//logger("org.eclipse.jetty.util.resource.JarResource", INFO)
/* Quieten Jetty in general. */
//logger("org.eclipse", DEBUG);
def appenders = []
appenders.add("STDOUT")
appenders.add("SYSERR")
appenders.add("FILE")
appenders.add("ERROR")
root(INFO, appenders)
Ended up using a solution that redirects System err to a custom output printstream
log4j redirect stdout to DailyRollingFileAppender

Read xml from an external jar not included in classpath

I created a javafx project using Netbeans, the project itself works just fine.
I'm now trying to implement a custom light-weight plugin system, the plugins are external JARs located inside the plugins/ directory of the main project. I'm using javax.security package to sandbox the plugins.
Here's the main project's structure:
MainProject
|
|---plugins/
| |---MyPlugin.jar
|
|---src/
| |---main.app.plugin
| |---Plugin.java
| |---PluginSecurityPolicy.java
| |---PluginClassLoader.java
| |---PluginContainer.java
....
And the plugin's one:
Plugin
|
|---src/
| |---my.plugin
| | |---MyPlugin.java
| |--settings.xml
|
|---dist/
|---MyPlugin.jar
|---META-INF/
| |---MANIFEST.MF
|---my.plugin
| |---MyPlugin.class
|---settings.xml
To load the plugins into the program i've made a PluginContainer class that gets all the jar files from the plugins directory, lists all file inside the jar and lookup for the plugin file and the settings file.
I can load and make an instance of the plugin class, but when it comes to the XML there's no way i can even list it among the jar contents.
Here's the code, maybe someone can see where i did it wrong.
PluginSecurityPolicy.java
import java.security.AllPermission;
import java.security.PermissionCollection;
import java.security.Permissions;
import java.security.Policy;
import java.security.ProtectionDomain;
public class PluginSecurityPolicy extends Policy {
#Override
public PermissionCollection getPermissions(ProtectionDomain domain) {
if (isPlugin(domain)) {
return pluginPermissions();
} else {
return applicationPermissions();
}
}
private boolean isPlugin(ProtectionDomain domain) {
return domain.getClassLoader() instanceof PluginClassLoader;
}
private PermissionCollection pluginPermissions() {
Permissions permissions = new Permissions();
//
return permissions;
}
private PermissionCollection applicationPermissions() {
Permissions permissions = new Permissions();
permissions.add(new AllPermission());
return permissions;
}
}
PluginClassLoader.java
import java.net.URL;
import java.net.URLClassLoader;
public class PluginClassLoader extends URLClassLoader {
public PluginClassLoader(URL jarFileUrl) {
super(new URL[] {jarFileUrl});
}
}
PluginContainer.java, the #load method is the one
import main.app.plugin.PluginClassLoader;
import main.app.plugin.PluginSecurityPolicy;
import java.io.File;
import java.net.URL;
import java.security.Policy;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
public class PluginContainer {
private ArrayList<Plugin> plugins;
private ManifestParser parser;
public PluginContainer() {
Policy.setPolicy(new PluginSecurityPolicy());
System.setSecurityManager(new SecurityManager());
plugins = new ArrayList<>();
parser = new ManifestParser();
}
public void init() {
File[] dir = new File(System.getProperty("user.dir") + "/plugins").listFiles();
for (File pluginJarFile : dir) {
try {
Plugin plugin = load(pluginJarFile.getCanonicalPath());
plugins.add(plugin);
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}
}
public <T extends Plugin> T getPlugin(Class<T> plugin) {
for (Plugin p : plugins) {
if (p.getClass().equals(plugin)) {
return (T)p;
}
}
return null;
}
private Plugin load(String pluginJarFile) throws Exception {
PluginManifest manifest = null;
Plugin plugin = null;
// Load the jar file
ZipFile jarFile = new ZipFile(pluginJarFile);
// Get all jar entries
Enumeration allEntries = jarFile.entries();
String pluginClassName = null;
while (allEntries.hasMoreElements()) {
// Get single file
ZipEntry entry = (ZipEntry) allEntries.nextElement();
String file = entry.getName();
// Look for classfiles
if (file.endsWith(".class")) {
// Set class name
String classname = file.replace('/', '.').substring(0, file.length() - 6);
// Look for plugin class
if (classname.endsWith("Plugin")) {
// Set the class name and exit loop
pluginClassName = classname;
break;
}
}
}
// Load the class
ClassLoader pluginLoader = new PluginClassLoader(new URL("file:///" + pluginJarFile));
Class<?> pluginClass = pluginLoader.loadClass(pluginClassName);
// Edit as suggested by KDM, still null
URL settingsUrl = pluginClass.getResource("/settings.xml");
manifest = parser.load(settingsUrl);
// Check if manifest has been created
if (null == manifest) {
throw new RuntimeException("Manifest file not found in " + pluginJarFile);
}
// Create the plugin
plugin = (Plugin) pluginClass.newInstance();
plugin.load(manifest);
return plugin;
}
}
And the autogenerated MANIFEST.MF
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.9.4
Created-By: 1.8.0_25-b18 (Oracle Corporation)
The Class-Path directive is missing, but if i force it to . or ./settings.xml or settings.xml (by manually editing the MANIFEST.MF file) it won't work either.
This is all I can think of, Thanks in advance for any help
[EDIT] I've created an images/monitor-16.png into the plugin jar root, added the #load2 method into the PluginContainer.
Since the method is called within a loop I left the Policy.setPolicy(new PluginSecurityPolicy()); and System.setSecurityManager(new SecurityManager()); inside the constructor.
Here's the new plugn jar structure:
TestPlugin.jar
|
|---META-INF/
| |---MANIFEST.MF
|
|---dev.jimbo
| |---TestPlugin.class
|
|---images
| |---monitor-16.png
|
|---settings.xml
The new method code:
private Plugin load2(String pluginJarFile) throws MalformedURLException, ClassNotFoundException {
PluginClassLoader urlCL = new PluginClassLoader(new File(pluginJarFile).toURL());
Class<?> loadClass = urlCL.loadClass("dev.jimbo.TestPlugin");
System.out.println(loadClass);
System.out.println("Loading the class using the class loader object. Resource = " + urlCL.getResource("images/monitor-16.png"));
System.out.println("Loading the class using the class loader object with absolute path. Resource = " + urlCL.getResource("/images/monitor-16.png"));
System.out.println("Loading the class using the class object. Resource = " + loadClass.getResource("images/monitor-16.png"));
System.out.println();
return null;
}
Here's the output
class dev.jimbo.TestPlugin
Loading the class using the class loader object. Resource = null
Loading the class using the class loader object with absolute path. Resource = null
Loading the class using the class object. Resource = null
The following program:
public static void main(String[] args) throws MalformedURLException, ClassNotFoundException {
Policy.setPolicy(new PluginSecurityPolicy());
System.setSecurityManager(new SecurityManager());
PluginClassLoader urlCL = new PluginClassLoader(new File(
"A Jar containing images/load.gif and SampleApp class").toURL());
Class<?> loadClass = urlCL.loadClass("net.sourceforge.marathon.examples.SampleApp");
System.out.println(loadClass);
System.out.println("Loading the class using the class loader object. Resource = " + urlCL.getResource("images/load.gif"));
System.out.println("Loading the class using the class loader object with absolute path. Resource = " + urlCL.getResource("/images/load.gif"));
System.out.println("Loading the class using the class object. Resource = " + loadClass.getResource("images/load.gif"));
}
Produces the following output:
class net.sourceforge.marathon.examples.SampleApp
Loading the class using the class loader object. Resource = jar:file:/Users/dakshinamurthykarra/Projects/install/marathon/sampleapp.jar!/images/load.gif
Loading the class using the class loader object with absolute path. Resource = null
Loading the class using the class object. Resource = null
So I do not think any problem with your class loader. Putting this as an answer so that the code can be formatted properly.
Nailed it! Seems that my previous Netbeans (8.0) was deleting the plugin directory from the added Jar/Folder Libraries references on Clean and Build action. I've downloaded and installed Netbeans 8.0.2 and the problem was solved. Couldn't find any related bug for that version on their tracker though..
Anyways Thanks for the help :)

how to create a log4j custom appender and control the filename

My company uses a software package that reads in log files from our servers, parses them, and spits performance data into a database. We dont have access / permission to modify the source code for the app that reads the files but we do have access to the code that writes the files. I need to change the way the log files are being written and I would like to use log4j (so I can use an AsyncAppender). The program expects a few things:
1). There should be 10 log files that roll and each log file will be one day of logs. The files need to be named 0 through 9 and I need to be able to programatically set the file name and when they roll based on the server time.
2). Essentially when generating the 11th log file it should delete the oldest one and start writing to that one.
3). When a new log file is generated I need to be able to insert a timestamp as the first line of the file (System.currentTimeMillis()).
Is it possible to meet the above requirements with a custom log4j file appender? Ive looked at DailyRollingFileAppender but cant seem to figure out how to control the file names exactly like I need to. Also I cant seem to figure out how to write the first line in the log when it is generated (for example is there some callback function I can register when a new log file gets rolled in)?
I think you can achieve first 2 with
using RollingFileAppender and specifying FixedWindowRollingPolicy for RollingPolicy
as for the #3 you can always write your own handler
For the sake of posterity. I used the below class as my custom rolling policy
import org.apache.log4j.rolling.RollingPolicyBase;
import org.apache.log4j.rolling.RolloverDescription;
import org.apache.log4j.rolling.RolloverDescriptionImpl;
import org.apache.log4j.rolling.TriggeringPolicy;
import org.apache.log4j.Appender;
import org.apache.log4j.spi.LoggingEvent;
public final class CustomRollingPolicy extends RollingPolicyBase
implements TriggeringPolicy
{
private short curFileId = -1;
private String lastFileName = null;
static private final long FILETIMEINTERVAL = 86400000l;
static private final int NUM_FILES = 10;//86400000l;
public String folderName = "";
public String getFolderName() {
return folderName;
}
public void setFolderName(String folderName) {
this.folderName = folderName;
}
private short calculateID(long startTime) {
return (short) ((startTime / FILETIMEINTERVAL) % NUM_FILES);
}
public String getCurrentFileName()
{
StringBuffer buf = new StringBuffer();
buf.append(folderName);
buf.append(calculateID(System.currentTimeMillis()));
return buf.toString();
}
public void activateOptions()
{
super.activateOptions();
this.lastFileName = getCurrentFileName();
}
public RolloverDescription initialize(String currentActiveFile, boolean append)
{
curFileId = this.calculateID(System.currentTimeMillis());
lastFileName = getCurrentFileName();
String fileToUse = activeFileName != null? activeFileName: currentActiveFile != null?currentActiveFile:lastFileName;
return new RolloverDescriptionImpl(fileToUse, append, null, null);
}
public RolloverDescription rollover(String currentActiveFile)
{
curFileId = this.calculateID(System.currentTimeMillis());
String newFileName = getCurrentFileName();
if (newFileName.equals(this.lastFileName))
{
return null;
}
String lastBaseName = this.lastFileName;
String nextActiveFile = newFileName;
if (!currentActiveFile.equals(lastBaseName))
{
nextActiveFile = currentActiveFile;
}
this.lastFileName = newFileName;
return new RolloverDescriptionImpl(nextActiveFile, false, null, null);
}
public boolean isTriggeringEvent(Appender appender, LoggingEvent event, String filename, long fileLength)
{
short fileIdForCurrentServerTime = this.calculateID(System.currentTimeMillis());
return curFileId != fileIdForCurrentServerTime;
}
}
And here is the appender config in my log4j xml file:
<!-- ROLLING FILE APPENDER FOR RUM LOGS -->
<appender name="rumRollingFileAppender" class="org.apache.log4j.rolling.RollingFileAppender">
<rollingPolicy class="com.ntrs.wpa.util.CustomRollingPolicy">
<param name="folderName" value="C:/bea-portal-10.3.2/logs/"/>
<param name="FileNamePattern" value="C:/bea-portal-10.3.2/logs/foo.%d{yyyy-MM}.gz"/>
</rollingPolicy>
<layout class="com.ntrs.els.log4j.AppServerPatternLayout">
<param name="ConversionPattern" value="%m%n" />
</layout>
</appender>

configuring log with Spring Application

I have developed a spring application, I want to configure it with apache log4j, have downloaded it and put the jar in project's class path. Below is my log4j.Properties file.
# Root logger option
log4j.rootLogger=INFO, file
# Direct log messages to a log file
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=C\:\\loging.log
log4j.appender.file.MaxFileSize=1MB
log4j.appender.file.MaxBackupIndex=1
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
And below is my main spring application class.
import org.apache.log4j.Logger;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.FileSystemResource;
public class DrawingClass {
public static void main(String args[])
{
//without dependency injection
/*Triangle t1 = new Triangle();
t1.draw();*/
//with dependency injection
BeanFactory factory = new XmlBeanFactory(new FileSystemResource("Spring.xml"));
Triangle t1 =(Triangle) factory.getBean("triangle");
t1.draw();
}
}
Please advise if I want to put the log.info in my above main class what modifications I need to do in my main class and also please advise what modifications I need to done to call log4j in my main class?
come up with this solution and it works ..the edited log4j.properties file is
### direct messages to file or.log ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=C:/logs/s.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1} - %m%n
log4j.appender.file.append=true
### set log levels - for more verbose logging change 'info' to 'debug' ##
log4j.rootCategory=ALL, file
log4j.logger.Demo=\=debug
log4j.logger.org.eclipse=debug
and the way to callit from main class is
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.FileSystemResource;
public class DrawingClass {
/* Get actual class name to be printed on */
static final Logger log = Logger.getLogger(DrawingClass.class);
public static void main(String args[])
{PropertyConfigurator.configure("log4j.properties");
//without dependency injection
/*Triangle t1 = new Triangle();
t1.draw();*/
log.info("Before execution");
//with dependency injection
BeanFactory factory = new XmlBeanFactory(new FileSystemResource("Spring.xml"));
Triangle t1 =(Triangle) factory.getBean("triangle");
log.info("Hello this is an info message");
t1.draw();
log.info("after object execution");
}
}
If there is any other better way then please advise .
Can you try adding this line to your class -
public class DrawingClass {
static final Logger log = Logger.getLogger(DrawingClass.class);
public static void main(String args[])
{
//without dependency injection
/*Triangle t1 = new Triangle();
t1.draw();*/
log.info("Before execution");
//with dependency injection
BeanFactory factory = new XmlBeanFactory(new FileSystemResource("Spring.xml"));
Triangle t1 =(Triangle) factory.getBean("triangle");
t1.draw();
}
}
Now let me know if anything gets added to the log.
This link might help you - http://www.dzone.com/tutorials/java/log4j/sample-log4j-properties-file-configuration-1.html
For using logging in my project I made such things:
1) Defined special annotation that should mark fields where logger should be injected:
#Retention(RUNTIME)
#Target(FIELD)
#Documented
public #interface InjectLogger {
}
2) Then created special BeanPostProcessor for injected logger into annotated field:
#Component(value="loggerInjector")
public class LoggerInjector implements BeanPostProcessor {
#Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
#Override
public Object postProcessBeforeInitialization(final Object bean, String beanName)
throws BeansException {
ReflectionUtils.doWithFields(bean.getClass(), new FieldCallback() {
public void doWith(Field field) throws IllegalArgumentException,
IllegalAccessException {
// make the field accessible if defined private
ReflectionUtils.makeAccessible(field);
if (field.getAnnotation(InjectLogger.class) != null) {
Logger log = LoggerFactory.getLogger(bean.getClass());
field.set(bean, log);
}
}
});
return bean;
}
}
3) After this in necessary bean mark property where logger should be injected by #InjectLogger annotation and use this logger in your code.
#InjectLogger
private Logger logger;
public void doSomething(...) {
try{
...
} catch (Exception e) {
logger.error("bla bla bla", e);
}
}
In my project I use slf4j with log4j as concrete implementation. But with log4j it will be similar.
Also you need to know that by default all Spring libs using common-logging. And if you want that Spring libs write their log to your file you should use special additionla lib. You can get them, as I remember, from appache site. For slf4j such lib is named jcl-over-slf4j-1.6.4.jar.
EDIT 2:
Also in spring the good practise is to use AOP for logging. Here is an example of simplified Aspect:
#Component
#Aspect
#Order(value=2)
public class LoggingAspect {
#Around("execution(* com.blablabla.server..*.*(..))")
public Object logMethod(ProceedingJoinPoint joinPoint) throws Throwable{
final Logger logger = LoggerFactory.getLogger(joinPoint.getTarget().getClass().getName());
Object retVal = null;
try {
StringBuffer startMessageStringBuffer = new StringBuffer();
startMessageStringBuffer.append("Start method ");
startMessageStringBuffer.append(joinPoint.getSignature().getName());
startMessageStringBuffer.append("(");
Object[] args = joinPoint.getArgs();
for (int i = 0; i < args.length; i++) {
startMessageStringBuffer.append(args[i]).append(",");
}
if (args.length > 0) {
startMessageStringBuffer.deleteCharAt(startMessageStringBuffer.length() - 1);
}
startMessageStringBuffer.append(")");
logger.trace(startMessageStringBuffer.toString());
StopWatch stopWatch = new StopWatch();
stopWatch.start();
retVal = joinPoint.proceed();
stopWatch.stop();
StringBuffer endMessageStringBuffer = new StringBuffer();
endMessageStringBuffer.append("Finish method ");
endMessageStringBuffer.append(joinPoint.getSignature().getName());
endMessageStringBuffer.append("(..); execution time: ");
endMessageStringBuffer.append(stopWatch.getTotalTimeMillis());
endMessageStringBuffer.append(" ms;");
logger.trace(endMessageStringBuffer.toString());
} catch (Throwable ex) {
StringBuffer errorMessageStringBuffer = new StringBuffer();
// Create error message
logger.error(errorMessageStringBuffer.toString(), e)
throw ex;
}
return retVal;
}
}
# LOG4J configuration
log4j.rootLogger= DEBUG, INFO, Appender1, Appender2
log4j.appender.Appender1=org.apache.log4j.ConsoleAppender
log4j.appender.Appender1.layout=org.apache.log4j.PatternLayout
log4j.appender.Appender1.layout.ConversionPattern=%-7p %d [%t] %c %x - %m%n
log4j.appender.Appender2=org.apache.log4j.FileAppender
log4j.appender.Appender2.File=D:/Project Log/Demo/demo.log
log4j.appender.Appender2.layout=org.apache.log4j.PatternLayout
log4j.appender.Appender2.layout.ConversionPattern=%-7p %d [%t] %c %x - %m%n
Write this code in log4j.properties which is in Resources folder of src folder.
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
Write this above code in porm.xml file or download jar for this dependency.
<context:property-placeholder location="classpath:log4j.properties" />
write this above code between and in your spring-config.xml
package com.apmc.controller;
import org.apache.log4j.Logger;
import java.text.DateFormat;
import java.util.Date;
import com.apmc.Generic.RandomGenerator;//This my own made class So You need to create //RandomGenerator for use it
#Controller
public class StateController {
private static Logger logger = Logger.getLogger(StateController.class);
DateFormat df = new SimpleDateFormat("ddMMyyHHmmss");
Date dateobj = new Date();
int randNum = RandomGenerator.randInt(1000, 9999);
String successMsg = "", errorMsg = "";
#Autowired
StateService stateService;
List<State> newList = new ArrayList();
#RequestMapping(value = "/Admin/admin/NewState_form")
public ModelAndView stateForm(#ModelAttribute State state) {
try {
logger.info("\n stateForm Started \n errorcode : "+errorcode);
newList = stateService.loadAll();
logger.info("\n stateForm Completed");
errorMsg = "";
} catch (Exception e) {
errorcode = ""+df.format(dateobj)+randNum;
errorMsg = " New State Form Error \n Please contact Admin and errorcode:" +errorcode;
successMsg = "";
logger.error("error code for stateForm in StateController" +df.format(dateobj)+" errorcode: "+errorcode);
}
return new ModelAndView("state").addObject("editState", new State())
.addObject("errorMsg", errorMsg) .addObject("showStateList",newList)
.addObject("successMsg", successMsg);
}
}
Above example for how to use log in Controller or any Java Class
Here I made for error Tracking create error code show for commmunicate with error using RandomGenerator.java write as below given
package com.apmc.Generic;
import java.util.Random;
public class RandomGenerator {
/**
* Returns a pseudo-random number between min and max, inclusive.
* The difference between min and max can be at most
* <code>Integer.MAX_VALUE - 1</code>.
*
* #param min Minimum value
* #param max Maximum value. Must be greater than min.
* #return Integer between min and max, inclusive.
* #see java.util.Random#nextInt(int)
*/
public static int randInt(int min, int max) {
// NOTE: Usually this should be a field rather than a method
// variable so that it is not re-seeded every call.
Random rand = new Random();
// nextInt is normally exclusive of the top value,
// so add 1 to make it inclusive
int randomNum = rand.nextInt((max - min) + 1) + min;
return randomNum;
}
}

Categories