iT邦幫忙

2021 iThome 鐵人賽

DAY 19
0
Software Development

一個新鮮人如何完轉Spring boot與DevOps從0到101系列 第 19

Spring boot 配置 Fluent bit 傳遞 Log

上篇文章透過 docker-compose 進行 Log 傳遞,這次則是使用 Spring boot 環境變數進行定義。範例在github 上,這個專案主要是實現簡單的 CRUD,後續有加 Log 相關的配置。

首先,主要加入以下的庫。

...
<dependency>
			<groupId>org.fluentd</groupId>
			<artifactId>fluent-logger</artifactId>
			<version>0.3.4</version>
		</dependency>

		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>1.2.3</version>
		</dependency>
		<!-- send to flunted -->
		<dependency>
			<groupId>com.sndyuk</groupId>
			<artifactId>logback-more-appenders</artifactId>
			<version>1.8.0</version>
		</dependency>

		<dependency>
			<groupId>net.logstash.logback</groupId>
			<artifactId>logstash-logback-encoder</artifactId>
			<version>6.6</version>
		</dependency>

configuration/Fluentd.java 中定義了一個關於 Fluent bit 的連線配置。

@Configuration
@ConfigurationProperties(prefix = "cch.fluentd")
@Setter
@Getter
public class Fluentd {
    @Value("${cch.fluentd.host}")
    private String host;
    @Value("${cch.fluentd.port}")
    private String port;
}

連線配置可以如在 src/main/resources/application.properties 下的配置

...
logging.config=classpath:logback-spring.xml 
cch.fluentd.host=192.168.101.129 # this
cch.fluentd.port=3003 # this

src/main/resources 下定義一個 logback-spring.xml 它是用來配置 Log,在上一篇並沒有這個。定義內容如下

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- springProperty 可當作是環境變數宣告-->
    <springProperty scope="context" name="fluentHost" source="cch.fluentd.host"/>
    <springProperty scope="context" name="fluentPort" source="cch.fluentd.port" defaultValue="24224" />
    <springProperty scope="context" name="springAppName" source="spring.application.name" />
    <springProperty scope="context" name="env" source="spring.profiles.active" defaultValue="local"/>
    <!-- 建立一個 `ConsoleAppender` 的類,相似於 `System.out.print` 打印數據一樣。該配置設置了日誌輸出的格式,這些表示方式根據已發送到記錄器的訊息替換為生成的值。
