【WEB系列】內嵌Tomcat配置Accesslog日誌檔案生成位置原始碼探索

語言: CN / TW / HK

現在SpringBoot應用大多是內嵌tomcat,以jar包方式啟動對外提供服務,最近遇到一個有意思的問題,當我希望輸出tomcat的 access.log 時,新增上對應的配置之後,發現windowns系統下找不到這個日誌檔案,而linux/mac則沒有什麼問題;

所以花了些時間定位一下,本文將記錄定位這個日誌檔案生成的全過程,當發現最後的結論時,更讓我吃驚的事情來了,就這麼個問題,在三年前我也遇到過,只不過當時的問題是上傳檔案之後,提示臨時目錄不存在,而這個臨時目錄和本文定位的目錄居然是一回事,可謂是來了一次夢幻的聯動,前面踩的坑不探究到底,後面遲早會繼續掉坑:joy:

I. 專案搭建與日誌配置

1. 專案依賴

本專案藉助SpringBoot 2.2.1.RELEASE + maven 3.5.3 + IDEA進行開發

啟動一個標準的SpringBoot專案,注意新增下面的依賴如下 (本文對應的原始碼可以在文末檢視)

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>

2. tomcat日誌配置

tomcat的日誌配置資訊,下面放在預設的配置檔案 application.yml 中,主要有下面幾個關鍵引數

server:
  port: 8080
  tomcat:
    accesslog:
      enabled: true
      directory: /tmp/logs/boot
      file-date-format: .yyyyMMdd
      pattern: '%h %l %u %t "%r" %s %b %Dms "%{Referer}i" "%{User-Agent}i" "%{X-Request-ID}i" "%{X-Forwarded-For}i"'

3. 一個簡單的rest介面

新增一個基礎的rest介面,用於接收請求

@RestController
@SpringBootApplication
public class Application {
    private DalConfig dalConfig;

    public Application(DalConfig dalConfig, Environment environment) {
        this.dalConfig = dalConfig;
        System.out.println(dalConfig);
    }

    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(Application.class);
        application.run(args);
    }

    @GetMapping(path = {"", "/", "/index"})
    public ModelAndView index() {
        return "hello";
    }
}

接下來請求一下介面,看下日誌是否正常

curl 'http://127.0.0.1:8080/'

如果是mac/linux系統的同學,就可以到 /tmp/logs/boot 目錄下檢視有沒有對應的日誌檔案了,那麼win的同學,到哪裡看?

II. 日誌檔案目錄定位

由於win和mac/linux對絕對路徑的定義不同,就會導致我們用到這個問題,一般來說,實際的專案最終都是跑在linux系統上,所以檔案路徑以上面的case居多,很少會說加一個 c:d: 開頭的

那麼問題就來了,同樣一份程式碼,win開發的同學到哪裡去找日誌

1. 原始碼定位

我們這裡討論的是內嵌Tomcat,要想定位日誌檔案在哪裡,就需要先找一下這個日誌哪裡輸出的,

直接google搜尋一下關鍵字,就可以得到有用的資訊

上面的第一項,給出了一個核心的類 AccessLogValve ,一下子就找到關鍵點了,核心地方打個端點,啟動一下看看是怎樣的

上面這個地址就是我們找的目標路徑,那麼這個是怎麼來的呢?這個字首有什麼套路麼?

核心來源點 File dir = this.getDirectoryFile(); ,對應的實現

private File getDirectoryFile() {
    // 這個directory就是我們對應的配置引數 server.tomcat.access_log.directory
    File dir = new File(this.directory);
    if (!dir.isAbsolute()) {
        dir = new File(this.getContainer().getCatalinaBase(), this.directory);
    }

    return dir;
}

所以重點需要關注 this.getContainer().getCatalinaBase() , 上面這個它對應的例項為 org.apache.catalina.core.StandardEngine#getCatalinaBase

接著向上朔源,可以找到設定這個路徑的地方,在 org.apache.catalina.startup.Tomcat#initBaseDir

