logback官方文檔中文翻譯第十二章:Groovy 配置

第十二章:Groovy 配置

領(lǐng)域特定語(yǔ)言或者 DSL 更加普遍。logback 基于 XML 的配置可以看做 DSL 的實(shí)例。由于 XML 的本質(zhì),基于 XML 的配置文件變得非常的啰嗦以及臃腫。另外,logback 中的 Joran 有一個(gè)相對(duì)龐大的代碼,用來(lái)專(zhuān)門(mén)處理基于 XML 的配置文件。Joran 支持一些非常好的特性,例如變量替換,條件處理,以及動(dòng)態(tài)擴(kuò)展。但是,不但 Joran 非常復(fù)雜,而且給用戶(hù)的體驗(yàn)非常的不好,或者至少不直觀(guān)。

本章敘述基于 Groovy 的 DSL 致力于一致性,直觀(guān)性,以及非常強(qiáng)大。任何你可以使用 XML 配置的文件,你都可以用更加簡(jiǎn)短的符號(hào)使用 Groovy 來(lái)實(shí)現(xiàn)。

常規(guī)建議

一般來(lái)說(shuō),logback.groovy 文件是 Groovy 程序。因?yàn)?Groovy 是 Java 的超集,所以無(wú)論你在 Java 執(zhí)行什么配置操作,你都可以在 logback.groovy 文件中做同樣的事情。但是,在 Java 中,使用變成的方式配置 logback 有點(diǎn)笨重,所以我們?cè)黾恿艘恍?logback 特有的擴(kuò)展來(lái)減輕你的負(fù)擔(dān)。我們嘗試限制 logback 特有的拓展符號(hào)盡量的少。如果你已經(jīng)熟悉了 Groovy,那么你應(yīng)該更加容易去讀,去理解甚至去寫(xiě)你自己的 logback.groovy 文件。那么不熟悉 Groovy 的人依然會(huì)發(fā)現(xiàn) logback.groovy 中的語(yǔ)法比 logback.xml 中的語(yǔ)法更加容易使用。

logback.groovy 文件是 Groovy 程序,具有最小的 logback 特定的拓展。所有常用的 groovy 結(jié)構(gòu),例如類(lèi)的導(dǎo)入,變量定義,字符串 (GString) 中包含 ${..} 評(píng)估表達(dá)式,以及 if-else 語(yǔ)句在 logback.grooby 文件中都是可用的。

自動(dòng)導(dǎo)入

1.0.10 版本以后 為了減少不必要的引用,一些共同的類(lèi)以及包會(huì)被自動(dòng)導(dǎo)入。因此,只要你只是配置了內(nèi)置的 appender,layout 等等,你不需要在你的腳本中添加相對(duì)應(yīng)的導(dǎo)入語(yǔ)句。當(dāng)然,對(duì)于默認(rèn)導(dǎo)入不會(huì)涉及到類(lèi),你需要自己導(dǎo)入。

下面是默認(rèn)導(dǎo)入的列表:

  • import ch.qos.logback.core.*;
  • import ch.qos.logback.core.encoder.*;
  • import ch.qos.logback.core.read.*;
  • import ch.qos.logback.core.rolling.*;
  • import ch.qos.logback.core.status.*;
  • import ch.qos.logback.classic.net.*;
  • import ch.qos.logback.classic.encoder.PatternLayoutEncoder;

另外,ch.qos.logback.classic.Level 中的所有常量 (大寫(xiě)) 都會(huì)被靜態(tài)導(dǎo)入,以及小寫(xiě)的別名。也就是說(shuō)在你的腳本中可以引用 INFO 以及 info,而不需要使用靜態(tài)導(dǎo)入語(yǔ)句。

不再支持 SiftingAppender

1.0.12 版本以后 在 groovy 配置文件中不再支持 SiftingAppender。但是,如果有需要,可以重新引進(jìn)。

logback.groovy 特定的拓展

