java传统web项目添加maven管理jar包,log4j无法正常输出日志

背景

  笔者最近在给公司一个老的web项目改造升级,项目使用springmvc+mybatis,由于项目比较久远,没有使用maven管理jar版本,有可能是当时开发任务比较紧迫,不同的同事在不同的时期放入了jar版本各不相同,

看到那么多混乱的jar,真是操心。笔者曾花了大概半个下午的时间,把jar版本整理好,编入pom.xml中,从那个时候,笔者本地项目的jar版本算是交给maven托管了。顿时间心里舒畅了一会儿。心里也计划着和项目组大

家一起统一使用maven管控jar版本混乱的问题。可是事实有时候并不会常常如人所有。就在部署web项目的时候,问题来了。控制台那么多可爱的日志怎么变少了呢。

原来正常的时候,日志输出大概如下:

而当笔者部署web项目到servlet容器(tomcat)中,问题来了,日志少的捉襟见肘,怎么也难应付平时的项目开发啊,开发测试过程中,怎么能少的了日志。

如此一来,这个maven上的有点让人措手不及,这不有点像邯郸学步,顾着把项目列入现代化管理方式,却失去了初衷:日志功能淡化,开发调试难度增加。

排错过程

本地日志输入代码如下:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.xxx.system.auth.User;
/***
 * 权限校验类
 */

public class AuthManager {
    private static Logger logger = LoggerFactory.getLogger(AuthManager.class);

    public static void check(HttpSession session)
   {
        User user = (User)session.getAttribute("userData");
       if (user == null) {
        if(logger.isDebugEnabled())
        logger.debug("当前用户session无效!");
        return false;
    }

   }
}

笔者按: 代码片段中使用了slf4j框架,slf4j是一个优秀的slf4j框架,他使用了适配器设计模式,由他来屏蔽不同日志框架的差异性,实现与不同日志框架的协作。

 

  以上一段简单的代码,在项目中不同地方高频次的调用,却一直不见控制台有日志输出,笔者起初以为是ide在作祟,各种更改ide配置也无效,

把编译好的项目单独放到servlet容器tomcat中,仍不凑效,即便捶胸顿足也无计可施,以上说明不是ide问题,也不是servlet容器的问题,这可憋坏了笔者。

解决方法

log4j一般性的用法如下:

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

public class LogTest {
    static Logger logger = Logger.getLogger(LogTest.class.getName());

    public static void main(String[] args) {
        PropertyConfigurator.configure("src/log4j.properties");
        logger.debug("Debug ...");
        logger.info("Info ...");
        logger.warn("Warn ...");
        logger.error("Error ...");
    }

}

我们通过对Logger.getLogger查看源码,在我们面前呈现出一个类LogManager

static
  public
  Logger getLogger(String name) {
    return LogManager.getLogger(name);
  }

Log4jManager代码如下:

package org.apache.log4j;

import org.apache.log4j.spi.LoggerRepository;
import org.apache.log4j.spi.LoggerFactory;
import org.apache.log4j.spi.RepositorySelector;
import org.apache.log4j.spi.DefaultRepositorySelector;
import org.apache.log4j.spi.RootLogger;
import org.apache.log4j.spi.NOPLoggerRepository;
import org.apache.log4j.helpers.Loader;
import org.apache.log4j.helpers.OptionConverter;
import org.apache.log4j.helpers.LogLog;

import java.net.URL;
import java.net.MalformedURLException;

import java.util.Enumeration;
import java.io.StringWriter;
import java.io.PrintWriter;

/**
 * Use the <code>LogManager</code> class to retreive {@link Logger}
 * instances or to operate on the current {@link
 * LoggerRepository}. When the <code>LogManager</code> class is loaded
 * into memory the default initalzation procedure is inititated. The
 * default intialization procedure</a> is described in the <a
 * href="../../../../manual.html#defaultInit">short log4j manual</a>.
 *
 * @author Ceki G&uuml;lc&uuml;
 */
public class LogManager {

    /**
     * @deprecated This variable is for internal use only. It will
     * become package protected in future versions.
     */
    static public final String DEFAULT_CONFIGURATION_FILE = "log4j.properties";

    static final String DEFAULT_XML_CONFIGURATION_FILE = "log4j.xml";

    /**
     * @deprecated This variable is for internal use only. It will
     * become private in future versions.
     */
    static final public String DEFAULT_CONFIGURATION_KEY = "log4j.configuration";

    /**
     * @deprecated This variable is for internal use only. It will
     * become private in future versions.
     */
    static final public String CONFIGURATOR_CLASS_KEY = "log4j.configuratorClass";

    /**
     * @deprecated This variable is for internal use only. It will
     * become private in future versions.
     */
    public static final String DEFAULT_INIT_OVERRIDE_KEY =
            "log4j.defaultInitOverride";

