zoukankan      html  css  js  c++  java
  • Log4j、Log4j 2、Logback、SFL4J、JUL、JCL的比较

    正文

    Log4j

         Log4j = Log for Java.
         author: Ceki Gülcü
         license: Apache License V2.0
         Log4j是Apache的一个开放源代码项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、数据库等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。
         Log4j有7种不同的log级别,按照等级从低到高依次为:TRACE<DEBUG<INFO<WARN<ERROR<FATAL<OFF。如果配置为OFF级别,表示关闭log。    
         Log4j支持两种格式的配置文件:properties和xml。包含三个主要的组件:Logger、appender、Layout。    
    Example for log4j 1.2     

    复制代码
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE log4j:configuration PUBLIC "-//LOGGER" "http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd">
    <log4j:configuration>    
      <!--an appender is an output destination, such as the console or a file; names of appenders are arbitrarily chosen-->    
      <appender name="stdout" class="org.apache.log4j.ConsoleAppender">
        <layout class="org.apache.log4j.PatternLayout">
          <param name="ConversionPattern" value="%d{ABSOLUTE} %5p %c{1}:%L - %m%n" />
        </layout>
      </appender>
    

      <!--loggers of category 'org.springframework' will only log messages of level "info" or higher; if you retrieve Loggers by using the class name (e.g. Logger.getLogger(AClass.class)) and if AClass is part of the org.springframework package, it will belong to this category-->
      <logger name="org.springframework">
        <level value="info"/>
      </logger>

      <!--everything of spring was set to "info" but for class PropertyEditorRegistrySupport we want "debug" logging-->
      <logger name="org.springframework.beans.PropertyEditorRegistrySupport">
        <level value="debug"/>
      </logger>
      
      <root>
      <!--all log messages of level "debug" or higher will be logged, unless defined otherwise all log messages will be logged to the appender "stdout", unless defined otherwise-->
        <level value="debug" />
        <appender-ref ref="stdout" />
      </root>

    </log4j:configuration> 

    复制代码

    SLF4J

         SLF4J = Simple Logging Facade for Java.
         author: Ceki Gülcü
         SLF4J,即简单日志门面(Simple Logging Facade for Java),不是具体的日志解决方案,而是通过Facade Pattern提供一些Java logging API,它只服务于各种各样的日志系统。按照官方的说法,SLF4J是一个用于日志系统的简单Facade,允许最终用户在部署其应用时使用其所希望的日志系统。作者创建SLF4J的目的是为了替代Jakarta Commons-Logging。
         实际上,SLF4J所提供的核心API是一些接口以及一个LoggerFactory的工厂类。在使用SLF4J的时候,不需要在代码中或配置文件中指定你打算使用那个具体的日志系统。SLF4J提供了统一的记录日志的接口,只要按照其提供的方法记录即可,最终日志的格式、记录级别、输出方式等通过具体日志系统的配置来实现,因此可以在应用中灵活切换日志系统。
         那么什么时候使用SLF4J比较合适呢?
         如果你开发的是类库或者嵌入式组件,那么就应该考虑采用SLF4J,因为不可能影响最终用户选择哪种日志系统。在另一方面,如果是一个简单或者独立的应用,确定只有一种日志系统,那么就没有使用SLF4J的必要。假设你打算将你使用log4j的产品卖给要求使用JDK 1.4 Logging的用户时,面对成千上万的log4j调用的修改,相信这绝对不是一件轻松的事情。但是如果开始便使用SLF4J,那么这种转换将是非常轻松的事情。 

    Logback

          author: Ceki Gülcü
         licences:EPL v1.0 and LGPL 2.1
         Logback,一个“可靠、通用、快速而又灵活的Java日志框架”。logback当前分成三个模块:logback-core,logback- classic和logback-access。logback-core是其它两个模块的基础模块。logback-classic是log4j的一个改良版本。此外logback-classic完整实现SLF4J API使你可以很方便地更换成其它日志系统如log4j或JDK14 Logging。logback-access访问模块与Servlet容器集成提供通过Http来访问日志的功能。
         
         1. logback-core: Joran, Status, context, pattern parsing
         2. logback-classic: developer logging
         3. logback-access: The log generated when a user accesses a web-page on a web server. Integrates seamlessly with Jetty and Tomcat.
    选择logback的理由:(http://logback.qos.ch/reasonsToSwitch.html#fasterImpl )
         1. logback比log4j要快大约10倍,而且消耗更少的内存。
         2. logback-classic模块直接实现了SLF4J的接口,所以我们迁移到logback几乎是零开销的。
         3. logback不仅支持xml格式的配置文件,还支持groovy格式的配置文件。相比之下,Groovy风格的配置文件更加直观,简洁。
         4. logback-classic能够检测到配置文件的更新,并且自动重新加载配置文件。
         5. logback能够优雅的从I/O异常中恢复,从而我们不用重新启动应用程序来恢复logger。
         6. logback能够根据配置文件中设置的上限值,自动删除旧的日志文件。
         7. logback能够自动压缩日志文件。
         8. logback能够在配置文件中加入条件判断(if-then-else)。可以避免不同的开发环境(dev、test、uat...)的配置文件的重复。
         9. logback带来更多的filter。
         10. logback的stack trace中会包含详细的包信息。
         11. logback-access和Jetty、Tomcat集成提供了功能强大的HTTP-access日志。
         配置文件:需要在项目的src目录下建立一个logback.xml。注:(1)logback首先会试着查找logback.groovy文件;(2)当没有找到时,继续试着查找logback-test.xml文件;(3)当没有找到时,继续试着查找logback.xml文件;(4)如果仍然没有找到,则使用默认配置(打印到控制台)。 详细的配置在http://aub.iteye.com/blog/1101222这篇博客中解释的非常清楚。在这里感谢一下原作者(^_^)。

    JUL

         JUL = java.util.logging.

         Java提供了自己的日志框架,类似于Log4J,但是API并不完善,对开发者不是很友好,而且对于日志的级别分类也不是很清晰,比如:SEVERE, WARNING, INFO, CONFIG, FINE,FINER, FINEST。所以不推荐使用这种方式输出日志。

    JCL

         JCL = Jakarta Commons-Logging.
         Jakarta Commons Logging和SLF4J非常类似,也是提供的一套API来掩盖了真正的Logger实现。便于不同的Logger的实现的替换,而不需要重新编译代码。缺点在于它的查找Logger的实现者的算法比较复杂,而且当出现了一些class loader之类的异常时,无法去修复它。

    Log4j2

         已经有很多其他的日志框架对Log4j进行了改良,比如说SLF4J、Logback等。而且Log4j 2在各个方面都与Logback非常相似,那么为什么我们还需要Log4j 2呢?
      1. 插件式结构。Log4j 2支持插件式结构。我们可以根据自己的需要自行扩展Log4j 2. 我们可以实现自己的appender、logger、filter。
      2. 配置文件优化。在配置文件中可以引用属性,还可以直接替代或传递到组件。而且支持json格式的配置文件。不像其他的日志框架,它在重新配置的时候不会丢失之前的日志文件。

    Example for log4j 2    

    复制代码
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE xml>
    <!-- Log4j 2.x 配置文件。每30秒自动检查和应用配置文件的更新; -->
    <Configuration status="warn" monitorInterval="30" strict="true" schema="Log4J-V2.2.xsd">
        <Appenders>
               <!-- 输出到控制台 -->
               <Console name="Console" target="SYSTEM_OUT">
                   <!-- 需要记录的级别 -->
                   <!-- <ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY" /> -->
                   <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss,SSS}:%4p %t (%F:%L) - %m%n" />
              </Console>
    
          <span style="color: #008000;">&lt;!--</span><span style="color: #008000;"> 输出到文件,按天或者超过80MB分割 </span><span style="color: #008000;">--&gt;</span>
          <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">RollingFile </span><span style="color: #ff0000;">name</span><span style="color: #0000ff;">="RollingFile"</span><span style="color: #ff0000;"> fileName</span><span style="color: #0000ff;">="../logs/xjj.log"</span><span style="color: #ff0000;">    filePattern</span><span style="color: #0000ff;">="../logs/$${date:yyyy-MM}/xjj-%d{yyyy-MM-dd}-%i.log.gz"</span><span style="color: #0000ff;">&gt;</span>
               <span style="color: #008000;">&lt;!--</span><span style="color: #008000;"> 需要记录的级别 </span><span style="color: #008000;">--&gt;</span>
               <span style="color: #008000;">&lt;!--</span><span style="color: #008000;"> &lt;ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY" /&gt; </span><span style="color: #008000;">--&gt;</span>
               <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">PatternLayout </span><span style="color: #ff0000;">pattern</span><span style="color: #0000ff;">="%d{yyyy-MM-dd HH:mm:ss,SSS}:%4p %t (%F:%L) - %m%n"</span> <span style="color: #0000ff;">/&gt;</span>
               <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">Policies</span><span style="color: #0000ff;">&gt;</span>
                    <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">OnStartupTriggeringPolicy </span><span style="color: #0000ff;">/&gt;</span>
                    <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">TimeBasedTriggeringPolicy </span><span style="color: #0000ff;">/&gt;</span>
                    <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">SizeBasedTriggeringPolicy </span><span style="color: #ff0000;">size</span><span style="color: #0000ff;">="80 MB"</span> <span style="color: #0000ff;">/&gt;</span>
               <span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">Policies</span><span style="color: #0000ff;">&gt;</span>
          <span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">RollingFile</span><span style="color: #0000ff;">&gt;</span>
     <span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">Appenders</span><span style="color: #0000ff;">&gt;</span>
     <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">Loggers</span><span style="color: #0000ff;">&gt;</span>
          <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">Root </span><span style="color: #ff0000;">level</span><span style="color: #0000ff;">="info"</span><span style="color: #0000ff;">&gt;</span> <span style="color: #008000;">&lt;!--</span><span style="color: #008000;"> 全局配置 </span><span style="color: #008000;">--&gt;</span>
               <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">AppenderRef </span><span style="color: #ff0000;">ref</span><span style="color: #0000ff;">="Console"</span> <span style="color: #0000ff;">/&gt;</span>
               <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">AppenderRef </span><span style="color: #ff0000;">ref</span><span style="color: #0000ff;">="RollingFile"</span><span style="color: #0000ff;">/&gt;</span>
          <span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">Root</span><span style="color: #0000ff;">&gt;</span>
          <span style="color: #008000;">&lt;!--</span><span style="color: #008000;"> 为sql语句配置特殊的Log级别,方便调试 </span><span style="color: #008000;">--&gt;</span>
          <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">Logger </span><span style="color: #ff0000;">name</span><span style="color: #0000ff;">="com.xjj.dao"</span><span style="color: #ff0000;"> level</span><span style="color: #0000ff;">="${log.sql.level}"</span><span style="color: #ff0000;"> additivity</span><span style="color: #0000ff;">="false"</span><span style="color: #0000ff;">&gt;</span>
               <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">AppenderRef </span><span style="color: #ff0000;">ref</span><span style="color: #0000ff;">="Console"</span> <span style="color: #0000ff;">/&gt;</span>
          <span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">Logger</span><span style="color: #0000ff;">&gt;</span>
     <span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">Loggers</span><span style="color: #0000ff;">&gt;</span>
    

    </Configuration>

    复制代码

      3. Java 5的并发性。Log4j 2利用Java 5中的并发特性支持,尽可能地执行最低层次的加锁。解决了在log4j 1.x中存留的死锁的问题。如果你的程序仍然在饱受内存泄露的折磨,请毫不犹豫地试一下log4j 2吧。

      4. 异步logger。Log4j 2是基于LMAX Disruptor库的。在多线程的场景下,和已有的日志框架相比,异步的logger拥有10左右的效率提升。
         还有更多的新特性,在这里就不一一赘述了。了解更多请移步到:http://logging.apache.org/log4j/2.x/manual/index.html。 

    注意:因为Log4j 2使用的是Jackson Data Processor来解析json文件的,所以想要使用json格式的配置文件,必须在项目中加入Jackson的依赖:

    复制代码
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.2.2</version>
    </dependency>
    

    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.2.2</version>
    </dependency> 

    复制代码

    slf4j与log4j联合使用

      slf4j是什么?slf4j只是定义了一组日志接口,但并未提供任何实现,既然这样,为什么要用slf4j呢?log4j不是已经满足要求了吗?

      是的,log4j满足了要求,但是,日志框架并不只有log4j一个,你喜欢用log4j,有的人可能更喜欢logback,有的人甚至用jdk自带的日志框架,这种情况下,如果你要依赖别人的jar,整个系统就用了两个日志框架,如果你依赖10个jar,每个jar用的日志框架都不同,岂不是一个工程用了10个日志框架,那就乱了!

      如果你的代码使用slf4j的接口,具体日志实现框架你喜欢用log4j,其他人的代码也用slf4j的接口,具体实现未知,那你依赖其他人jar包时,整个工程就只会用到log4j日志框架,这是一种典型的门面模式应用,与jvm思想相同,我们面向slf4j写日志代码,slf4j处理具体日志实现框架之间的差异,正如我们面向jvm写java代码,jvm处理操作系统之间的差异,结果就是,一处编写,到处运行。况且,现在越来越多的开源工具都在用slf4j了

      那么,怎么用slf4j呢?

      首先,得弄到slf4j的jar包,maven依赖如下,log4j配置过程完全不变

    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.21</version>
    </dependency>

    然后,弄到slf4j与log4j的关联jar包,通过这个东西,将对slf4j接口的调用转换为对log4j的调用,不同的日志实现框架,这个转换工具不同

    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.21</version>
    </dependency>

    当然了,slf4j-log4j12这个包肯定依赖了slf4j和log4j,所以使用slf4j+log4j的组合只要配置上面这一个依赖就够了

    最后,代码里声明logger要改一下,原来使用log4j是这样的

    复制代码
    import org.apache.log4j.Logger;
    class Test {
        final Logger log = Logger.getLogger(Test.class);
        public void test() {
            log.info("hello this is log4j info log");
        }
    }
    复制代码

    现在要改成这样

    复制代码
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    class Test {
        Logger log = LoggerFactory.getLogger(Test.class);
        public void test() {
            log.info("hello, my name is {}", "chengyi");
        }
    }
    复制代码

    依赖的Logger变了,而且,slf4j的api还能使用占位符,很方便。 

    slf4j与log4j2联合使用

    1、引用依赖包及相关注释:

    复制代码
    <!-- log配置:Log4j2 + Slf4j -->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-api</artifactId>
        <version>2.2</version>
    </dependency>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.2</version>
    </dependency>
    <dependency> <!-- 桥接:告诉Slf4j使用Log4j2 -->
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-slf4j-impl</artifactId>
        <version>2.2</version>
    </dependency>
    <dependency> <!-- 桥接:告诉commons logging使用Log4j2 -->
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-jcl</artifactId>
        <version>2.2</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.10</version>
    </dependency> 
    复制代码

    **注:**log4j-api-2.x 和 log4j-core-2.x是必须的,其他包根据需要引入,如下图所示: 
    这里写图片描述

    2、代码中使用:

    复制代码
    @RunWith(SpringJUnit4ClassRunner.class)  //使用Spring Junit4进行测试  
    @ContextConfiguration ({"classpath:spring/applicationContext.xml"}) //加载配置文件
    public abstract class BaseJunit4Test {
    }
    

    import org.junit.Test;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    mport com.xjj.test.BaseJunit4Test;
    public class Log4j2Test extends BaseJunit4Test {
    static public Logger logger = LoggerFactory.getLogger(Log4j2Test.class);

    @Test
    public void logTC1(){
    logger.error(
    "error");
    logger.debug(
    "debug");
    logger.info(
    "info");
    logger.trace(
    "trace");
    logger.warn(
    "warn");
    logger.error(
    "error {}", "param");
        logger.info("请求处理结束,耗时:{}毫秒", (System.currentTimeMillis() - beginTime)); //第一种用法
    logger.info("请求处理结束,耗时:" + (System.currentTimeMillis() - beginTime) + "毫秒"); //第二种用法

    }
    }
     

    复制代码

    输出结果:

    16:19:28.779 [main] ERROR com.xjj.test.mytest.Log4j2Test - error
    16:19:28.781 [main] ERROR com.xjj.test.mytest.Log4j2Test - error param

    注:如果没有任何配置,Log4j2会使用缺省配置(级别:ERROR):

    root logger:ConsoleAppender
    PatternLayout:"%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"

    Tips:根据官方测试的数据,第一种用法比第二种快47倍! 

    日志级别动态调整

    以下代码演示动态调整log4j的日志级别:

    复制代码
    public class Log4jTest {
        private static final ConcurrentHashMap<String, Object> loggerContainer = new ConcurrentHashMap<>();
        private static final LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);
        private static final Logger LOGGER = LoggerFactory.getLogger(Log4jTest.class);
    
    @Test
    </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span> put() <span style="color: #0000ff;">throws</span><span style="color: #000000;"> InterruptedException {
        LOGGER.debug(</span>"log4j debug msg:{}","start"<span style="color: #000000;">);
        LOGGER.info(</span>"log4j info msg:{}","start"<span style="color: #000000;">);
        LOGGER.error(</span>"log4j error msg:{}","start"<span style="color: #000000;">);
    

    setLoggerLevel4Log4j("com.junzi.log", Level.ERROR.toString());

        LOGGER.debug(</span>"log4j debug msg:{}","end"<span style="color: #000000;">);
        LOGGER.info(</span>"log4j info msg:{}","end"<span style="color: #000000;">);
        LOGGER.error(</span>"log4j error msg:{}","end"<span style="color: #000000;">);
    }
    
    @Before
    </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> init() {
        String type </span>=<span style="color: #000000;"> StaticLoggerBinder.getSingleton().getLoggerFactoryClassStr();
        System.out.println(type);
        Enumeration enumeration </span>=<span style="color: #000000;"> org.apache.log4j.LogManager.getCurrentLoggers();
        </span><span style="color: #0000ff;">while</span><span style="color: #000000;"> (enumeration.hasMoreElements()) {
            org.apache.log4j.Logger logger </span>=<span style="color: #000000;"> (org.apache.log4j.Logger) enumeration.nextElement();
            </span><span style="color: #0000ff;">if</span> (logger.getLevel() != <span style="color: #0000ff;">null</span><span style="color: #000000;">) {
                loggerContainer.put(logger.getName(), logger);
            }
        }
        org.apache.log4j.Logger rootLogger </span>=<span style="color: #000000;"> org.apache.log4j.LogManager.getRootLogger();
        loggerContainer.put(rootLogger.getName(), rootLogger);
    }
    
    </span><span style="color: #008000;">/**</span><span style="color: #008000;">
     * 适用于log4j2
     * </span><span style="color: #808080;">@param</span><span style="color: #008000;"> loggerName
     * </span><span style="color: #808080;">@param</span><span style="color: #008000;"> level
     </span><span style="color: #008000;">*/</span>
    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> setLoggerLevel4Log4j2(String loggerName, String level) {
        LoggerConfig loggerConfig </span>=<span style="color: #000000;"> (LoggerConfig) loggerContainer.get(loggerName);
        Level targetLevel </span>= StringUtils.isBlank(level) ?<span style="color: #000000;"> Level.INFO : Level.toLevel(level);
        </span><span style="color: #0000ff;">if</span> (loggerConfig == <span style="color: #0000ff;">null</span><span style="color: #000000;">) {
            System.out.println(loggerName </span>+ " logger not exist."<span style="color: #000000;">);
            </span><span style="color: #0000ff;">return</span><span style="color: #000000;">;
        }
        loggerConfig.setLevel(targetLevel);
        loggerContext.updateLoggers();
        System.out.println(</span>"set logger: " + loggerName + ", level: " +<span style="color: #000000;"> targetLevel.name());
        </span><span style="color: #0000ff;">return</span><span style="color: #000000;">;
    }
    
    </span><span style="color: #008000;">/**</span><span style="color: #008000;">
     * 适用于log4j
     * </span><span style="color: #808080;">@param</span><span style="color: #008000;"> loggerName
     * </span><span style="color: #808080;">@param</span><span style="color: #008000;"> level
     </span><span style="color: #008000;">*/</span>
    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> setLoggerLevel4Log4j(String loggerName, String level) {
        org.apache.log4j.Logger targetLogger </span>=<span style="color: #000000;"> (org.apache.log4j.Logger) loggerContainer.get(loggerName);
        org.apache.log4j.Level targetLevel </span>=<span style="color: #000000;"> org.apache.log4j.Level.toLevel(level);
        targetLogger.setLevel(targetLevel);
    }</span>&nbsp;</pre>
    
    复制代码

    参考资料

    http://tech.lede.com/2017/02/06/rd/server/log4jSearch/

    http://www.cnblogs.com/penghongwei/p/3417179.html

    http://www.cnblogs.com/ywlaker/p/6124067.html

    https://tech.meituan.com/change_log_level.html

    http://hongbing.github.io/posts/blog/17/10/15/dynamic-change-logger-level.html

    原文地址:https://www.cnblogs.com/junzi2099/p/7930268.html
  • 相关阅读:
    ubuntu下文件安装与卸载
    webkit中的JavaScriptCore部分
    ubuntu 显示文件夹中的隐藏文件
    C语言中的fscanf函数
    test
    Use SandCastle to generate help document automatically.
    XElement Getting OuterXML and InnerXML
    XUACompatible meta 用法
    Adobe Dreamweaver CS5.5 中文版 下载 注册码
    The Difference Between jQuery’s .bind(), .live(), and .delegate()
  • 原文地址:https://www.cnblogs.com/jpfss/p/11044220.html
Copyright © 2011-2022 走看看