基于Logback底層原理實現ELK日志分析系統(tǒng)

看了很多ELK日子分析系統(tǒng)的實現,多數是寫怎么搭建,至于怎么在不影響主項目的情況下汤踏、異步去記錄日志沒有過多說明,為此我以Logback為列舔腾,通過Logback底層原理溪胶,在不影響主代碼的情況下,實現ELK日志分析系統(tǒng)稳诚。
PS:至于安裝ELK哗脖,和ELK的說明的,可以參考一下連接:
https://www.cnblogs.com/kevingrace/p/5919021.html
https://www.cnblogs.com/yuhuLin/p/7018858.html
https://my.oschina.net/itblog/blog/547250

Logback:
Logback是由log4j創(chuàng)始人設計的另一個開源日志組件,官方網站: http://logback.qos.ch扳还。

看看(最)簡單的Logback配置logback.xml:

    <configuration>
    
        <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
            <!-- encoder 默認配置為PatternLayoutEncoder -->
            <encoder>
                <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
            </encoder>
        </appender>
    
        <root level="INFO">
            <appender-ref ref="STDOUT" />
        </root>
    
    </configuration>  

我們配置一個ConsoleAppender就可以在控制臺上打印出日志才避。

實現思路:
Logback(我用的是logback-classic-1.2.3版本)有個DBAppender,功能就是將日志插入數據庫氨距,這樣的話我們可以仿照它桑逝,實現將日志插入到elasticsearch的功能。
我們看看DBAppender所需要加的配置:

<appender name="db-classic-mysql" class="ch.qos.logback.classic.db.DBAppender">
<connectionSource class="ch.qos.logback.core.db.DataSourceConnectionSource">
    <dataSource class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <driverClass>com.mysql.jdbc.Driver</driverClass>
        <jdbcUrl>jdbc:mysql://{$server ip}:3306/{$dbname}</jdbcUrl>
        <user>{$user}</user>
        <password>{$password}</password>
    </dataSource>
</connectionSource>
</appender>

從配置上看就是把參數傳到DBAppender里面俏让,我們ES的Appender只需仿照它的做法就可以楞遏。

目標:找出DBAppender的sql在哪里commit
我們再看看DBAppender:

image.png

其初始化參數:

protected String insertPropertiesSQL;
protected String insertExceptionSQL;
protected String insertSQL;
protected static final Method GET_GENERATED_KEYS_METHOD;
private DBNameResolver dbNameResolver;
static final int TIMESTMP_INDEX = 1;
static final int FORMATTED_MESSAGE_INDEX = 2;
static final int LOGGER_NAME_INDEX = 3;
static final int LEVEL_STRING_INDEX = 4;
static final int THREAD_NAME_INDEX = 5;
static final int REFERENCE_FLAG_INDEX = 6;
static final int ARG0_INDEX = 7;
static final int ARG1_INDEX = 8;
static final int ARG2_INDEX = 9;
static final int ARG3_INDEX = 10;
static final int CALLER_FILENAME_INDEX = 11;
static final int CALLER_CLASS_INDEX = 12;
static final int CALLER_METHOD_INDEX = 13;
static final int CALLER_LINE_INDEX = 14;
static final int EVENT_ID_INDEX = 15;
static final StackTraceElement EMPTY_CALLER_DATA = CallerData.naInstance();

涉及sql的就只有
insertPropertiesSQL茬暇,insertExceptionSQL,insertSQL寡喝。而我們順藤摸瓜而钞,發(fā)現最后他們都做這樣的操作:

super.start();

我們看看他的類繼承關系圖(idea自帶這個插件,eclipse可以安裝類似的插件):

image.png

看看DBAppender繼承的DBAppenderBase:
protected abstract的就有getGeneratedKeysMethod();getInsertSQL();subAppend(E var1, Connection var2, PreparedStatement var3)拘荡;secondarySubAppend(E var1, Connection var2, long var3)臼节;
就說我們仿照DBAppender繼承DBAppenderBase就要實現上面4個方法;

看看DBAppenderBase繼承的UnsynchronizedAppenderBase:
protected abstract的就只有有append(E var1);

再看回DBAppenderBase的append(E eventObject):

connection.commit();

I好蟆M臁!sā7垭!J欢怠6笾佟!3纭M佬住!K磷省4@ⅰ!VT0隆!7咐纭J舴摺!K嵋邸W≈睢!4睾础V豢恰!J钏堋吼句!
!J赂瘛L柩蕖8阋!T短隆A痈佟!Kⅰq尽!倘潜!就是在這里1疗狻!d桃颉7夏馈!Q荨J扰取!@窖凇9号!!
!2琛=柘!S旄健@商印!MΨ荨0病!T炔础S叛怠!8髌浮4Х恰!6阋颉T缇础<缮怠!8慵唷Kⅰ!K雎俊7帧!>0擦啤!