本質(zhì)上,logback.groovy 語(yǔ)法包含以下所說(shuō)的六個(gè)方法;按照它們習(xí)慣上相反的順序出現(xiàn)。嚴(yán)格來(lái)說(shuō),這些方法的調(diào)用順序并重要,但是有一個(gè)例外:appender 附加到 logger 之前必須**被定義。

  • root(Level level, List<String> appenderNames = [])

root 方法可以用來(lái)設(shè)置 root logger 的日志級(jí)別。第二個(gè)可選參數(shù)的類(lèi)型為 List<String>,可以用來(lái)添加之前定義的 appender 的名字。如果你不想指定 appenderNames,那么就是一個(gè)空 (empty) 的列表。在 Groovy 中,用 [] 表示一個(gè)空的列表。

設(shè)置 root logger 的級(jí)別為 WARN,你可以這樣寫(xiě):

root(WARN)

設(shè)置 root logger 的級(jí)別為 INFO,并且將名為 "CONSOLE" 與 "FILE" 的 appender 附加到 root 上,你可以這樣寫(xiě):

root(INFO, ["CONSOLE", "FILE"])

在前面的例子中,假設(shè)名為 "CONSOLE" 與 "FILE" 的 appender 已經(jīng)被定義好了。很快將會(huì)討論有關(guān) appender 的定義。

  • logger(String name, Level level, List<String> appenderNames = [], Boolean additivity = null)

logger() 方法接收四個(gè)參數(shù),最后兩個(gè)是可選的。第一個(gè)參數(shù)表示配置 logger 的名字。第二參數(shù)表示指定 logger 的級(jí)別。設(shè)置 logger 的級(jí)別為 null 將強(qiáng)制它從它最近的祖先那里繼承級(jí)別。第三個(gè)參數(shù)的類(lèi)型為 List<String>,是可選的,默認(rèn)為空列表。列表中 appender 會(huì)被附加到指定的 logger 上去。第四個(gè)參數(shù)的類(lèi)型為 Boolean,也是可選的,用來(lái)控制疊加性。如果忽略,默認(rèn)值為 null。

例如,下面這個(gè)腳本設(shè)置 "com.foo" 這個(gè) logger 的級(jí)別為 INFO:

logger("com.foo", INFO)

下個(gè)腳本設(shè)置 "com.foo" 這個(gè) logger 的級(jí)別為 DEBUG,并且將名為 "CONSOLE" 的 appender 附加到其上:

logger("com.foo", DEBUG, ["CONSOLE"])

下個(gè)腳本跟上一個(gè)類(lèi)似,只是這個(gè)還設(shè)置了 "com.foo" 這個(gè) logger 的疊加性為 false:

logger("com.foo", DEBUG, ["CONSOLE"],false)
  • appender(String name, Class clazz, Closure closure = null)

appender 方法的第一個(gè)參數(shù)接收 appender 的名字進(jìn)行配置。第二個(gè)參數(shù)是強(qiáng)制的,表示 appender 實(shí)例化的類(lèi)。第三個(gè)參數(shù)包含更多的配置信息。如果忽略,默認(rèn)為 null。

大部分 appender 都需要設(shè)置屬性,并且注入子組件才能正常工作。屬性通過(guò) '=' 進(jìn)行設(shè)置。子組件的注入通過(guò)調(diào)用以屬性命名的方法,并且將實(shí)例化的類(lèi)作為參數(shù)傳遞給該方法。這個(gè)約定可以被遞歸的應(yīng)用到配置的屬性以及任何 appender 子組件的子組件中。這個(gè)方法是 logback.groovy 的核心,可能是唯一需要去學(xué)習(xí)的約定。

例如,接下來(lái)的腳本實(shí)例化一個(gè) FileAppender 命名為 "FILE",設(shè)置它的 file 屬性為 "testFile.log",以及它的 append 屬性設(shè)置為 false。類(lèi)型為 PatternLayoutEncoder 的 encoder 被注入到這個(gè) appender 中。encoder 的模式屬性設(shè)置為 "%level %logger - %msg%n"。然后將這個(gè) appender 附加到 root logger 上。