最後就是定位baseDir的初始化了,再網上找一下,可以看到關鍵資訊

再直達一步,原來這個目錄建立是基於 jdkFile.createTempFile() 來實現的,又學到一個沒什麼鳥用的知識點了

2. 小結

本文可能對大部分小夥伴來說沒什麼鳥用,基本上也不太會有用到需要取查詢tomcat的訪問日誌 access.log 的時候(這裡指SpringBoot應用),在定位這個具體路徑的時候,想起了很久之前也踩過的一個坑,上傳檔案時,提示臨時目錄不存在,而這個目錄和我們上面查詢定位的可以說是一個地方了,仔細看來,現在算是填了一個時隔三年的坑了:grin:

最後小結下本文對應的知識點

tip1 accesslog日誌配置

核心配置資訊如下

server:
  port: 8080
  tomcat:
    accesslog:
      enabled: true # 設定為true,表示輸出 accesslog 日誌
      directory: /logs/boot  # 日誌檔案所在的目錄,注意不同作業系統,對絕對路徑的定位不同
      file-date-format: .yyyyMMdd # 按日期進行歸檔
      pattern: '%h %l %u %t "%r" %s %b %Dms "%{Referer}i" "%{User-Agent}i" "%{X-Request-ID}i" "%{X-Forwarded-For}i"' # 日誌輸出格式,類似Logback配置
#    basedir: /logs  # 全域性基本目錄,如果配置,則日誌檔案對應的目錄為 basedir + directory

tip2 絕對路徑的判斷

在Tomcat中,對於絕對路徑的判斷非常有參考價值,當然也可能是因為我對於jdk基本的api不夠熟悉的原因 ,之前我的判斷方式是

/**
 * 是否windows系統
 */
public static boolean isWinOS() {
    boolean isWinOS = false;
    try {
        String osName = System.getProperty("os.name").toLowerCase();
        String sharpOsName = osName.replaceAll("windows", "{windows}").replaceAll("^win([^a-z])", "{windows}$1")
                .replaceAll("([^a-z])win([^a-z])", "$1{windows}$2");
        isWinOS = sharpOsName.contains("{windows}");
    } catch (Exception e) {
        e.printStackTrace();
    }
    return isWinOS;
}

public static boolean isAbsFile(String fileName) {
    if (isWinOS()) {
        // windows 作業系統時,絕對地址形如  c:\descktop
        return fileName.contains(":") || fileName.startsWith("\\");
    } else {
        // mac or linux
        return fileName.startsWith("/");
    }
}

現在則有更簡單的方式了

private boolean isAbs(String path) {
    return new File(path).isAbsolute();
}

tip3 臨時目錄建立

同樣是直接藉助File來實現, File.createTempFile 即可,下面是Tomcat的建立方式,還非常貼心的加上了虛擬機器終止時,自動刪除相關的檔案

/**
 * Return the absolute temp dir for given web server.
 * @param prefix server name
 * @return the temp dir for given server.
 */
protected final File createTempDir(String prefix) {
    try {
        File tempDir = File.createTempFile(prefix + ".", "." + getPort());
        tempDir.delete();
        tempDir.mkdir();
        tempDir.deleteOnExit();
        return tempDir;
    }
    catch (IOException ex) {
        throw new WebServerException(
                "Unable to create tempDir. java.io.tmpdir is set to " + System.getProperty("java.io.tmpdir"), ex);
    }
}

III. 不能錯過的原始碼和相關知識點

0. 專案

1. 微信公眾號: 一灰灰Blog

盡信書則不如,以上內容,純屬一家之言,因個人能力有限,難免有疏漏和錯誤之處,如發現bug或者有更好的建議,歡迎批評指正,不吝感激

下面一灰灰的個人部落格,記錄所有學習和工作中的博文,歡迎大家前去逛逛

打賞 如果覺得我的文章對您有幫助,請隨意打賞。