为库的每个实例分别记录器

时间:2011-12-13 18:10:48

标签: java oop design-patterns logging log4j

我们有一个普通的旧java库,它可以从许多不同的应用程序中实例化。在这种情况下,每个应用程序都是一个Web应用程序,它们都位于同一个tomcat容器中。

每个应用程序使用自己的记录器记录到自己的日志文件。我们希望库生成的与特定应用程序相关的日志也会转到该应用程序单独的日志文件。

为此,一种方法是允许应用程序将其记录器传递给库:

library = new library(Logger applicationsVeryOwnLogger);

然后使用该记录器记录库中的所有语句。但是,这意味着记录器现在是库中的类变量,并且库中的每个类都需要对库的引用才能使用正确的记录器。

有更好的方法吗?

2 个答案:

答案 0 :(得分:3)

您已使用log4j标记标记了您的问题,因此我认为这就是您正在使用的内容。

我希望你的图书馆使用独特的包名。

如果是这种情况,您实际上只需为该软件包设置一个记录器。

log4j.category.my.lib.package = INFO, libFileAppender
log4j.rootLogger = INFO, rootFileAppender

这样做会将您的库中的消息记录到 libFileAppender rootFileAppender

如果您不希望来自库的邮件出现在 rootFileAppender 中,您可以关闭该记录器的可加性,如下所示:

log4j.category.my.lib.package = INFO, libFileAppender
log4j.additivity.my.lib.package = false
log4j.rootLogger = INFO, rootFileAppender

有了这个,你只会在 libFileAppender

中看到消息

答案 1 :(得分:2)

我们在一个旧的应用程序中有类似的需求。我们提出的解决方案是一个ResourceManager,它将通过(上下文)ClassLoader检索资源(Logger,Config文件等)。

通常,作为EAR部署的每个应用程序都有自己的ClassLoader,然后库可以只调用ResourceManager.getLogger()来获取与当前线程/应用程序关联的Logger。这样你就不需要通过库中的每个方法调用来传递它(它要求你可以更改库)。

import java.util.*;
import java.util.logging.*;

public class ResourceManager 
{
    private static final Map<ClassLoader, Map<String, Object>> resources = 
        Collections.synchronizedMap(new WeakHashMap<ClassLoader, Map<String, Object>>());
    public static final String LOGGER = Logger.class.getName();

    static
    {
        // adjust for log4j or other frameworks
        final Logger logger = Logger.getLogger("logging.default");
        logger.setLevel(Level.ALL);
        logger.addHandler(new ConsoleHandler() 
        {
            {
                setOutputStream(System.out);
                setLevel(Level.ALL);
            }
        });
        registerResource(null, LOGGER, logger);
    }

    private static ClassLoader getApplicationScope()
    {
        return Thread.currentThread().getContextClassLoader();
    }

    public static void registerResource(final String name, final Object resource)
    {
        registerResource(getApplicationScope(), name, resource);
    }

    public static synchronized void registerResource(final ClassLoader scope, final String name, final Object resource)
    {
        Map<String, Object> hm = null;
        hm = resources.get(scope);
        if (hm == null)
        {
            hm = Collections.synchronizedMap(new HashMap<String, Object>());
            resources.put(scope, hm);
        }
        hm.put(name, resource);
    }

    public static Object getResource(final String name)
    {
        for(ClassLoader scope = getApplicationScope();;scope = scope.getParent())
        {
            final Map<String, Object> hm = resources.get(scope);
            if ((hm != null) && hm.containsKey(name)) 
            {
                return hm.get(name);
            }
            if (scope == null) break;
        }
        return null;
    }

    public static void registerLogger(final Logger logger)
    {
        registerResource(LOGGER, logger);
    }

    public static Logger getLogger()
    {
        return (Logger)getResource(LOGGER);
    }       
}

在EJB / WebApp的init阶段注册Logger(需要在调用getLogger之前注册):

Logger logger = Logger.getLogger([Application Logger Name]);
ResourceManager.registerLogger(logger);

在库中检索记录器(实用程序方法):

private Logger getLogger()
    {
        return ResourceManager.getLogger();     
    }

这将返回与当前线程关联的应用程序(EAR)的记录器。

不限于记录器,它也适用于您想要共享的其他资源。

限制:

    如果您为每个部署的EAR打包多个应用程序/ EJB,
  • 将无法工作

  • ResourceManager和Logging库需要与库和应用程序位于相同或更高的ClassLoader上。如果有捆绑选项,那么Alexanders方法更清晰。 (我们使用java.util.logging,默认情况下在服务器级别,因此他的方法不起作用)