appender("FILE", FileAppender) {
    file = "testFile.log"
    append = false
    encoder(PatternLayoutEncoder) {
        pattern = "%level %logger - %msg%n"
    }
}

root(DEBUG, ["FILE"])
  • timestamp(String datePattern, long timeReference = -1)

timestamp() 方法根據(jù) datePatterntimeReference 參數(shù)格式化,返回一個(gè)對(duì)應(yīng)的字符串。datePattern 參數(shù)應(yīng)該尊村SimpleDateFormat中定義的約定。如果 timeReference 沒(méi)有指定,那么默認(rèn)為 -1。在這種情況下,當(dāng)解析配置文件時(shí),當(dāng)前時(shí)間作為 timeReference 參數(shù)的值。

在下個(gè)例子中,bySecond 變量表示被 "yyyyMMdd'T'HHmmss" 格式化之后的當(dāng)前時(shí)間。之后,"bySecond" 變量被用于 file 屬性的定義中。

def bySecond = timestamp("yyyyMMdd'T'HHmmss")

appender("FILE", FileAppender) {
    file = "log-${bySecond}.txt"
    encoder(PatternLayoutEncoder) {
        pattern = "%logger{35} - %msg%n"
    }
}

root(DEBUG, ["FILE"])
  • conversionRule(String conversionWord, Class converterClass)

在創(chuàng)建了你自己的轉(zhuǎn)換說(shuō)明符之后,你需要通知 logback 它的存在。下面這個(gè)簡(jiǎn)單的 logback.groovy 文件告訴 logback 在遇到 %sample 轉(zhuǎn)換字符時(shí)使用 MySampleConverter。

import chapters.layouts.MySampleConverter

conversionRule("sample", MySampleConverter)
appender("STDOUT", ConsoleAppender) {
    encoder(PatternLayoutEncoder) {
        pattern = "%-4relative [%thread] %sample - %msg%n"
    }
}

root(DEBUG, ["STDOUT"])
  • scan(String scanPeriod = null)

調(diào)用 scan() 方法告訴 logback 周期性的掃描 logback.groovy 文件的變化。當(dāng)檢測(cè)到變化時(shí),logback.groovy 文件會(huì)被重新加載。

scan()

默認(rèn)情況下,一分鐘掃描一次配置文件。你可以通過(guò) "scanPeriod" 來(lái)指定一個(gè)不同的掃描周期。它的值可以被指定以 milliseconds, seconds, minutes 或者 hours 位單位。例如:

scan("30 seconds")

如果沒(méi)有指定時(shí)間單位,那么默認(rèn)的時(shí)間單位為 milliseconds,但是通常來(lái)說(shuō)是不合適的 (既然不合適,為什么默認(rèn)還是毫秒,費(fèi)解??)。如果你更改了默認(rèn)的掃描周期,記得要指定時(shí)間單位。更多關(guān)于掃描工作的細(xì)節(jié),請(qǐng)查看自動(dòng)加載部分。

  • statusListener(Class listenerClass)

你可以通過(guò)調(diào)用 statusListener 方法,并給該方法傳遞一個(gè)監(jiān)聽(tīng)器類(lèi),來(lái)添加一個(gè)狀態(tài)監(jiān)聽(tīng)器。例:

import chapters.layouts.MySampleConverter

// 強(qiáng)烈建議在最后一個(gè)導(dǎo)入語(yǔ)句之后,其它所有語(yǔ)句之前添加狀態(tài)監(jiān)聽(tīng)器
statusListener(OnConsoleStatusListener)

關(guān)于狀態(tài)監(jiān)聽(tīng)器請(qǐng)查看之前的章節(jié)。

  • jmxConfigurator(String name)

你可以通過(guò)該方法注冊(cè)一個(gè) JMXConfigurator MBean。無(wú)參調(diào)用將會(huì)使用 logback 默認(rèn)的對(duì)象名 (ch.qos.logback.classic:Name=default,Type=ch.qos.logback.classic.jmx.JMXConfigurator) 去注冊(cè) MBean。

