一 概述

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 加载配置文件的顺序

  1. Log4j will inspect the log4j.configurationFile system property and, if set, will attempt to load the configuration using the ConfigurationFactory that matches the file extension.
  2. If no system property is set the properties ConfigurationFactory will look for log4j2-test.properties in the classpath.
  3. If no such file is found the YAML ConfigurationFactory will look for log4j2-test.yaml or log4j2-test.yml in the classpath.
  4. If no such file is found the JSON ConfigurationFactory will look for log4j2-test.json or log4j2-test.jsn in the classpath.
  5. If no such file is found the XML ConfigurationFactory will look for log4j2-test.xml in the classpath.
  6. If a test file cannot be located the properties ConfigurationFactory will look for log4j2.properties on the classpath.
  7. If a properties file cannot be located the YAML ConfigurationFactory will look for log4j2.yaml or log4j2.yml on the classpath.
  8. If a YAML file cannot be located the JSON ConfigurationFactory will look for log4j2.json or log4j2.jsn on the classpath.
  9. If a JSON file cannot be located the XML ConfigurationFactory will try to locate log4j2.xml on the classpath.
  10. If no configuration file could be located the DefaultConfiguration will be used. This will cause logging output to go to the console.

五 对于 log4j 2 配置文件的理解

配置文件结构:

  1. Appdenders部分

    1. Appender

      1. Filter
      2. Layout
      3. Policies
      4. Strategy
  2. Loggers部分
    1. Logger(ROOT)

      1. Level
      2. additivity
      3. AppenderRef
      4. Filter

5.1 对于 Logger 的理解

简单说 Logger 就是一个路由器,指定包下面的类或者指定某个类的日志信息流向哪个 Appender,以及控制他们的流量(日志级别)。

其中最重要的一个是 Root 这个标签,这个标签是必须定义的!如果没有指定 Logger,则默认会打印到 Root 标签下定义的 Appender 中。

5.2 对于 Appender 的理解

简单说 Appender 就是一个管道,定义了日志内容的去向(控制台、文件、网络等等)。

配置一个或者多个FilterFilter的过滤机制和ServletFilter有些差别,下文会进行说明。

配置Layout来控制日志信息的输出格式。

配置Policies以控制日志何时(When)进行滚动。

配置Strategy以控制日志如何(How)进行滚动。

六 Appender

其实这些标签都是类名去掉 Appender 后缀的形式。

这里我选择几个基础的代表性的 Appender 进行讲解:

  1. ConsoleAppender(Console)
  2. FileAppender(File) 和 RandomAccessFileAppender(RandomAccessFile)
  3. RollingFileAppender(RollingFile) 和 RollingRandomAccessFileAppender(RollingRandomAccessFile)

6.1 ConsoleAppender

该实现类会把日志输出到控制台中。

它有两种输出方式:

  1. SYSTEM_OUT(System.out)
  2. 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>

常用属性:

  1. fileName:来指定文件位置,文件或目录不存在则会自动创建。
  2. immediateFlush:是否每次写入都要立刻刷新到硬盘中。默认true,如果使用默认值可能会影响性能。

其它属性可以参见官方文档:

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>
  1. filePattern:指定了日志滚动之后的文件命名规则。
  2. DirectWriteRolloverStrategy:指定了如何(How)进行翻滚,并且指定了最大翻滚次数(%i参数值),超过次数之后会按照相应的规则删除旧日志。
  3. 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实现类

  1. BurstFilter:提供了一种机制来控制处理打印日志的速率,超过之后将无声的丢弃日志,一般不用。
  2. LevelRangeFilter:过滤日志的级别。
  3. TimeFilter: 提供基于时间段内的日志过滤。
  4. ThresholdFilter:对日志级别进行进一步的限制。
  5. 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)进行滚动的。

如果配置的是RollingFileRollingRandomAccessFile,则必须配置一个Policy

Policy常用的实现类

  1. SizeBasedTriggeringPolicy
  2. CronTriggeringPolicy
  3. TimeBasedTriggeringPolicy
  4. 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 :

  1. Root(必须配置)
  2. 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 是一样的配置,则日志会打印两次。

