Loggifier.unkrig.de: Difference between revisions

From unkrig.de
Jump to navigation Jump to search
(Die Seite wurde neu angelegt: „== Getting started with LOGGIFIER == Now you want to know how you can use the LOGGIFIER. I presume that you know what java.util.logging is and why you want to…“)
 
mNo edit summary
 
(46 intermediate revisions by the same user not shown)
Line 1: Line 1:
== Getting started with LOGGIFIER ==
<meta name="keywords" content="java, logging, inject, automated"></meta>
<meta name="description" content="A tool for automated injection of logging code into Java class files"></meta>
== Motivation ==


Now you want to know how you can use the LOGGIFIER. I presume that you know what java.util.logging is and why you want
Have you ever wondered how much cleaner your code would look if it weren't littered with all that logging code? And, when you're hunting bugs in production environments, do you produce tons of logging output, but often find that the critical bit of information that you need is missing?
to use logging at all (e.g. in favor of an interactive debugger).


So let's start with a very simple program:
If the answers are "no" and "no", then stop reading. Otherwise, LOGGIFIER may be the tool you need.


    '''import''' java.util.logging.Logger;
== Overview ==
   
    '''public class''' Main {
   
        '''private static final''' Logger LOGGER = Logger.getLogger("Main.USER");
   
        '''public static void''' main(String[] args) {
            System.out.println("HELLO WORLD");
            LOGGER.fine("About to terminate");
        }
    }


By default, java.util.logging is disabled, so we won't get any logging output:
The concept of LOGGIFIER is as follows:


     '''$ javac Main.java'''
* Relevant class files are instrumented during the build process:<br />Code similar to
     '''$ java Main'''
     <nowiki>if (MyClass.__logger123.isLoggable(Level.FINE)) {
     HELLO WORLD
        Helper.logp(
    '''$'''
            MyClass.__logger123,      // logger
            Level.FINE,              // level
            "com.acme.MyClass",      // sourceClass
            "main(String[])",        // sourceMethod
            "MyClass.java",          // source
            120,                      // lineNumber
            "ENTRY  args=(args={0})", // message
            new Object[] { arg1 }     // parameters
        );
     }</nowiki>