-->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <!-- console 打印 -->
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level - %logger{36} - %msg trace_id=%X{trace_id} span_id=%X{span_id} trace_flags=%X{trace_flags} %n</pattern> <!-- 定義 Log 結構,可以參考 https://ithelp.ithome.com.tw/articles/10263898 -->
        </encoder>
    </appender>
    <appender name="FLUENT" class="ch.qos.logback.more.appenders.DataFluentAppender"> <!-- 定義 傳遞到 fluent bit 的連線 -->
        <tag>debug</tag>
        <label>normal</label>
        <remoteHost>${fluentHost}</remoteHost>
        <port>${fluentPort}</port>

        <additionalField>
            <key>env</key>
            <value>${env}</value>
        </additionalField>

        <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder"> <!-- 將日誌轉 Json 格式-->
            <providers class="net.logstash.logback.composite.loggingevent.LoggingEventJsonProviders">
                <pattern>
                    <pattern>
                        {
                        "timestamp": "%date{ISO8601}",
                        "level": "%level",
                        "application": "${springAppName:-}",
                        "trace": "%X{trace_id:-}",
                        "span": "%X{span_id:-}",
                        "trace_flags": "%X{trace_flags:-}",
                        "pid": "${PID:-}",
                        "thread": "%thread",
                        "class": "%logger{40}",
                        "message": "%message"
                        }
                    </pattern>
                </pattern>
            </providers>
        </encoder>
    </appender>
   
    <springProfile name="dev">
        <root level="DEBUG">
			<appender-ref ref="STDOUT" />
            <appender-ref ref="FLUENT" />
		</root>
    </springProfile>
    <!-- 配置將日誌寫入檔案 -->
    <springProfile name="file">
        <property name="logPath" value="/var/log"/>
        <appender name="fileInfoLog" filePermissions="rw-r--r--" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
                <providers class="net.logstash.logback.composite.loggingevent.LoggingEventJsonProviders">
                    <pattern>
                        <pattern>
                            {
                                "timestamp": "%date{ISO8601}",
                                "level": "%level",
                                "application": "${springAppName:-}",
                                "trace": "%X{trace_id:-}",
                                "span": "%X{span_id:-}",
                                "trace_flags": "%X{trace_flags:-}",
                                "pid": "${PID:-}",
                                "thread": "%thread",
                                "class": "%logger{40}",
                                "message": "%message"
                            }
                        </pattern>
                    </pattern>
                </providers>
            </encoder>
            <!--滾動策略-->
            <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
                <!--路徑-->
                <fileNamePattern>${logPath}/info.%d{dd-MM-yyyy}_%i.log</fileNamePattern>
                <maxHistory>7</maxHistory>
                <maxFileSize>10MB</maxFileSize>
                <totalSizeCap>100MB</totalSizeCap>
            </rollingPolicy>
        </appender>

        <root level="INFO">
            <appender-ref ref="STDOUT" />
            <appender-ref ref="fileInfoLog" /> 
        </root>
    </springProfile>

    <springProfile name="prod">
        <root level="ERROR">
			<appender-ref ref="STDOUT" />
            <appender-ref ref="FLUENT" />
		</root>
    </springProfile>

    <root level="INFO">
        <appender-ref ref="STDOUT" />
        <appender-ref ref="FLUENT" />
    </root>
</configuration>

在配置上使用了 springProfile 來切分不同環境上的輸出要什麼,當中 <springProfile name="file"> 的範疇定義了以檔案方式儲存 Log,並定義其生命週期,如下

<!--滾動策略-->
            <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
                <!--路徑-->
                <fileNamePattern>${logPath}/info.%d{dd-MM-yyyy}_%i.log</fileNamePattern>
                <maxHistory>7</maxHistory>
                <maxFileSize>10MB</maxFileSize>
                <totalSizeCap>100MB</totalSizeCap>
            </rollingPolicy>

下面的配置可以說是,prod 環境,打印的 Log 是 ERROR 等級,其輸出內容參照 STDOUT(<appender name="STDOUT"... ) 和 FLUENT(<appender name="FLUENT"...)

    <springProfile name="prod">
        <root level="ERROR">
			<appender-ref ref="STDOUT" />
            <appender-ref ref="FLUENT" />
		</root>
    </springProfile>

下面是預設環境的輸出

    <root level="INFO">
        <appender-ref ref="STDOUT" />
        <appender-ref ref="FLUENT" />
    </root>

在此範例中我們可以透過 otel-docker-compose.yml 環境變數指定我們的環境變數

SPRING_PROFILES_ACTIVE: prod  # 指定我們要 DEV 還是 PROD,它會對應不同 Log 的打印和配置
CCH_FLUENTD_PORT: 3003 # 指定 Fluent bit port
CCH_FLUENTD_HOST: 192.168.101.129 # Fluent bit IP 位置

要運行的話須將 OTEL 開頭相關的環境變數進行註解

至於在 Spring boot 中為不同開發環境設置不同的配置可以如下設置,它們都定義在 resources 目錄下,命名規則通常是 application-${開發環境}.properties。如果要執行的話我們可以將 SPRING_PROFILES_ACTIVE 設置為 file 或是 dev 值,就會讀取相對應的 properties 檔案

.
├── application-dev.properties # 針對 dev
├── application-file.properties # 針對 file
├── application-prod.properties # 針對 prod
├── application.properties # 預設

上一篇
實驗 Spring boot 將 Log 傳給 EFK
下一篇
Prometheus 與 Spring boot
系列文
一個新鮮人如何完轉Spring boot與DevOps從0到10130

尚未有邦友留言

立即登入留言