【重新整理】log4j 2的使用
一 概述
1.1 日志框架
- 日志接口(slf4j)
slf4j是对所有日志框架制定的一种规范、标准、接口,并不是一个框架的具体的实现,因为接口并不能独立使用,需要和具体的日志框架实现配合使用(如log4j、logback) - 日志实现(log4j、logback、log4j2)
- log4j是apache实现的一个开源日志组件
 - logback同样是由log4j的作者设计完成的,拥有更好的特性,用来取代log4j的一个日志框架,是slf4j的原生实现
 - log4j2是log4j 1.x和logback的改进版,据说采用了一些新技术(无锁异步、等等),使得日志的吞吐量、性能比log4j 1.x提高10倍,并解决了一些死锁的bug,而且配置更加简单灵活。
 
 
关于 Log4j2 的介绍可以参考官网:https://logging.apache.org/log4j/2.x/
1.2 为什么需要日志接口,直接使用具体的实现不就行了吗?
接口用于定制规范,可以有多个实现,使用时是面向接口的(导入的包都是slf4j的包而不是具体某个日志框架中的包),即直接和接口交互,不直接使用实现,所以可以任意的更换实现而不用更改代码中的日志相关代码。
比如:slf4j定义了一套日志接口,项目中使用的日志框架是logback,开发中调用的所有接口都是slf4j的,不直接使用logback,调用是 自己的工程调用slf4j的接口,slf4j的接口去调用logback的实现,可以看到整个过程应用程序并没有直接使用logback,当项目需要更换更加优秀的日志框架时(如log4j2)只需要引入Log4j2的jar和Log4j2对应的配置文件即可,完全不用更改Java代码中的日志相关的代码logger.info(“xxx”),也不用修改日志相关的类的导入的包(import org.slf4j.Logger; import org.slf4j.LoggerFactory;)
使用日志接口便于更换为其他日志框架
log4j、logback、log4j2都是一种日志具体实现框架,所以既可以单独使用也可以结合slf4j一起搭配使用。
二 引入 Maven 依赖
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.25</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j-impl</artifactId>
    <version>2.11.0</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.11.0</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.11.0</version>
</dependency>
三 log4j 2 日志级别
从大到小依次是: off, fatal, error, warn, info, debug, trace, all
由于我们使用的是slf4j接口包,该接口包中只提供了未标有删除线的日志级别的输出。
四 log4j 2 加载配置文件的顺序
- Log4j will inspect the 
log4j.configurationFilesystem property and, if set, will attempt to load the configuration using theConfigurationFactorythat matches the file extension. - If no system property is set the properties 
ConfigurationFactorywill look forlog4j2-test.propertiesin the classpath. - If no such file is found the YAML ConfigurationFactory will look for 
log4j2-test.yamlorlog4j2-test.ymlin the classpath. - If no such file is found the JSON ConfigurationFactory will look for 
log4j2-test.jsonorlog4j2-test.jsnin the classpath. - If no such file is found the XML ConfigurationFactory will look for 
log4j2-test.xmlin the classpath. - If a test file cannot be located the properties ConfigurationFactory will look for 
log4j2.propertieson the classpath. - If a properties file cannot be located the YAML ConfigurationFactory will look for 
log4j2.yamlorlog4j2.ymlon the classpath. - If a YAML file cannot be located the JSON ConfigurationFactory will look for 
log4j2.jsonorlog4j2.jsnon the classpath. - If a JSON file cannot be located the XML ConfigurationFactory will try to locate 
log4j2.xmlon the classpath. - If no configuration file could be located the DefaultConfiguration will be used. This will cause logging output to go to the console.
 