這樣的話我們仿照DBAppenderBase寫一個ESAppender够委,繼承UnsynchronizedAppenderBase荐类,實現append方法,將日志插入ES里茁帽!

下面就是貼代碼玉罐、貼圖片時間:
項目結構圖:


image.png

主類ESAppender:

package com.elk.log.appender;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.IThrowableProxy;
import ch.qos.logback.classic.spi.ThrowableProxyUtil;
import ch.qos.logback.core.UnsynchronizedAppenderBase;
import com.alibaba.fastjson.JSON;
import com.elk.log.utils.InitES;
import com.elk.log.vo.EsLogVo;
import com.elk.log.vo.Location;
import io.searchbox.client.JestClient;
import io.searchbox.client.JestResult;
import io.searchbox.core.Index;


import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.Properties;

public class ESAppender  extends UnsynchronizedAppenderBase<ILoggingEvent> {

    private static JestClient jestClient ;

    //索引名稱
    String esIndex = "java-log-#date#";
    //索引類型
    String esType = "java-log";
    //是否打印行號
    boolean isLocationInfo = true;
    //運行環(huán)境
    String env = "";
    //es地址
    String esAddress = "";

    public String getEsIndex() {
        return esIndex;
    }

    public void setEsIndex(String esIndex) {
        this.esIndex = esIndex;
    }

    public String getEsType() {
        return esType;
    }

    public void setEsType(String esType) {
        this.esType = esType;
    }

    public boolean isLocationInfo() {
        return isLocationInfo;
    }

    public void setLocationInfo(boolean locationInfo) {
        isLocationInfo = locationInfo;
    }

    public String getEnv() {
        return env;
    }

    public void setEnv(String env) {
        this.env = env;
    }

    public String getEsAddress() {
        return esAddress;
    }

    public void setEsAddress(String esAddress) {
        this.esAddress = esAddress;
    }

    @Override
    protected void append(ILoggingEvent event) {
        EsLogVo esLogVo = new EsLogVo();
        esLogVo.setHost("HostName");
        esLogVo.setIp("127.0.0.1");
        esLogVo.setEnv(this.env);
        esLogVo.setLevel(event.getLevel().toString());
        Location location = new Location();
        StackTraceElement[] callerDataArray = event.getCallerData();
        if(callerDataArray != null && callerDataArray.length >0){
            StackTraceElement immediateCallerData = callerDataArray[0];
            location.setClassName(immediateCallerData.getClassName());
            location.setMethod(immediateCallerData.getMethodName());
            location.setFile(immediateCallerData.getFileName());
            location.setLine(Integer.toString(immediateCallerData.getLineNumber()));
        }
        IThrowableProxy tp = event.getThrowableProxy();
        if (tp != null){
            String throwable = ThrowableProxyUtil.asString(tp);
            esLogVo.setThrowable(throwable);
        }
        esLogVo.setLocation(location);
        esLogVo.setLogger(event.getLoggerName());
        esLogVo.setMessage(event.getFormattedMessage());

        SimpleDateFormat df = new SimpleDateFormat("yyyy.MM.dd");
        SimpleDateFormat df2 = new SimpleDateFormat("yyyy-MM-dd");
        esLogVo.setTimestamp(df2.format(new Date(event.getTimeStamp())));
        esLogVo.setThread(event.getThreadName());

        Map<String ,String > mdcPropertyMap = event.getMDCPropertyMap();
        esLogVo.setTraceId(mdcPropertyMap.get("traceId"));
        esLogVo.setRpcId(mdcPropertyMap.get("rpcId"));


        String jsonString = JSON.toJSONString(esLogVo);
        String esIndex_format = esIndex.replace("#date#",df.format(new Date(event.getTimeStamp())));
        Index index = new Index.Builder(esLogVo).index(esIndex_format).type(esType).build();
        try{
            JestResult result = jestClient.execute(index);
            System.out.println(result);
        }catch (Exception e){
            e.printStackTrace();
        }

    }

    @Override
    public void start() {
        super.start();
        Properties properties = new Properties();
        properties.put("es.hosts",esAddress);
        properties.put("es.username","zkpk");
        properties.put("es.password","123456");
        jestClient = InitES.jestClient(properties);
    }
    
    @Override
    public void stop() {
        super.stop();
        jestClient.shutdownClient();
    }
}

