Skip to content

Server logging middleware: change logAction to F[String => F[Unit]]#7446

Open
iRevive wants to merge 2 commits into
http4s:mainfrom
iRevive:topic/1.0/logging-middleware
Open

Server logging middleware: change logAction to F[String => F[Unit]]#7446
iRevive wants to merge 2 commits into
http4s:mainfrom
iRevive:topic/1.0/logging-middleware

Conversation

@iRevive

@iRevive iRevive commented May 15, 2024

Copy link
Copy Markdown
Contributor

The follow-up to https://discord.com/channels/632277896739946517/632286375311573032/1239565715036442686.
Repo example: https://github.com/alexcardell/otel4s-server-logger-repro-example/tree/master

Motivation

We want to allow capturing the 'state' while we are still within Kleisli closure. This is needed to make the MDC logging work.

Context

When logBody = false, the logging is 'streamlined' - the message will be logged within the Kleisli closure.

However, when logBody = true, the logic changes drastically. The middleware binds logging action to the materialization of the body.

val newBody = Stream.eval(vec.get).flatMap(v => Stream.emits(v)).unchunks
// Cannot Be Done Asynchronously - Otherwise All Chunks May Not Be Appended Previous to Finalization
val logPipe: Pipe[F, Byte, Byte] =
_.observe(_.chunks.flatMap(c => Stream.exec(vec.update(_ :+ c))))
.onFinalizeWeak(logMessage(response.withBodyStream(newBody)))
response.pipeBodyThrough(logPipe)

Since the body's materialization may happen outside of the Kelisli (and/or on a different fiber), the relevant IOLocal context is missing.

Comment on lines +100 to +103
val logF: IO[String => IO[Unit]] =
for {
state <- local.get
} yield (message: String) => logs.update(_ :+ (message, state))

@iRevive iRevive May 15, 2024

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test fails on the current main branch.

@iRevive iRevive force-pushed the topic/1.0/logging-middleware branch from 1bd4833 to 227234b Compare May 15, 2024 07:53
fk: F ~> G,
redactHeadersWhen: CIString => Boolean = defaultRedactHeadersWhen,
logAction: Option[String => F[Unit]] = None,
logAction: Option[F[String => F[Unit]]] = None,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can also use something more flexible:

sealed trait LogAction[F[_]]

object LogAction {
  def plain[F[_]](f: String => F[Unit]): LogAction[F] = PlainAction(f)
  def deferred[F[_]](f: F[String => F[Unit])]: LogAction[F] = DeferredAction(f)
  
  private[http4s] final case class LogAction[F[_]](f: String => F[Unit]) extends LogAction[F]
  private[http4s] final case class DeferredAction[F[_]](f: F[String => F[Unit]]) extends LogAction[F]
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe even an implicit conversion can be handy:

implicit def toPlainLogAction[F[_]](f: String => F[Unit]): LogAction[F] = LogAction.plain(f)
implicit def toDeferredLogAction[F[_]](f: F[String => F[Unit]]): LogAction[F] = LogAction.deferred(f)

@iRevive iRevive force-pushed the topic/1.0/logging-middleware branch from 227234b to 5c11224 Compare May 15, 2024 08:29
@mergify mergify Bot added the docs Relates to our website or tutorials label May 15, 2024
@samspills samspills added this to the 0.23.29 milestone Sep 9, 2024
@samspills samspills modified the milestones: 0.23.29, 0.23.30 Oct 23, 2024
@alexcardell

Copy link
Copy Markdown

I've noticed the milestone on this keeps moving and obviously we're interested in this change. Is there a strategy to do this in a way without breaking bin compat I can take a crack at, and get it in 0.23.x?

@rossabaker

Copy link
Copy Markdown
Member

Is the breakage just on the various logAction parameters?

It would be tedious and messy, but a parallel set of constructors with different names might work, with the original ones lifting the String => F[Unit] into F and delegating to the new. The loggers are getting a lot of parameters, so potentially introducing some sort of builder pattern for the new. That approach might play more nicely with a LogAction.

@rossabaker

Copy link
Copy Markdown
Member

Targets main, so unmilestoning 0.23. Maybe those suggestions above help get a variant into 0.23.

@rossabaker rossabaker removed this from the 0.23.31 milestone Oct 30, 2025
@alexcardell

alexcardell commented Oct 30, 2025

Copy link
Copy Markdown

Thanks Ross, I'll have a go at that

@alexcardell

Copy link
Copy Markdown

@rossabaker I've opened #7744 with bin compatible changes

Not sure of best practises adding changes on someone else's PR so @iRevive if you want to cherry-pick my changes into here to merge this PR, feel free!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Relates to our website or tutorials module:server

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants