Sunday, April 3, 2011

Line number information for SLF4J and LOG4J log messages

Briefly;

Line number information should be put into the log messages by processing classes and enhancing the byte code.


#Ant task definition and usage can be like below. Feel free to customize according to your requirements


<taskdefname="enhanceLogs"classname="your.lovely.company.LoggerEnhancement"classpathref="lib.ant.path"/>

<enhanceLogs classesDir="${classesDir}"loggerJarsDirectory="${logging.jars.dir}"/>

import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.expr.ExprEditor;
import javassist.expr.MethodCall;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;

public class LoggerEnhancement extends Task {
    public String classesDir;
    public String loggerJarsDirectory;

    public String getLoggerJarsDirectory() {
        returnthis.loggerJarsDirectory;
    }

    public voidsetLoggerJarsDirectory(final String loggerJarsDirectory) {
        this.loggerJarsDirectory= loggerJarsDirectory;
    }

    public String getClassesDir() {
        returnthis.classesDir;
    }

    public voidsetClassesDir(final String classesDir) {
        this.classesDir= classesDir;
    }

    @Override
    public voidexecute() throws BuildException {
        try{
            this.enhanceClasses();
        } catch(final Exception e) {
            e.printStackTrace();
            throw new BuildException(e);
        }
    }

    private voidenhanceClasses() throws FileNotFoundException, Exception {
        finalList classFiles = this.getClassFiles(this.classesDir);
        for(final File file : classFiles) {
            String qualifiedClassName = file.toString().substring(this.classesDir.length() + 1, file.toString().length() - 6);
            qualifiedClassName = qualifiedClassName.replaceAll("\\\\", ".");
            this.enhanceClassFile(qualifiedClassName);
        }
    }

    private voidenhanceClassFile(final String className) throws Exception {
        finalClassPool pool = ClassPool.getDefault();
        pool.appendClassPath(this.classesDir);
        pool.appendClassPath(this.loggerJarsDirectory+ "\\" + "slf4j-api-1.6.1.jar");
        pool.appendClassPath(this.loggerJarsDirectory+ "\\" + "org.apache.log4j_1.2.15.v201005080500.jar");
        finalCtClass compiledClass = pool.get(className);
        if(compiledClass == null) {
            System.err.println("Class " + className + " not found");
        } else{
            final LoggerEnhancementExpEditor exp = new LoggerEnhancementExpEditor();
            exp.setClaz(compiledClass);
            compiledClass.instrument(exp);
            compiledClass.writeFile(this.classesDir);
        }
    }

    private List getClassFiles(final String classesDir) throws FileNotFoundException {
        finalList result = new ArrayList();

        finalFile startingDirectory = new File(classesDir);
        finalList files = this.getFileListing(startingDirectory);
        System.out.println(" -- Processing classes under directory:" + classesDir);
        for(final File file : files) {
            if (file.isFile() && file.getName().endsWith(".class")) {
                result.add(file);
            }
        }
        returnresult;
    }

    private List getFileListing(final File aStartingDir) throws FileNotFoundException {
        this.validateDirectory(aStartingDir);
        finalList result = this.getFileListingNoSort(aStartingDir);
        Collections.sort(result);
        returnresult;
    }

    private List getFileListingNoSort(final File aStartingDir) throws FileNotFoundException {
        finalList result = new ArrayList();
        finalFile[] filesAndDirs = aStartingDir.listFiles();
        finalList filesDirs = Arrays.asList(filesAndDirs);
        for(final File file : filesDirs) {
            result.add(file);
            if (!file.isFile()) {
                final List deeperList = this.getFileListingNoSort(file);
                result.addAll(deeperList);
            }
        }
        returnresult;
    }

    private voidvalidateDirectory(final File aDirectory) throws FileNotFoundException {
        if(aDirectory == null) {
            throw new IllegalArgumentException("Directory should not be null.");
        }
        if(!aDirectory.exists()) {
            throw new FileNotFoundException("Directory not exist: " + aDirectory.getAbsolutePath());
        }
        if(!aDirectory.isDirectory()) {
            throw new IllegalArgumentException("Is not a directory: " + aDirectory);
        }
        if(!aDirectory.canRead()) {
            throw new IllegalArgumentException("Directory cannot be read: " + aDirectory);
        }
    }