我們主要實現append方法,重寫start()潘拨,stop()吊输。
append主要是插入es索引,start和stop主要用來打開和關閉ES的鏈接铁追。

工具類InitES(用于初始化es連接用的):

    package com.elk.log.utils;

import io.searchbox.client.JestClientFactory;
import io.searchbox.client.config.HttpClientConfig;
import io.searchbox.client.JestClient;


import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Properties;
public class InitES {

    private static io.searchbox.client.JestClient JestClient;

    public static JestClient jestClient(Properties properties) {
        JestClientFactory factory = new JestClientFactory();
        String userName = properties.getProperty("es.username");
        String password = properties.getProperty("es.password");
        String esHosts = properties.getProperty("es.hosts");
        List<String> serverList = new ArrayList <String>();
        for (String address:esHosts.split(",")) {
            serverList.add("http://"+address);
        }

        HttpClientConfig.Builder builder = new HttpClientConfig.Builder(serverList);
        builder.maxTotalConnection(20);
        builder.defaultMaxTotalConnectionPerRoute(5);
        builder.defaultCredentials(userName,password);
        builder.multiThreaded(true);

        factory.setHttpClientConfig(builder.build());

        if (JestClient == null) {
            JestClient = factory.getObject();
        }
        return JestClient;
    }
}

這里季蚂,我們用Jest方式連接ES。

兩個Javabean:EsLogVo琅束,Location:

public  class EsLogVo {

    private String host;
    private String ip;
    private String env;
    private String message;
    private String timestamp;
    private String logger;
    private String level;
    private String thread;
    private String throwable;
    private Location location;
    private String traceId;
    private String rpcId;
  ...set and get...
    
}

public class Location {
    private String className;
    private String method;
    private String file;
    private String line;

  ...set and get...
    
}

測試類ESLogTest:

package com.elk.log;

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.TimeUnit;

public class ESLogTest {
    private static final Logger logger = LoggerFactory.getLogger(ESLogTest.class);

    @Test
    public  void testLog() throws InterruptedException{
    logger.info("我是正常信息 test message info");
    logger.error("我一條異常的信息",new Exception("項目報錯了扭屁,加班吧!I鳌A侠摹!"));
    logger.debug("debug消息 debug hello hi");
    logger.warn("警告警告");
    TimeUnit.SECONDS.sleep(10);
    }

}

配置文件logback.xml:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <contextName>logback-api</contextName>
    <property name="logback.system" value="logback-system"/>
    <property name="logback.path" value="../logs/logback-system"/>
    <property name="logback.level" value="DEBUG"/>
    <property name="logback.pattern" value="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p - %m%n"/>
    <property name="logback.env" value="dev"/>
    <property name="logback.isLocation" value="true"/>
    <property name="logback.esAddress" value="192.168.0.128:9200,192.168.0.129:9200,192.168.0.130:9200"/>


    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>[%-5p %d{yy-MM-dd HH:mm:ss} %m] %caller{1}</pattern>
        </encoder>
    </appender>


    <appender name="ES" class="com.elk.log.appender.ESAppender">
        <!--索引名字 date為通配符 艾船,會自動替換為yyyy.MM.dd格式-->
        <esIndex>java-log-#date#</esIndex>
        <!--索引類型-->
        <esType>${logback.system}</esType>
        <!--運行環(huán)境-->
        <env>${logback.env}</env>
        <!--ES地址-->
        <esAddress>${logback.esAddress}</esAddress>
    </appender>

    <appender name="ASYNC_ES" class="ch.qos.logback.classic.AsyncAppender">
        <!--默認情況下葵腹,當BlockingQueue還有20%容量,他將丟棄TRACE,DEBUG和INFO級別的event屿岂,只保留warn和error-->
        <discardingThreshold>0</discardingThreshold>
        <!--BlockingQueue的最大容量践宴,默認情況下,大小為256-->
        <queueSize>256</queueSize>
        <appender-ref ref="ES"/>
        <!--要是保留行號爷怀,需要開啟為true-->
        <includeCallerData>true</includeCallerData>
    </appender>

    <logger name="com.elk.log" additivity="true">
        <level value="${logback.level}"></level>
        <appender-ref ref="ASYNC_ES" />
    </logger>

    <root level="${logback.level}">
        <appender-ref ref="stdout" />
    </root>
</configuration>

這里我們定義自己的ESAppender阻肩,取名未ES,并將所需參數傳到ESAppender霉撵。
然后我們再把它放到AsyncAppender里面進行異步處理磺浙,防止ELK報錯影響到主程序洪囤。

