本文轉載自Tyr Chen的博客,在文中作者總結了他認為高效能程序員應該具備的七個習慣,原文內容如下。 昨天收到一個讀者留言,問作為程序員,有什么學習和工作上的好習慣可以借鑒?想了想,干脆附庸風雅一下,總結個『高效能程序員的七個習慣』吧。Disclaimer:一家之言,可不信,但也可以部分信。 1. 擁抱unix哲學 每個程序員入門的第一堂和第二堂課應該是和unix哲學相關的內容,簡言之就是:做一件事,做好它。具體點:- 小即是美。
- 讓程序只做好一件事。
- 盡可能早地創建原型。
- 可移植性比效率更重要。
- 數據應該保存為文本文件。
- 盡可能地榨取軟件的全部價值。
- 使用shell腳本來提高效率和可移植性。
- 避免使用可定制性低下的用戶界面。
- 所有程序都是數據的過濾器。
2. 選一個樣板,follow之 每個NBA新秀都有自己的樣板,我們也總習慣稱某足球新星為『小羅』,『小小羅』。樣板為你提供了可模仿可追趕的對象,同時也讓你審視自己究竟想成為什么樣的程序員。我的樣板是Greg Pass和Werner Vogels,雖然我這輩子可能也達不到他們的高度,可這并不妨礙向著我心目中的明星一步步靠近。 3. 寫代碼,而不是調代碼 寫軟件最糟糕的體驗恐怕是邊寫邊調,寫一點,運行一下,再寫一點。是很多程序員都會這么干。原因有二:1. 不熟悉相關的代碼(類庫),需要邊寫邊運行保證代碼的正確。2. 現代編程語言的REPL(Read-Evaluate-Print-Loop,就是語言的shell)能力助長了這一行為。 寫系統軟件的人很少這么做。他們手頭糟糕的工具讓邊寫邊調的行為成為效率殺手 —— 如果稍稍改動,編譯就要花去幾分鐘,甚至更長的時間,你還會這么干么?所以他們往往是寫完一個模塊,再編譯調試。(由此看來,高效的工具有時候是把雙刃劍啊) 我覺得寫代碼就跟寫文章一樣,構思好,有了大綱,就應該行云流水一樣寫下去,一氣呵成,然后回過頭來再調整語句,修改錯別字。如果寫完一段,就要回溯檢查之前寫的內容,效率很低,思維也會被打散。 靠邊寫邊調做出來的代碼還往往質量不高。雖然局部經過了雕琢,但整體上不那么協調,看著總是別扭。這就好比雕刻,拿著一塊石頭,你先是精修了鼻子,然后再一點一點刻畫面部。等修到耳朵的時候,鼻子可能過大或過小,即便再精美,它也得不到贊賞。 4. 聰明地調試 軟件總會出問題。遇到問題,很多程序員就會用IDE在各種可能的地方加斷點調試,如果沒有IDE,那么各種print/log手段一齊拋出,有棗沒棗打一桿子再說。 優秀的程序員會在撰寫代碼的時候就考慮到調試問題,在系統關鍵的節點上注入各種等級的調試信息,然后在需要的時候打開相應的調試級別,順藤摸瓜,避免了不靠譜的臆測。這是調試之『道』。 很多問題打開調試開關后就原形畢露,但有時候靠調試信息找到了初步原因,進一步定位問題還需要具體的工具,也就是調試之『術』,如上文所述之斷點調試。有些時候,遇到靠類似gdb(如python的pdb)的工具無法解決的問題時(如性能問題),你還需要更多的調試工具做runtime profiling,如systemtap。 5. 使用標記語言來寫文檔,而非word/power point 不要使用只能使用特定軟件才能打開的工具寫文檔,如word/page或者power point/keynote。要使用『放之四海而皆可用』的工具。 java的市場口號是:『一次編寫,到處運行』,對于文檔,你也需要這樣的工具。Markdown(md) / Restructured Text(rst)(以及任何編輯語言,甚至是jade)就是這樣的工具。通過使用一種特定的文本格式,你的文檔可以被編譯成幾乎任意格式(html,rtf,latex,pdf,epub,...),真正達到了『一次編寫,到處運行』。最重要的是,由于邏輯層(文章本身)和表現層(各種格式,字體,行距等)分離,同樣的文檔,換個模板,就有完全不一樣的形象。 除非必須,我現在所有的文檔都是md或者rst格式。 6. 一切皆項目 程序員的所有產出應該項目制。軟件自不必說,文檔和各種碎片思想也要根據相關性組織成項目。舉一些我自己的例子: - 我的博客是一個名叫jobs的github項目
- 我的微信文章全部放在craftsman這個項目中
- 我學習某種知識的過程(比如說golang)會放在一個或若干個項目中
- 我工作上每個項目的各種產出(包括會議紀要)會按照項目對應生成git repo
項目制的好處是具備可回溯性。每個項目我可以用git來管理,這樣,幾乎在任何一臺設備上我都可以看到我之前的工作。想想你三年前寫的某個文檔,你還能找到它么?你還能找回你的修改歷史么? 項目制的另一大好處是可以在其之上使能工具。比如說你看到的這些微信文章,我隨時可以“make publish YEAR=2014”來生成包含了2014年我所寫文章的pdf。 7. 心態開放,勇于嘗試 在程序員社區里,語言之爭,系統之爭,軟件思想之爭幾乎是常態。python vs ruby,go vs java vs erlang vs rust,scala vs cljure,OOP vs FP,iOS vs Android。其實不管黑貓白貓,抓到老鼠的就是好貓,facebook還用php呢。程序員應該用開放的心態去包容新的技術,新的思想,勇于嘗試,而不是立即否定。這個世界最悲哀的是,手里有把錘子,看什么都是釘子(或者說,眼里就只能看見釘子)。 我接觸mac時間不過三年。可這三年時間,我從對mac不屑,到深深熱愛,最終成為mac的一個重度用戶。很多東西用過才知道,不嘗試不接觸我可能永遠活在自己下意識構筑的無形之墻的另一邊。
posted @
2014-04-13 10:17 永志歌德 閱讀(332) |
評論 (0) |
編輯 收藏
common-logging
common-logging是apache提供的一個通用的日志接口。用戶可以自由選擇第三方的日志組件作為具體實現,像log4j,或者jdk自帶的logging, common-logging會通過動態查找的機制,在程序運行時自動找出真正使用的日志庫。當然,common-logging內部有一個Simple logger的簡單實現,但是功能很弱。所以使用common-logging,通常都是配合著log4j來使用。使用它的好處就是,代碼依賴是common-logging而非log4j, 避免了和具體的日志方案直接耦合,在有必要時,可以更改日志實現的第三方庫。
使用common-logging的常見代碼:
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
-
- public class A {
- private static Log logger = LogFactory.getLog(this.getClass());
- }
動態查找原理:Log 是一個接口聲明。LogFactory 的內部會去裝載具體的日志系統,并獲得實現該Log 接口的實現類。LogFactory 內部裝載日志系統的流程如下:
- 首先,尋找org.apache.commons.logging.LogFactory 屬性配置。
- 否則,利用JDK1.3 開始提供的service 發現機制,會掃描classpah 下的META-INF/services/org.apache.commons.logging.LogFactory文件,若找到則裝載里面的配置,使用里面的配置。
- 否則,從Classpath 里尋找commons-logging.properties ,找到則根據里面的配置加載。
- 否則,使用默認的配置:如果能找到Log4j 則默認使用log4j 實現,如果沒有則使用JDK14Logger 實現,再沒有則使用commons-logging 內部提供的SimpleLog 實現。
從上述加載流程來看,只要引入了log4j 并在classpath 配置了log4j.xml ,則commons-logging 就會使log4j 使用正常,而代碼里不需要依賴任何log4j 的代碼。slf4j
slf4j全稱為Simple Logging Facade for JAVA,java簡單日志門面。類似于Apache Common-Logging,是對不同日志框架提供的一個門面封裝,可以在部署的時候不修改任何配置即可接入一種日志實現方案。但是,他在編譯時靜態綁定真正的Log庫。使用SLF4J時,如果你需要使用某一種日志實現,那么你必須選擇正確的SLF4J的jar包的集合(各種橋接包)。使用slf4j的常見代碼:
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
-
- public class A {
- private static Log logger = LogFactory.getLog(this.getClass());
- }
slf4j靜態綁定原理:SLF4J 會在編譯時會綁定import org.slf4j.impl.StaticLoggerBinder; 該類里面實現對具體日志方案的綁定接入。任何一種基于slf4j 的實現都要有一個這個類。如:org.slf4j.slf4j-log4j12-1.5.6: 提供對 log4j 的一種適配實現。注意:如果有任意兩個實現slf4j 的包同時出現,那么就可能出現問題。slf4j 與 common-logging 比較
common-logging通過動態查找的機制,在程序運行時自動找出真正使用的日志庫。由于它使用了ClassLoader尋找和載入底層的日志庫, 導致了象OSGI這樣的框架無法正常工作,因為OSGI的不同的插件使用自己的ClassLoader。 OSGI的這種機制保證了插件互相獨立,然而卻使Apache Common-Logging無法工作。
slf4j在編譯時靜態綁定真正的Log庫,因此可以再OSGI中使用。另外,SLF4J 支持參數化的log字符串,避免了之前為了減少字符串拼接的性能損耗而不得不寫的if(logger.isDebugEnable()),現在你可以直接寫:logger.debug(“current user is: {}”, user)。拼裝消息被推遲到了它能夠確定是不是要顯示這條消息的時候,但是獲取參數的代價并沒有幸免。
Log4j
Apache的一個開放源代碼項目,通過使用Log4j,我們可以控制日志信息輸送的目的地是控制臺、文件、GUI組件、甚至是套接口服務 器、NT的事件記錄器、UNIX Syslog守護進程等;用戶也可以控制每一條日志的輸出格式;通過定義每一條日志信息的級別,用戶能夠更加細致地控制日志的生成過程。這些可以通過一個 配置文件來靈活地進行配置,而不需要修改程序代碼。LogBack
Logback是由log4j創始人設計的又一個開源日記組件。logback當前分成三個模塊:logback-core,logback- classic和logback-access。logback-core是其它兩個模塊的基礎模塊。logback-classic是log4j的一個 改良版本。此外logback-classic完整實現SLF4J API使你可以很方便地更換成其它日記系統如log4j或JDK14 Logging。logback-access訪問模塊與Servlet容器集成提供通過Http來訪問日記的功能。 Log4j 與 LogBack 比較
LogBack作為一個通用可靠、快速靈活的日志框架,將作為Log4j的替代和SLF4J組成新的日志系統的完整實現。LOGBack聲稱具有極佳的性能,“ 某些關鍵操作,比如判定是否記錄一條日志語句的操作,其性能得到了顯著的提高。這個操作在LogBack中需要3納秒,而在Log4J中則需要30納秒。 LogBack創建記錄器(logger)的速度也更快:13微秒,而在Log4J中需要23微秒。更重要的是,它獲取已存在的記錄器只需94納秒,而 Log4J需要2234納秒,時間減少到了1/23。跟JUL相比的性能提高也是顯著的”。 另外,LOGBack的所有文檔是全面免費提供的,不象Log4J那樣只提供部分免費文檔而需要用戶去購買付費文檔。
slf4j與其他各種日志組件的橋接
應用代碼中使用slf4j接口,接入具體實現的方法
應用代碼中使用別的日志接口,轉成slf4j的方法

