๐ ๋์ ๋ฐฐ๊ฒฝ
์๋ WARN ๋ ๋ฒจ ์ด์ ๋ก๊ทธ๋ฅผ Sentry๋ก ๋ณด๋์๋ค.
ํ์ง๋ง Sentry ๋ฌด๋ฃ ๋ผ์ด์ ์ค๋ฅผ ์ด์ฉ ์ค์ด๋ผ์ ๋์ ํ ์ง 10์ผ ๋ง์ ์ฉ๋ ์ด๊ณผ๋ก ์ด๋ฉ์ผ์ด ์๋ค.
๊ทธ๋์ ์ฐพ์ ๋์์ด ์ฌ๋์ด์๋ค.
์ด๋ป๊ฒ ํด์ผํ ๊น?
๋จผ์ spring์์ log๋ฅผ ์ด๋ป๊ฒ ์ฒ๋ฆฌํ ์ ์๋์ง ์์๋ณด์.
๐ Log
๐ต System.out.printIn()
์๋ฐ์์ ๋ก๊ทธ๋ฅผ ํ์ํ๋ ๊ธฐ๋ณธ ์ค์ ๊ธฐ๋ณธ ํจ์์ด๋ค.
ํ์ง๋ง ์ด๋ ์๋์ ๊ฐ์ ์ด์ ๋ก ์ฐ๋ฉด ์ ๋๋ค.
- ํ๋ฐ๋๋ค
๋ก๊ทธ๋ฅผ ๋จ์ง ์ฝ์์ ๋์ฐ๊ธฐ๋ง ํ๊ณ , ๋ค๋ฅธ ํ์ผ์ ์ ์ฅํ์ง ์๋๋ค.
์ค์ ๋ก ์ ์ฅ๋๋ ๊ฒ์ด ์์ผ๋ฏ๋ก, ์ถํ ๋ก๊ทธ๋ฅผ ์ด์ฉํ ์์คํ ๊ด๋ จ ์์ ์ด ๋ถ๊ฐ๋ฅํ๋ค. - ๋ก๊ทธ๋ง ๋์จ๋ค
๋ก๊ทธ๊ฐ ์ ๋์ค๋๋ฐ ๋ฌด์จ ๋ฌธ์ ๊ฐ ์๋๊ฐ?๋ผ๋ ์ง๋ฌธ์ด ๋์ฌ ์ ์๋ค.
ํ์ง๋ง ์ด๋ ๋ฌธ์ ๋ก ์์ฉํ๋ค.
๋ก๊ทธ๋ฟ๋ง์ด ์๋๋ผ ์๊ฐ, ์ค๋ ๋ ์ด๋ฆ ๋ฑ ๋ค์ํ ์ ๋ณด๊ฐ ๋๋ฒ๊น ์ ์ด๋ ค์์ด ์์ ์ ์๋ค. - ์ฑ๋ฅ ์ ํ์ ์์ธ์ด ๋ ์ ์๋ค.
์ด ํจ์๋ synchronized๋ก ์๋ํ๋ค.
์คํ๋ง๊ณผ ๊ฐ์ด ๋ฉํฐ ์ค๋ ๋๋ฅผ ์ง์ํ๋ ํ๊ฒฝ์ด๊ณ ๋ค๋์ ์ค๋ ๋๊ฐ ๋ก๊ทธ๋ฅผ ํ์ํ๊ธธ ์ํ๋ค๋ฉด,
๋๊ธฐ์ ์ธ ์๋ ๋๋ฌธ์ ์ฑ๋ฅ์ ๋ฌธ์ ๊ฐ ์๊ธธ ๊ฐ๋ฅ์ฑ์ด ์๋ค.
๐ต slf4j
Simple Logging Facade for Java์ ์ค์๋ง์ด๋ค.
๋ก๊ทธ๋ฅผ ์ฒ๋ฆฌํด ์ฃผ๋ ํ๋ ์์ํฌ์ด๋ค.
์คํ๋ง์ ์ด์ฉํด ๊ฐ๋ฐํ๋ค ๋ณด๋ฉด ๋ง์ด ๋ดค์ ๊ฑฐ๋ผ ์๊ฐํ๋ค.
๋กฌ๋ณต ์ด๋ ธํ ์ด์ ์ค @Slf4j ๊ฐ ๋ฐ๋ก ์ด๊ฒ์ ๋งํ๋ ๊ฒ์ด๋ค.
ํ์ง๋ง ์ด๊ฒ์ ๊ตฌํ์ฒด๊ฐ ์๋๋ค.
์ธํฐํ์ด์ค์ด๋ค.
๊ทธ๋ ๋ค๋ฉด ์ด๊ฒ์ ๊ตฌํ์ฒด๊ฐ ๋ฌด์์ผ๊น?
log4 j, logback, log4j2 ๊ฐ ์์ ๊ตฌํ์ฒด์ด๋ค.
log4j -> logback -> log4j2 ์์๋ก ๋ฐํ๋์๋ค.
ํ๋ํ๋ ์์๋ณด์.
log4j
- ์๋ฌ ๋ ๋ฒจ ๊ธฐ๋ฅ ์ง์
- ๋ก๊ทธ ์ถ๋ ฅ ํ์ผ ์ง์ ๋ฐ ๋ค์ํ ๋ก๊ทธ ์ถ๋ ฅ ํ์ ์ง์
logback
- log4j ํ์ ๋ฒ์
- Spring์ spring-boot-starter-logging์์ ์ง์ํด ์ฃผ๋ ๋ํดํธ ๊ตฌํ์ฒด
- ํํฐ ์ ์ฑ ์ฌ์ฉ ๊ฐ๋ฅ
- XML, groovy ์ค์ ์ง์
log4j2
- logback ํ์ ๋ฒ์
- ๋ฉํฐ์ค๋ ๋ ํ๊ฒฝ์์ async logger ์ฑ๋ฅ์ด logback๋ณด๋ค ์ข์
- lambda, lazy evaluation ์ง์
- ์์ ๊ฐ์ฒด๊ฐ ๊ฐ๋ฅํ ๋ง์ด ํ ๋น๋์ง ์๋ ๊ฐ๋น์ง ์๋ ๋ชจ๋์์ ์คํ ๊ฐ๋ฅ
์๋ฅผ ๋ดค์ ๋, log4j2๊ฐ ๊ฐ์ฅ ์ข์ ๋ณด์ธ๋ค.
ํ์ง๋ง ์ด๋ ๋ณด์ ๊ด๋ จ ์ด์ ์์๋ค.
ํ์ฌ๋ ํด๊ฒฐ๋์๋ค๊ณ ํ๋ค.
ํ์๋ ์ด๋ฅผ ์ฌ์ฉํ์ง ์๊ณ spring ๊ธฐ๋ณธ์ธ logback์ ์ฌ์ฉํ๋ค.
๐ Logback
๐ต Logback
์ด๋ ๋ก๊น ์์คํ ์ด๋ค.
logback์ ์๋์ ๊ฐ์ ๊ตฌ์ฑ ์์๋ฅผ ๊ฐ์ง๋ค.
- appender
: logging ์ด๋ฒคํธ ์ฒ๋ฆฌ ์ญํ ์ ๋ด๋น. Appender ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํด ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌ - logger
: ์ด๋ฒคํธ์ ๋์, ์ด๋ค ๋ด์ฉ์ log๋ก ๋จ๊ธธ ๊ฒ์ธ์ง ์ ์ํ๋ฉฐ log level์ ์ ํ์ ์ผ๋ก ์ค์ ํ ์ ์์ - root
: ์ต์๋จ์ logger๋ฅผ root๋ผ ์นญํจ
์ ๊ตฌ์ฑ์์ ์ค, ์ฐ๋ฆฌ๊ฐ ์ํ๋ ๊ฒ์ ๊ตฌํํ๊ธฐ ์ํด์ appender๋ฅผ ์ปค์คํ ํด์ผ ํ๋ค.
๐ Appender, Slack ์ฐ๋
๐ต Appender ์ปค์คํ
class SlackAppender : AppenderBase<ILoggingEvent>() {
private var token: String = ""
fun setToken(token: String) {
this.token = token
}
override fun append(event: ILoggingEvent) {
val message = SlackMessageModel(
text = """
$event
""".trimIndent()
)
WarningLogService.sendLog(message, token)
}
}
์ปค์คํ appender๋ฅผ ๋ง๋ค๊ธฐ ์ํด์ AppenderBase์ ๊ตฌํ์ฒด๋ฅผ ๋ง๋ค๋ฉด ๋๋ค.
๊ทธ๋ฆฌ๊ณ ์ด๋ฅผ logback.xml์ ๋ฑ๋กํ๋ฉด ์ฌ์ฉํ ์ ์๋ค.
์ด๋, logback์ ์คํ๋ง์ด ์์๋๊ธฐ ์ด์ ์ ์ด๊ธฐํ๋๋ค.
์ฆ, ๋น, IoC ๋ฑ ์คํ๋ง์ ์ฅ์ ์ ์ด์ฉํ์ง ๋ชปํ๋ค.
๊ทธ๋ฌ๋ฏ๋ก ๋ค๋ฅธ ํด๋์ค์ ํจ์๋ฅผ ์ด์ฉํ๊ณ ์ถ๋ค๋ฉด, ํด๋น ํด๋์ค์ ์ ์ ํจ์๋ก ์ ์ธํด์ผ ํ๋ค.
์ฌ๋ ์ ์ก ์ฃผ์๋ ์ธ๋ถ๋ก ๋ ธ์ถํ๋ฉด ์ ๋๋ฏ๋ก, xml ํ์ผ์์ ๋ฐ์ ์ ์๊ฒ ํ๋ค.
๐ต Slack ์ ์ก ์๋น์ค
class WarningLogService {
companion object {
private const val SLACK_WEBHOOKS_DOMAIN = "https://hooks.slack.com/services"
private val webClient = WebClientFactory.generate(baseUrl = SLACK_WEBHOOKS_DOMAIN)
fun sendLog(message: SlackMessageModel, token: String) {
CoroutineScope(Dispatchers.IO).launch {
webClient.post()
.uri("/$token")
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(message)
.retrieve()
.awaitBody()
}
}
}
}
์์์ ์ค๋ช ํ ์ด์ ๋ก ์ ์ ํจ์๋ก ์ฌ๋ ๋ฉ์์ง ์ ์ก ํจ์๋ฅผ ๋ง๋ค์๋ค.
webclient๋ฅผ ์์ฑํ์ฌ ๋ฉ์์ง๋ฅผ ๋ณด๋ด์ฃผ๋ ์ญํ ์ ํด์ค๋ค.
๋ก๊น ์ ๋๊ธฐ์ ์ผ๋ก ์๋ํ๋ค.
์ด๋, ํ ๋ก๊น ํ๋ก์ธ์ค๊ฐ slack๊ณผ์ ํต์ ์ ์ด์ ๋ก block ํ๋ฉด,
๋ค์ ๋ฉ์์ง๋ ์์ ๋ฉ์์ง์ ํต์ ์ด ๋๋๋ ์์ ๊น์ง ๊ธฐ๋ค๋ ค์ผ ํ๋ค.
๊ทธ๋์ ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ฅผ ๊ผญ ํด์ค์ผ ํ๋ค.
AsyncAppender๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ๋ ์์ผ๋, ํ ํ๋ก์ ํธ์์๋ ์ฝ๋ฃจํด์ ์ฌ์ฉํ๋ฏ๋ก AsyncAppender๋ฅผ ์ฌ์ฉํ์ง ์์๋ค.
๐ต Slack ์ ์ก ์๋น์ค
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true">
<include resource="org/springframework/boot/logging/logback/base.xml"/>
<springProperty scope="context" name="logLevel" source="logging.level.root" defaultValue="INFO"/>
<springProperty scope="context" name="warningLogToken" source="slack.warning-log-token" defaultValue=""/>
...
<appender name="SLACK_WARNING_LOG" class="com.oksusu.susu.client.appender.SlackAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>WARN</level>
</filter>
<token>${warningLogToken}</token>
</appender>
<springProfile name="default, dev">
<root level="${logLevel}">
</root>
</springProfile>
<springProfile name="prod">
<root level="${logLevel}">
<appender-ref ref="SLACK_WARNING_LOG"/>
<!-- <appender-ref ref="SENTRY"/>-->
</root>
</springProfile>
</configuration>
logback์ ์ค์ ํ์ผ์ด๋ค.
์ฌ๊ธฐ์ ์์์ ๋ง๋ appender๋ฅผ ๋ฑ๋กํ๊ณ ์ฌ์ฉํ๋ฉด ๋๋ค.
์์์ ์ค๋ช ํ ๊ฒ์ฒ๋ผ, slack ์ฃผ์๋ฅผ ๊ฐ์ ธ์์ appender์ ์ฃผ์ ํด์ค์ผ ํ๋ค.
๊ทธ๋ฌ๋ฏ๋ก <springProperty...>๋ฅผ ์ด์ฉํ์ฌ ์ฃผ์๋ฅผ ๊ฐ์ ธ์์ appender์ ์ฃผ์ ํด ์คฌ๋ค.
์ด ํ๋ก์ ํธ์์๋ prod ํ๊ฒฝ์์๋ง ์๋ํ๋๋ก ํ ์์ ์ด๋ฏ๋ก, ํ๋กํ์ผ์ด prod์ผ ๊ฒฝ์ฐ ์คํ๋๋ ๋ถ๋ถ์ ์ ์ด๋๋ค.
๐ต ๊ฒฐ๊ณผ
์ด ๊ธ์ ์ฐ๋ ์ง๊ธ๊น์ง ์ ์์ ์ผ๋ก ์๋ํ๊ณ ์๋ค.
'Backend' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
<Spring> SUSU์ Coroutine (0) | 2024.05.11 |
---|---|
<Spring> Webflux + Coroutine + MDC (0) | 2024.05.10 |
<Spring> Spring Bean, IoC/DI ์ ๋ฆฌ 2 (0) | 2023.10.03 |
<Spring> Spring Bean, IoC/DI ์ ๋ฆฌ 1 (0) | 2023.10.03 |
<Spring> Redis ๊ธฐ๋ฐ ๊ฒ์์ด ์๋ ์์ฑ ๊ธฐ๋ฅ ๊ตฌํํ๊ธฐ (1) | 2023.10.01 |