Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 男女爽爽大片,久久精品日本免费线,91在线免费公开视频

          整合營(yíng)銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          Java日志通關(guān) - 前世今生

          Java日志通關(guān) - 前世今生


          作者日常在與其他同學(xué)合作時(shí),經(jīng)常發(fā)現(xiàn)不合理的日志配置以及五花八門的日志記錄方式,后續(xù)作者打算在團(tuán)隊(duì)內(nèi)做一次Java日志的分享,本文是整理出的系列文章第一篇。

          寫這篇文章的初衷,是想在團(tuán)隊(duì)內(nèi)做一次Java日志的分享,因?yàn)槿粘T谂c其他同學(xué)合作時(shí),經(jīng)常發(fā)現(xiàn)不合理的日志配置以及五花八門的日志記錄方式。但在準(zhǔn)備分享、補(bǔ)充細(xì)節(jié)的過程中,我又進(jìn)一步發(fā)現(xiàn)目前日志相關(guān)的文章,都只是專注于某一個(gè)方面,或者講歷史和原理,或者解決包沖突,卻都沒有把整個(gè)Java日志知識(shí)串聯(lián)起來。最終這篇文章超越了之前的定位,越寫越豐富,為了讓大家看得不累,我的文章將以系列的形式展示。


          一、前言


          日志發(fā)展到今天,被抽象成了三層:接口層、實(shí)現(xiàn)層、適配層:

          • 接口層:或者叫日志門面(facade),就是interface,只定義接口,等著別人實(shí)現(xiàn)。
          • 實(shí)現(xiàn)層:真正干活的、能夠把日志內(nèi)容記錄下來的工具。但請(qǐng)注意它不是上邊接口實(shí)現(xiàn),因?yàn)樗桓兄膊恢苯訉?shí)現(xiàn)接口,僅僅是獨(dú)立的實(shí)現(xiàn)。
          • 適配層:一般稱為Adapter,它才是上邊接口的implements。因?yàn)榻涌趯雍瓦m配層并非都出自一家之手,它們之間無法直接匹配。而魯迅曾經(jīng)說過:「計(jì)算機(jī)科學(xué)領(lǐng)域的任何問題都可以通過增加一個(gè)中間層來解決」(All problems in computer science can be solved by another level of indirection. -- David Wheeler[1]),所以就有了適配層。

          適配層又可以分為綁定(Binding)和橋接(Bridging)兩種能力:

          • 綁定(Binding):將接口層綁定到某個(gè)實(shí)現(xiàn)層(實(shí)現(xiàn)一個(gè)接口層,并調(diào)用實(shí)現(xiàn)層的方法)
          • 橋接(Bridging):將接口層橋接到另一個(gè)接口層(實(shí)現(xiàn)一個(gè)接口層,并調(diào)用另一個(gè)接口層的接口),主要作用是方便用戶低成本的在各接口層和適配層之間遷移

          如果你覺得上面的描述比較抽象生硬,可以先跳過,等把本篇看完自然就明白了。
          接下來我們就以時(shí)間順序,回顧一下Java日志的發(fā)展史,這有助于指導(dǎo)我們后續(xù)的實(shí)踐,真正做到知其所以然。

          二、歷史演進(jìn)



          2.1 標(biāo)準(zhǔn)輸出 (<1999)

          Java最開始并沒有專門記錄日志的工具,大家都是用System.outSystem.err輸出日志。但它們只是簡(jiǎn)單的信息輸出,無法區(qū)分錯(cuò)誤級(jí)別、無法控制輸出粒度,也沒有什么管理、過濾能力。隨著Java工程化的深入,它們的能力就有些捉襟見肘了。

          雖然System.outSystem.err默認(rèn)輸出到控制臺(tái),但它們是有能力將輸出保存到文件的:

          System.setOut(new PrintStream(new FileOutputStream("log.txt", true)));
          System.out.println("這句將輸出到 log.txt 文件中");
          
          
          System.setErr(new PrintStream(new FileOutputStream("error.txt", true)));
          System.err.println("這句將輸出到 error.txt 文件中");


          2.2 Log4j (1999)


          在1996年,一家名為SEMPER的歐洲公司決定開發(fā)一款用于記錄日志的工具。經(jīng)過多次迭代,最終發(fā)展成為L(zhǎng)og4j。這款工具的主要作者是一位名叫Ceki Gülcü[2]的俄羅斯程序員,請(qǐng)記住他的名字:Ceki,后面還會(huì)多次提到他。

          到了1999年,Log4j已經(jīng)被廣泛使用,隨著用戶規(guī)模的增長(zhǎng),用戶訴求也開始多樣化。于是Ceki在2001年選擇將Log4j開源,希望借助社區(qū)的力量將Log4j發(fā)展壯大。不久之后Apache基金會(huì)向Log4j拋出了橄欖枝,自然Ceki也加入Apache繼續(xù)從事 Log4j的開發(fā),從此Log4j改名Apache Log4j[3]并進(jìn)入發(fā)展的快車道。

          Log4j相比于System.out提供了更強(qiáng)大的能力,甚至很多思想到現(xiàn)在仍被廣泛接受,比如:

          • 日志可以輸出到控制臺(tái)、文件、數(shù)據(jù)庫(kù),甚至遠(yuǎn)程服務(wù)器和電子郵件(被稱做 Appender);
          • 日志輸出格式(被稱做 Layout)允許定制,比如錯(cuò)誤日志和普通日志使用不同的展現(xiàn)形式;
          • 日志被分為5個(gè)級(jí)別(被稱作Level),從低到高依次是debug, info, warn, error, fatal,輸出前會(huì)校驗(yàn)配置的允許級(jí)別,小于此級(jí)別的日志將被忽略。除此之外還有all, off兩個(gè)特殊級(jí)別,表示完全放開和完全關(guān)閉日志輸出;
          • 可以在工程中隨時(shí)指定不同的記錄器(被稱做Logger),可以為之配置獨(dú)立的記錄位置、日志級(jí)別;
          • 支持通過properties或者xml文件進(jìn)行配置;

          隨著Log4j的成功,Apache又孵化了Log4Net[4]、Log4cxx[5]、Log4php[6]產(chǎn)品,開源社區(qū)也模仿推出了如Log4c[7]、Log4cpp[8]、Log4perl[9]等眾多項(xiàng)目。從中也可以印證Log4j在日志處理領(lǐng)域的江湖影響力。

          不過Log4j有比較明顯的性能短板,在Logback和Log4j 2推出后逐漸式微,最終Apache在2015年宣布終止開發(fā)Log4j并全面遷移至Log4j 2[10](可參考【2.7 Log4j 2 (2012)】)。


          2.3 JUL (2002.2)


          隨著Java工程的發(fā)展,Sun也意識(shí)到日志記錄非常重要,認(rèn)為這個(gè)能力應(yīng)該由JRE原生支持。所以在1999年Sun提交了JSR 047[11]提案,標(biāo)題就叫「Logging API Specification」。不過直到2年后的2002年,Java官方的日志系統(tǒng)才隨Java 1.4發(fā)布。這套系統(tǒng)稱做Java Logging API,包路徑是java.util.logging,簡(jiǎn)稱JUL。

          在某些追溯歷史的文章中提到,「Apache曾希望將 Log4j加入到JRE中作為默認(rèn)日志實(shí)現(xiàn),但傲慢的Sun沒有答應(yīng),反而很快推出了自己的日志系統(tǒng)」。對(duì)于這個(gè)說法我并沒有找到出處,無法確認(rèn)其真實(shí)性。

          不過從實(shí)際推出的產(chǎn)品來看,更晚面世的JUL無論是功能還是性能都落后于Log4j,頗有因被寄予厚望而倉(cāng)促發(fā)布的味道,也許那個(gè)八卦并非空穴來風(fēng),哈哈。雖然在2004年推出的Java 5.0 (1.5) [12]上JUL進(jìn)步不小,但它在Log4j面前仍無太多亮點(diǎn),廣大開發(fā)者并沒有遷移的動(dòng)力,導(dǎo)致JUL始終未成氣候。

          我們?cè)诤笪臎]有推薦JUL的計(jì)劃,所以這里也不多介紹了(主要是我也不會(huì))。


          2.4 JCL (2002.8)


          在Log4j和JUL之外,當(dāng)時(shí)市面上還有像Apache Avalon[13](一套服務(wù)端開發(fā)框架)、 Lumberjack[14](一套跑在JDK 1.2/1.3上的開源日志工具)等日志工具。
          對(duì)于獨(dú)立且輕量的項(xiàng)目來說,開發(fā)者可以根據(jù)喜好使用某個(gè)日志方案即可。但更多情況是一套業(yè)務(wù)系統(tǒng)依賴了大量的三方工具,而眾多三方工具會(huì)各自使用不同的日志實(shí)現(xiàn),當(dāng)它們被集成在一起時(shí),必然導(dǎo)致日志記錄混亂。

          為此Apache在2002年推出了一套接口Jakarta Commons Logging[15],簡(jiǎn)稱 JCL,它的主要作者仍然是Ceki。這套接口主動(dòng)支持了Log4j、JUL、Apache Avalon、Lumberjack等眾多日志工具。開發(fā)者如果想打印日志,只需調(diào)用JCL的接口即可,至于最終使用的日志實(shí)現(xiàn)則由最上層的業(yè)務(wù)系統(tǒng)決定。我們可以看到,這其實(shí)就是典型的接口與實(shí)現(xiàn)分離設(shè)計(jì)。

          但因?yàn)槭窍扔械膶?shí)現(xiàn)(Log4j、JUL)后有的接口(JCL),所以JCL配套提供了接口與實(shí)現(xiàn)的適配層(沒有使用它的最新版,原因會(huì)在【1.2.7 Log4j2 (2012)】提到):

          簡(jiǎn)單介紹一下JCL自帶的幾個(gè)適配層/實(shí)現(xiàn)層:

          • AvalonLogger/LogKitLogger:用于綁定Apache Avalon的適配層,因?yàn)锳valon 不同時(shí)期的日志包名不同,適配層也對(duì)應(yīng)有兩個(gè)
          • Jdk13LumberjackLogger:用于綁定Lumberjack的適配層
          • Jdk14Logger:用于綁定JUL(因?yàn)镴UL從JDK 1.4開始提供)的適配層
          • Log4JLogger:用于綁定Log4j的適配層
          • NoOpLog:JCL自帶的日志實(shí)現(xiàn),但它是空實(shí)現(xiàn),不做任何事情
          • SimpleLog:JCL自帶的日志實(shí)現(xiàn) ,讓用戶哪怕不依賴其他工具也能打印出日志來,只是功能非常簡(jiǎn)單

          當(dāng)時(shí)項(xiàng)目前綴取名Jakarta,是因?yàn)樗鼘儆贏pache與Sun共同推出的Jakarta Project[16]項(xiàng)目(郵件[17])?,F(xiàn)在JCL作為Apache Commons[18]的子項(xiàng)目,叫 Apache Commons Logging,與我們常用的Commons Lang[19]、Commons Collections [20]等是師兄弟。但JCL的簡(jiǎn)寫命名被保留了下來,并沒有改為ACL。


          2.5 Slf4j (2005)


          Log4j的作者Ceki看到了很多Log4j和JCL的不足,但又無力推動(dòng)項(xiàng)目快速迭代,加上對(duì)Apache的管理不滿,認(rèn)為自己失去了對(duì)Log4j項(xiàng)目的控制權(quán)(博客[21]、郵件[22]),于是在2005年選擇自立門戶,并很快推出了一款新作品Simple Logging Facade for Java[23],簡(jiǎn)稱Slf4j。

          Slf4j也是一個(gè)接口層,接口設(shè)計(jì)與JCL非常接近(畢竟有師承關(guān)系)。相比JCL有一個(gè)重要的區(qū)別是日志實(shí)現(xiàn)層的綁定方式:JCL是動(dòng)態(tài)綁定,即在運(yùn)行時(shí)執(zhí)行日志記錄時(shí)判定合適的日志實(shí)現(xiàn);而Slf4j選擇的是靜態(tài)綁定,應(yīng)用編譯時(shí)已經(jīng)確定日志實(shí)現(xiàn),性能自然更好。這就是常被提到的classloader問題,更詳細(xì)地討論可以參考What is the issue with the runtime discovery algorithm of Apache Commons Logging[24]以及Ceki自己寫的文章Taxonomy of class loader problems encountered when using Jakarta Commons Logging[25]。

          在推出Slf4j的時(shí)候,市面上已經(jīng)有了另一套接口層JCL,為了將選擇權(quán)交給用戶(我猜也為了挖JCL的墻角),Slf4j推出了兩個(gè)橋接層:

          • jcl-over-slf4j:作用是讓已經(jīng)在使用JCL的用戶方便的遷移到Slf4j 上來,你以為調(diào)的是JCL接口,背后卻又轉(zhuǎn)到了Slf4j接口。我說這是在挖JCL的墻角不過分吧?
          • slf4j-jcl:讓在使用Slf4j的用戶方便的遷移到JCL上,自己的墻角也挖,主打的就是一個(gè)公平公正公開。

          Slf4j通過推出各種適配層,基本滿足了用戶的所有場(chǎng)景,我們來看一下它的全家桶:

          網(wǎng)上介紹Slf4j的文章,經(jīng)常會(huì)引用它官網(wǎng)上的兩張圖:

          感興趣的同學(xué)也可以參考。

          這里解釋一下slf4j-log4j12這個(gè)名字,它表示Slf4j + Log4j 1.2(Log4j的最后一個(gè)版本) 的適配層。類似的,slf4j-jdk14表示Slf4j + JDK 1.4(就是 JUL)的適配層。


          2.6 Logback (2006)


          然而Ceki的目標(biāo)并不止于Slf4j,面對(duì)自己一手創(chuàng)造的Log4j,作為原作者自然是知道它存在哪些問題的。于是在2006年Ceki又推出了一款日志記錄實(shí)現(xiàn)方案:Logback[26]。無論是易用度、功能、還是性能,Logback 都要優(yōu)于Log4j,再加上天然支持Slf4j而不需要額外的適配層,自然擁躉者眾。目前Logback已經(jīng)成為Java社區(qū)最被廣泛接受的日志實(shí)現(xiàn)層(Logback自己在2021年的統(tǒng)計(jì)是48%的市占率[27])。

          相比于Log4j,Logback提供了很多我們現(xiàn)在看起來理所當(dāng)然的新特性:

          • 支持日志文件切割滾動(dòng)記錄、支持異步寫入
          • 針對(duì)歷史日志,既支持按時(shí)間或按硬盤占用自動(dòng)清理,也支持自動(dòng)壓縮以節(jié)省硬盤空間
          • 支持分支語法,通過<if>, <then>, <else>可以按條件配置不同的日志輸出邏輯,比如判斷僅在開發(fā)環(huán)境輸出更詳細(xì)的日志信息
          • 大量的日志過濾器,甚至可以做到通過登錄用戶Session識(shí)別每一位用戶并輸出獨(dú)立的日志文件
          • 異常堆棧支持打印jar包信息,讓我們不但知道調(diào)用出自哪個(gè)文件哪一行,還可以知道這個(gè)文件來自哪個(gè)jar包

          Logback主要由三部分組成(網(wǎng)上各種文章在介紹classic和access時(shí)都描述的語焉不詳,我不得不直接翻官網(wǎng)文檔找更明確的解釋):

          • logback-core:記錄/輸出日志的核心實(shí)現(xiàn)
          • logback-classic:適配層,完整實(shí)現(xiàn)了Slf4j接口
          • logback-access[28]:用于將Logback集成到Servlet容器(Tomcat、Jetty)中,讓這些容器的HTTP訪問日志也可以經(jīng)由強(qiáng)大的Logback輸出


          2.7 Log4j 2 (2012)


          看著Slf4j + Logback搞的風(fēng)生水起,Apache自然不會(huì)坐視不理,終于在2012年憋出一記大招:Apache Log4j 2[29],它自然也有不少亮點(diǎn):

          • 插件化結(jié)構(gòu)[30],用戶可以自己開發(fā)插件,實(shí)現(xiàn)Appender、Logger、Filter完成擴(kuò)展
          • 基于LMAX Disruptor的異步化輸出[31],在多線程場(chǎng)景下相比Logback有10倍左右的性能提升,Apache官方也把這部分作為主要賣點(diǎn)加以宣傳,詳細(xì)可以看Log4j 2 Performance[32]。

          Log4j 2主要由兩部分組成:

          • log4j-core:核心實(shí)現(xiàn),功能類似于logback-core
          • log4j-api:接口層,功能類似于Slf4j,里面只包含Log4j 2的接口定義

          你會(huì)發(fā)現(xiàn)Log4j 2的設(shè)計(jì)別具一格,提供JCL和Slf4j之外的第三個(gè)接口層(log4j-api,雖然只是自己的接口),它在官網(wǎng)API Separation[33]一節(jié)中解釋說,這樣設(shè)計(jì)可以允許用戶在一個(gè)項(xiàng)目中同時(shí)使用不同的接口層與實(shí)現(xiàn)層。

          不過目前大家一般把Log4j 2作為實(shí)現(xiàn)層看待,并引入JCL或Slf4j作為接口層。特別是JCL,在時(shí)隔近十年后,于2023年底推出了1.3.0 版[34],增加了針對(duì)Log4j 2的適配。還記得我們?cè)凇?.2.4 JCL (2002.8)】中沒有用最新版的JCL做介紹嗎,就是因?yàn)檫@個(gè)十年之后的版本把那些已經(jīng)「作古」的日志適配層@Deprecated掉了。

          多說一句,其實(shí)Logback和Slf4j就像log4j-core和log4j-api的關(guān)系一下,目前如果你想用Logback也只能借助Slf4j。但誰讓它們生逢其時(shí)呢,大家就會(huì)分別討論認(rèn)為是兩個(gè)產(chǎn)品。

          雖然Log4j 2發(fā)布至今已有十年(本文寫于2024年),但它仍然無法撼動(dòng)Logback的江湖地位,我個(gè)人總結(jié)下來主要有兩點(diǎn):

          • Log4j 2雖然頂著Log4j的名號(hào),但卻是一套完全重寫的日志系統(tǒng),無法只通過修改Log4j版本號(hào)完成升級(jí),歷史用戶升級(jí)意愿低
          • Log4j 2比Logback晚面世6年,卻沒有提供足夠亮眼及差異化的能力(前邊介紹的兩個(gè)亮點(diǎn)對(duì)普通用戶并沒有足夠吸引力),而Slf4j+Logback這套組合已經(jīng)非常優(yōu)秀,先發(fā)優(yōu)勢(shì)明顯

          比如,曾有人建議Spring Boot將日志系統(tǒng)從Logback切換到Log4j2[35],但被Phil Webb[36](Spring Boot核心貢獻(xiàn)者)否決。他在回復(fù)中給出的原因包括:Spring Boot需要保證向前兼容以方便用戶升級(jí),而切換Log4j 2是破壞性的;目前絕大部分用戶并未面臨日志性能問題,Log4j 2所推崇的性能優(yōu)勢(shì)并非框架與用戶的核心關(guān)切;以及如果用戶想在Spring Boot中切換到Log4j 2也很方便(如需切換可參考 官方文檔[37])。


          2.8 spring-jcl (2017)


          因?yàn)槟壳按蟛糠謶?yīng)用都基于Spring/Spring Boot搭建,所以我額外介紹一下spring-jcl [38]這個(gè)包,目前Spring Boot用的就是spring-jcl + Logback這套方案。

          Spring曾在它的官方Blog《Logging Dependencies in Spring》[39]中提到,如果可以重來,Spring會(huì)選擇李白Slf4j而不是JCL作為默認(rèn)日志接口。

          現(xiàn)在Spring又想支持Slf4j,又要保證向前兼容以支持JCL,于是從5.0(Spring Boot 2.0)開始提供了spring-jcl這個(gè)包。它頂著Spring的名號(hào),代碼中包名卻與JCL 一致(org.apache.commons.logging),作用自然也與JCL一致,但它額外適配了Slf4j,并將Slf4j放在查找的第一順位,從而做到了「既要又要」(你可以回到【1.2.4 JCL (2002.8)】節(jié)做一下對(duì)比)。

          如果你是基于Spring Initialize [40]新創(chuàng)建的應(yīng)用,可以不必管這個(gè)包,它已經(jīng)在背后默默工作了;如果你在項(xiàng)目開發(fā)過程中遇到包沖突,或者需要自己選擇日志接口和實(shí)現(xiàn),則可以把spring-jcl當(dāng)作JCL對(duì)待,大膽排除即可。


          2.9 其他


          除了我們上邊提到的日志解決方案,還有一些不那么常見的,比如:

          • Flogger[41]:由Google在2018年推出的日志接口層。首字母F的含義是Fluent,這也正是它的最大特點(diǎn):鏈?zhǔn)秸{(diào)用(或者叫流式API,Slf4j 2.0也支持Fluent API 了,我們會(huì)在后續(xù)系列文章中介紹)
          • JBoss Logging[42]:由RedHat在約2010年推出,包含完整的接口層、實(shí)現(xiàn)層、適配層
          • slf4j-reload4j[43]:Ceki基于Log4j 1.2.7 fork出的版本,旨在解決Log4j的安全問題,如果你的項(xiàng)目還在使用Log4j且不想遷移,建議平替為此版本。(但也不是所有安全問題都能解決,具體可以參考上邊的鏈接)

          因?yàn)檫@些日志框架我們?cè)趯?shí)際開發(fā)中用的很少,此文也不再贅述了(主要是我也不會(huì))。

          三、總結(jié)


          歷史介紹完了,但故事并沒有結(jié)束。兩個(gè)接口(JCL、Slf4j)四個(gè)實(shí)現(xiàn)(Log4j、JUL、Logback、Log4j2),再加上無數(shù)的適配層,它們之間串聯(lián)成了一個(gè)網(wǎng),我專門畫了一張圖:

          解釋/補(bǔ)充一下這張圖:

          1. 相同顏色的模塊擁有相同的groupId,可以參考圖例中給出的具體值。
          2. JCL的適配層是直接在它自己的包中提供的,詳情我們?cè)谇斑呉呀?jīng)介紹過,可以回【1.2.4 JCL (2002.8)】查看。
          3. 要想使用Logback,就一定繞不開Slf4j(引用它的適配層也算);同樣的,要想使用 Log4j 2,那它的log4j-api也繞不開。

          如果你之前在看「1.1 前言」時(shí)覺得過于抽象,那么此時(shí)建議你再回頭看一下,相信會(huì)有更多體會(huì)。

          從這段歷史,我也發(fā)現(xiàn)了幾個(gè)有趣的細(xì)節(jié):

          • 在Log4j 2面世前后的很長(zhǎng)一段時(shí)間,Slf4j及Logback因?yàn)闆]有競(jìng)爭(zhēng)對(duì)手而更新緩慢。英雄沒有對(duì)手只能慢慢垂暮,只有棋逢對(duì)手才能笑傲江湖。
          • 技術(shù)人的善良與倔強(qiáng):面世晚的產(chǎn)品都針對(duì)前輩產(chǎn)品提供支持;面世早的產(chǎn)品都不搭理它的「后輩」。
          • 計(jì)算機(jī)科學(xué)領(lǐng)域的任何問題都可以通過增加一個(gè)中間層來解決,如果不行就兩個(gè)(橋接層干的事兒)。
          • Ceki一人肩挑Java日志半壁江山25年(還在增長(zhǎng)ing),真神人也。(當(dāng)然在代碼界有很多這樣的神人,比如Linus Torvalds[44]維護(hù)Linux至今已有33年,雖然后期主要作為產(chǎn)品經(jīng)理參與,再比如已故的Bram Moolenaar[45]老爺子持續(xù)維護(hù) Vim 32年之久)。

          參考鏈接:

          [1]https://codedocs.org/what-is/david-wheeler-computer-scientist
          [2]https://github.com/ceki
          [3]https://logging.apache.org/log4j/1.2/
          [4]https://logging.apache.org/log4net/
          [5]https://logging.apache.org/log4cxx/
          [6]https://logging.apache.org/log4php/
          [7]https://log4c.sourceforge.net/
          [8]https://log4cpp.sourceforge.net/
          [9]https://mschilli.github.io/log4perl/
          [10]https://news.apache.org/foundation/entry/apache_logging_services_project_announces
          [11]https://jcp.org/en/jsr/detail
          [12]https://www.java.com/releases/
          [13]https://avalon.apache.org/
          [14]https://javalogging.sourceforge.net/
          [15]https://commons.apache.org/proper/commons-logging/
          [16]https://jakarta.apache.org/
          [17]https://lists.apache.org/thread/53otcqljjfnvjs3hv8m4ldzlgz59yk6k
          [18]https://commons.apache.org/
          [19]https://commons.apache.org/proper/commons-lang/
          [20]https://commons.apache.org/proper/commons-collections/
          [21]http://ceki.blogspot.com/2010/05/forces-and-vulnerabilites-of-apache.html
          [22]https://lists.apache.org/thread/dyzmtholjdlf3h32vvl85so8sbj3v0qz
          [23]https://www.slf4j.org/
          [24]https://stackoverflow.com/questions/3222895/what-is-the-issue-with-the-runtime-discovery-algorithm-of-apache-commons-logging
          [25]https://articles.qos.ch/classloader.html
          [26]https://logback.qos.ch/
          [27]https://qos.ch/
          [28]https://logback.qos.ch/access.html
          [29]https://logging.apache.org/log4j/2.x/
          [30]https://logging.apache.org/log4j/2.x/manual/extending.html
          [31]https://logging.apache.org/log4j/2.x/manual/async.html
          [32]https://logging.apache.org/log4j/2.x/performance.html
          [33]https://logging.apache.org/log4j/2.x/manual/api-separation.html
          [34]https://commons.apache.org/proper/commons-logging/changes-report.html
          [35]https://github.com/spring-projects/spring-boot/issues/16864
          [36]https://spring.io/team/philwebb
          [37]https://docs.spring.io/spring-boot/docs/3.2.x/reference/html/howto.html
          [38]https://docs.spring.io/spring-framework/reference/core/spring-jcl.html
          [39]https://spring.io/blog/2009/12/04/logging-dependencies-in-spring
          [40]https://start.spring.io/
          [41]https://google.github.io/flogger/
          [42]https://github.com/jboss-logging
          [43]https://reload4j.qos.ch/
          [44]https://github.com/torvalds
          [45]https://moolenaar.net/



          作者:尚左

          來源-微信公眾號(hào):阿里云開發(fā)者

          出處:https://mp.weixin.qq.com/s/eIiu08fVk194E0BgGL5gow

          置Postfix

          Postfix有數(shù)百種配置選項(xiàng)。 我概述了/etc/postfix/main.cf文件中的一些常見問題。

          用于出站郵件的域名

          將此選項(xiàng)設(shè)置為出站郵件所需的域名。 默認(rèn)情況下,此選項(xiàng)使用出站電子郵件的主機(jī)名。 該選項(xiàng)還指定附加到非限定收件人地址的域。

          #default value is:
          #myorigin=$myhostname
          #myorigin=$mydomain
          #we are going to send outbound mail as originating from example.com
          mydomain=example.com
          myorigin=$mydomain
          

          要接收電子郵件的域名

          此選項(xiàng)指定Postfix接收電子郵件的域名。 默認(rèn)情況下,Postfix僅接收主機(jī)名的電子郵件。 對(duì)于域郵件服務(wù)器,請(qǐng)更改該值以包含域名。

          #default value
          mydestination=$myhostname, localhost.$mydomain, localhost
          #we are going to change it so that we can receive email for example.com
          mydestination=$myhostname, localhost.$mydomain, localhost, $mydomain
          

          允許中繼的網(wǎng)絡(luò)

          默認(rèn)情況下,Postfix允許Postfix服務(wù)器的本地子網(wǎng)上的客戶端將其用作中繼 - 換句話說,即$ mynetworks配置參數(shù)中定義的那些網(wǎng)絡(luò)。 更改此項(xiàng)以包括組織內(nèi)應(yīng)允許使用此Postfix服務(wù)器發(fā)送電子郵件的所有網(wǎng)絡(luò)。

          #default value
          #mynetworks_style=class
          #mynetworks_style=subnet
          #mynetworks_style=host
          #mynetworks=168.100.189.0/28, 127.0.0.0/8
          #mynetworks=$config_directory/mynetworks
          #mynetworks=hash:/etc/postfix/network_table
          #change the default values to be your network, assuming your network is 10.0.0.0/8
          mynetworks=10.0.0.0/8
          #another way of accomplishing the above is:
          mynetworks_style=class
          #if you want to forward e-mail only from the Postfix host:
          mynetworks_style=host
          #if you want only the Postfix server subnet to forward e-mail via the Postfix server:
          mynetworks_style=subnet
          

          將郵件中繼到目的地

          當(dāng)郵件來自授權(quán)網(wǎng)絡(luò)之外的客戶端時(shí),Postfix僅將電子郵件轉(zhuǎn)發(fā)給授權(quán)域。 您可以使用relay_domains參數(shù)指定哪些域可以是未經(jīng)身份驗(yàn)證的發(fā)件人的收件人域。

          #default value is:
          #relay_domains=$mydestination
          #if you do not want to forward e-mail from strangers, then change it as follows (recommended for outgoing mail servers, not for incoming):
          relay_domains=#if you want to forward e-mail from strangers to your domain:
          relay_domains=$mydomain
          

          運(yùn)輸方式

          Postfix使用收件人的郵件交換器(MX)記錄直接發(fā)送郵件。 您可能不需要此功能,因?yàn)檗D(zhuǎn)發(fā)到過濾出站郵件的外部郵件托管提供商可能更好。

          #default value is:
          #relayhost=$mydomain
          #relayhost=[gateway.my.domain]
          #relayhost=[mailserver.isp.tld]
          #relayhost=uucphost
          #relayhost=[an.ip.add.ress]
          #change the value to be
          relayhost=external.mail.provider.ip.address
          

          報(bào)告麻煩

          您可能想要定義的另一個(gè)值是在出現(xiàn)任何問題時(shí)向誰發(fā)送電子郵件。 postmaster電子郵件地址在/ etc / aliases中指定,而不是在/etc/postfix/main.cf中指定。

          #default value is:
          $ grep -i postmaster /etc/aliases
          mailer-daemon: postmaster
          postmaster: root
          #change to an e-mail address in your org that is an alias for the team responsible for Postfix
          postmaster: email-admins@example.com
          

          使用NAT

          如果Postfix服務(wù)器使用NAT(換句話說,它在私有IP空間中),并且它正在接收公共IP地址的電子郵件地址,則還需要指定它。

          #default value is:
          #proxy_interfaces=#proxy_interfaces=1.2.3.4
          #change it to your external IP address to which e-mail is sent
          proxy_interfaces=your.public.email.server.ip.address
          

          記錄

          Postfix日志記錄通過/etc/rsyslog.conf中的Syslog完成。 默認(rèn)情況下,通常會(huì)將所有郵件日志發(fā)送到/ var / log / maillog,這是存儲(chǔ)郵件日志的好地方。

          #default value is:
          mail.* -/var/log/maillog
          

          電子郵件協(xié)議

          設(shè)置Postfix后,下一個(gè)要解決的問題是如何讓客戶端訪問電子郵件。 在客戶端訪問方面,一些流行的協(xié)議包括

          1. IMAP
          2. POP(也稱為POP3)

          通常,避免直接傳遞到組織中的目標(biāo)服務(wù)器,以保持簡(jiǎn)單; 相反,應(yīng)用程序服務(wù)器從郵箱服務(wù)器中提取電子郵件。 應(yīng)用程序服務(wù)器可以使用一個(gè)或多個(gè)協(xié)議,例如IMAP或POP。 您也可以讓最終用戶使用這些協(xié)議來訪問其郵箱,而不是應(yīng)用程序服務(wù)器。 IMAP在RFC 3501(http://tools.ietf.org/html/rfc3501)中定義,并且具有比POP更多的功能。 POP在RFC 1939(https://www.ietf.org/rfc/rfc1939.txt)中定義,并且已經(jīng)存在了很長(zhǎng)時(shí)間。

          如果您有需要提取電子郵件的應(yīng)用程序服務(wù)器,可以提供幫助的潛在設(shè)計(jì)如圖4-12所示。 郵件傳遞代理(MDA)和郵件提交代理(MSA)都可以是Postfix服務(wù)器。 MDA是將郵件發(fā)送到郵箱的代理; MSA接受來自郵件用戶代理的電子郵件。

          For the MSA, there are numerous options, including

          • Dovecot (http://www.dovecot.org/)
          • Courier (http://www.courier-mta.org/imap/)
          • Cyrus (https://www.cyrusimap.org/)

          這些MSA選項(xiàng)中的每一個(gè)都是開源和免費(fèi)的。 它們都支持?jǐn)?shù)千個(gè)郵箱,可以幫助您輕松管理電子郵件環(huán)境。

          獲得Postfix的幫助

          Postfix支持有很多選項(xiàng)。 在線文檔非常好,與任何開源免費(fèi)軟件一樣,用戶社區(qū)是您閱讀文檔后獲得幫助的最佳選擇。 一些在線幫助選項(xiàng)包括:

          • Mailing lists (http://www.postfix.org/lists.html)
          • IRC channel (http://irc.lc/freenode/postfix/irctc@@@)
          • Online documentation (http://www.postfix.org/documentation.html)

          企業(yè)中的版本控制

          版本控制在企業(yè)基礎(chǔ)結(jié)構(gòu)中有許多用途。 傳統(tǒng)方法是使用修訂控制來進(jìn)行源代碼管理。 但是,修訂控制也可以在基礎(chǔ)設(shè)施管理中發(fā)揮重要作用。 例如,如果在環(huán)境中使用BIND,則BIND配置可以存儲(chǔ)在Git中。 Postfix和其他軟件(如OpenVPN,iptables和Hadoop)等軟件的配置也可以存儲(chǔ)在版本控制中。 Puppet和Salt等配置管理工具也可以輕松地與Git集成。

          大量的開源軟件源代碼存儲(chǔ)在Git中。 在企業(yè)中使用Git的一個(gè)優(yōu)點(diǎn)是與Internet社區(qū)的協(xié)作變得更加容易。 一些使用Git的主要開源項(xiàng)目包括:

          • Linux kernel
          • OpenStack
          • OpenVZ
          • GNOME
          • Yum

          A more complete list is found at https://git.wiki.kernel.org/index.php/GitProjects.

          ython的list是一種非常靈活的數(shù)據(jù)結(jié)構(gòu),它允許存儲(chǔ)任意類型的有序集合,包括數(shù)字、字符串、甚至是其他的list,其強(qiáng)大的特性為Python編程提供了很大的便利。在這個(gè)文檔中,我們將介紹Python list的基礎(chǔ)知識(shí),涵蓋了list的定義、創(chuàng)建、訪問、操作等方面的內(nèi)容。

          定義與創(chuàng)建

          在Python中,可以使用方括號(hào)[]來創(chuàng)建一個(gè)空的list,例如:

          Copy codea=[]
          

          也可以使用方括號(hào)[]初始化一個(gè)包含元素的list,例如:

          Copy codeb=[1, 2, 3, 4, 5]
          c=['apple', 'banana', 'orange']
          d=[1, 'hello', 3.14]
          

          list的元素可以是任何類型,而且list中的元素可以是不同的類型,這是它非常靈活的地方。

          訪問元素

          Python的list可以使用索引來訪問其中的元素,索引從0開始,例如:

          Copy codea=[1, 2, 3, 4, 5]
          print(a[0])  # 1
          print(a[2])  # 3
          

          list還可以使用負(fù)數(shù)索引來訪問其中的元素,例如:

          Copy codeprint(a[-1])  # 5
          print(a[-3])  # 3
          

          這樣,我們可以更加方便地訪問list中的元素。

          操作list

          1. 添加元素

          向list中添加元素有兩種方式:使用append()方法添加,或使用+運(yùn)算符連接兩個(gè)list。例如:

          Copy codea=['apple', 'banana', 'orange']
          a.append('watermelon')
          print(a)  # ['apple', 'banana', 'orange', 'watermelon']
          
          b=['pear', 'grape']
          c=a + b
          print(c)  # ['apple', 'banana', 'orange', 'watermelon', 'pear', 'grape']
          
          1. 刪除元素

          可以使用del語句或remove()方法從list中刪除元素:

          Copy codea=['apple', 'banana', 'orange']
          del a[0]
          print(a)  # ['banana', 'orange']
          
          a.remove('banana')
          print(a)  # ['orange']
          
          1. 切片

          我們可以使用切片來訪問list的一個(gè)子集合。切片使用[start:stop:step]的格式,例如:

          Copy codea=[1, 2, 3, 4, 5]
          print(a[1:3])  # [2, 3]
          print(a[:3])  # [1, 2, 3]
          print(a[2:])  # [3, 4, 5]
          print(a[::2])  # [1, 3, 5]
          
          1. 排序

          Python提供了兩種方法對(duì)list進(jìn)行排序:sort()方法和sorted()函數(shù)。sort()方法會(huì)改變?cè)璴ist,而sorted()函數(shù)返回一個(gè)新的已排序的list。例如:

          Copy codea=[3, 1, 4, 2, 5]
          a.sort()
          print(a)  # [1, 2, 3, 4, 5]
          
          b=[3, 1, 4, 2, 5]
          c=sorted(b)
          print(b)  # [3, 1, 4, 2, 5]
          print(c)  # [1, 2, 3, 4, 5]
          

          學(xué)習(xí)資源

          以下是一些有用的學(xué)習(xí)資源,幫助你學(xué)習(xí)Python list:

          1. Python官方文檔:https://docs.python.org/3/tutorial/datastructures.html#more-on-lists,這是Python官方文檔中關(guān)于list的章節(jié),詳細(xì)介紹了list的操作和用法。
          2. W3Schools Python List教程:https://www.w3schools.com/python/python_lists.asp,這是W3Schools提供的Python list教程,簡(jiǎn)單易懂地介紹了list的基礎(chǔ)知識(shí)和操作。
          3. Python for Data Science Handbook:Jake VanderPlas著,這是一本深入探討Python數(shù)據(jù)科學(xué)的書籍,其中包括了從基礎(chǔ)知識(shí)到高級(jí)技巧的Python list的操作與應(yīng)用。

          總結(jié)

          Python的list是一種非常靈活的數(shù)據(jù)結(jié)構(gòu),它可以存儲(chǔ)任何類型的元素,包括數(shù)字、字符串、甚至是其他的list。Python list提供了豐富的操作方法,包括訪問元素、添加元素、刪除元素、切片、排序等。我們希望通過這篇文檔提供了Python list的基礎(chǔ)知識(shí)和一些有用的學(xué)習(xí)資源,幫助你更好地掌握Python編程中的list。


          主站蜘蛛池模板: 国产精品日韩欧美一区二区三区| 无码人妻久久久一区二区三区| 精品在线视频一区| 亚洲Av永久无码精品一区二区| 亚洲AV无一区二区三区久久| 久久精品无码一区二区三区| 亚洲中文字幕无码一区| 日本一区午夜艳熟免费| 国产亚洲日韩一区二区三区 | 无码人妻久久久一区二区三区| 爆乳熟妇一区二区三区| 久久精品国产一区二区| 农村人乱弄一区二区| 国产亚洲福利精品一区| 国产精品女同一区二区| 亚洲一区电影在线观看| 午夜在线视频一区二区三区| 国产一区二区三区内射高清| 欧洲精品码一区二区三区免费看| 无码精品蜜桃一区二区三区WW | 国产精品成人一区二区| 中文字幕无码一区二区三区本日| 国产凹凸在线一区二区| 亚洲AV无码一区二区乱子伦| 亚洲AV无码一区二区三区在线| 97精品国产一区二区三区| 午夜一区二区在线观看| 国产成人av一区二区三区在线| 视频一区精品自拍| 久久se精品一区精品二区| 国产一区二区三区在线电影| 无码人妻久久一区二区三区蜜桃| 色老板在线视频一区二区 | 国模吧一区二区三区| 亚洲免费视频一区二区三区| 亚洲色精品vr一区二区三区| 国产精品合集一区二区三区| 日韩在线视频一区二区三区| 中文字幕一区二区三区精华液| 久久精品亚洲一区二区三区浴池| 欧洲精品码一区二区三区|