关于 log4j 升级到 log4j2 的小结

关于升级 jar 包等前提要求

  • 删掉原先的 log4j-1.XX 等 jar 包

  • 从 1.XX 升级到 2.XX 平稳升级需要的 jar 包,其中包括用 sl4j-1.7.25 (1.7.21 暂时也不需要升级) 的版本包,具体可以看自己的工程配置是否需要

  • slf4j-log4j-升级到 2.8.2.zip

  • 新建 log4j2.xml 配置文件

对于 tomcat 项目的版本要求:(以 7.0.43 为分界线),目前 jdk1.8 可以在 tomcat 7.0.32 ,7.0.94 正常启动代码

image.png
image.png

三种配置方式:

1、关于 log4j2.xml 的默认存放位置(WEB 项目可以使用)

按照官方推荐默认放到 resource 目录下面,名称必须叫 log4j2.xml ,其余文件名会读取失败,无需配置其它文件,如下图:


image.png
image.png

image.png
image.png

2、关于使用自定义路径配置文件(推荐 jar 包读取外部配置文件,比如 Eserver ItmsService 等模块)

2.1 在代码的 web.xml 文件里,增加如下配置,其中 param-value 为配置文件的绝对路径,包含 file:// 开头文件

<!-- 配置log4j2 -->
    <listener>
        <listener-class>org.apache.logging.log4j.web.Log4jServletContextListener</listener-class>
    </listener>
    <context-param>
        <description>日志配置文件的路径</description>
        <param-name>log4jConfiguration</param-name>
        <param-value>file:///export/home/rms/EServer4WS/WEB-INF/log4j2.xml</param-value>
    </context-param>

2.2 修改 web.xml 文件的开头关于 servlet 的版本,因为 tomcat 7.0 之后,lib 包里面的 servlet-api.jar 包的版本是 3.0,所以必须修改 web.xml 的版本声明,否则会无法初始化 log4j2。

(将图一三个标红的地方应该改为 3.0),结果如图二
图一:


image.png
image.png

图二:


image.png
image.png
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    id="WebApp_ID" version="3.0">

3、自定义路径并且实现相关 jar 包的预留接口(不需要改动代码和配置,目前只需要替换相关的 jar 包,把新建的 log4j2.xml 改名为 log4j.xml ,并且放在原先同级目录即可)

针对目前代码现状,为了不改动配置和代码,实现了 log4j-1.2-api-2.8.2.jar 预留的相关接口,具体哪个接口需要查看原本工程初始化 log4j 的类用的是哪个接口,比如 org.apache.log4j.xml.DOMConfigurator 类里面的相关接口,如下所示:

private static Logger logger = LoggerFactory.getLogger(DOMConfigurator.class);
// 如下相关通用实现log4j2初始化代码
public static void configureAndWatch(String configFilename)
    {
        LoggerContext logContext = (LoggerContext) LogManager.getContext(false);
        File conFile = new File(configFilename);
        logContext.setConfigLocation(conFile.toURI());
        logContext.reconfigure();
        logger.debug("init log4j2 ok");
    }

该方式只需要按照第一步删除旧的 jar 包,上传新的 jar 包,特别是 log4j-1.2-api-2.8.2.jar 包,即可无缝完成升级

关于 log4j2.xml 配置文件中的注意事项

1、关于 log4j2 日志等级组合过滤器的问题

<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<Filters>
      <ThresholdFilter level="warn" onMatch="DENY" onMismatch="NEUTRAL"/>
      <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
</Filters>
表示只输出 info <= level < warn 的 level 日志,也就是只打印 info 级别

2、关于 DefaultRolloverStrategy 默认删除以及个数的问题

注意%d{MM-dd-yyyy}要用年月日格式,不能加上时分秒,并且最后要有%i,这样log4j2才能判断出哪天一共产生几个文件
示例:
<RollingFile name="RollingFile" fileName="logs/app.log"
filePattern="logs/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz">

3、完整的 log4j2.xml 配置

