Datadog 을 SpringBoot 와 연동하기

본 글에서는 Spring Boot 애플리케이션을 Datadog에 연동하여 로깅 데이터를 실시간으로 전송하는 방법을 상세히 안내합니다.

 

회사에서 Datadog이라는 로깅 관련 솔루션을 사용하게 되었다. 이 솔루션에 Spring Boot 를 연동하는 방법을 기술한다.

 

Datadog 공식 문서 를 보면, 두 가지 방법을 통해 로그를 동기화할 수 있다고 밝힌다.

1. Agent Job 을 통해서 로그를 동기화
2. logger 에서 실시간으로 로그를 Datadog 에 전송

 

 Agent Job 을 통해 파일 시스템의 로그 파일들을 Datadog 에 연동하는 것을 권장하고 있다. 이 방법으로 연동하려면, 서버 내에 Datadog Agent 를 설치해서 주기적으로 로그 파일들을 전송하게 설정해야 한다. 추후에는 이 방법으로 변경하는 것이 바람직해 보인다. API 서버 퍼포먼스 자체에 영향이 있을 수 있기 때문이다. 하지만 현재 상황은 다음과 같은 두 가지 어려움에 봉착하게 된다.

1. API 서버들이 ECS 내부의 Docker Container 로 존재하기 때문에, 도커 내부에 agent 파일을 설치하고, 동기화하는 작업을 수행해야 한다.
2. 현재 로그 파일이 Daily rolling 되기 때문에, 동기화될 파일의 이름과 경로를 명확히 알기 어렵다.

 

따라서, 2. logger 에서 실시간으로 로그를 Datadog 에 전송하는 방식을 선택하였다. 구체적인 방법은 다음과 같다.

 


 

1. Dependency 추가

    <properties>
        <java.version>11</java.version>
        <lib.dependency.path>${project.basedir}/lib</lib.dependency.path>
        <ch.qos.logback.version>1.2.0</ch.qos.logback.version>
    </properties>
    <!-- Spring Boot 내장 logging dependency 와 충돌하지 않도록, logback 버전을 통일한다. -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-core</artifactId>
                <version>${ch.qos.logback.version}</version>
            </dependency>
            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-classic</artifactId>
                <version>${ch.qos.logback.version}</version>
            </dependency>
            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-access</artifactId>
                <version>${ch.qos.logback.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

        <!-- Dependencies 내부에 logstash 추가 -->
        <dependency>
            <groupId>net.logstash.logback</groupId>
            <artifactId>logstash-logback-encoder</artifactId>
            <version>6.4</version>
        </dependency>

 

2. logback-spring.xml 추가

LogstashTcpSocketAppender 을 appender 에 추가하고, 각각의 profile 에 등록해준다.

<?xml version="1.0" encoding="UTF-8"?>
<!--  
jdbc.sqlonly    : Logs only SQL
jdbc.sqltiming  : Logs the SQL, post-execution, including timing execution statistics
jdbc.audit      : Logs ALL JDBC calls except for ResultSets
jdbc.resultset  : all calls to ResultSet objects are logged
jdbc.connection : Logs connection open and close events
-->
<configuration>

    <springProperty name="DATADOG_API_KEY" source="datadog.api-key" />
    <springProperty name="ACTIVE_PROFILE" source="spring.profiles.active" />

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <!-- By default, encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss, Asia/Seoul} [%thread] %-3level %logger{5} - %msg %n</pattern>
        </encoder>
    </appender>


    <appender name="DATADOG" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
        <destination>intake.logs.datadoghq.com:10514</destination>
        <keepAliveDuration>1 minute</keepAliveDuration>
        <includeCallerData>true</includeCallerData>
        <encoder class="net.logstash.logback.encoder.LogstashEncoder">
            <includeCallerData>true</includeCallerData>
            <includeTags>true</includeTags>
            <customFields>{"ddsource": "springboot", "service": "java-api-server", "label": "${ACTIVE_PROFILE}"}</customFields>
            <prefix class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
                <layout class="ch.qos.logback.classic.PatternLayout">
                    <pattern>${DATADOG_API_KEY} %mdc{weJustNeedSthEmptyHereSoTheXMLParserWillKeepAWhitespace}</pattern>
                </layout>
            </prefix>
        </encoder>
    </appender>

    <appender name="dailyRollingFileAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_FILE}/api.%d{yyyy-MM-dd}-%i.log</fileNamePattern>
            <maxHistory>10</maxHistory>
            <cleanHistoryOnStart>true</cleanHistoryOnStart>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder>
            <charset>UTF-8</charset>
            <pattern>%d{yyyy:MM:dd HH:mm:ss.SSS, Asia/Seoul} %-5level --- [%thread] %logger{35} : %msg %n</pattern>
        </encoder>
    </appender>

    <logger name="jdbc" level="OFF"/>

    <!-- <logger name="jdbc.sqlonly" level="INFO" additivity="false">
      <appender-ref ref="STDOUT" />
      <appender-ref ref="dailyRollingFileAppender" />
    </logger>

    <logger name="jdbc.resultset" level="INFO" additivity="false">
      <appender-ref ref="STDOUT" />
      <appender-ref ref="dailyRollingFileAppender" />
    </logger> -->

    <!-- <logger name="jdbc.resultsettable" level="INFO" additivity="false">
      <appender-ref ref="STDOUT" />
      <appender-ref ref="dailyRollingFileAppender" />
    </logger>   -->

    <logger name="jdbc.sqltiming" level="INFO" additivity="false">
        <appender-ref ref="dailyRollingFileAppender" />
    </logger>

    <root level="INFO">
        <springProfile name="local">
            <appender-ref ref="STDOUT"/>
        </springProfile>
        <springProfile name="dev">
            <appender-ref ref="DATADOG" />
            <appender-ref ref="dailyRollingFileAppender"/>
        </springProfile>
        <springProfile name="prod">
            <appender-ref ref="DATADOG" />
            <appender-ref ref="dailyRollingFileAppender"/>
        </springProfile>
        <springProfile name="green">
            <appender-ref ref="DATADOG" />
            <appender-ref ref="dailyRollingFileAppender"/>
        </springProfile>
        <springProfile name="blue">
            <appender-ref ref="DATADOG" />
            <appender-ref ref="dailyRollingFileAppender"/>
        </springProfile>
        <springProfile name="staging">
            <appender-ref ref="DATADOG" />
            <appender-ref ref="dailyRollingFileAppender"/>
        </springProfile>
    </root>

</configuration>

 


이것도 읽어보세요