Skip to content

Commit

Permalink
Merge pull request #17 from Kilemonn/correlation-id
Browse files Browse the repository at this point in the history
Correlation
  • Loading branch information
Kilemonn authored Aug 25, 2023
2 parents a62f5cd + ba097e7 commit 835ebac
Show file tree
Hide file tree
Showing 10 changed files with 330 additions and 51 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package au.kilemon.messagequeue.filter

import au.kilemon.messagequeue.logging.HasLogger
import org.slf4j.Logger
import org.slf4j.MDC
import org.springframework.core.annotation.Order
import org.springframework.stereotype.Component
import org.springframework.web.filter.OncePerRequestFilter
import java.util.*
import javax.servlet.FilterChain
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse

/**
* A request filter that either takes the incoming provided [CORRELATION_ID_HEADER] and sets it into the [MDC] OR
* generates a new [UUID] that is used as the [CORRELATION_ID] for this request.
* This [CORRELATION_ID] is removed from the [MDC] when the request is returned to the caller.
*
* @author github.com/Kilemonn
*/
@Component
@Order(1)
class CorrelationIdFilter: OncePerRequestFilter(), HasLogger
{
companion object
{
const val CORRELATION_ID_HEADER = "X-Correlation-Id"

// This is also used in the logback.xml as a parameter
const val CORRELATION_ID = "correlationId"
}

override val LOG: Logger = initialiseLogger()

override fun doFilterInternal(request: HttpServletRequest, response: HttpServletResponse, filterChain: FilterChain)
{
try
{
setCorrelationId(request.getHeader(CORRELATION_ID_HEADER))

filterChain.doFilter(request, response)
}
finally
{
MDC.clear()
}
}

/**
* Handle the setting of the [CORRELATION_ID] based on the [providedId] if it is not null it will be used, otherwise
* a new [UUID] will be generated and set into the [MDC].
*
* @param providedId the correlation ID provided by the user, if it is null a new one will be generated
*/
fun setCorrelationId(providedId: String?)
{
val correlationId: String
if (providedId != null)
{
correlationId = providedId
LOG.trace("Using provided ID [{}] as correlation id.", correlationId)
}
else
{
correlationId = UUID.randomUUID().toString()
LOG.trace("Using generated UUID [{}] as correlation id.", correlationId)
}

MDC.put(CORRELATION_ID, correlationId)
}
}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package au.kilemon.messagequeue.rest.controller

/**
* A collection of constants used as REST parameters.
*
* @author github.com/Kilemonn
*/
object RestParameters
{
const val ASSIGNED_TO = "assignedTo"

const val QUEUE_TYPE = "queueType"

const val DETAILED = "detailed"

const val UUID = "uuid"

const val INCLUDE_EMPTY = "includeEmpty"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package au.kilemon.messagequeue.rest.response

import au.kilemon.messagequeue.filter.CorrelationIdFilter
import com.fasterxml.jackson.annotation.JsonPropertyOrder
import org.slf4j.MDC

/**
* An error response object returned when errors occur.
*
* @author github.com/Kilemonn
*/
@JsonPropertyOrder("correlationId", "message")
data class ErrorResponse(val message: String?, val correlationId: String? = MDC.get(CorrelationIdFilter.CORRELATION_ID))
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package au.kilemon.messagequeue.rest.response

import au.kilemon.messagequeue.filter.CorrelationIdFilter
import au.kilemon.messagequeue.message.QueueMessage
import com.fasterxml.jackson.annotation.JsonPropertyOrder
import org.slf4j.MDC

/**
* A response object which wraps the [QueueMessage], and exposes the `type` [String].
*
* @author github.com/Kilemonn
*/
@JsonPropertyOrder("queueType", "message")
data class MessageResponse(val message: QueueMessage, val queueType: String = message.type)
@JsonPropertyOrder("correlationId", "queueType", "message")
data class MessageResponse(val message: QueueMessage, val queueType: String = message.type, val correlationId: String? = MDC.get(CorrelationIdFilter.CORRELATION_ID))
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package au.kilemon.messagequeue.rest.response

import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.ControllerAdvice
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.server.ResponseStatusException
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler

/**
* A response handler which maps the thrown [ResponseStatusException] into the [ErrorResponse].
*
* @author github.com/Kilemonn
*/
@ControllerAdvice
class RestResponseExceptionHandler: ResponseEntityExceptionHandler()
{
@ExceptionHandler(ResponseStatusException::class)
fun handleResponseStatusException(ex: ResponseStatusException): ResponseEntity<ErrorResponse>
{
return ResponseEntity<ErrorResponse>(ErrorResponse(ex.reason), ex.status)
}
}
2 changes: 1 addition & 1 deletion src/main/resources/logback.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<configuration>
<!-- Properties list for use below -->
<property name="LOGS" value="./logs"/>
<property name="PATTERN" value="%d %p %C{1.} [%t] %m%n"/>
<property name="PATTERN" value="%d %p [%X{correlationId}] %C{1.} [%t] %m%n"/>
<property name="ARCHIVED_FILE_SUFFIX" value="-%d{yyyy-MM-dd}.%i"/>

<!-- Console appender formatting -->
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package au.kilemon.messagequeue.filter

import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.slf4j.MDC
import java.util.*

/**
* Test [CorrelationIdFilter] to make sure the [CorrelationIdFilter.CORRELATION_ID] is set correctly.
*
* @author github.com/Kilemonn
*/
class TestCorrelationIdFilter
{
private val correlationIdFilter = CorrelationIdFilter()

@BeforeEach
fun setUp()
{
MDC.clear()
}

/**
* Ensure the provided `correlationId` is used when [CorrelationIdFilter.setCorrelationId] is called with a non-null
* argument.
*/
@Test
fun testSetCorrelationId_providedId()
{
val correlationId = "a-correlation-id-123456"
Assertions.assertNull(MDC.get(CorrelationIdFilter.CORRELATION_ID))

correlationIdFilter.setCorrelationId(correlationId)
Assertions.assertEquals(correlationId, MDC.get(CorrelationIdFilter.CORRELATION_ID))
}

/**
* Ensure that a [UUID] `correlationId` will be generated when [CorrelationIdFilter.setCorrelationId] is called
* with a `null` argument.
*/
@Test
fun testSetCorrelationId_generatedId()
{
Assertions.assertNull(MDC.get(CorrelationIdFilter.CORRELATION_ID))
correlationIdFilter.setCorrelationId(null)
val generatedCorrelationId = MDC.get(CorrelationIdFilter.CORRELATION_ID)
Assertions.assertEquals(UUID.fromString(generatedCorrelationId).toString(), generatedCorrelationId)
}
}
Loading

0 comments on commit 835ebac

Please sign in to comment.