<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出-->
<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数-->
<configuration status="WARN" monitorInterval="30">
    <!--先定义所有的appender-->
    <appenders>
        <!--这个输出控制台的配置-->
        <console name="con" target="SYSTEM_OUT">
            <!--输出日志的格式-->
            <Filters>
                <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
            <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
        </console>
        <!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,这个也挺有用的,适合临时测试用-->
        <!--<File name="log" fileName="log/test.log" append="false">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
        </File>-->
        <!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
        <RollingFile name="sql" fileName="/export/home/rms/WEB/logs/sql/sql"
                     filePattern="/export/home/rms/WEB/logs/sql/sql.%d{yyyy-MM-dd}-%i">
            <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <Filters>
                <ThresholdFilter level="warn" onMatch="DENY" onMismatch="NEUTRAL"/>
                <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
            <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
            <Policies>
                <TimeBasedTriggeringPolicy/>
                <SizeBasedTriggeringPolicy size="100 MB"/>
            </Policies>
        </RollingFile>
        <RollingFile name="log" fileName="/export/home/rms/WEB/logs/log/log"
                     filePattern="/export/home/rms/WEB/logs/log/log.%d{yyyy-MM-dd}-%i">
            <Filters>
                <ThresholdFilter level="error" onMatch="DENY" onMismatch="NEUTRAL"/>
                <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
            <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
            <Policies>
                <TimeBasedTriggeringPolicy/>
                <SizeBasedTriggeringPolicy size="100 MB"/>
            </Policies>
            <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件,这里设置了20 -->
            <DefaultRolloverStrategy max="20"/>
        </RollingFile>
        <RollingFile name="err" fileName="/export/home/rms/WEB/logs/err/err"
                     filePattern="/export/home/rms/WEB/logs/err/err.%d{yyyy-MM-dd}-%i">
            <Filters>
                <ThresholdFilter level="FATAL" onMatch="DENY" onMismatch="NEUTRAL"/>
                <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
            <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
            <Policies>
                <TimeBasedTriggeringPolicy/>
                <SizeBasedTriggeringPolicy size="100 MB"/>
            </Policies>
        </RollingFile>
    </appenders>
    <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效-->
    <loggers>
        <!--过滤掉spring和mybatis的一些无用的DEBUG信息-->
        <logger name="org.springframework" level="error"></logger>
        <logger name="org.mybatis" level="error"></logger>
        <logger name="org.apache.struts2" level="error"></logger>
        <logger name="org.apache.zookeeper" level="error"></logger>
        <logger name="com.opensymphony.xwork2" level="error"></logger>
        <root level="info">
            <appender-ref ref="con"/>
            <appender-ref ref="sql"/>
            <appender-ref ref="log"/>
            <appender-ref ref="err"/>
        </root>
    </loggers>
</configuration>

衍生多线程配置问题

不同的线程输出日志到不同的文件中

不同的线程输出日志到不同的文件中有关 Log4j2 的内容很多,在此不一一列出,这里只介绍一种常用方法。如果在开发中遇到任何问题,推荐去官方文档中寻找解决方案。

实现 StrLookup

修改 log4j2.xml 配置文件如下,主要是添加 Routes 标签:


image.png
image.png

实现 StrLookup 中的 lookup 方法,代码如下:

import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.lookup.StrLookup;

(name = "thread", category = StrLookup.CATEGORY)
public class ThreadLookup implements StrLookup {


    public String lookup(String s) {
        return Thread.currentThread().getName();
    }


    public String lookup(LogEvent logEvent, String s) {
        return logEvent.getThreadName() == null ? Thread.currentThread().getName()
                : logEvent.getThreadName();
    }
}

测试方法如下:

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class TestLog2 {

    private static final Logger logger = LogManager.getLogger(TestLog2.class);

    public static void main(String[] args) {
        new Thread(() -> {
            logger.info("info");
            logger.debug("debug");
            logger.error("error");
        }).start();
        new Thread(() -> {
            logger.info("info");
            logger.debug("debug");
            logger.error("error");
        }).start();
    }

}

不同线程不同级别的日志输出到不同的文件中

