Skip to content

Commit

Permalink
Switch to Common Log
Browse files Browse the repository at this point in the history
  • Loading branch information
ChristopherDavenport committed Jul 10, 2023
1 parent 1130df1 commit 64e96de
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ import scala.concurrent.duration.FiniteDuration
import org.typelevel.log4cats.LoggerFactory
import fs2.{Stream, Pure}
import org.http4s.client.middleware.Retry
import SharedStructureLogging._
import SharedStructuredLogging._
import org.http4s.client.Client

import java.time.ZoneId

object ClientMiddleware {

Expand All @@ -40,8 +40,8 @@ object ClientMiddleware {
val responseBodyMaxSize = 65535
val removedContextKeys = Set.empty[String]
def logLevel(prelude: Request[Pure], outcome: Outcome[Option, Throwable, Response[Pure]]): Option[LogLevel] = LogLevel.Info.some
def logMessage(prelude: Request[Pure], outcome: Outcome[Option, Throwable, Response[Pure]], now: FiniteDuration): String = s"Http Server - ${prelude.method}"

def logMessage(prelude: Request[Pure], outcome: Outcome[Option, Throwable, Response[Pure]], now: FiniteDuration): String =
CommonLog.logMessage(ZoneId.systemDefault())(prelude, outcome, now)
}

def fromLoggerFactory[F[_]: Concurrent: Clock: LoggerFactory]: ClientMiddlewareBuilder[F] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import cats.syntax.all._
import com.comcast.ip4s._


object HttpStructuredContext {
private[contextlog] object HttpStructuredContext {

object Common {
def method(m: Method): (String, String) = ("http.method", m.name)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import org.typelevel.vault.Key
import scala.concurrent.duration.FiniteDuration
import org.typelevel.log4cats.LoggerFactory
import fs2.{Stream, Pure}
import SharedStructureLogging._
import SharedStructuredLogging._
import java.time.ZoneId

object ServerMiddleware {
object Defaults {
Expand All @@ -37,8 +38,8 @@ object ServerMiddleware {
val responseBodyMaxSize = 65535
val removedContextKeys = Set.empty[String]
def logLevel(prelude: Request[Pure], outcome: Outcome[Option, Throwable, Response[Pure]]): Option[LogLevel] = LogLevel.Info.some
def logMessage(prelude: Request[Pure], outcome: Outcome[Option, Throwable, Response[Pure]], now: FiniteDuration): String = s"Http Server - ${prelude.method}"

def logMessage(prelude: Request[Pure], outcome: Outcome[Option, Throwable, Response[Pure]], now: FiniteDuration): String =
CommonLog.logMessage(ZoneId.systemDefault())(prelude, outcome, now)
}

def fromLoggerFactory[F[_]: Concurrent: Clock: LoggerFactory]: ServerMiddlewareBuilder[F] =
Expand Down Expand Up @@ -577,4 +578,6 @@ object ServerMiddleware {

builder.result()
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,22 @@ import org.typelevel.log4cats.StructuredLogger
import org.typelevel.log4cats.extras.LogLevel
import scala.concurrent.duration._
import org.http4s.Charset
import java.time.{Instant, ZoneId}
import java.time.format.DateTimeFormatter

private[log4cats] object SharedStructureLogging {
def pureRequest[F[_]](req: Request[F]): Request[Pure] = Request(req.method, req.uri, req.httpVersion, req.headers, Stream.empty, req.attributes)
def pureResponse[F[_]](resp: Response[F]): Response[Pure] = Response(resp.status, resp.httpVersion, resp.headers, Stream.empty, resp.attributes)
object SharedStructuredLogging {
private[contextlog] def pureRequest[F[_]](req: Request[F]): Request[Pure] = Request(req.method, req.uri, req.httpVersion, req.headers, Stream.empty, req.attributes)
private[contextlog] def pureResponse[F[_]](resp: Response[F]): Response[Pure] = Response(resp.status, resp.httpVersion, resp.headers, Stream.empty, resp.attributes)

def outcomeContext[F[_], E, A](outcome: Outcome[F, E, A]): (String, String) = {
private[contextlog] def outcomeContext[F[_], E, A](outcome: Outcome[F, E, A]): (String, String) = {
outcome match {
case Outcome.Canceled() => "exit.case" -> "canceled"
case Outcome.Errored(_) => "exit.case" -> "errored"
case Outcome.Succeeded(_) => "exit.case" -> "succeeded"
}
}

def logLevelAware[F[_]: Applicative](
private[contextlog] def logLevelAware[F[_]: Applicative](
logger: StructuredLogger[F],
ctx: Map[String, String],
prelude: Request[Pure],
Expand Down Expand Up @@ -58,7 +60,7 @@ private[log4cats] object SharedStructureLogging {
}
}

def logBody[F[_]: Concurrent](message: Message[F]): F[String] = {
private[contextlog] def logBody[F[_]: Concurrent](message: Message[F]): F[String] = {
val isBinary = message.contentType.exists(_.mediaType.binary)
val isJson = message.contentType.exists(mT =>
mT.mediaType == MediaType.application.json || mT.mediaType.subType.endsWith("+json")
Expand All @@ -72,4 +74,68 @@ private[log4cats] object SharedStructureLogging {

}

object CommonLog {
private val LSB = "["
private val RSB = "]"
private val DASH = "-"
private val SPACE = " "
private val DQUOTE = "\""

private val dateTimeFormat = DateTimeFormatter.ofPattern("dd/MMM/yyyy:HH:mm:ss Z")

def logMessage(zone: ZoneId)(request: Request[Pure], outcome: Outcome[Option, Throwable, Response[Pure]], now: FiniteDuration): String = {

val dateString = Instant.ofEpochMilli(now.toMillis).atZone(zone).format(dateTimeFormat)

val statusS = outcome match {
case Outcome.Succeeded(Some(resp)) => resp.status.code.toString()
case Outcome.Succeeded(None) => DASH
case Outcome.Errored(e) => DASH
case Outcome.Canceled() => DASH
}
val respLengthS = outcome match {
case Outcome.Succeeded(Some(resp)) => resp.contentLength
case Outcome.Succeeded(None) => DASH
case Outcome.Errored(e) => DASH
case Outcome.Canceled() => DASH
}

val sb = new StringBuilder()
sb.append(request.remote.fold(DASH)(sa => sa.host.toString())) // Remote
sb.append(SPACE)

sb.append(DASH) // Ident Protocol Not Implemented
sb.append(SPACE)

sb.append(request.remoteUser.fold(DASH)(identity)) // User
sb.append(SPACE)

sb.append(LSB)
sb.append(dateString)
sb.append(RSB)
sb.append(SPACE)

sb.append(DQUOTE)

sb.append(request.method.renderString)
sb.append(SPACE)
sb.append(request.uri.toOriginForm.renderString)
sb.append(SPACE)
sb.append(request.httpVersion.renderString)


sb.append(DQUOTE)
sb.append(SPACE)


sb.append(statusS)
sb.append(SPACE)

sb.append(respLengthS)

sb.toString()
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,13 @@ import org.typelevel.log4cats.testing.StructuredTestingLogger.INFO
import org.typelevel.log4cats.testing.StructuredTestingLogger.WARN
import org.typelevel.log4cats.testing.StructuredTestingLogger.ERROR
import org.http4s.client.Client
import fs2.Pure
import scala.concurrent.duration.FiniteDuration

class MainSpec extends CatsEffectSuite {

def logMessage(prelude: Request[Pure], outcome: Outcome[Option, Throwable, Response[Pure]], now: FiniteDuration): String = s"Http Server - ${prelude.method}"

test("Successfully Create a Server Context Log") {
val logger = StructuredTestingLogger.impl[IO]()

Expand All @@ -26,6 +30,7 @@ class MainSpec extends CatsEffectSuite {
.withLogRequestBody(false)
.withLogResponseBody(false)
.withRemovedContextKeys(Set("http.duration_ms"))
.withLogMessage(logMessage)

val finalApp = builder.httpApp(server)

Expand Down Expand Up @@ -64,6 +69,7 @@ class MainSpec extends CatsEffectSuite {
val builder = ServerMiddleware.fromLogger(logger)
.withLogRequestBody(true)
.withLogResponseBody(true)
.withLogMessage(logMessage)
.withRemovedContextKeys(Set("http.duration_ms"))

val finalApp = builder.httpApp(server)
Expand Down Expand Up @@ -160,6 +166,7 @@ class MainSpec extends CatsEffectSuite {
val builder = ClientMiddleware.fromLogger(logger)
.withLogRequestBody(true)
.withLogResponseBody(true)
.withLogMessage(logMessage)
.withRemovedContextKeys(Set("http.duration_ms"))

val finalApp = builder.client(client)
Expand Down

0 comments on commit 64e96de

Please sign in to comment.