苹果Mac电脑,Java web项目用Eclipse Jetty插件启动时报log4j:ERROR setFile(null,true) call failed,java.io.FileNotFoundException Permission denied错误,具体如下:

log4j:ERROR setFile(null,true) call failed.
java.io.FileNotFoundException: /error_ (Permission denied)
	at java.io.FileOutputStream.open0(Native Method)
	at java.io.FileOutputStream.open(FileOutputStream.java:270)
	at java.io.FileOutputStream.<init>(FileOutputStream.java:213)
	at java.io.FileOutputStream.<init>(FileOutputStream.java:133)
	at org.apache.log4j.FileAppender.setFile(FileAppender.java:290)
	at org.apache.log4j.FileAppender.activateOptions(FileAppender.java:164)
	at org.apache.log4j.DailyRollingFileAppender.activateOptions(DailyRollingFileAppender.java:216)
	at org.apache.log4j.config.PropertySetter.activate(PropertySetter.java:257)
	at org.apache.log4j.config.PropertySetter.setProperties(PropertySetter.java:133)
	at org.apache.log4j.config.PropertySetter.setProperties(PropertySetter.java:97)
	at org.apache.log4j.PropertyConfigurator.parseAppender(PropertyConfigurator.java:689)
	at org.apache.log4j.PropertyConfigurator.parseCategory(PropertyConfigurator.java:647)
	at org.apache.log4j.PropertyConfigurator.parseCatsAndRenderers(PropertyConfigurator.java:568)
	at org.apache.log4j.PropertyConfigurator.doConfigure(PropertyConfigurator.java:442)
	at org.apache.log4j.PropertyConfigurator.doConfigure(PropertyConfigurator.java:476)
	at org.apache.log4j.helpers.OptionConverter.selectAndConfigure(OptionConverter.java:471)
	at org.apache.log4j.LogManager.<clinit>(LogManager.java:125)
	at org.apache.log4j.Logger.getLogger(Logger.java:105)
	at org.apache.commons.logging.impl.Log4JLogger.getLogger(Log4JLogger.java:289)
	at org.apache.commons.logging.impl.Log4JLogger.<init>(Log4JLogger.java:109)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:422)
	at org.apache.commons.logging.impl.LogFactoryImpl.createLogFromClass(LogFactoryImpl.java:1116)
	at org.apache.commons.logging.impl.LogFactoryImpl.discoverLogImplementation(LogFactoryImpl.java:914)
	at org.apache.commons.logging.impl.LogFactoryImpl.newInstance(LogFactoryImpl.java:604)
	at org.apache.commons.logging.impl.LogFactoryImpl.getInstance(LogFactoryImpl.java:336)
	at org.apache.commons.logging.impl.LogFactoryImpl.getInstance(LogFactoryImpl.java:310)
	at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:685)
	at com.leeyue.common.listener.SqlMapConfigListener.<init>(SqlMapConfigListener.java:21)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:422)
	at java.lang.Class.newInstance(Class.java:442)
	at org.eclipse.jetty.server.handler.ContextHandler$Context.createInstance(ContextHandler.java:2481)
	at org.eclipse.jetty.webapp.StandardDescriptorProcessor.newListenerInstance(StandardDescriptorProcessor.java:1943)
	at org.eclipse.jetty.webapp.StandardDescriptorProcessor.visitListener(StandardDescriptorProcessor.java:1898)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:497)
	at org.eclipse.jetty.webapp.IterativeDescriptorProcessor.visit(IterativeDescriptorProcessor.java:83)
	at org.eclipse.jetty.webapp.IterativeDescriptorProcessor.process(IterativeDescriptorProcessor.java:70)
	at org.eclipse.jetty.webapp.MetaData.resolve(MetaData.java:403)
	at org.eclipse.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1364)
	at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:778)
	at org.eclipse.jetty.servlet.ServletContextHandler.doStart(ServletContextHandler.java:262)
	at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:520)
	at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
	at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:131)
	at org.eclipse.jetty.server.Server.start(Server.java:411)
	at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:105)
	at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:61)
	at org.eclipse.jetty.server.Server.doStart(Server.java:378)
	at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
	at net.sourceforge.eclipsejetty.starter.jetty9.Jetty9Adapter.start(Jetty9Adapter.java:68)
	at net.sourceforge.eclipsejetty.starter.common.AbstractJettyLauncherMain.launch(AbstractJettyLauncherMain.java:84)
	at net.sourceforge.eclipsejetty.starter.jetty9.Jetty9LauncherMain.main(Jetty9LauncherMain.java:42)
log4j:ERROR Either File or DatePattern options are not set for appender [error].

二、项目关键文件