要实现该功能,还要从 RoutingAppender 身上做文章。RoutingAppender 主要用来评估 LogEvents,然后将它们路由到下级 Appender。目标 Appender 可以是先前配置的并且可以由其名称引用的 Appender,或者可以根据需要动态地创建 Appender。RoutingAppender 应该在其引用的任何 Appenders 之后配置,以确保它可以正确关闭。
RoutingAppender 中的 name 属性用来指定该 Appender 的名字,它可以包含多个 Routes 子节点,用来标识选择 Appender 的条件,而 Routes 只有一个属性 “pattern”,该 pattern 用来评估所有注册的 Lookups,并且其结果用于选择路由。在 Routes 下可以有多个 Route,每个 Route 都必须配置一个 key,如果这个 key 匹配 “pattern” 的评估结果,那么这个 Route 就被选中。同时每个 Route 都必须引用一个 Appender,如果这个 Route 包含一个 ref 属性,那么这个 Route 将引用一个在配置中定义的 Appender,如果这个 Route 包含一个 Appender 的定义,那么这个 Appender 将会根据 RoutingAppender 的上下文创建并被重用。
废话说多了,直接上配置才简洁明了。log4j2.xml 配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<!-- status 的含义为是否记录 log4j2 本身的 event 信息,默认是 OFF -->
<Configuration status="OFF">
    <Properties>
        <!-- 自定义一些常量,之后使用${变量名}引用 -->
        <Property name="logFilePath">logs</Property>
        <Property name="logFileName">testLog</Property>
    </Properties>
    <Appenders>
        <!-- 很直白,Console 指定了结果输出到控制台 -->
        <Console name="ConsolePrint" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{yyyy.MM.dd HH:mm:ss z} %t %-5level %class{36} %L %M - %msg%xEx%n"/>
        </Console>
        <!-- <File>输出结果到指定文件</File> -->
        <!-- <RollingFile>同样输出结果到指定文件,但是使用 buffer,速度会快点</RollingFile> -->
        <!-- filePattern:表示当日志到达指定的大小或者时间,产生新日志时,旧日志的命名路径 -->
        <!-- PatternLayout:和 log4j 一样,指定输出日志的格式,append 表示是否追加内容,值默认为 true -->
        <Routing name="RollingFileDebug_${thread:threadName}">
            <Routes pattern="$${thread:threadName}">
                <Route>
                    <RollingFile name="RollingFileDebug_${thread:threadName}"
                                 fileName="${logFilePath}/${logFileName}_${thread:threadName}_debug.log"
                                 filePattern="${logFilePath}/$${date:yyyy-MM}/${logFileName}-%d{yyyy-MM-dd}-${thread:threadName}-debug_%i.log.gz">
                        <PatternLayout pattern="%d{yyyy.MM.dd HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/>
                        <!-- 注意,如果有多个 ThresholdFilter,那么 Filters 标签是必须的 -->
                        <Filters>
                            <!-- 首先需要过滤不符合的日志级别,把不需要的首先 DENY 掉,然后在 ACCEPT 需要的日志级别,次序不能颠倒 -->
                            <!-- INFO 及以上级别拒绝输出 -->
                            <ThresholdFilter level="INFO" onMatch="DENY" onMismatch="NEUTRAL"/>
                            <!-- 只输出 DEBUG 级别信息 -->
                            <ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/>
                        </Filters>
                        <Policies>
                            <!-- 时间策略,每隔 24h 产生新的日志文件 -->
                            <TimeBasedTriggeringPolicy/>
                            <!-- 大小策略,每到 30MB 时产生新的日志文件 -->
                            <SizeBasedTriggeringPolicy size="30MB"/>
                        </Policies>
                    </RollingFile>
                </Route>
            </Routes>
        </Routing>

        <Routing name="RollingFileInfo_${thread:threadName}">
            <Routes pattern="$${thread:threadName}">
                <Route>
                    <RollingFile name="RollingFileInfo_${thread:threadName}"
                                 fileName="${logFilePath}/${logFileName}_${thread:threadName}_info.log"
                                 filePattern="${logFilePath}/$${date:yyyy-MM}/${logFileName}-%d{yyyy-MM-dd}-${thread:threadName}-info_%i.log.gz">
                        <Filters>
                            <!-- onMatch:Action to take when the filter matches. The default value is NEUTRAL -->
                            <!-- onMismatch:    Action to take when the filter does not match. The default value is DENY -->
                            <!-- 级别在 ERROR 之上的都拒绝输出 -->
                            <!-- 在组合过滤器中,接受使用 NEUTRAL(中立),被第一个过滤器接受的日志信息,会继续用后面的过滤器进行过滤,只有符合所有过滤器条件的日志信息,才会被最终写入日志文件 -->
                            <ThresholdFilter level="ERROR" onMatch="DENY" onMismatch="NEUTRAL"/>
                            <ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
                        </Filters>
                        <PatternLayout pattern="%d{yyyy.MM.dd HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/>
                        <Policies>
                            <TimeBasedTriggeringPolicy/>
                            <SizeBasedTriggeringPolicy size="30MB"/>
                        </Policies>
                    </RollingFile>
                </Route>
            </Routes>
        </Routing>

        <Routing name="RollingFileError_${thread:threadName}">
            <Routes pattern="$${thread:threadName}">
                <Route>
                    <RollingFile name="RollingFileError_${thread:threadName}"
                                 fileName="${logFilePath}/${logFileName}_${thread:threadName}_error.log"
                                 filePattern="${logFilePath}/$${date:yyyy-MM}/${logFileName}-%d{yyyy-MM-dd}-${thread:threadName}-error_%i.log.gz">
                        <Filters>
                            <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
                        </Filters>
                        <PatternLayout pattern="%d{yyyy.MM.dd HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/>
                        <Policies>
                            <TimeBasedTriggeringPolicy/>
                            <SizeBasedTriggeringPolicy size="30MB"/>
                        </Policies>
                    </RollingFile>
                </Route>
            </Routes>
        </Routing>
        <!-- bufferSize 整数,指定可以排队的 events 最大数量,如果使用 BlockingQueue,这个数字必须是 2 的幂次-->
        <!-- includeLocation 默认值是 FALSE,如果指定为 TRUE,会降低性能,但是推荐设置为 TRUE,否则不打印位置行信息-->
        <Async name="async" bufferSize="262144" includeLocation="true">
            <AppenderRef ref="RollingFileDebug_${thread:threadName}"/>
            <AppenderRef ref="RollingFileInfo_${thread:threadName}"/>
            <AppenderRef ref="RollingFileError_${thread:threadName}"/>
            <!-- 只要是级别比 ERROR 高的,包括 ERROR 就输出到控制台 -->
            <AppenderRef ref="ConsolePrint" level="ERROR"/>
        </Async>
    </Appenders>
    <Loggers>
        <!-- logger 用于定义 log 的 level 以及所采用的 appender,如果无需自定义,可以使用 root 解决,root 标签是 log 的默认输出形式 -->
        <!-- 级别顺序(低到高):TRACE < DEBUG < INFO < WARN < ERROR < FATAL -->
        <Root level="DEBUG" includeLocation="true">
            <!-- appender-ref 中的值必须是在前面定义的 appender -->
            <AppenderRef ref="async"/>
        </Root>
    </Loggers>