: is inserted all over the place. (<code>Helper.logp()</code> eventually calls <code>java.util.logging.Logger.logp()</code>, after having done some pretty-printing on the message arguments.) Where exactly (in the example: At the top of 'MyClass.main()'), and at what log level (here: FINE) is controlled by the applicable [[#Loggification rules|loggification rules]]. In typical scenarios, the default rules should generate a reasonable amount of logging points.
* The instrumentation remains even in production. This is possible because the instrumentation is optimized for very low overhead (as you can see in the code above: The call to 'isLoggable()' is typically super fast).
* Alternatively you loggify the code only when you start debugging. This is often less practical, because it requires that you either have access to the .jar/.war/.ear files in the target environment., or restart the JVM with loggification activated. Also, the restart or the loggification may influence the problems that you want to track down.
* Then, when it comes to debugging, you enable logging at runtime, e.g. by editing the 'logging.properties' file, or by connecting to the running JVM through JMX. LOGGIFIER does not have one single Logger or one Logger per class, but separate loggers for all classes, methods and events. (Loggers are cheap, so the cost for having hundreds of them is low.) Hence you can enable the logging very selectively and thereby keep the amount of logging output reasonable.


Thus we need to create one more file
== Getting started ==


    # File 'my_logging.properties'.
Click [[Loggifier.unkrig.de / Getting Started|here]] to take the fast track to Loggifier.
    handlers = java.util.logging.ConsoleHandler
    java.util.logging.ConsoleHandler.level = FINEST
    .level = FINEST


, and run our program with the 'java.util.logging.config.file' system property set:
== Tools ==


    '''$ java -Djava.util.logging.config.file=my_logging.properties Main'''
There are various tools to loggify your classes:
    HELLO WORLD
    26.08.2011 11:01:53 Main main
    FINE: About to terminate
    '''$'''


(If you don't like the two-line output format, then you can configure a different log formatter, e.g.
* At build time:
'de.unkrig.commons.util.logging.formatter.PrintfFormatter', contained in [http://loggifier.unkrig.de/download/ de.unkrig.commons.jar], in the 'my_logging.properties' file:
** The LOGGIFIER [[Loggifier.unkrig.de / Tools / Command line tool|command line tool]]
** The LOGGIFIER [[Loggifier.unkrig.de / Tools / Eclipse plug-in|ECLIPSE plug-in]]
** The <code><de.unkrig.loggifier></code> [[Loggifier.unkrig.de / Tools / Ant task|ANT task]]
* At runtime:
** The [[Loggifier.unkrig.de / Tools / LoggifyingClassLoader|LoggifyingClassLoader]]
** The LOGGIFIER [[Loggifier.unkrig.de / Tools / Java agent|Java agent]]


    java.util.logging.ConsoleHandler.formatter = de.unkrig.commons.util.logging.formatter.PrintfFormatter
== Loggification ==
    de.unkrig.commons.util.logging.formatter.PrintfFormatter.format = COMPACT


Then you'll get:
The LOGGIFIER loads a class file and analyzes the bytecode of the class's constructors and methods. Looking at the code, it identifies "interesting" points where logging code could be inserted. Each of the logging points is characterized by an [[#Logging actions|action]]. For each logging point, the relevant [[#Loggification rules|loggification rules]] are executed, and the first that matches determines at what level the logging event is to be logged (or if no logging code is injected for this logging point). Loggification rules optionally have a [[#Discriminator expression|discriminator expression]], which has a set of [[#Discriminator expression parameters|parameters]].


    '''$ java -Djava.util.logging.config.file=my_logging.properties -cp de.unkrig.commons.jar\;. Main'''
=== Logging actions ===
    HELLO WORLD
    FINE Main::main About to terminate
    '''$'''


But that's only a matter of taste.)
Each section of code is characterized by an 'Action'. For example, the JVM bytecodes <code>PUTFIELD</code> and <code>PUTSTATIC</code> are characterized by action '''PUT'''.


So now let's loggify the 'Main' class! Get 'de.unkrig.loggifier.jar' and 'de.unkrig.loggifier-runtime.jar' from [http://loggifier.unkrig.de/download/] and run:
{| class="wikitable"
|-
|'''ALOAD'''    ||Log array type, array value, index and element value read from an array
|-
|'''ARITH'''     ||Log operands, operator (+, -, ...) and result of an arithmetic operation
|-
|'''ASTORE'''    ||Log the array, the index and the value being stored in an array
|-
|'''CAST'''      ||Log the type to which the operand is casted
|-
|'''CATCH'''    ||Log the declared catch type and the actually caught exception
|-
|'''CLINIT'''    ||Log the name of a class that is initialized
|-
|'''CONST'''    ||Log the value of a constant being pushed on the operand stack
|-
|'''CONVERT'''  ||Log a value that is arithmetically converted and the conversion result
|-
|'''ENTRY'''    ||Log the target object (if non-static) and the arguments as a method begins execution
|-
|'''GET'''      ||Log declaring class of, name of, source instance of (if non-static) and value read from a field
|-
|'''INVOKE'''    ||Log signature, target object (if non-static) and arguments right before a method is invoked
|-
|'''INSTANCEOF'''||Log the type to which the operand type is compared
|-
|'''LENGTH'''    ||Log the array who's length is determined
|-
|'''LOAD'''     ||Log type of, name of and value read from a local variable
|-
|'''NEW'''      ||Log the type of a new object being instantiated
|-
|'''PUT'''      ||Log declaring class of, name of, target instance of (if non-static) and value assigned to field
|-
|'''RESULT'''    ||Log method signature and invocation result (if not 'void')
|-
|'''RETURN'''    ||Log the value that is being returned by a method (if not 'void')
|-
|'''STORE'''    ||Log value assigned to, type of and name of a local variable
|-
|'''SWITCH'''    ||Log the integer value that a switch is being executed on
|-
|'''THROW'''    ||Log an exception right before it is thrown
|}


    '''$ ls -l'''
=== Loggification rule syntax ===
    -rwx------+ 1 H7DA016 Kein 969 Aug 26 11:23 Main.class
    '''$ java -cp de.unkrig.loggifier.jar de.unkrig.loggifier.Main Main.class'''
    '''$ ls -l'''
    -rwx------+ 1 H7DA016 Kein 2314 Aug 26 11:23 Main.class
    '''$'''


Not only can you pass class files to LOGGIFER, but also directories, files in ZIP format that contain '.class' files
A rule triggers if and only if
(e.g. JAR and WAR files), and even ZIP files that contain nested ZIP files with '.class' files (e.g. EAR files). In
* It applies to the action of the loggification point (ENTRY, EXIT, THROW, ...)
any case, LOGGIFIER will modify the contained '.class' "in-place".
* Its discriminator expression (if any) evaluates to TRUE for the actual point (who's properties are availables as variables in the discriminator expression, see above)


Now what logging do we get now?
The syntax of all loggification rules is:


    '''$ java -Djava.util.logging.config.file=my_logging.properties -cp de.unkrig.commons.jar\;de.unkrig.loggifier-runtime.jar\;. Main'''
loggification-rule :=
    FINER Main::<clinit>() (Main.java:5) ENTRY  args=()
    ( action { ',' action } | 'ALL' ) '=' level [ ':' discriminator-expression ]
    FINER Main::<clinit>() (Main.java:5) RETURN
    FINER Main::main(String[]) (Main.java:8) ENTRY args=(String[0])
action :=
    HELLO WORLD
    (One of the actions documented above)
    FINE Main::main About to terminate
   
    FINER Main::main(String[]) (Main.java:10) RETURN
level :=
    '''$'''
    'SEVERE' | 'WARNING' | 'INFO' | 'CONFIG' | 'FINE' | 'FINER' | 'FINEST'
discriminator-expression :=
    (Syntax defined below)


Notice that you now need to have the (very small) 'de.unkrig.loggifier-runtime.jar' on the class path, because the
Examples of valid loggification rules are:
injected logging code needs that.


By default, LOGGIFIER injects a 'reasonable' amount of logging: You can see how the class initializer of the 'Main'
ALL=FINE
class is executed, then how the 'main()' method is executed. Notice how the hand-written logging seamlessly mixes with
ENTRY=FINE
the automatically injected logging.
ARITH=FINE
LOAD,CONVERT,ARITH,STORE=FINE
ALL=FINE:class =* "de.*"


Effectively, we can now remove the hand-written logging code from our class, because the automatically injected logging
(<code>=*</code> is the 'glob matching operator', e.g. "hello" matches "he*".)
code now does the job:


    '''public class''' Main {
=== Loggification rule precedence ===
   
        '''public static void''' main(String[] args) {
            System.out.println("HELLO WORLD");
        }
    }


Now let's compile, loggify and run again:
The sequence of checked rules for each logging point is as follows:


    '''$ javac Main.java'''
;If the constructor or method being instrumented has a '@Loggify' annotation (not for action CLINIT)
    '''$ java -cp de.unkrig.loggifier.jar de.unkrig.loggifier.Main Main.class'''
:Its values in reverse order (i.e. the last value/rule takes the highest precedence).
    '''$ java -Djava.util.logging.config.file=my_logging.properties -cp de.unkrig.commons.jar\;de.unkrig.loggifier-runtime.jar\;. Main'''
    FINER Main::<clinit>() (Main.java:5) ENTRY  args=()
    FINER Main::<clinit>() (Main.java:5) RETURN
    FINER Main::main(String[]) (Main.java:8) ENTRY  args=(String[0])
    HELLO WORLD
    FINER Main::main(String[]) (Main.java:10) RETURN
    '''$'''


For demonstration, we now set the amount of logging to the highest possible level. There are several ways to configure
;If a field 'static final String $$loggifierRules = "xyz"' was declared before the contructor or method being instrumented
the injection of logging code: We can pass a 'rule' to the LOGGIFER like '-rule ALL=FINE', or add a '@Loggify'
:Its value, split at '\n', in reverse order (i.e. the last fragment/rule takes the highest precedence).
annotation to any class or method declaration:


    import de.unkrig.loggifier.runtime.annotation.Loggify;
;If the class or interface being instrumented has a '@Loggify' annotation
   
:Its values of in reverse order (i.e. the last value/rule takes the highest precedence).
    @Loggify("ALL=FINE")
    public class Main {
        // ...


Then compile, loggify and run again:
;If using the four-parameter [[Loggifier.unkrig.de / Tools / LoggifyingClassLoader|LoggifyingClassLoader]] constructor
:The 'logLevelCalculator' argument.


    '''$ javac Main.java -cp de.unkrig.loggifier-runtime.jar'''
;If using the [[Loggifier.unkrig.de / Tools / Ant task|ANT task]]
    '''$ java -cp de.unkrig.loggifier.jar de.unkrig.loggifier.Main Main.class'''
:<code><rule></code> subelements, in reverse order (i.e. the last <code><rule></code> takes the highest precedence).
    '''$ java -Djava.util.logging.config.file=my_logging.properties -cp de.unkrig.loggifier-runtime.jar\;de.unkrig.commons.jar\;. Main'''
    FINE Main::<clinit>() (Main.java:1) CLINIT Main
    FINE Main::<clinit>() (Main.java:7) ENTRY  args=()
    FINE Main::<clinit>() (Main.java:7) CONST  "Main.USER"
    FINE Main::<clinit>() (Main.java:7) INVOKE Logger.getLogger(String): args=("Main.USER")
    FINE Main::<clinit>() (Main.java:7) RESULT Logger.getLogger(String) => java.util.logging.Logger@156ee8e
    FINE Main::<clinit>() (Main.java:7) PUT    Main.LOGGER <== java.util.logging.Logger@156ee8e
    FINE Main::<clinit>() (Main.java:7) RETURN
    FINE Main::main(String[]) (Main.java:10) ENTRY  args=(String[0])
    FINE Main::main(String[]) (Main.java:10) GET    System.out ==> java.io.PrintStream@47b480
    FINE Main::main(String[]) (Main.java:10) CONST  "HELLO WORLD"
    FINE Main::main(String[]) (Main.java:10) INVOKE PrintStream.println(String): target=java.io.PrintStream@47b480, args=("HELLO WORLD")
    HELLO WORLD
    FINE Main::main(String[]) (Main.java:10) RESULT PrintStream.println(String)
    FINE Main::main(String[]) (Main.java:12) RETURN
    '''$'''


(Notice that JAVAC now needs "de.unkrig.loggifier-runtime.jar" on the class path, because "@de.unkrig.loggifier.runtime.annotation.Loggify" lives there.)
;If using the [[Loggifier.unkrig.de / Tools / Command line tool|LOGGIFIER command line tool]]
:The '-rule' options, in reverse order (i.e. the last '-rule' option takes the highest precedence).


Now you also get informed how the 'System.out' field is read and the string constant "HELLO WORLD" is used - if
;If using the [[Loggifier.unkrig.de / Tools / Eclipse plug-in|ECLIPSE plug-in]]
you're interested.
:The levels on the "Loggifier" property page of each project. (This rule ALWAYS applies, hence the following rules are never executed).


LOGGIFIER is not only available as a command-line tool, but also as an ECLIPSE plug-in and an ANT task.
;If using the [[Loggifier.unkrig.de / Tools / Java agent|Java agent]]
:Its argument string passed to the agent, split at '\n', in reverse order (i.e. the last fragment/rule takes the highest precedence).
 
;If the action is LOAD or STORE, and the local variable's name ends with '__'
:FINER
 
;If the action is GET or PUT, and the field's name ends with '__'
:FINER
 
;If the action is NEW, CAST or INSTANCEOF, and the target class's name ends with '__'
:FINER
 
;If the action is ENTRY, RETURN or THROW
:FINER
 
;Last resort (when none of the above rules triggered)
:OFF
 
Notice that non-static fields (a.k.a. 'instance variables') are initialized by the class's constructors. Static fields ('class variables') are initialized by the synthetic <code><clinit>()</code> method, which cannot be annotated in source code.
 
=== Discriminator expression ===
 
Discriminator expressions are implemented by means of <code>de.unkrig.commons.text.expression.ExpressionEvaluator</code>; find the expression syntax documented [http://commons.unkrig.de/javadoc/de/unkrig/commons/text/expression/Parser.html here].
 
=== Discriminator expression parameters ===
 
To provide fine-grained control over loggification, the properties of each loggification point are provided as parameters to the discriminator expression:
 
;action
:One of the [[#Logging actions|documented logging actions]]
;source
:The name of the Java source file that JAVAC stores in each <code>.class</code> file if enabled by <code>-g:source</code>.
;classAccess
:The accessibility of the class being instrumented (<code>public</code>, <code>protected</code> or <code>private</code>)
;class
:The fully qualified name of the class being instrumented, e.g. <code>pkg.MyClass</code>
;methodAccess
:The accessibility of the method being instrumented (<code>public</code>, <code>protected</code> or <code>private</code>)
;method
:The enhanced, dequalified name of the method being instrumented, e.g. <code>meth(String, MyClass)</code> or <code><T extends IOException>meth(T, MyClass, List<T>)</code>
;line
:The line number related to the code point being instrumented that JAVAC stores in each .class file if enabled by <code>-g:lines</code>
;type
:The relevant type for the operation, e.g. <code>pkg.Class</code>, <code>pkg.Class[][]</code>, <code>int[][]</code> (only for actions '''CAST''', '''INSTANCEOF''' and '''NEW''')
;classAndField
:The subject class and field, e.g. <code>pkg.Class.field</code> (only for actions '''GET''' and '''PUT''')
;variable
:The name of the subject local variable (only for actions '''LOAD''' and '''STORE''')
 
At each loggification point, the LOGGIFIER determines if logging code is to be inserted or not ('discrimination') and
on which level the event will be logged at runtime ('level'). This is achieved by checking the 'loggification
rules'.
 
== Download ==
 
To get the LOGGIFIER ECLIPSE plugins, define an update site
 
    http://loggifier.unkrig.de/update/
 
in "Help / Install new software...".
 
To get the JAR files mentioned above, download them from
 
    http://loggifier.unkrig.de/download/
 
The source code is available through SVN:
 
    https://svn.code.sf.net/p/loggifier/code
 
== License ==
 
LOGGIFIER is published under the "[[New BSD License]]".

Latest revision as of 19:10, 21 January 2016

<meta name="keywords" content="java, logging, inject, automated"></meta> <meta name="description" content="A tool for automated injection of logging code into Java class files"></meta>

Motivation[edit]

Have you ever wondered how much cleaner your code would look if it weren't littered with all that logging code? And, when you're hunting bugs in production environments, do you produce tons of logging output, but often find that the critical bit of information that you need is missing?

If the answers are "no" and "no", then stop reading. Otherwise, LOGGIFIER may be the tool you need.

Overview[edit]

The concept of LOGGIFIER is as follows:

  • Relevant class files are instrumented during the build process:
    Code similar to
   if (MyClass.__logger123.isLoggable(Level.FINE)) {
        Helper.logp(
            MyClass.__logger123,      // logger
            Level.FINE,               // level
            "com.acme.MyClass",       // sourceClass
            "main(String[])",         // sourceMethod
            "MyClass.java",           // source
            120,                      // lineNumber
            "ENTRY  args=(args={0})", // message
            new Object[] { arg1 }     // parameters
        );
    }
is inserted all over the place. (Helper.logp() eventually calls java.util.logging.Logger.logp(), after having done some pretty-printing on the message arguments.) Where exactly (in the example: At the top of 'MyClass.main()'), and at what log level (here: FINE) is controlled by the applicable loggification rules. In typical scenarios, the default rules should generate a reasonable amount of logging points.
  • The instrumentation remains even in production. This is possible because the instrumentation is optimized for very low overhead (as you can see in the code above: The call to 'isLoggable()' is typically super fast).
  • Alternatively you loggify the code only when you start debugging. This is often less practical, because it requires that you either have access to the .jar/.war/.ear files in the target environment., or restart the JVM with loggification activated. Also, the restart or the loggification may influence the problems that you want to track down.
  • Then, when it comes to debugging, you enable logging at runtime, e.g. by editing the 'logging.properties' file, or by connecting to the running JVM through JMX. LOGGIFIER does not have one single Logger or one Logger per class, but separate loggers for all classes, methods and events. (Loggers are cheap, so the cost for having hundreds of them is low.) Hence you can enable the logging very selectively and thereby keep the amount of logging output reasonable.

Getting started[edit]

Click here to take the fast track to Loggifier.

Tools[edit]

There are various tools to loggify your classes:

Loggification[edit]

The LOGGIFIER loads a class file and analyzes the bytecode of the class's constructors and methods. Looking at the code, it identifies "interesting" points where logging code could be inserted. Each of the logging points is characterized by an action. For each logging point, the relevant loggification rules are executed, and the first that matches determines at what level the logging event is to be logged (or if no logging code is injected for this logging point). Loggification rules optionally have a discriminator expression, which has a set of parameters.

Logging actions[edit]

Each section of code is characterized by an 'Action'. For example, the JVM bytecodes PUTFIELD and PUTSTATIC are characterized by action PUT.

ALOAD Log array type, array value, index and element value read from an array
ARITH Log operands, operator (+, -, ...) and result of an arithmetic operation
ASTORE Log the array, the index and the value being stored in an array
CAST Log the type to which the operand is casted
CATCH Log the declared catch type and the actually caught exception
CLINIT Log the name of a class that is initialized
CONST Log the value of a constant being pushed on the operand stack
CONVERT Log a value that is arithmetically converted and the conversion result
ENTRY Log the target object (if non-static) and the arguments as a method begins execution
GET Log declaring class of, name of, source instance of (if non-static) and value read from a field
INVOKE Log signature, target object (if non-static) and arguments right before a method is invoked
INSTANCEOF Log the type to which the operand type is compared
LENGTH Log the array who's length is determined
LOAD Log type of, name of and value read from a local variable
NEW Log the type of a new object being instantiated
PUT Log declaring class of, name of, target instance of (if non-static) and value assigned to field
RESULT Log method signature and invocation result (if not 'void')
RETURN Log the value that is being returned by a method (if not 'void')
STORE Log value assigned to, type of and name of a local variable
SWITCH Log the integer value that a switch is being executed on
THROW Log an exception right before it is thrown

Loggification rule syntax[edit]

A rule triggers if and only if

  • It applies to the action of the loggification point (ENTRY, EXIT, THROW, ...)
  • Its discriminator expression (if any) evaluates to TRUE for the actual point (who's properties are availables as variables in the discriminator expression, see above)

The syntax of all loggification rules is:

loggification-rule :=
    ( action { ',' action } | 'ALL' ) '=' level [ ':' discriminator-expression ]

action :=
    (One of the actions documented above)

level :=
    'SEVERE' | 'WARNING' | 'INFO' | 'CONFIG' | 'FINE' | 'FINER' | 'FINEST'

discriminator-expression :=
    (Syntax defined below)

Examples of valid loggification rules are:

ALL=FINE
ENTRY=FINE
ARITH=FINE
LOAD,CONVERT,ARITH,STORE=FINE
ALL=FINE:class =* "de.*"

(=* is the 'glob matching operator', e.g. "hello" matches "he*".)

Loggification rule precedence[edit]

The sequence of checked rules for each logging point is as follows:

If the constructor or method being instrumented has a '@Loggify' annotation (not for action CLINIT)
Its values in reverse order (i.e. the last value/rule takes the highest precedence).
If a field 'static final String $$loggifierRules = "xyz"' was declared before the contructor or method being instrumented
Its value, split at '\n', in reverse order (i.e. the last fragment/rule takes the highest precedence).
If the class or interface being instrumented has a '@Loggify' annotation
Its values of in reverse order (i.e. the last value/rule takes the highest precedence).
If using the four-parameter LoggifyingClassLoader constructor
The 'logLevelCalculator' argument.
If using the ANT task
<rule> subelements, in reverse order (i.e. the last <rule> takes the highest precedence).
If using the LOGGIFIER command line tool
The '-rule' options, in reverse order (i.e. the last '-rule' option takes the highest precedence).
If using the ECLIPSE plug-in
The levels on the "Loggifier" property page of each project. (This rule ALWAYS applies, hence the following rules are never executed).
If using the Java agent
Its argument string passed to the agent, split at '\n', in reverse order (i.e. the last fragment/rule takes the highest precedence).
If the action is LOAD or STORE, and the local variable's name ends with '__'
FINER
If the action is GET or PUT, and the field's name ends with '__'
FINER
If the action is NEW, CAST or INSTANCEOF, and the target class's name ends with '__'
FINER
If the action is ENTRY, RETURN or THROW
FINER
Last resort (when none of the above rules triggered)
OFF

Notice that non-static fields (a.k.a. 'instance variables') are initialized by the class's constructors. Static fields ('class variables') are initialized by the synthetic <clinit>() method, which cannot be annotated in source code.

Discriminator expression[edit]

Discriminator expressions are implemented by means of de.unkrig.commons.text.expression.ExpressionEvaluator; find the expression syntax documented here.

Discriminator expression parameters[edit]

To provide fine-grained control over loggification, the properties of each loggification point are provided as parameters to the discriminator expression:

action
One of the documented logging actions
source
The name of the Java source file that JAVAC stores in each .class file if enabled by -g:source.
classAccess
The accessibility of the class being instrumented (public, protected or private)
class
The fully qualified name of the class being instrumented, e.g. pkg.MyClass
methodAccess
The accessibility of the method being instrumented (public, protected or private)
method
The enhanced, dequalified name of the method being instrumented, e.g. meth(String, MyClass) or <T extends IOException>meth(T, MyClass, List<T>)
line
The line number related to the code point being instrumented that JAVAC stores in each .class file if enabled by -g:lines
type
The relevant type for the operation, e.g. pkg.Class, pkg.Class[][], int[][] (only for actions CAST, INSTANCEOF and NEW)
classAndField
The subject class and field, e.g. pkg.Class.field (only for actions GET and PUT)
variable
The name of the subject local variable (only for actions LOAD and STORE)

At each loggification point, the LOGGIFIER determines if logging code is to be inserted or not ('discrimination') and on which level the event will be logged at runtime ('level'). This is achieved by checking the 'loggification rules'.

Download[edit]

To get the LOGGIFIER ECLIPSE plugins, define an update site

   http://loggifier.unkrig.de/update/

in "Help / Install new software...".

To get the JAR files mentioned above, download them from

   http://loggifier.unkrig.de/download/

The source code is available through SVN:

   https://svn.code.sf.net/p/loggifier/code

License[edit]

LOGGIFIER is published under the "New BSD License".