logging-log4j-user mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Ebersole, Steven" <steven.ebers...@vignette.com>
Subject RE: MDC in J2EE environment
Date Fri, 25 Apr 2003 17:59:34 GMT
Ok, so I finished coding this as kind of a hybrid between what you and I
were talking about (the code is below).  I now have a LoggingContext class
which my app will use to access the log4j MDC.  It incorporates the concept
of the ContextCounter (the inner ContextEntry class) plus this new concept
of a closure (the inner LoggingClosure class).

I use socket-based or jms-based appenders, so I needed to make sure to
account for possible serialization of the LoggingEvent.  I noticed that this
process forces the LoggingEvent to obtain a copy of the current MDC through
a clone() call.  However, that would not force the closure to be "resolved".
The only way I saw around that was to override the serialization of the
LoggingClosure to force this lookup.

Just wanted to make sure there was nothing I was missing.





public class LoggingContext
{
    private static final java.util.Set closureKeys = new
java.util.HashSet();

    // Disallow construction
    private LoggingContext() {}

    public static void registerGlobal( String key, LoggingClosure closure )
    {
        MDC.put( key, closure );
        closureKeys.add( key );
    }

    public static Object get( String key )
    {
        Object tmp = MDC.get( key );
        if (tmp == null)
        {
            return null;
        }
        else if (ContextEntry.class.isAssignableFrom( tmp.getClass() ))
        {
            return ((ContextEntry)tmp).getEntryValue();
        }
        else
        {
            return tmp;
        }
    }

    public static void set( String key, Object value )
    {
        if (closureKeys.contains( key ))
            throw new IllegalStateException( "Attempting to set a value on
logging context under a key which has been locked out" );

        Object tmp = MDC.get( key );
        if (tmp == null)
        {
            tmp = new ContextEntry( value );
            ((ContextEntry)tmp).incReferenceCount();
            MDC.put( key, tmp );
        }
        else if (ContextEntry.class.isAssignableFrom( tmp.getClass() ))
        {
            ((ContextEntry)tmp).setEntryValue( value );
            ((ContextEntry)tmp).incReferenceCount();
        }
        else
        {
            MDC.put( key, value );
        }
    }

    public static void remove( String key )
    {
        if (closureKeys.contains( key ))
            throw new IllegalStateException( "Attempting to remove a value
from logging context under a key which has been locked out" );

        Object tmp = MDC.get( key );
        if (tmp == null)
        {
            // No action required...
        }
        else if (ContextEntry.class.isAssignableFrom( tmp.getClass() ))
        {
            ((ContextEntry)tmp).decReferenceCount();
            if (((ContextEntry)tmp).isCountZero())
            {
                MDC.remove( key );
            }
        }
        else
        {
            MDC.remove( key );
        }
    }

    public static void clear()
    {
        for (java.util.Iterator itr = MDC.getContext().keySet().iterator();
itr.hasNext(); )
        {
            final String key = (String)itr.next();
            if (closureKeys.contains( key ))
            {
                // do nothing
            }
            else
            {
                remove( key );
            }
        }       
    }

    private static final class ContextEntry
    {
        private int referenceCount = 0;
        private Object value;

        public ContextEntry( Object value ) { this.value = value; }

        public void setEntryValue( Object value ) { this.value = value; }
        public Object getEntryValue() { return value; }

        public boolean isCountZero() { return (referenceCount == 0); }
        public void incReferenceCount() { referenceCount++; }
        public void decReferenceCount() { referenceCount--; }

        public String toString() { return "" + value; }
    }

    public static abstract class LoggingClosure
    implements java.io.Serializable
    {
        public abstract String getStringRepresentation();
        private void writeObject( java.io.ObjectOutputStream oos )
        throws java.io.IOException
        {
            oos.writeChars( this.getStringRepresentation() );
        }
    }

}




Then my weblogic-based closure:


    public static final class WeblogicUserLookupClosure
    extends com.vignette.it.apps.common.util.LoggingContext.LoggingClosure
    {
        public String getStringRepresentation()
        {
            return
weblogic.security.acl.Security.getCurrentUser().getName();
        }
    }





-----Original Message-----
From: Steve Ebersole [mailto:steveebersole@austin.rr.com]
Sent: Monday, April 21, 2003 9:14 AM
To: Log4J Users List
Subject: Re: MDC in J2EE environment



>That's quite an original idea. What would happen if the same user were
>logged in twice?

I use weblogic, so for my purposes my closure would basically perform a
weblogic.security.Security.getCurrentlyExecutingUser().getName() call.  If
they were logged in twice, this would have the same effect as me placing the
user's username in context twice (i.e., once per thread).

I am thinking this registration would ideally occur just one time, in my
startup class/servlet.

This really only works for things, like executing user, which are always
needed and can be gotten in a generic fashion from the execution environment
already.


>Like you suggested in the previous paragraph. After modifying the MDC class
>to more or less match XMDC ckass, you would register a "closure" with the
>modified MDC class. The closure would be used instead of ContextCounter.

I guess the difference is that with this approach the MDC.put() would still
need to be performed for each inconing execution request.  That's why I was
asking about the InheritableThreadLocal ascept of MDC.  Would be great to
perform this "registration" at the level of the thread which would be used
to spawn all the execution threads.  Then the closure would automatically be
inherited into the context of each of these execution threads.

As I mentioned, I am lazy.  I really don't want to have to MDC.put() or
XMDC.put in each of my session bean methods if there is an easier way.


As an example, I am thinkning of something along these lines:

Closure class:
public class WeblogicUserLookup
{
    public String toString()
    {
        return
weblogic.security.Security.getCurrentlyExecutingUser().getName();
    }
}

then:
{
    ...
    MDC.put( "VEDA_USER_NAME", new WeblogicUserLookup() );
}

Hope this helps clarify.


---------------------------------------------------------------------
To unsubscribe, e-mail: log4j-user-unsubscribe@jakarta.apache.org
For additional commands, e-mail: log4j-user-help@jakarta.apache.org

---------------------------------------------------------------------
To unsubscribe, e-mail: log4j-user-unsubscribe@jakarta.apache.org
For additional commands, e-mail: log4j-user-help@jakarta.apache.org


Mime
View raw message