</Configuration>

Asynchronous 全局配置

根据官方的性能测试我们知道,Loggers all async 的性能最高,但是我们在上边使用的是 Sync 模式(方法一,因为 Appender 默认是 synchronous 的)或 Async Appender 模式(方法二),那么如何更进一步让所有的 Loggers 都是 Asynchronous 的,让我们的配置更完美呢?想要使用 Loggers all async 只需要做两步操作。
因为 Loggers all async 是基于 LMAX Disruptor 实现的,所以我们首先需要添加这个依赖

<dependency>
    <groupId>com.lmax</groupId>
    <artifactId>disruptor</artifactId>
    <version>3.4.2</version>
</dependency>

其次是设置系统属性

log4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector

可以在前面提到的 ThreadLookup 类中,添加静态代码块

static {
    System.setProperty("log4j2.contextSelector", "org.apache.logging.log4j.core.async.AsyncLoggerContextSelector");
}

src/main/resources 目录下添加 log4j2.component.properties 配置文件,其内容为

log4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector

混合使用 Synchronous 和 Asynchronous Loggers

依旧需要依赖 com.lmax:disruptor,但不需要设置系统属性 log4j2.contextSelector,在配置中可以混合使用同步和异步的 loggers,使用 <AsyncRoot> 或者 <AsyncLogger> 去指定需要异步的 loggers,<AsyncLogger> 元素还可以包含 <Root><Logger> 用于同步的 loggers。
一个混合了同步和异步的 Loggers 配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<!-- No need to set system property "Log4jContextSelector" to any value
     when using <asyncLogger> or <asyncRoot>. -->
<Configuration status="WARN">
  <Appenders>
    <!-- Async Loggers will auto-flush in batches, so switch off immediateFlush. -->
    <RandomAccessFile name="RandomAccessFile" fileName="asyncWithLocation.log"
              immediateFlush="false" append="false">
      <PatternLayout>
        <Pattern>%d %p %class{1.} [%t] %location %m %ex%n</Pattern>
      </PatternLayout>
    </RandomAccessFile>
  </Appenders>
  <Loggers>
    <!-- pattern layout actually uses location, so we need to include it -->
    <AsyncLogger name="com.foo.Bar" level="trace" includeLocation="true">
      <AppenderRef ref="RandomAccessFile"/>
    </AsyncLogger>
    <Root level="info" includeLocation="true">
      <AppenderRef ref="RandomAccessFile"/>
    </Root>
  </Loggers>
</Configuration>

在上面示例的配置中,root logger 就是同步的,但是 com.foo.Bar 的 logger 就是异步的。

使用 Log4j 日志的注意事项

在使用异步日志的时候需要注意一些事项,如下:

  1. 不要同时使用 AsyncAppender 和 AsyncLogger,也就是在配置中不要在配置 Appender 的时候,使用 Async 标识的同时,又配置 AsyncLogger,这不会报错,但是对于性能提升没有任何好处。
  2. 不要在开启了全局同步的情况下,仍然使用 AsyncAppender 和 AsyncLogger。这和上一条是同一个意思,也就是说,如果使用异步日志,AsyncAppender、AsyncLogger 和全局日志,不要同时出现。
  3. 如果不是十分必须,不管是同步异步,都设置 immediateFlush 为 false,这会对性能提升有很大帮助。
    4、如果不是确实需要,不要打印 location 信息,比如 HTML 的 location,或者 pattern 模式里的%C or $class, %F or %file, %l or %location, %L or %line, %M or %method, 等,因为 Log4j 需要在打印日志的时候做一次栈的快照才能获取这些信息,这对于性能来说是个极大的损耗。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343