    static classLoggerEnhancementExpEditor extends ExprEditor {
        privateCtClass claz;

        publicCtClass getClaz() {
            return this.claz;
        }

        publicvoid setClaz(final CtClass claz) {
            this.claz= claz;
        }

        @Override
        publicvoid edit(final MethodCall m) throws CannotCompileException {

            if (this.isLoggerMethodCall(m)) {
                try {
                    System.out.println("      -- Enhancing log line: "
                                       + this.getClaz().getName()
                                       + "."
                                       + m.where().getName()
                                       + "("
                                       + this.getClaz().getSimpleName()
                                       + ".java:"
                                       + m.getLineNumber()
                                       + ")");

                    m.replace("{ $1 = $1 +\", "
                              + this.getClaz().getName()
                              + "."
                              + m.where().getName()
                              + "("
                              + this.getClaz().getSimpleName()
                              + ".java:"
                              + m.getLineNumber()
                              + ") "
                              + "\"; $_ = $proceed($$); }");
                } catch (final Exception e) {
                    throw new CannotCompileException(e);
                }
            }
        }

        privateboolean isLoggerMethodCall(final MethodCall m) {
            return this.isSlf4jLoggerMethod(m) || this.isLog4jLoggerMethod(m);
        }

        privateboolean isSlf4jLoggerMethod(final MethodCall m) {
            return m.getClassName().equals("org.slf4j.Logger") && this.isALogMethod(m);
        }

        privateboolean isLog4jLoggerMethod(final MethodCall m) {
            return m.getClassName().equals("org.apache.log4j.Logger") && this.isALogMethod(m);
        }

        privateboolean isALogMethod(final MethodCall m) {
            boolean result = false;

            final String[] slf4LogLevels = { "trace", "debug", "info", "warn", "error", "fatal" };
            final String methodName = m.getMethodName();
            for (final String logLevel : slf4LogLevels) {
                if (methodName.equals(logLevel)) {
                    result = true;
                }
            }
            return result;
        }

    }

}

Saturday, March 5, 2011

LOG4J Usage

<appender name="errorAppender" class="org.apache.log4j.RollingFileAppender">
            <param name="File" value="logs/errors.log" />
            <param name="Append" value="true" />
            <param name="MaxFileSize" value="10000KB" />
            <param name="MaxBackupIndex" value="50" />
            <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d %-5p [%-16t]%m [%l : %t] %n" />
            </layout>
<filter class="org.apache.log4j.varia.LevelRangeFilter">
            <param name="LevelMin" value="ERROR" />
</filter>
</appender>
<appender name="ASYNC_errorAppender" class="org.apache.log4j.AsyncAppender">
            <param name="BufferSize" value="1000"/>
            <param name="Blocking" value="true"/>
            <param name="LocationInfo" value="false"/>
            <appender-ref ref="errorAppender"/>
</appender>

<logger name="com.mycompany.product.core" additivity="false"<
            <level value="DEBUG" /<
            <appender-ref ref="ASYNC_coreappender" /<
            <appender-ref ref="ASYNC_errorAppender" /<
            <appender-ref ref="ASYNC_ConsoleAppender" /<
</logger<


"LocationInfo" parameter is set to FALSE because of line number calculation's performance impact.You can set it to TRUE when you needed it. Log4j will take necessary actions on the fly. (Nevertheless i don't undertsand why this calculation needed to to be done on my thread! )

"Blocking" parameter is set to TRUE. Normally I prefer to set it true but this is my error log so i don't want to miss a error log if it happens.

"BufferSize" param is set to 1000 means :
     1) if you set blocking param to false : you can miss some logs when your buffer is full.
     2)if you set blocking param to true : your threads can be suspended while trying to log something while buffer is full. I don't prefer this kind of usage in production mode if my log entries not so critical like callDetailRecords/errors.

I will not write about why/when you need to use async logger. I want to finalize this blog entry ASAP before the blogger.com is blocked county wide  (Turkey : A Middle East Country)

Take Care..

Friday, November 12, 2010