    static private Object guard = null;
    static private RepositorySelector repositorySelector;

    static {
        // By default we use a DefaultRepositorySelector which always returns ‘h‘.
        Hierarchy h = new Hierarchy(new RootLogger((Level) Level.DEBUG));
        repositorySelector = new DefaultRepositorySelector(h);

        /** Search for the properties file log4j.properties in the CLASSPATH.  */
        String override = OptionConverter.getSystemProperty(DEFAULT_INIT_OVERRIDE_KEY,
                null);

        // if there is no default init override, then get the resource
        // specified by the user or the default config file.
        if (override == null || "false".equalsIgnoreCase(override)) {

            String configurationOptionStr = OptionConverter.getSystemProperty(
                    DEFAULT_CONFIGURATION_KEY,
                    null);

            String configuratorClassName = OptionConverter.getSystemProperty(
                    CONFIGURATOR_CLASS_KEY,
                    null);

            URL url = null;

            // if the user has not specified the log4j.configuration
            // property, we search first for the file "log4j.xml" and then
            // "log4j.properties"
            if (configurationOptionStr == null) {
                url = Loader.getResource(DEFAULT_XML_CONFIGURATION_FILE);
                if (url == null) {
                    url = Loader.getResource(DEFAULT_CONFIGURATION_FILE);
                }
            } else {
                try {
                    url = new URL(configurationOptionStr);
                } catch (MalformedURLException ex) {
                    // so, resource is not a URL:
                    // attempt to get the resource from the class path
                    url = Loader.getResource(configurationOptionStr);
                }
            }

            // If we have a non-null url, then delegate the rest of the
            // configuration to the OptionConverter.selectAndConfigure
            // method.
            if (url != null) {
                LogLog.debug("Using URL [" + url + "] for automatic log4j configuration.");
                try {
                    OptionConverter.selectAndConfigure(url, configuratorClassName,
                            LogManager.getLoggerRepository());
                } catch (NoClassDefFoundError e) {
                    LogLog.warn("Error during default initialization", e);
                }
            } else {
                LogLog.debug("Could not find resource: [" + configurationOptionStr + "].");
            }
        } else {
            LogLog.debug("Default initialization of overridden by " +
                    DEFAULT_INIT_OVERRIDE_KEY + "property.");
        }
    }

    /**
     * Sets <code>LoggerFactory</code> but only if the correct
     * <em>guard</em> is passed as parameter.
     *
     * <p>Initally the guard is null.  If the guard is
     * <code>null</code>, then invoking this method sets the logger
     * factory and the guard. Following invocations will throw a {@link
     * IllegalArgumentException}, unless the previously set
     * <code>guard</code> is passed as the second parameter.
     *
     * <p>This allows a high-level component to set the {@link
     * RepositorySelector} used by the <code>LogManager</code>.
     *
     * <p>For example, when tomcat starts it will be able to install its
     * own repository selector. However, if and when Tomcat is embedded
     * within JBoss, then JBoss will install its own repository selector
     * and Tomcat will use the repository selector set by its container,
     * JBoss.
     */
    static
    public void setRepositorySelector(RepositorySelector selector, Object guard)
            throws IllegalArgumentException {
        if ((LogManager.guard != null) && (LogManager.guard != guard)) {
            throw new IllegalArgumentException(
                    "Attempted to reset the LoggerFactory without possessing the guard.");
        }

        if (selector == null) {
            throw new IllegalArgumentException("RepositorySelector must be non-null.");
        }

        LogManager.guard = guard;
        LogManager.repositorySelector = selector;
    }

    /**
     * This method tests if called from a method that
     * is known to result in class members being abnormally
     * set to null but is assumed to be harmless since the
     * all classes are in the process of being unloaded.
     *
     * @param ex exception used to determine calling stack.
     * @return true if calling stack is recognized as likely safe.
     */
    private static boolean isLikelySafeScenario(final Exception ex) {
        StringWriter stringWriter = new StringWriter();
        ex.printStackTrace(new PrintWriter(stringWriter));
        String msg = stringWriter.toString();
        return msg.indexOf("org.apache.catalina.loader.WebappClassLoader.stop") != -1;
    }

    static
    public LoggerRepository getLoggerRepository() {
        if (repositorySelector == null) {
            repositorySelector = new DefaultRepositorySelector(new NOPLoggerRepository());
            guard = null;
            Exception ex = new IllegalStateException("Class invariant violation");
            String msg =
                    "log4j called after unloading, see http://logging.apache.org/log4j/1.2/faq.html#unload.";
            if (isLikelySafeScenario(ex)) {
                LogLog.debug(msg, ex);
            } else {
                LogLog.error(msg, ex);
            }
        }
        return repositorySelector.getLoggerRepository();
    }