五 对于 log4j 2 配置文件的理解
配置文件结构:
Appdenders部分AppenderFilterLayoutPoliciesStrategy
Loggers部分Logger(ROOT)LeveladditivityAppenderRefFilter
5.1 对于 Logger 的理解
简单说 Logger 就是一个路由器,指定包下面的类或者指定某个类的日志信息流向哪个 Appender,以及控制他们的流量(日志级别)。
其中最重要的一个是 Root 这个标签,这个标签是必须定义的!如果没有指定 Logger,则默认会打印到 Root 标签下定义的 Appender 中。
5.2 对于 Appender 的理解
简单说 Appender 就是一个管道,定义了日志内容的去向(控制台、文件、网络等等)。
配置一个或者多个Filter,Filter的过滤机制和Servlet的Filter有些差别,下文会进行说明。
配置Layout来控制日志信息的输出格式。
配置Policies以控制日志何时(When)进行滚动。
配置Strategy以控制日志如何(How)进行滚动。
六 Appender
其实这些标签都是类名去掉 Appender 后缀的形式。
这里我选择几个基础的代表性的 Appender 进行讲解:
- ConsoleAppender(Console)
 - FileAppender(File) 和 RandomAccessFileAppender(RandomAccessFile)
 - RollingFileAppender(RollingFile) 和 RollingRandomAccessFileAppender(RollingRandomAccessFile)
 
6.1 ConsoleAppender
该实现类会把日志输出到控制台中。
它有两种输出方式:
- SYSTEM_OUT(System.out)
 - SYSTEM_ERR(System.err)
 
如果不配置,默认使用SYSTEM_OUT进行输出(源码Target DEFAULT_TARGET = Target.SYSTEM_OUT;)。
示例:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration name="baseConf" status="warn" monitorInterval="30">
    <Appenders>
        <!-- target 是 "SYSTEM_OUT" or "SYSTEM_ERR". 默认是 "SYSTEM_OUT". -->
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout>
                <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
            </PatternLayout>
        </Console>
    </Appenders>
    <Loggers>
        <ROOT level="info">
            <AppenderRef ref="Console"/>
        </ROOT>
    </Loggers>
</Configuration>
其它属性可以参见官方文档: http://logging.apache.org/log4j/2.x/manual/appenders.html#ConsoleAppender
提示
如果出现控制台日志乱码,通过查看 ConsoleAppender#Target 的源码你就能解决了。target 属性也可以这么配置:
<Console name="Console">
    <Target>SYSTEM_ERR</Target>
</Console>
现在我们看一下 <Appenders> 这个标签,它的源码是 AppendersPlugin,它有一个 createAppenders 的方法,那么现在可以知道这些 Appender 是如何组织在一起的了,而且发现它返回了一个 Map 结构,并且使用 Appender 的 name 作为 key,所以 Appender 必须有 name 属性。
6.2 FileAppender 和 RandomAccessFileAppender
写入日志信息到文件。使用 FileOutputStream#getChannel 进行文件的写入。它是默认有 4M 的缓冲区的。
两个基本相似,除了 RandomAccessFileAppender 的 bufferedIO 属性是不能关闭的,但是官网上说后者比前者提高了 20-200% 的性能(在 bufferedIO=true 的情况下)。
简单示例:
<appenders>
    <File name="File">
        <fileName>log4j2/app.log</fileName>
        <immediateFlush>false</immediateFlush>
        <PatternLayout pattern="%d %p %c{1.} [%t] %m%n"/>
    </File>
</appenders>
<loggers>
    <root level="info">
        <AppenderRef ref="File"/>
    </root>
</loggers>
常用属性:
fileName:来指定文件位置,文件或目录不存在则会自动创建。immediateFlush:是否每次写入都要立刻刷新到硬盘中。默认true,如果使用默认值可能会影响性能。
其它属性可以参见官方文档:
- http://logging.apache.org/log4j/2.x/manual/appenders.html#FileAppender
 - http://logging.apache.org/log4j/2.x/manual/appenders.html#RandomAccessFileAppender
 
6.3 RollingFileAppender 和 RollingRandomAccessFileAppender
与 6.2 的 Appender 一样,后者同样比前者提高了 20-200% 的性能。
它们的主要功能也是将日志写入到文件,但是增加了一个功能,就是日志文件可以进行切分。
RollingFileAppender 需要 TriggeringPolicy 和 RolloverStrategy。触发策略确定是否应执行过渡,而RolloverStrategy 定义应如何进行过渡。如果未配置 RolloverStrategy,则 RollingFileAppender 将使用 DirectWriteRolloverStrategy。DirectWriteRolloverStrategy 默认的最大文件数是 7。
演示每 2 秒切分一下日志文件,并且最大的文件数是 7。示例:
<appenders>
    <RollingFile name="RollingFile">
        <filename>log4j2/rolling_app.log</filename>
        <filePattern>log4j2/rolling_app_%i.log</filePattern>
        <PatternLayout pattern="%d %p %c{1.} [%t] %m%n"/>
        <!-- 每 2 秒切分一次日志 -->
        <CronTriggeringPolicy schedule="0/2 * * * * ?" />
    </RollingFile>