IODH

Zero Syncronization issues ! Simple ! Clean !

public class Something {
private Something() {
}

private static class LazyHolder {
private static final Something INSTANCE = new Something();
}

public static final Something getInstance() {
return LazyHolder.INSTANCE;
}
}

OR

static Singleton instance;

public static synchronized Singleton getInstance() {
if (instance == null)
instance == new Singleton();
return instance;
}

Why it is called LAZY ? : IODH utilizes lazy class initialization. The JVM won't execute a class's static initializer until you actually touch something in the class. This applies to static nested classes, too...

Read more on : wikipedia , Double-checked_locking

Wednesday, November 3, 2010

Transaction isolation levels and 3 phenomena..


dirty read
A transaction reads data written by a concurrent uncommitted transaction.
nonrepeatable read
A transaction re-reads data it has previously read and finds that data has been modified by another transaction (that committed since the initial read).
phantom read
A transaction re-executes a query returning a set of rows that satisfy a search condition and finds that the set of rows satisfying the condition has changed due to another recently-committed transaction.
Isolation Level Dirty Read Nonrepeatable Read Phantom Read
Read uncommitted Possible Possible Possible
Read committed Not possible Possible Possible
Repeatable read Not possible Not possible Possible
Serializable Not possible Not possible Not possible

see also : http://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html Similar things to Java's ReentrantReadWriteLock

Thursday, October 28, 2010

Basic Drools example!

what is wrong with this ??

rule "Apply 10% discount if total purcahses is over 100"           
    no-loop true   
    dialect "java"
    when
        $c : Customer()
        $i : Double(doubleValue  > 100) from accumulate ( Purchase( customer == $c, $price : product.price ),
                                                                    sum( $price ) )
    then
          $c.setDiscount( 10 );
        insertLogical( new Discount($c, 10) );   
        System.out.println( "Customer " + $c.getName() + " now has a shopping total of " + $i );
end

Hi All,

We see a part of .drl file above :) I have found it in the Drools 5.1 examples.I tried to understand it's world. I can not define exactly but i think there is something dirty around here.

I write java. I want to write java. I know java (I guess:))...

But what is the no-loop & accumulate & sum & insertLogical & assigments in text & calling java methods from a text file ?

Why i have have learn some other literals ? If I am expected & free to write Java in there (meaning lovely .drl file) why & what the other things are ?

Come on guys. I am someone who really curious for new technologies. I am not resisting to learn those.


Only those make me scared . Calling my methods  from a text file ??? i was expecting to see only execution paths (in some other clean waylike when->what). Maybe I could implement some interfaces i could return some preDefined RETURN_TYPEs also. Even I could write simple Java Statements in an XML file. Those are all accepted by me.

But,,, Calling java methods from text context, doing assignments in there .Learning its nature .....I felt myself like developing PLSQL . I don't want implement a business logic inside a text file. I don't know why ...Just my comments.

PS: I could validate a xml file or Java file in my IDE (which is not eclipse). I don't want to be mad bec.of a typo or some extra comma while dealing with a weird text file in the future.


Sorry...

I will be searching for another fwk ....

****

A few days later....

A small update :

1) Download JSR94 jar :Java Rule Engine Specification
http://jcp.org/aboutJava/communityprocess/final/jsr094/index.html
2) Download JRuleEngine from :
http://sourceforge.net/projects/jruleengine/files/
3) Create a project with these 2 jars. Compile and run ..

See sample rule.xml :

I liked it very much...Sweet huh ?

Nevertheless I have a question mark for JRuleEngine too. As a commentator :D

In its web page there is a link: To do:

It says :
- Rules graphical editor: a JSP web application to define rules that will be saved on XML file.



****

JSP's and web applications is a good but i think it is not the right choice.
Application Management and Monitoring must be done via MBeans. JConsole a is good tool.

InputStream inStream = new FileInputStream( "/home/sunels/JRuleEngine1.3/examples/example1.xml" );
RuleExecutionSet res1 = ruleAdministrator.getLocalRuleExecutionSetProvider(null).createRuleExecutionSet( inStream, null );


Maybe rules can be served as a mbean while creating the RuleExecutionSet...