OK,測試:
我們啟動ELK,在Kibana的Dev Tools上創(chuàng)建一個java的動態(tài)日志模板:

    PUT _template/java-log
    {
        "template" : "java-log-*",
            "order" : 0,
            "settings" : {
        "index":{
            "refresh_interval": "5s"
        }
    },
        "mappings": {
        "_default_": {
            "dynamic_templates": [
            {
                "message_field": {
                "match_mapping_type": "string",
                        "path_match": "message",
                        "mapping": {
                    "norms": false,
                            "type": "text",
                            "analyzer": "ik_max_word",
                            "search_analyzer": "ik_max_word"
                }
            }
            },
            {
                "throwable_fields": {
                "match_mapping_type": "string",
                        "path_match": "throwable",
                        "mapping": {
                    "norms": false,
                            "type": "text",
                            "analyzer": "ik_max_word",
                            "search_analyzer": "ik_max_word"
                }
            }
            },
            {
                "string_fields": {
                "match_mapping_type": "string",
                        "match": "*",
                        "mapping": {
                    "norms": false,
                            "type": "text",
                            "analyzer": "ik_max_word",
                            "search_analyzer": "ik_max_word",
                            "fields": {
                        "keyword": {
                            "type": "keyword"
                        }
                    }
                }
            }
            }
  ],
            "_all": {
                "enabled": false
            },
            "properties": {
                "env": {
                    "type": "keyword"
                },
                "host": {
                    "type": "keyword"
                },
                "ip": {
                    "type": "ip"
                },
                "level": {
                    "type": "keyword"
                },
                "location": {
                    "properties": {
                        "line": {
                            "type": "integer"
                        }
                    }
                },
                "timestamp": {
                    "type": "date"
                }
            }
        }
    }
    }

用的是IK分詞器撕氧,refresh時間定為5s用于生產上調高效率瘤缩,定義了幾個參數作為關鍵搜索。

然后將java-log-*作為主要觀察對象伦泥,回到Discover

回到idea剥啤,在ESLogTest上跑testLog:

        [INFO  18-03-07 16:56:49 我是正常信息 test message info] Caller+0  at com.elk.log.ESLogTest.testLog(ESLogTest.java:14)
[ERROR 18-03-07 16:56:49 我一條異常的信息] Caller+0  at com.elk.log.ESLogTest.testLog(ESLogTest.java:15)
        java.lang.Exception: 項目報錯了,加班吧2桓8印!防楷!
        at com.elk.log.ESLogTest.testLog(ESLogTest.java:15)
        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:498)
        at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:59)
        at org.junit.internal.runners.MethodRoadie.runTestMethod(MethodRoadie.java:98)
        at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:79)
        at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:87)
        at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:77)
        at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:42)
        at org.junit.internal.runners.JUnit4ClassRunner.invokeTestMethod(JUnit4ClassRunner.java:88)
        at org.junit.internal.runners.JUnit4ClassRunner.runMethods(JUnit4ClassRunner.java:51)
        at org.junit.internal.runners.JUnit4ClassRunner$1.run(JUnit4ClassRunner.java:44)
        at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:27)
        at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:37)
        at org.junit.internal.runners.JUnit4ClassRunner.run(JUnit4ClassRunner.java:42)
        at org.junit.runner.JUnitCore.run(JUnitCore.java:130)
        at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
        at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
        at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
        at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
[DEBUG 18-03-07 16:56:49 debug消息 debug hello hi] Caller+0    at com.elk.log.ESLogTest.testLog(ESLogTest.java:16)
[WARN  18-03-07 16:56:49 警告警告] Caller+0  at com.elk.log.ESLogTest.testLog(ESLogTest.java:17)
[DEBUG 18-03-07 16:56:50 Request and operation succeeded] Caller+0   at io.searchbox.action.AbstractAction.createNewElasticSearchResult(AbstractAction.java:75)
        io.searchbox.core.DocumentResult@4421d75d
                [DEBUG 18-03-07 16:56:51 Request and operation succeeded] Caller+0   at io.searchbox.action.AbstractAction.createNewElasticSearchResult(AbstractAction.java:75)
        io.searchbox.core.DocumentResult@25a5426c
                [DEBUG 18-03-07 16:56:51 Request and operation succeeded] Caller+0   at io.searchbox.action.AbstractAction.createNewElasticSearchResult(AbstractAction.java:75)
        io.searchbox.core.DocumentResult@5fd33fbc
                [DEBUG 18-03-07 16:56:51 Request and operation succeeded] Caller+0   at io.searchbox.action.AbstractAction.createNewElasticSearchResult(AbstractAction.java:75)
        io.searchbox.core.DocumentResult@25a52369