</appenders>
<loggers>
    <root level="info">
        <AppenderRef ref="RollingFile"/>
    </root>
</loggers>
filePattern:指定了日志滚动之后的文件命名规则。DirectWriteRolloverStrategy:指定了如何(How)进行翻滚,并且指定了最大翻滚次数(%i参数值),超过次数之后会按照相应的规则删除旧日志。Policy: 这里就是规定了何时进行滚动(When)。
日志切分覆盖原则
第一次翻滚:app.log app.1.log // app.log -> app.1.log
第二次翻滚:app.log app.1.log  app.2.lop // app.log -> app.2.log
第三次翻滚:app.log app.1.log  app.2.lop  app.3.lop // app.log -> app.3.log
第四次翻滚:app.log app.1.log  app.2.lop  app.3.lop  app.4.lop // app.log -> app.4.log
一直到设定的翻滚次数 7 之后,会把旧的日志内容覆盖。
app.2.lop -> app.1.lop
app.3.lop -> app.2.lop
...
app.7.lop -> app.6.lop
app.log -> app.7.lop
一直这样循环下去。
Policy是用来控制日志文件何时(When)进行滚动的;Strategy是用来控制日志文件如何(How)进行滚动的。
更详细的 Policy & Strategy 介绍在第九节和第十节介绍。
七 Filter
Filters决定日志事件能否被输出。过滤条件有三个值:ACCEPT(接受),DENY(拒绝),NEUTRAL(中立)。
常用的Filter实现类:
- BurstFilter:提供了一种机制来控制处理打印日志的速率,超过之后将无声的丢弃日志,一般不用。
 - LevelRangeFilter:过滤日志的级别。
 - TimeFilter: 提供基于时间段内的日志过滤。
 - ThresholdFilter:对日志级别进行进一步的限制。
 - ThreadContextMapFilter:验证 ThreadContentMap 中是否含有某个 key-value 值。
 
ThresholdFilter 示例:
<appenders>
    <Console name="Console">
        <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
        <PatternLayout>
            <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
        </PatternLayout>
    </Console>
</appenders>
<loggers>
    <root level="info">
        <AppenderRef ref="Console"/>
    </root>   
</loggers>
虽然 root 标签配置的日志级别是 info, 但是对应的 appender 中配置的 ThresholdFilter#level 是 warn,所以所有 info 级别的日志是打印不出来的。
ThreadContextMapFilter 示例:
<appenders>
    <Console name="Console">
        <ThreadContextMapFilter onMatch="ACCEPT" onMismatch="DENY" operator="or">
            <KeyValuePair key="name" value="Mike" />
            <KeyValuePair key="age" value="20" />
        </ThreadContextMapFilter>
        <PatternLayout>
            <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
        </PatternLayout>
    </Console>
</appenders>
<loggers>
    <root level="info">
        <AppenderRef ref="Console"/>
    </root>   
</loggers>
operator="or":表示有一个匹配到即可通过过滤器,KeyValuePair 中的元素必须在 org.apache.logging.log4j.ThreadContext.put("name", "Mike") 中存在。否则会匹配 onMismatch。如果没有配置 operator,默认匹配所有的 KeyValuePair。
八 Layout
这个组件是将 log 日志格式化为各种想要的格式,进行输出的。在 AbstractStringLayout 中默认的编码是 UTF-8。
它有很多实现类,最常用的就是 PatternLayout,它还可以将日志格式化为 xml、json、yml、html等等,感兴趣的可以找一下 AbstractStringLayout 的实现类。
这里只说一下 PatternLayout。
简单示例:
<PatternLayout>
    <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
</PatternLayout>
授人以鱼不如授人以渔。关于 pattern 的格式化方式点击 http://logging.apache.org/log4j/2.x/manual/layouts.html#PatternLayout 查看官方的详细介绍。
九 Policy
Policy是用来控制日志文件何时(When)进行滚动的。
如果配置的是RollingFile或RollingRandomAccessFile,则必须配置一个Policy。
Policy常用的实现类:
- SizeBasedTriggeringPolicy
 - CronTriggeringPolicy
 - TimeBasedTriggeringPolicy
 - CompositeTriggeringPolicy
 