    /**
     * Retrieve the appropriate root logger.
     */
    public
    static Logger getRootLogger() {
        // Delegate the actual manufacturing of the logger to the logger repository.
        return getLoggerRepository().getRootLogger();
    }

    /**
     * Retrieve the appropriate {@link Logger} instance.
     */
    public
    static Logger getLogger(final String name) {
        // Delegate the actual manufacturing of the logger to the logger repository.
        return getLoggerRepository().getLogger(name);
    }

    /**
     * Retrieve the appropriate {@link Logger} instance.
     */
    public
    static Logger getLogger(final Class clazz) {
        // Delegate the actual manufacturing of the logger to the logger repository.
        return getLoggerRepository().getLogger(clazz.getName());
    }

    /**
     * Retrieve the appropriate {@link Logger} instance.
     */
    public
    static Logger getLogger(final String name, final LoggerFactory factory) {
        // Delegate the actual manufacturing of the logger to the logger repository.
        return getLoggerRepository().getLogger(name, factory);
    }

    public
    static Logger exists(final String name) {
        return getLoggerRepository().exists(name);
    }

    public
    static Enumeration getCurrentLoggers() {
        return getLoggerRepository().getCurrentLoggers();
    }

    public
    static void shutdown() {
        getLoggerRepository().shutdown();
    }

    public
    static void resetConfiguration() {
        getLoggerRepository().resetConfiguration();
    }
}

  笔者本地使用log4j-1.2.16,通过阅读静态域代码了解到,log4j寻找配置文件顺序如下

1、检测当前JVM是否配置log4j.configuration属性,如果有,加载对应的配置文件,也就是说,你可以在JVM启动时加载参数

-Dlog4j.configuration=d:\log4j.properties

或者在程序里注册系统属性

System.setProperty("log4j.configuration","d:\\log4j.properties"); 

  2、当前jvm环境中log4j.configuration属性查找不到,再从当前类加载路径依次查找log4j.xml、log4j.properties。显然,是要从jar包加载了。这不是我们希望的结果,

而我们需要的从当前web应用类路径加载,问题的症结也便在此。

公司项目是web项目,显然更适合在Servlet上下文监听器(ServletContextListener)注册这个属性,如果项目比较紧急,到这一步已经基本算是临时性解决问题了,其他的留作后期代码重构再解决。

根本性解决方法

话到此处,有一些洁癖的程序员心里嘀咕着:这一点也不优雅,一点都不适合我的作风,这样不行,肯定有更好的办法。好吧,笔者也是这么认为(窃笑中.gif),能有别人封装好的成熟代码最好不过,

不要重复造轮子;当然写博文这个时间算是充足,考虑到项目用了spring,以往经常用spring来托管log4j。spring针对log4j 1.x有一个Log4jConfigListener

package org.springframework.web.util;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

/**
 * Bootstrap listener for custom log4j initialization in a web environment.
 * Delegates to {@link Log4jWebConfigurer} (see its javadoc for configuration details).
 *
 * <b>WARNING: Assumes an expanded WAR file</b>, both for loading the configuration
 * file and for writing the log files. If you want to keep your WAR unexpanded or
 * don‘t need application-specific log files within the WAR directory, don‘t use
 * log4j setup within the application (thus, don‘t use Log4jConfigListener or
 * Log4jConfigServlet). Instead, use a global, VM-wide log4j setup (for example,
 * in JBoss) or JDK 1.4‘s {@code java.util.logging} (which is global too).
 *
 * <p>This listener should be registered before ContextLoaderListener in {@code web.xml}
 * when using custom log4j initialization.
 *
 * @author Juergen Hoeller
 * @since 13.03.2003
 * @see Log4jWebConfigurer
 * @see org.springframework.web.context.ContextLoaderListener
 * @see WebAppRootListener
 * @deprecated as of Spring 4.2.1, in favor of Apache Log4j 2
 * (following Apache‘s EOL declaration for log4j 1.x)
 */
@Deprecated
public class Log4jConfigListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent event) {
        Log4jWebConfigurer.initLogging(event.getServletContext());
    }

    @Override
    public void contextDestroyed(ServletContextEvent event) {
        Log4jWebConfigurer.shutdownLogging(event.getServletContext());
    }

}

通过跟踪代码,我们发现spring采用迂回的方式,通过我们刚刚提及的类Log4jManager,初始化了日志功能。

当然,在spring4.2.1以后,已经过期,各位酌情使用,时间太晚,笔者没有再继续追下去。