1、web.xml 片段

  <context-param>
    <param-name>log4jConfigLocation</param-name>
    <param-value>classpath:log4j.properties</param-value>
  </context-param>  
  <listener>
	<listener-class>com.leeyue.common.listener.ApplicationInitListener</listener-class>
  </listener>   
  <listener>
	<listener-class>com.leeyue.common.listener.SessionMonitorListener</listener-class>
  </listener>
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <listener>
    <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
  </listener>    
  <listener>
	<listener-class>com.leeyue.common.listener.SqlMapConfigListener</listener-class>
  </listener>
  <listener>
	<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
  </listener>

2、ApplicationInitListener.java

@Override
public void contextInitialized(ServletContextEvent event) {
	// 略去其它代码
        String key = "ly_jxsywy.log_dir";            //实际代码为读取配置文件
        String value = "Users/maben/logs/ly_jxsywy"  //实际代码为读取配置文件
        System.setProperty(key, value);
        System.out.println("ApplicationInitListener contextInitialized!");
}

3、SqlMapConfigListener.java

public class SqlMapConfigListener implements ServletContextListener {
	@SuppressWarnings("unused")
	private Log log = LogFactory.getLog(SqlMapConfigListener.class);

        public SqlMapConfigListener() {
            System.out.println("SqlMapConfigListener constrcuted.");
        }

        @Override
	public void contextDestroyed(ServletContextEvent event) {

	}

4、log4j.properties,文件指向使用了环境变量:${ly_jxsywy.log_dir}

#log4j.logger.org.logicalcobwebs.proxool=debug,stdout
log4j.rootLogger=warn,stdout
log4j.logger.com.leeyue.jpa=info,sql
log4j.logger.cc.leeyue.service=info,service
log4j.logger.cc.leeyue.error=error,error

#
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%t \: %d %p [%c] - %m%n 
#
#sqlProcess config
log4j.appender.sql=org.apache.log4j.DailyRollingFileAppender
log4j.additivity.sql=false
log4j.appender.sql.File=${ly_jxsywy.log_dir}/sql_
log4j.appender.sql.DatePattern=yyyy-MM-dd'.log'
log4j.appender.sql.layout=org.apache.log4j.PatternLayout
log4j.appender.sql.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %p [%c] - %m%n

log4j.appender.service=org.apache.log4j.DailyRollingFileAppender
#log4j.additivity.service=false
log4j.appender.service.File=${ly_jxsywy.log_dir}/service_
log4j.appender.service.DatePattern=yyyy-MM-dd'.log'
log4j.appender.service.layout=org.apache.log4j.PatternLayout
log4j.appender.service.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %p [%c] - %m%n

log4j.appender.error=org.apache.log4j.DailyRollingFileAppender
#log4j.additivity.service=false
log4j.appender.error.File=${ly_jxsywy.log_dir}/error_
log4j.appender.error.DatePattern=yyyy-MM-dd'.log'
log4j.appender.error.layout=org.apache.log4j.PatternLayout
log4j.appender.error.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %p [%c] - %m%n

三、项目启动顺序说明

    根据web规范 ,web.xml中配置的listener,filter,serlet启动顺为:先读取context-para节点参数,然后Listener > Filter > Servlet,相同元素如listener则按web.xml文件中的出现顺序执行。

    所以,项目原本设想是在ApplicationListener中设置环境变量ly_jxsywy.logdir, Log目录,然后Log4jConfigListener执行加载log4j配置文件时,引用${ly_jxsywy.logdir}环境变量。

    该项目在win7下用jetty启动没报错,在linux下以root用户启动tomcat也没报错;但是在Mac下,却报文中开始处错误提示。通过分析,应用启动没报错是巧合,启动报错才是正常,见下文原因分析。

四、报错原因分析

    诚然根据web规范,Listener执行顺序是web.xml文件中出现的顺序,但是Listener执行前,Listener已经实例化,即先创建Listener对象,放在容器里,然后从容器里安个获取Listener并执行。上文提到的SqlmapConfigListener,构建该listener实例时,会初始化Log,而这时ApplicationListener还没有执行,看jetty控制台输出,我们就一目了然。因为ApplicationListener还没执行,所以环境变量ly_jxsywy.logdir为空,所以根据log4j配置文件,日志文件为/error_,mac下非root用户没有该文件的访问权限,所以就报启动错误。而win7下,jetty插件启动时,没有文件访问权限限制,所以没报错;linux下,tomcat是root用户启动的,所以也没权限问题。

SqlMapConfigListener constrcuted.
2018-07-03 14:12:03.224:INFO:/ly-jxsywy:main: No Spring WebApplicationInitializer types detected on classpath
2018-07-03 14:12:03.235:WARN:oejs.SecurityHandler:main: ServletContext@o.e.j.w.WebAppContext@311bf055...
ApplicationInitListener contextInitialized!

简单解决方案,SqlmapConfigListener里把log去掉,或移到contextInitlized方法里。

虽然启动报错,但不影响应用访问,而且日志设置也没问题,后续的Log4jConfigListener会重新初始化Log。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