jmxConfigurator()

要改變 Name 鍵的值,而不是 "default",僅僅只需要給 jmxConfigurator 方法傳遞一個(gè)不同的名字參數(shù)就可以了。

jmxConfigurator('MyName')

如果你想要完整的定義對(duì)象名,可以使用同樣的語(yǔ)法,但是需要傳遞一個(gè)有效的對(duì)象名字符串作為參數(shù):

jmxConfigurator('myApp:type=LoggerManager')

該方法首先會(huì)去嘗試將該參數(shù)作為對(duì)象名,如果它不表示一個(gè)有效的對(duì)象名,則會(huì)把它當(dāng)作 "Name" 鍵的值。

內(nèi)置 DSL

logback.groovy 是一個(gè)內(nèi)置 DSL 的意思是,它的內(nèi)容可以作為 Groovy 腳本執(zhí)行。因此,所有常用的 Groovy 指令,例如類(lèi)的導(dǎo)入,GString,變量的定義,包含字符串 (GString) 的 ${..} 評(píng)估表達(dá)式,if-else 語(yǔ)句這些在 logback.groovy 文件中都是可用的。在接下來(lái)的討論中,我們將會(huì)展示 Groovy 指令在 logback.groovy 文件中的典型用法。

變量定義與 GString

你可以在 logback.groovy 文件中的任何地方定義變量,然后在 GString 中使用該變量。例如:

def USER_HOME = System.getProperty("user.home")

appender("FILE", FileAppender) {
    // 使用 USER_HOME 變量
    file = "${USER_HOME}/myApp.log"
    encoder(PatternLayoutEncoder) {
        pattern = "%msg%n"
    }
}
root(DEBUG, ["FILE"])

在控制臺(tái)打印

通過(guò)調(diào)用 Groovy 的 println 方法在控制臺(tái)進(jìn)行打印。例如:

def USER_HOME = System.getProperty("user.home");
println "USER_HOME=${USER_HOME}"

appender("FILE", FileAppender) {
    println "Setting [file] property to [${USER_HOME}/myApp.log]"
    file = "${USER_HOME}/myApp.log"
    encoder(PatternLayoutEncoder) {
        pattern = "%msg%n"
    }
}
root(DEBUG, ["FILE"])

自動(dòng)輸出字段

'hostname' 變量

'hostname' 變量包含當(dāng)前 host 的名字。但是由于作用域規(guī)則,所以作者不能完全解釋清楚 (??)。'hostname' 變量只在最上層的作用域中有效,但是在內(nèi)部的作用域中無(wú)效。下面的例子應(yīng)該可以解釋這一點(diǎn):

// 如果當(dāng)前 host 的名字為 x,那么將會(huì)輸出 "hostname is x"
println "hostname is ${hostname}"

appender("STDOUT", ConsoleAppender) {
    // 將會(huì)輸出 "hostname is null"
    println "hostname is ${hostname}"
}

如果你想要在所有的作用域中使用 hostname 變量。那么你需要定義一個(gè)變量,并將 'hostname' 的值賦給它。如下:

// 將 hostname 的值賦給 HOSTNAME
def HOSTNAME = hostname

// 如果當(dāng)前 host 的名字為 x,那么將會(huì)輸出 "hostname is x"
println "hostname is ${HOSTNAME}"

appender("STDOUT", ConsoleAppender) {
    // 如果當(dāng)前 host 的名字為 x,那么將會(huì)輸出 "hostname is x"
    println "hostname is ${HOSTNAME}"
}

任何對(duì)于當(dāng)前上下文的引用都是上下文感知的

logback.groovy 腳本是在 ContextAware 對(duì)象的范圍內(nèi)執(zhí)行完成的。因此,在當(dāng)前上下文的范圍內(nèi),你可以使用 'context',并且可以通過(guò) addInfo()、addWarn()、與 addError() 方法將狀態(tài)信息發(fā)送給上下文的 StatusManager。