9.1 SizeBasedTriggeringPolicy
根据日志文件的大小进行滚动,当文件大小达到指定的大小时,就会进行文件的切分。
最多产生 7 个日志文件:
<RollingFile name="RollingFile">
    <filename>log4j2/rolling_app.log</filename>
    <filePattern>log4j2/rolling_app_%i.log</filePattern>
    <PatternLayout pattern="%d %p %c{1.} [%t] %m%n"/>
    <SizeBasedTriggeringPolicy size="1K"/>
</RollingFile>
日志文件满 1K 大小,就要进行切分。
size 的单位有:K,M,G(参见源码org.apache.logging.log4j.core.appender.rolling.FileSize#parse)。
为什么最多产生 7 个日志文件呢?因为这里有一个默认的 Stragety,那就是 DefaultRolloverStrategy,为什么是 1~7,与两个参数有关 MIN_WINDOW_SIZE 和 DEFAULT_WINDOW_SIZE。
<filePattern>log4j2/rolling_app_%i.log</filePattern> 也可以写为 <filePattern>log4j2/rolling_app_%i.log.tar.gz</filePattern> 来启动日志压缩。
注意
这里如果配置了 <filename> 属性,就是用默认的 DefaultRolloverStrategy,如果没有配置的话就使用 DirectWriteRolloverStrategy,关于 DirectWriteRolloverStrategy 的使用,我没细研究,一般情况下使用 DefaultRolloverStrategy 即可。
9.2 CronTriggeringPolicy
使用 Cron 表达式进行日志滚动,很灵活。
最多产生 7 个日志文件:
<RollingFile name="RollingFile">
    <filename>log4j2/rolling_app.log</filename>
    <filePattern>log4j2/rolling_app_%i.log</filePattern>
    <PatternLayout pattern="%d %p %c{1.} [%t] %m%n"/>
    <CronTriggeringPolicy schedule="0/5 * * * * ?" />
</RollingFile>
没有配置 Stragety,就使用默认的 DefaultRolloverStrategy。
9.3 TimeBasedTriggeringPolicy
这个滚动策略依赖于 filePattern 中配置的最具体的时间单位,根据最具体的时间单位进行滚动。
这种方式比较简洁, 但是 CronTriggeringPolicy 策略更强大,也更易读。
最多产生 7 个日志文件:
<RollingFile name="RollingFile">
    <filename>log4j2/rolling_app.log</filename>
    <filePattern>log4j2/rolling_app_%d{hh-mm-ss}.log</filePattern>
    <PatternLayout pattern="%d %p %c{1.} [%t] %m%n"/>
    <TimeBasedTriggeringPolicy interval="5"/>
</RollingFile>
没有配置 Stragety,就使用默认的 DefaultRolloverStrategy。
这个滚动策略是根据 filePattern 中配置的具体时间粒度来滚动的。比如 interval 是 5,而 filePattern 中配置的时间粒度是秒,所有是每 5 秒滚动一次。
9.4 CompositeTriggeringPolicy
它可以配置多个 Policy。
比如我想每 5 秒生成一个新文件,但同时每个文件的大小不超过 1KB,最大 5 秒内不能超过 20 个文件,就是 5秒内打印的日志不超过 20 K,就需要这样配置:
<RollingFile name="RollingFile">
    <filename>log4j2/rolling_app.log</filename>
    <filePattern>log4j2/rolling_app_%d{hh-mm-ss}_%i.log.tar.gz</filePattern>
    <PatternLayout pattern="%d %p %c{1.} [%t] %m%n"/>
    <Policies>
        <CronTriggeringPolicy schedule="0/5 * * * * ?" />
        <SizeBasedTriggeringPolicy size="1K"/>
    </Policies>
    <DefaultRolloverStrategy max="20"/>
</RollingFile>
DefaultRolloverStrategy 默认的 min 是 1,查看源码可知,这里配置 20 之后,会产生下标 1~20 的文件,如果修改了 min 为 5 则就是 5~20。
十 Strategy
Strategy 都是控制如何(How)进行日志滚动的。
Strategy常用的实现类:DefaultRolloverStrategy,这个也是默认的实现类。
常用的属性:
- min:最小文件下标。
 - max:最大文件下标。
 - compressionLevel:日志压缩级别,0-9,越大压缩越厉害。
 
十一 Logger
Logger 部分就比较简单了,分为两个 Logger :
- Root(必须配置)
 - Logger
 
11.1 Root
简单示例:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration name="baseConf" status="warn" monitorInterval="30">
    <Appenders>
        <Console name="Console">
            <PatternLayout>
                <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
            </PatternLayout>
        </Console>
    </Appenders>
    <Loggers>
        <Root level="trace">
            <AppenderRef ref="Console"/>
            <LevelRangeFilter minLevel="error" maxLevel="info" onMatch="ACCEPT" onMismatch="DENY" />
        </Root>
    </Loggers>
</Configuration>
注意:Logger中也可以加过滤器的哟~
11.2 Logger
如果 Root 中的日志包含了 Logger 中的日志信息,并且 AppenderRef 是一样的配置,则日志会打印两次。
注意:有两个条件
Root中的日志包含了Logger中的日志信息。- 且
AppenderRef是一样的配置。 
这时候我们需要使用一个 Logger 的属性来解决,那就是 additivity,其默认值为 true,需要配置为 false。
<?xml version="1.0" encoding="UTF-8"?>
<Configuration name="baseConf" status="warn" monitorInterval="30">
    <Appenders>
        <Console name="Console">
            <PatternLayout>
                <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
            </PatternLayout>
        </Console>
    </Appenders>
    <Loggers>
        <Logger name="com.snailwu.log" level="info" additivity="false">
            <AppenderRef ref="Console"/>
        </Logger>
        <Root level="trace">
            <AppenderRef ref="Console"/>
        </Root>
    </Loggers>
</Configuration>
这样就不会打印重复的日志了。
十二 ThreadContent
这个与 logback 的 MDC 类似。
log4j2.xml 文件:
<appenders>
    <Console name="Console">
        <PatternLayout>
            <Pattern>用户: %X{name} %d %p %c{1.} [%t] %m%n</Pattern>
        </PatternLayout>
    </Console>
</appenders>
<loggers>
    <root level="info">
        <AppenderRef ref="Console"/>
    </root>   
</loggers>
Java 代码:
import org.apache.logging.log4j.CloseableThreadContext;
import org.apache.logging.log4j.ThreadContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.Map;
public class App {
    private static final Logger log = LoggerFactory.getLogger(App.class);
    public static void main(String[] args) throws InterruptedException {
        ThreadContext.put("name", "Mike");
        log.info("Hello Info");
        new Thread((() -> {
            log.info("无 Name");
        })).start();
        Map<String, String> stringMap = ThreadContext.getImmutableContext();
        List<String> list = ThreadContext.getImmutableStack().asList();
        new Thread((() -> {           
            CloseableThreadContext.putAll(stringMap).pushAll(list);
            log.info("有 Name");
        })).start();
    }
}
这个目前广泛使用的就是将 http 请求串起来。
疫情在家里重新整理了一遍,也加深了一下。
【重新整理】log4j 2的使用的更多相关文章
- log4j.properties 配置的学习整理
		
参考资料: log4j.properties:用来做什么的(日志) Log4j:由2部分组成 :loggers(记录器) ----日志的类别 appender(输出源) ...
 - log4j个人使用整理
		
Log4j介绍: 略过. 配置: Eclipse项目中添加log4j.jar到lib下. 在bin目录下新建log4j.properties,编辑好log4j配置文件. 样例分析: log4j.roo ...
 - log4j和commons- logging(好文整理转载)
		
一 :为什么同时使用commons-logging和Log4j?为什么不仅使用其中之一? Commons-loggin的目的是为 “所有的Java日志实现”提供一个统一的接口,它自身的日志功能平常弱( ...
 - log4j 2整理
		
# Log4j 2最佳实践 #Log4j的1.x版本已经被广泛使用于很多应用程序中.然而,它这些年的发展已经放缓.它变得越来越难以维护,因为它需要严格遵循很老的Java版本,并在2015年8月寿终正寝 ...
 - log4j整理
		
<meta http-equiv="refresh" content="1"/> # log4j日志组件 #- SLF4J,一个**通用日志接口** ...
 - 国外程序员整理的Java资源大全分享
		
Java 几乎是许多程序员们的入门语言,并且也是世界上非常流行的编程语言.国外程序员 Andreas Kull 在其 Github 上整理了非常优秀的 Java 开发资源,推荐给大家. 译文由 Imp ...
 - 基于java平台的常用资源整理
		
这里整理了基于java平台的常用资源 翻译 from :akullpp | awesome-java 大家一起学习,共同进步. 如果大家觉得有用,就mark一下,赞一下,或评论一下,让更多的人知道.t ...
 - log4j使用快速入门【转】
		
1.引言 在应用程序中添加日志记录总的来说基于三个目的: .监视代码中变量的变化情况,周期性的记录到文件中供其他应用进行统计分析工作 .跟踪代码运行时轨迹,作为日后审计的依据 .担当集成开发环境中的调 ...
 - 这里整理了基于java平台的常用资源
		
这里整理了基于java平台的常用资源 翻译 from :akullpp | awesome-java 大家一起学习,共同进步. 如果大家觉得有用,就mark一下,赞一下,或评论一下,让更多的人知道.t ...
 
随机推荐
- dp-划分数 (递推)
			
问题描述 : 有 n 个无区别的物品 , 将他们分成 不超过 m 堆, 问有多少种分法 ? 例如 : n = 4 , m = 3 , 则总共有的分法是 1 + 2 +1 , 0 + 1 + 3 , 0 ...
 - Linux下搭建Jmeter+Ant+Jenkins自动化测试框架
			
前言 在之前的文章中,我们学习了通过Ant调用Jmeter脚本生成HTML测试报告,但未实现自动执行脚本生成报告,同时生成的报告是在Linux下,查看报告很不方便.因此,我们将结合Jenkins来进一 ...
 - 【数据结构】FHQ Treap详解
			
FHQ Treap是什么? FHQ Treap,又名无旋Treap,是一种不需要旋转的平衡树,是范浩强基于Treap发明的.FHQ Treap具有代码短,易理解,速度快的优点.(当然跟红黑树比一下就是 ...
 - [uoj#34] [洛谷P3803] 多项式乘法(FFT)
			
新技能--FFT. 可在 \(O(nlogn)\) 时间内完成多项式在系数表达与点值表达之间的转换. 其中最关键的一点便为单位复数根,有神奇的折半性质. 多项式乘法(即为卷积)的常见形式: \[ C_ ...
 - .NetCore自定义WebAPI返回Json的格式大小写的三种方式
			
.NetCore的Controller/WebAPI可以帮我们将返回结果自动转换为Json格式给前台,而且可以自由设定格式(大写.小写.首字母大写等),我总结了三种方法,对应三种灵活度,供大家参考 ( ...
 - java8新特性Lambda和Stream
			
Java8出来已经4年,但还是有很多人用上了jdk8,但并没用到里面的新东西,那不就等于没用?jdk8有许多的新特性,详细可看下面脑图 我只讲两个最重要的特性Lambda和Stram,配合起来用可以极 ...
 - Firebase REST API
			
use firebase and firesharp to do a Library management system . look github.com/ziyasal/FireSharp
 - T117897 七步洗手法 / PJT1(洛谷)
			
题目:现在有n个人需要依次使用1个洗手池洗手,进行一步洗手需要1单位时间.他们每个人至少会进行一步洗手,但是却不一定进行了完整的七部洗手. 现在你知道了他们总共的洗手时间为t,请你推测他们有多少人进行 ...
 - Vim学习之路1
			
与之前的随笔一样,这个也是记录Vim常用命令以供日后查找所用.对于Vim,简介而又功能强大,学习之后代码书写相当愉快. 1. 保存并退出 :wq 2. 进入标准插入模式退出命令模式 i 3. 退出标准 ...
 - HGE引擎改进——2014/1/27
			
2014/1/27 更新 hge库: 1.增加回调函数procResizeFunc(),这个函数会在窗口大小改变时调用,不是必要函数 2.修复LOG信息显示为乱码的错误 项目主页:https://co ...