注意:有两个条件

  1. Root 中的日志包含了 Logger 中的日志信息。
  2. 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的使用的更多相关文章

  1. log4j.properties 配置的学习整理

    参考资料: log4j.properties:用来做什么的(日志) Log4j:由2部分组成 :loggers(记录器)            ----日志的类别 appender(输出源)     ...

  2. log4j个人使用整理

    Log4j介绍: 略过. 配置: Eclipse项目中添加log4j.jar到lib下. 在bin目录下新建log4j.properties,编辑好log4j配置文件. 样例分析: log4j.roo ...

  3. log4j和commons- logging(好文整理转载)

    一 :为什么同时使用commons-logging和Log4j?为什么不仅使用其中之一? Commons-loggin的目的是为 “所有的Java日志实现”提供一个统一的接口,它自身的日志功能平常弱( ...

  4. log4j 2整理

    # Log4j 2最佳实践 #Log4j的1.x版本已经被广泛使用于很多应用程序中.然而,它这些年的发展已经放缓.它变得越来越难以维护,因为它需要严格遵循很老的Java版本,并在2015年8月寿终正寝 ...

  5. log4j整理

    <meta http-equiv="refresh" content="1"/> # log4j日志组件 #- SLF4J,一个**通用日志接口** ...

  6. 国外程序员整理的Java资源大全分享

    Java 几乎是许多程序员们的入门语言,并且也是世界上非常流行的编程语言.国外程序员 Andreas Kull 在其 Github 上整理了非常优秀的 Java 开发资源,推荐给大家. 译文由 Imp ...

  7. 基于java平台的常用资源整理

    这里整理了基于java平台的常用资源 翻译 from :akullpp | awesome-java 大家一起学习,共同进步. 如果大家觉得有用,就mark一下,赞一下,或评论一下,让更多的人知道.t ...

  8. log4j使用快速入门【转】

    1.引言 在应用程序中添加日志记录总的来说基于三个目的: .监视代码中变量的变化情况,周期性的记录到文件中供其他应用进行统计分析工作 .跟踪代码运行时轨迹,作为日后审计的依据 .担当集成开发环境中的调 ...

  9. 这里整理了基于java平台的常用资源

    这里整理了基于java平台的常用资源 翻译 from :akullpp | awesome-java 大家一起学习,共同进步. 如果大家觉得有用,就mark一下,赞一下,或评论一下,让更多的人知道.t ...

随机推荐

  1. dp-划分数 (递推)

    问题描述 : 有 n 个无区别的物品 , 将他们分成 不超过 m 堆, 问有多少种分法 ? 例如 : n = 4 , m = 3 , 则总共有的分法是 1 + 2 +1 , 0 + 1 + 3 , 0 ...

  2. Linux下搭建Jmeter+Ant+Jenkins自动化测试框架

    前言 在之前的文章中,我们学习了通过Ant调用Jmeter脚本生成HTML测试报告,但未实现自动执行脚本生成报告,同时生成的报告是在Linux下,查看报告很不方便.因此,我们将结合Jenkins来进一 ...

  3. 【数据结构】FHQ Treap详解

    FHQ Treap是什么? FHQ Treap,又名无旋Treap,是一种不需要旋转的平衡树,是范浩强基于Treap发明的.FHQ Treap具有代码短,易理解,速度快的优点.(当然跟红黑树比一下就是 ...

  4. [uoj#34] [洛谷P3803] 多项式乘法(FFT)

    新技能--FFT. 可在 \(O(nlogn)\) 时间内完成多项式在系数表达与点值表达之间的转换. 其中最关键的一点便为单位复数根,有神奇的折半性质. 多项式乘法(即为卷积)的常见形式: \[ C_ ...

  5. .NetCore自定义WebAPI返回Json的格式大小写的三种方式

    .NetCore的Controller/WebAPI可以帮我们将返回结果自动转换为Json格式给前台,而且可以自由设定格式(大写.小写.首字母大写等),我总结了三种方法,对应三种灵活度,供大家参考 ( ...

  6. java8新特性Lambda和Stream

    Java8出来已经4年,但还是有很多人用上了jdk8,但并没用到里面的新东西,那不就等于没用?jdk8有许多的新特性,详细可看下面脑图 我只讲两个最重要的特性Lambda和Stram,配合起来用可以极 ...

  7. Firebase REST API

    use firebase and firesharp to do a Library management system . look github.com/ziyasal/FireSharp

  8. T117897 七步洗手法 / PJT1(洛谷)

    题目:现在有n个人需要依次使用1个洗手池洗手,进行一步洗手需要1单位时间.他们每个人至少会进行一步洗手,但是却不一定进行了完整的七部洗手. 现在你知道了他们总共的洗手时间为t,请你推测他们有多少人进行 ...

  9. Vim学习之路1

    与之前的随笔一样,这个也是记录Vim常用命令以供日后查找所用.对于Vim,简介而又功能强大,学习之后代码书写相当愉快. 1. 保存并退出 :wq 2. 进入标准插入模式退出命令模式 i 3. 退出标准 ...

  10. HGE引擎改进——2014/1/27

    2014/1/27 更新 hge库: 1.增加回调函数procResizeFunc(),这个函数会在窗口大小改变时调用,不是必要函数 2.修复LOG信息显示为乱码的错误 项目主页:https://co ...