// 添加一個(gè)控制臺(tái)轉(zhuǎn)態(tài)監(jiān)聽(tīng)器總是沒(méi)錯(cuò)的
statusListener(OnConsoleStatusListener)

// 設(shè)置上下文的名字為 wombat
context.name = 'wombat'

// 添加一個(gè)關(guān)于上下文名字的狀態(tài)信息
addInfo("Context name has been set to ${context_name}")

def USER_HOME = System.getProperty("user.home")

// 添加關(guān)于 USRE_HOME 的狀態(tài)信息
addInfo("USER_HOME=${USER_HOME}")

appender("FILE", FileAppender) {
    addInfo("Setting [file] property to [${USER_NAME}/myApp.log]")
    file = "${USER_HOME}/myApp.log"
    encoder(PatternLayoutEncoder) {
        pattern = "%msg%n"
    }
}
root(DEBUG, ["FILE"])

條件配置

由于 Groovy 是一種完全成熟的編程語(yǔ)言,條件語(yǔ)句允許單一的 logback.groovy 文件用來(lái)適用不同的環(huán)境,例如開(kāi)發(fā),測(cè)試以及生產(chǎn)。

在下個(gè)腳本中,console appender 根據(jù) host 來(lái)激活,而不是我們的生產(chǎn)環(huán)境 pixie 或 orion。rolling file appender 的輸出目錄也是根據(jù) host 來(lái)確定。

statusListener(OnConsoleStatusListener)

def appenderList = ["ROLLING"]
def WEBAPP_DIR = "."
def consoleAppender = true;

// hostname 是否匹配 pixie 或 orion
if (hostname =~ /pixie|orion/) {
    WEBAPP_DIR = "/opt/myapp"
    consoleAppender = false
} else {
    appenderList.add("CONSOLE")
}

if (consoleAppender) {
    appender("CONSOLE", ConsoleAppender) {
        encoder(PatternLayoutEncoder) {
            pattern = "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
        }
    }
}

appender("ROLLING", RollingFileAppender) {
    encoder(PatternLayoutEncoder) {
        Pattern = "%d %level %thread %mdc %logger - %m%n"
    }
    rollingPolicy(TimeBasedRollingPolicy) {
        FileNamePattern = "${WEBAPP_DIR}/log/translator-%d{yyyy-MM}.zip"
    }
}

root(INFO, appenderList)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 最近在學(xué)習(xí)使用同事開(kāi)發(fā)的日志框架,感覺(jué)對(duì)于logback除了基本的使用并沒(méi)有很深刻的了解,遂對(duì)debug過(guò)程做大概...
    Eshin_Ye閱讀 2,726評(píng)論 0 2
  • 在logback學(xué)習(xí)筆記(上)中我們介紹了logback中的一些核心概念,在這篇文章中我們共同來(lái)學(xué)習(xí)一下如何利用配...
    阿龍的學(xué)與思閱讀 1,837評(píng)論 0 3
  • 第十四章:Receivers receiver 是 logback 的一個(gè)組件,用于接收遠(yuǎn)程 appender 的...
    ChinaXieShuai閱讀 1,385評(píng)論 0 0
  • 我不太喜歡看小說(shuō)總覺(jué)得虛構(gòu)類(lèi)的作品沒(méi)太大意義大多是打發(fā)時(shí)間用說(shuō)來(lái)慚愧知曉嚴(yán)歌苓是《金陵十三釵》電影上映而后是鞏俐陳...
    一頓要吃三兩肉閱讀 392評(píng)論 0 0
  • 第六章溫暖的旅程 “到昆明的旅客現(xiàn)在開(kāi)始登機(jī)?!睆V播里面一遍遍播放著不同的語(yǔ)言。 起飛。 飛機(jī)升起的瞬間,突然覺(jué)得...
    林嘉瑞閱讀 657評(píng)論 8 5

友情鏈接更多精彩內(nèi)容