我们只需要在web.xml中配置一个监听器和上下文参数,即可解决问题

  <context-param>
    <param-name>log4jConfigLocation</param-name>
    <param-value>classpath:log4j.properties</param-value>
  </context-param>

 <listener>
    <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
  </listener>

至此,可爱的log4j日志又在控制台活蹦乱跳了。

原文地址:https://www.cnblogs.com/passedbylove/p/9960966.html

时间: 2024-11-08 07:52:30

java传统web项目添加maven管理jar包,log4j无法正常输出日志的相关文章

Eclipse添加maven第三方jar包

Eclipse添加maven依赖找不到maven库中的jar 开发中,经常需要添加第三方jar到maven库,添加jar的方法,这里不多介绍,网上很多,下面就以添加dana 的memcached客户端jar的例子来说明这次遇到的问题与解决的方法. 1. 安装第三方jar 进入cmd, F:\java_memcached-release_2.6.6>mvn install:install-file -Dfile=java_memcached-r elease_2.6.6.jar -DgroupId

springboot项目用maven打jar包

clean package -Dmaven.test.skip=true idea eclipse STS 原文地址:https://www.cnblogs.com/skyislimit/p/10624986.html

怎样把Maven项目共享为传统Web项目

Maven的优点有很多,几个突出的比如说 1.代码库的存在 2.依赖管理 3.自动找到对应的javadoc和src 4.众多的插件支持 当然也有不足,比如说 1.代码库有的lib包结构混乱存在冗余 2.找到的所有lib总是>=预期 3.也有10%找不到javadoc和src 4.插件执行参数繁琐 现在考虑这种情况:如果团队成员都没有采用Maven,SVN上的也是个传统Web项目 想直接检出为Maven项目或者直接提交是不太可能了,如果还是坚持使用呢? 我们的思路是这样:Maven项目->传统W

maven 如何给web项目添加jar包依赖

maven 如何给web项目添加jar包依赖 CreateTime--2018年4月19日19:06:21 Author:Marydon 开发工具:eclipse 1.打开pom.xml文件-->切换到pom.xml视图--> 2.在build标签上,创建dependencies标签,jar包的引入都放到该标签内: 3.查看项目都引用了哪些jar包:src/main/webapp/WEB-INF/lib,然后将下面的jar包一个一个的从maven资源库中http://mvnrepository

用ideal创建java项目,并添加maven管理

第一步 创建项目 File->New->Project 第二步 修改项目结构 在项目的src下面创建main/java和main/resources目录. File->Project Structure 或者快捷键Ctrl+Shift+Alt+s.将Sources定位到java目录下,将Resources定位到resources下面. 此处补充idea下的java项目关于不同资源的标识符. Sources 一般用于标注类似 src 这种可编译目录.有时候我们不单单项目的 src 目录要可

web 项目引入 maven jar 工具类异常

普通的web 项目引入 maven   子项目后,,启动web不会出现异常,登录web 页面异常提示: HTTP Status 500 - java.lang.NoSuchMethodError: org.eclipse.jdt.internal.compiler.CompilationResult.getProblems()[Lorg/eclipse/jdt/core/compiler/IProblem; 在子项目中做了处理 <dependency> <groupId>org.a

tomcat没有发布maven项目依赖的本地jar包

开发工具:STS(Spring Tool Suite) 建立springMVC的maven项目,平时使用的jar包都是在pom.xml文件配置依赖关系, maven会自动从仓库中下载,这样使用tomcat部署发布都没有问题.但有时我们需要使用maven仓库中不存在的包,这就需要自己配置. 右键单击工程名—>Build Path—>Configure Build Path—>Java Build Path—>Add JARs/Add External JARs,将需要的jar包加进去

普通的java项目生成可执行jar包

普通的java项目生成可执行jar包 1.右键选中项目,选择export 2.选择runnable jar file 然后next 3.填入相关信息 4.点击finish ,有提示的话点确定即可. 5.在自定义jar包生成的路径里面可以看到下面 6.命令行执行程序 Java  -jar JDBC.jar XXX XXX 后面的XXX 表示的是mian函数上的arg[]参数,String数组,有就添加,没有可以不用管. 如果是要copy到别的机器或者目录下面跑.一定要将生成的两个文件(jar包目录

Maven把项目依赖的所有jar包都打到同一个jar中

目录 1 使用maven-shade-plugin 2 推荐: 使用maven-assembly-plugin 3 扩展: Maven安装本地jar包到本地仓库 4 扩展: 手动生成jar包 5 扩展: Linux下运行jar包的几种方式 5.1 阻塞式方式 5.2 后台运行方式 5.3 后台持续运行方式 5.4 其他命令扩展 1 使用maven-shade-plugin (1) 在项目的pom.xml文件中加入如下插件: <build> <plugins> <!-- Mav