为库的每个实例单独的日志记录器



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

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

为此,一种方法是允许应用程序将其记录器传递给库:
library = new library(Logger applicationsVeryOwnLogger);

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

有更好的方法吗?

你已经用log4j标记了你的问题,所以我认为这就是你正在使用的。

我希望你的库使用一个唯一的包名。

如果是这种情况,您实际上可以为该包设置一个记录器。

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

这样做会将来自库的消息记录到libFileAppenderrootFileAppender

如果你不想让你的库中的消息出现在rootFileAppender中,你可以关闭记录器的可加性,像这样:

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

这样,您将只在libFileAppender

中看到消息

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

通常,作为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的初始化阶段注册记录器(需要在调用getLogger之前注册):

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

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

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

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

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

限制:

  • 不能工作,如果你为每个部署的EAR打包多个应用程序/ejb

  • ResourceManager和Logging库需要在与库和应用程序相同或更高的ClassLoader上。如果有捆绑的选项,那么alexander的方法就更清晰了。(我们使用java.util.logging,这是默认的服务器级,所以他的方法不起作用)

最新更新