generated from pricelees/issue-pr-template
feat: 기존 웹 로그에서 사용하던 payload map을 빌더 형식으로 만드는 클래스 및 테스트 추가
This commit is contained in:
parent
be19e57b61
commit
6cd269e772
@ -0,0 +1,75 @@
|
|||||||
|
package com.sangdol.common.web.support.log
|
||||||
|
|
||||||
|
import com.sangdol.common.log.constant.LogType
|
||||||
|
import com.sangdol.common.utils.MdcPrincipalIdUtil
|
||||||
|
import com.sangdol.common.utils.MdcStartTimeUtil
|
||||||
|
import jakarta.servlet.http.HttpServletRequest
|
||||||
|
|
||||||
|
class LogPayloadBuilder(
|
||||||
|
private val type: LogType,
|
||||||
|
private val servletRequest: HttpServletRequest,
|
||||||
|
private val payload: MutableMap<String, Any> = mutableMapOf("type" to type)
|
||||||
|
) {
|
||||||
|
fun endpoint(): LogPayloadBuilder {
|
||||||
|
payload["endpoint"] = "${servletRequest.method} ${servletRequest.requestURI}"
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clientIp(): LogPayloadBuilder {
|
||||||
|
servletRequest.remoteAddr?.let { payload["client_ip"] = it }
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun userAgent(): LogPayloadBuilder {
|
||||||
|
servletRequest.getHeader("User-Agent")?.let { payload["user_agent"] = it }
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun queryString(): LogPayloadBuilder {
|
||||||
|
servletRequest.queryString?.let { payload["query_params"] = it }
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun httpStatus(statusCode: Int?): LogPayloadBuilder {
|
||||||
|
statusCode?.let { payload["status_code"] = it }
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun responseBody(body: Any?): LogPayloadBuilder {
|
||||||
|
body?.let { payload["response_body"] = it }
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun durationMs(): LogPayloadBuilder {
|
||||||
|
MdcStartTimeUtil.extractDurationMsOrNull()?.let { payload["duration_ms"] = it }
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun principalId(): LogPayloadBuilder {
|
||||||
|
MdcPrincipalIdUtil.extractAsLongOrNull()
|
||||||
|
?.let { payload["principal_id"] = it }
|
||||||
|
?: run { payload["principal_id"] = "UNKNOWN" }
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun exception(exception: Exception?): LogPayloadBuilder {
|
||||||
|
exception?.let {
|
||||||
|
payload["exception"] = mapOf(
|
||||||
|
"class" to it.javaClass.simpleName,
|
||||||
|
"message" to it.message
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun additionalPayloads(payload: Map<String, Any>): LogPayloadBuilder {
|
||||||
|
this.payload.putAll(payload)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun build(): Map<String, Any> = payload
|
||||||
|
}
|
||||||
@ -0,0 +1,233 @@
|
|||||||
|
package com.sangdol.common.web.support.log
|
||||||
|
|
||||||
|
import com.sangdol.common.log.constant.LogType
|
||||||
|
import com.sangdol.common.utils.MdcPrincipalIdUtil
|
||||||
|
import com.sangdol.common.utils.MdcStartTimeUtil
|
||||||
|
import io.kotest.core.spec.style.FunSpec
|
||||||
|
import io.kotest.matchers.nulls.shouldNotBeNull
|
||||||
|
import io.kotest.matchers.shouldBe
|
||||||
|
import io.mockk.clearMocks
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
import jakarta.servlet.http.HttpServletRequest
|
||||||
|
|
||||||
|
class LogPayloadBuilderTest : FunSpec({
|
||||||
|
val servletRequest: HttpServletRequest = mockk()
|
||||||
|
|
||||||
|
lateinit var method: String
|
||||||
|
lateinit var requestUri: String
|
||||||
|
lateinit var remoteAddr: String
|
||||||
|
lateinit var userAgent: String
|
||||||
|
lateinit var queryString: String
|
||||||
|
|
||||||
|
beforeTest {
|
||||||
|
method = "GET".also { every { servletRequest.method } returns it }
|
||||||
|
requestUri = "/converter/test".also { every { servletRequest.requestURI } returns it }
|
||||||
|
remoteAddr = "localhost".also { every { servletRequest.remoteAddr } returns it }
|
||||||
|
userAgent = "Mozilla/5.0".also { every { servletRequest.getHeader("User-Agent") } returns it }
|
||||||
|
queryString = "key=value".also { every { servletRequest.queryString } returns it }
|
||||||
|
}
|
||||||
|
|
||||||
|
afterSpec {
|
||||||
|
clearMocks(servletRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
context("endpoint") {
|
||||||
|
test("정상 응답") {
|
||||||
|
val result = LogPayloadBuilder(type = LogType.INCOMING_HTTP_REQUEST, servletRequest = servletRequest)
|
||||||
|
.endpoint()
|
||||||
|
.build()
|
||||||
|
|
||||||
|
result["endpoint"] shouldBe "$method $requestUri"
|
||||||
|
}
|
||||||
|
|
||||||
|
test("ServletRequest에서 null이 반환되면 그대로 들어간다.") {
|
||||||
|
every { servletRequest.method } returns null
|
||||||
|
every { servletRequest.requestURI } returns null
|
||||||
|
|
||||||
|
val result = LogPayloadBuilder(type = LogType.INCOMING_HTTP_REQUEST, servletRequest = servletRequest)
|
||||||
|
.endpoint()
|
||||||
|
.build()
|
||||||
|
|
||||||
|
result["endpoint"] shouldBe "null null"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context("clientIp") {
|
||||||
|
test("정상 응답") {
|
||||||
|
val result = LogPayloadBuilder(type = LogType.INCOMING_HTTP_REQUEST, servletRequest = servletRequest)
|
||||||
|
.clientIp()
|
||||||
|
.build()
|
||||||
|
|
||||||
|
result["client_ip"] shouldBe remoteAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
test("ServletRequest에서 null이 반환되면 추가되지 않는다.") {
|
||||||
|
every { servletRequest.remoteAddr } returns null
|
||||||
|
|
||||||
|
val result = LogPayloadBuilder(type = LogType.INCOMING_HTTP_REQUEST, servletRequest = servletRequest)
|
||||||
|
.clientIp()
|
||||||
|
.build()
|
||||||
|
|
||||||
|
result["client_ip"] shouldBe null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context("userAgent") {
|
||||||
|
test("정상 응답") {
|
||||||
|
val result = LogPayloadBuilder(type = LogType.INCOMING_HTTP_REQUEST, servletRequest = servletRequest)
|
||||||
|
.userAgent()
|
||||||
|
.build()
|
||||||
|
|
||||||
|
result["user_agent"] shouldBe userAgent
|
||||||
|
}
|
||||||
|
|
||||||
|
test("ServletRequest에서 null이 반환되면 추가되지 않는다.") {
|
||||||
|
every { servletRequest.getHeader("User-Agent") } returns null
|
||||||
|
|
||||||
|
val result = LogPayloadBuilder(type = LogType.INCOMING_HTTP_REQUEST, servletRequest = servletRequest)
|
||||||
|
.userAgent()
|
||||||
|
.build()
|
||||||
|
|
||||||
|
result["user_agent"] shouldBe null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context("queryString") {
|
||||||
|
test("정상 응답") {
|
||||||
|
val result = LogPayloadBuilder(type = LogType.INCOMING_HTTP_REQUEST, servletRequest = servletRequest)
|
||||||
|
.queryString()
|
||||||
|
.build()
|
||||||
|
|
||||||
|
result["query_params"] shouldBe queryString
|
||||||
|
}
|
||||||
|
|
||||||
|
test("ServletRequest에서 null이 반환되면 추가되지 않는다.") {
|
||||||
|
every { servletRequest.queryString } returns null
|
||||||
|
|
||||||
|
val result = LogPayloadBuilder(type = LogType.INCOMING_HTTP_REQUEST, servletRequest = servletRequest)
|
||||||
|
.queryString()
|
||||||
|
.build()
|
||||||
|
|
||||||
|
result["query_params"] shouldBe null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context("httpStatus") {
|
||||||
|
test("정상 응답") {
|
||||||
|
val result = LogPayloadBuilder(type = LogType.INCOMING_HTTP_REQUEST, servletRequest = servletRequest)
|
||||||
|
.httpStatus(200)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
result["status_code"] shouldBe 200
|
||||||
|
}
|
||||||
|
|
||||||
|
test("null을 입력하면 추가되지 않는다.") {
|
||||||
|
val result = LogPayloadBuilder(type = LogType.INCOMING_HTTP_REQUEST, servletRequest = servletRequest)
|
||||||
|
.httpStatus(null)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
result["status_code"] shouldBe null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context("responseBody") {
|
||||||
|
test("정상 응답") {
|
||||||
|
val body = mapOf("key" to "value")
|
||||||
|
val result = LogPayloadBuilder(type = LogType.INCOMING_HTTP_REQUEST, servletRequest = servletRequest)
|
||||||
|
.responseBody(body)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
result["response_body"] shouldBe body
|
||||||
|
}
|
||||||
|
|
||||||
|
test("null을 입력하면 추가되지 않는다.") {
|
||||||
|
val result = LogPayloadBuilder(type = LogType.INCOMING_HTTP_REQUEST, servletRequest = servletRequest)
|
||||||
|
.responseBody(null)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
result["response_body"] shouldBe null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context("durationMs") {
|
||||||
|
test("정상 응답") {
|
||||||
|
MdcStartTimeUtil.setCurrentTime()
|
||||||
|
|
||||||
|
val result = LogPayloadBuilder(type = LogType.INCOMING_HTTP_REQUEST, servletRequest = servletRequest)
|
||||||
|
.durationMs()
|
||||||
|
.build()
|
||||||
|
|
||||||
|
result["duration_ms"].shouldNotBeNull()
|
||||||
|
MdcStartTimeUtil.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
test("MDC에서 값을 가져올 수 없으면 추가되지 않는다.") {
|
||||||
|
val result = LogPayloadBuilder(type = LogType.INCOMING_HTTP_REQUEST, servletRequest = servletRequest)
|
||||||
|
.durationMs()
|
||||||
|
.build()
|
||||||
|
|
||||||
|
result["duration_ms"] shouldBe null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context("principalId") {
|
||||||
|
test("정상 응답") {
|
||||||
|
val principalId = 759980174446956066L.also {
|
||||||
|
MdcPrincipalIdUtil.set(it.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
val result = LogPayloadBuilder(type = LogType.INCOMING_HTTP_REQUEST, servletRequest = servletRequest)
|
||||||
|
.principalId()
|
||||||
|
.build()
|
||||||
|
|
||||||
|
result["principal_id"] shouldBe principalId
|
||||||
|
MdcPrincipalIdUtil.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
test("MDC에서 값을 가져올 수 없으면 UNKNOWN 으로 표기된다.") {
|
||||||
|
val result = LogPayloadBuilder(type = LogType.INCOMING_HTTP_REQUEST, servletRequest = servletRequest)
|
||||||
|
.principalId()
|
||||||
|
.build()
|
||||||
|
|
||||||
|
result["principal_id"] shouldBe "UNKNOWN"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context("exception") {
|
||||||
|
test("정상 응답") {
|
||||||
|
val exception = RuntimeException("hello")
|
||||||
|
val result = LogPayloadBuilder(type = LogType.INCOMING_HTTP_REQUEST, servletRequest = servletRequest)
|
||||||
|
.exception(exception)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
result["exception"] shouldBe mapOf(
|
||||||
|
"class" to exception.javaClass.simpleName,
|
||||||
|
"message" to exception.message
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
test("null을 입력하면 추가되지 않는다.") {
|
||||||
|
val result = LogPayloadBuilder(type = LogType.INCOMING_HTTP_REQUEST, servletRequest = servletRequest)
|
||||||
|
.exception(null)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
result["exception"] shouldBe null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context("additionalPayloads") {
|
||||||
|
test("정상 응답") {
|
||||||
|
val payload = mapOf(
|
||||||
|
"key1" to "value1",
|
||||||
|
"key2" to "value2"
|
||||||
|
)
|
||||||
|
val result = LogPayloadBuilder(type = LogType.INCOMING_HTTP_REQUEST, servletRequest = servletRequest)
|
||||||
|
.additionalPayloads(payload)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
result["key1"] shouldBe "value1"
|
||||||
|
result["key2"] shouldBe "value2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
Loading…
x
Reference in New Issue
Block a user