日志組件相關歷史
Java 界里有許多實現日志功能的工具,最早得到廣泛使用的是 log4j,許多應用程序的日志部分都交給了 log4j,不過作為組件開發者,他們希望自己的組件不要緊緊依賴某一個工具,畢竟在同一個時候還有很多其他很多日志工具,假如一個應用程序用到了兩個組件,恰好兩個組件使用不同的日志工具,那么應用程序就會有兩份日志輸出了。
為了解決這個問題,Apache Commons Logging (之前叫 Jakarta Commons Logging,JCL)粉墨登場,JCL 只提供 log 接口,具體的實現則在運行時動態尋找。這樣一來組件開發者只需要針對 JCL 接口開發,而調用組件的應用程序則可以在運行時搭配自己喜好的日志實踐工具。
所以即使到現在你仍會看到很多程序應用 JCL + log4j 這種搭配,不過當程序規模越來越龐大時,JCL的動態綁定并不是總能成功,具體原因大家可以 Google 一下,這里就不再贅述了。解決方法之一就是在程序部署時靜態綁定指定的日志工具,這就是 SLF4J 產生的原因。
跟 JCL 一樣,SLF4J 也是只提供 log 接口,具體的實現是在打包應用程序時所放入的綁定器(名字為 slf4j-XXX-version.jar)來決定,XXX 可以是 log4j12, jdk14, jcl, nop 等,他們實現了跟具體日志工具(比如 log4j)的綁定及代理工作。舉個例子:如果一個程序希望用 log4j 日志工具,那么程序只需針對 slf4j-api 接口編程,然后在打包時再放入 slf4j-log4j12-version.jar 和 log4j.jar 就可以了。
現在還有一個問題,假如你正在開發應用程序所調用的組件當中已經使用了 JCL 的,還有一些組建可能直接調用了 java.util.logging,這時你需要一個橋接器(名字為 XXX-over-slf4j.jar)把他們的日志輸出重定向到 SLF4J,所謂的橋接器就是一個假的日志實現工具,比如當你把 jcl-over-slf4j.jar 放到 CLASS_PATH 時,即使某個組件原本是通過 JCL 輸出日志的,現在卻會被 jcl-over-slf4j “騙到”SLF4J 里,然后 SLF4J 又會根據綁定器把日志交給具體的日志實現工具。過程如下
Component
|
| log to Apache Commons Logging
V
jcl-over-slf4j.jar --- (redirect) ---> SLF4j ---> slf4j-log4j12-version.jar ---> log4j.jar ---> 輸出日志
看到上面的流程圖可能會發現一個有趣的問題,假如在 CLASS_PATH 里同時放置 log4j-over-slf4j.jar 和 slf4j-log4j12-version.jar 會發生什么情況呢?沒錯,日志會被踢來踢去,最終進入死循環。
所以使用 SLF4J 的比較典型搭配就是把 slf4j-api、JCL 橋接器、java.util.logging(JUL)橋接器、log4j 綁定器、log4j 這5個 jar 放置在 CLASS_PATH 里。
不過并不是所有APP容器都是使用 log4j 的,比如 Google AppEngine 它使用的是 java.util.logging(JUL),這時應用 SLF4J 的搭配就變成 slf4j-api、JCL橋接器、logj4橋接器、JUL綁定器這4個 jar 放置在 WEB-INF/lib 里。
posted @
2014-04-13 09:49 永志歌德 閱讀(14911) |
評論 (4) |
編輯 收藏
幾乎在每個jar包里都可以看到log4j的身影,在多個子工程構成項目中,slf4j相關的沖突時不時就跳出來讓你不爽,那么slf4j-api、slf4j-log4j12還有log4j他們是什么關系?我把自己了解的和大家簡單分享一下:
slf4j:Simple Logging Facade for Java,為java提供的簡單日志Facade。Facade:門面,更底層一點說就是接口。他允許用戶以自己的喜好,在工程中通過slf4j接入不同的日志系統。更直觀一點,slf4j是個數據線,一端嵌入程序,另一端鏈接日志系統,從而實現將程序中的信息導入到日志系統并記錄。
因此,slf4j入口就是眾多接口的集合,他不負責具體的日志實現,只在編譯時負責尋找合適的日志系統進行綁定。具體有哪些接口,全部都定義在slf4j-api中。查看slf4j-api源碼就可以發現,里面除了public final class LoggerFactory類之外,都是接口定義。因此,slf4j-api本質就是一個接口定義。
下圖比較清晰的描述了他們之間的關系:

當系統采用log4j作為日志框架實現的調用關系:
首先系統包含slf4j-api作為日志接入的接口;
at compile時slf4j-api中public final class LoggerFactor類中
private final static void bind() 方法會尋找具體的日志實現類綁定,主要通過
StaticLoggerBinder.getSingleton();語句調用
slf4j-log4j12:鏈接slf4j-api和log4j中間的適配器。它實現了slf4j-apiz中StaticLoggerBinder接口,從而使得在編譯時綁定的是slf4j-log4j12的getSingleton()方法
log4j:這個是具體的日志系統。通過slf4j-log4j12初始化Log4j,達到最終日志的輸出。
posted @
2014-04-13 09:17 永志歌德 閱讀(630) |
評論 (0) |
編輯 收藏