可以說都是我們想要的信息牺丙。

我們再回到Kibana:

image.png

可以看到,測試上的4條日志信息已經可以插到ES上复局。

然后再看看錯誤的那條日志信息:

image.png

報錯的類冲簿,方法,行數亿昏,報錯內容峦剔。。角钩。都一一列出來吝沫。

任務完成,到此递礼,我們可以在不影響其他代碼的情況下惨险,將日志插入到ES里,在以后的編程中可以快速定位問題宰衙。


擴展:
我們看看除了DBAppender實現Appender這條線外平道,還有哪些類走這條線:


image.png

我們看到實現Appender接口的就有兩個類:AppenderBase和UnsynchronizedAppenderBase。
從字面上的意思就是一個同步的供炼,一個異步的,我們用對比工具對比一下窘疮,發(fā)現這兩個類幾乎沒什么區(qū)別袋哼,最大不同就是AppenderBase的doAppend方法多了synchronized的鎖。
像插到數據庫日志的DBAppender闸衫,控制臺打印日志的ConsoleAppender涛贯。。蔚出。就會走異步的UnsynchronizedAppenderBase這條線弟翘。
像SMTPAppender虫腋,JMSAppenderBase,SocketAppenderBase稀余。悦冀。。就會走AppenderBase這條線睛琳。
所以像DBAppender和DBAppenderBase盒蟆,這兩個類可以合成一個整體,直接繼承UnsynchronizedAppenderBase师骗,走異步這條線历等。

-----------------------謝謝觀看,希望本文對你有幫助----------------------------

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末辟癌,一起剝皮案震驚了整個濱河市寒屯,隨后出現的幾起案子,更是在濱河造成了極大的恐慌黍少,老刑警劉巖浩螺,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異仍侥,居然都是意外死亡要出,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進店門农渊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來患蹂,“玉大人,你說我怎么就攤上這事砸紊〈冢” “怎么了?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵醉顽,是天一觀的道長沼溜。 經常有香客問我,道長游添,這世上最難降的妖魔是什么系草? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮唆涝,結果婚禮上找都,老公的妹妹穿的比我還像新娘。我一直安慰自己廊酣,他們只是感情好能耻,可當我...
    茶點故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般晓猛。 火紅的嫁衣襯著肌膚如雪饿幅。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天戒职,我揣著相機與錄音栗恩,去河邊找鬼。 笑死帕涌,一個胖子當著我的面吹牛摄凡,可吹牛的內容都是我干的。 我是一名探鬼主播蚓曼,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼亲澡,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了纫版?” 一聲冷哼從身側響起床绪,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎其弊,沒想到半個月后癞己,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡梭伐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年痹雅,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片糊识。...
    茶點故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡绩社,死狀恐怖,靈堂內的尸體忽然破棺而出赂苗,到底是詐尸還是另有隱情愉耙,我是刑警寧澤,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布拌滋,位于F島的核電站朴沿,受9級特大地震影響,放射性物質發(fā)生泄漏败砂。R本人自食惡果不足惜赌渣,卻給世界環(huán)境...
    茶點故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望吠卷。 院中可真熱鬧锡垄,春花似錦、人聲如沸祭隔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽疾渴。三九已至千贯,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間搞坝,已是汗流浹背搔谴。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留桩撮,地道東北人敦第。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像店量,于是被迫代替她去往敵國和親芜果。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,452評論 2 348

推薦閱讀更多精彩內容

  • 在應用程序中添加日志記錄總的來說基于三個目的:監(jiān)視代碼中變量的變化情況融师,周期性的記錄到文件中供其他應用進行統(tǒng)計分析...
    時待吾閱讀 4,982評論 1 13
  • 1右钾、ELK平臺介紹 在搜索ELK資料的時候,發(fā)現這篇文章比較好旱爆,于是摘抄一小段:以下內容來自:http://bai...
    螺旋上升的世界閱讀 3,003評論 0 20
  • logback詳解 說明:下面內容從網上搜集整理而來 與log4j對比 更快的執(zhí)行速度: 基于我們先前在log4j...
    億萬年星空閱讀 3,056評論 1 1
  • 前言 今天來介紹下Spring Boot如何配置日志logback,我剛學習的時候舀射,是帶著下面幾個問題來查資料的 ...
    OzanShareing閱讀 1,038評論 0 2
  • 前言 今天來介紹下Spring Boot如何配置日志logback,我剛學習的時候,是帶著下面幾個問題來查資料的怀伦,...
    嘟爺MD閱讀 3,098評論 1 26