h4sbl is a Scala 3 library that provides enhanced logging middleware for http4s applications. It offers colorful, configurable, and comprehensive logging for HTTP operations.
The standard http4s logger is functional but limited. h4sbl provides a cleaner, more informative logging experience with color-coded output, sensitive header redaction, and log level-aware verbosity.
- Colorful Output - Color-coded HTTP methods, status codes, and headers for easy visual parsing
- Configurable - Full control over colors, secret redaction, and body logging
- Log Level Aware - Automatically adjusts verbosity based on your logger configuration
- Header Redaction - Automatically redacts sensitive headers (Authorization, Cookie, etc.) with support for custom headers
- URI Redaction - Redacts sensitive query parameter values (tokens, API keys, passwords) when secret redaction is enabled
- Body Capture - Optionally logs request and response bodies at TRACE level with a sanitization hook
- Minimal Boilerplate - Simple API that wraps your existing http4s client
Add the following dependencies to your build.sbt:
libraryDependencies ++= Seq(
"com.colofabrix.scala" %% "h4sbl" % "<see Maven Central badge>",
"org.http4s" %% "http4s-client" % <version>, // Required peer dependency
"org.typelevel" %% "cats-effect" % <version>, // Required peer dependency
)Note: h4sbl uses
Providedscope for http4s and Cats Effect, giving you full control over the versions in your project.
Wrap your http4s client with the logging middleware:
import cats.effect.*
import com.colofabrix.scala.http4s.middleware.betterlogger.*
import org.http4s.ember.client.EmberClientBuilder
object MyApp extends IOApp.Simple:
def run: IO[Unit] =
EmberClientBuilder
.default[IO]
.build
.map(ClientLogger(_)) // Wrap with logging
.use { client =>
client.expect[String]("https://httpbin.org/get").flatMap(IO.println)
}Control whether sensitive data is redacted in logs:
import com.colofabrix.scala.http4s.middleware.betterlogger.*
// Redact secrets (default behavior)
val safeLoggingClient = ClientLogger(httpClient)
// Show everything (useful for debugging)
val debugClient = ClientLogger.withConfig(LogConfig(redactSecrets = false))(httpClient)When redactSecrets = true (the default), the following are automatically redacted:
- Headers: Authorization, Cookie, Set-Cookie (built-in via http4s
Headers.SensitiveHeaders) plus any custom headers specified insensitiveHeaders - URI query parameters: Parameter values matching the built-in set (
token,api_key,password,secret, etc.) or custom names specified insensitiveQueryParams
For complete control over logging behavior:
import com.colofabrix.scala.http4s.middleware.betterlogger.*
import org.typelevel.ci.CIString
val config =
LogConfig(
redactSecrets = true,
sensitiveHeaders = Set(CIString("X-Api-Key"), CIString("X-Auth-Token")),
sensitiveQueryParams = Set("my_secret_param"),
colors = LogColors.default,
logRequestBody = true,
logResponseBody = true,
sanitizeBody = body => body.replaceAll("\"password\"\\s*:\\s*\"[^\"]*\"", "\"password\": \"<REDACTED>\""),
)
val loggingClient = ClientLogger.withConfig(config)(httpClient)When logging bodies at TRACE level, you can provide a sanitizeBody function to redact
sensitive data before it appears in logs. The function receives the raw body string and returns
the sanitized version. Body sanitization is only applied when redactSecrets = true:
import com.colofabrix.scala.http4s.middleware.betterlogger.*
val config = LogConfig(
sanitizeBody = body =>
body
.replaceAll("\"password\"\\s*:\\s*\"[^\"]*\"", "\"password\": \"<REDACTED>\"")
.replaceAll("\"token\"\\s*:\\s*\"[^\"]*\"", "\"token\": \"<REDACTED>\"")
)
val loggingClient = ClientLogger.withConfig(config)(httpClient)Note: Body sanitization is applied at the string level. For complex formats (JSON, XML, protobuf), consider sanitizing upstream at the application layer before the body reaches the logger.
For log files or environments that don't support ANSI colors:
import com.colofabrix.scala.http4s.middleware.betterlogger.*
val config = LogConfig(colors = LogColors.noColors)
val loggingClient = ClientLogger.withConfig(config)(httpClient)The main entry point for creating a logging middleware.
| Method | Description |
|---|---|
apply(client) |
Wrap client with default configuration |
withConfig(config)(client) |
Wrap client with full configuration |
Configuration case class for logging behavior.
| Parameter | Type | Default | Description |
|---|---|---|---|
redactSecrets |
Boolean |
true |
Master switch for all redaction (headers, query params, body) |
sensitiveHeaders |
Set[CIString] |
Set.empty |
Header names to redact (replaces built-in set when non-empty) |
sensitiveQueryParams |
Set[String] |
Set.empty |
Query param names to redact (replaces built-in set when non-empty) |
colors |
LogColors |
LogColors.default |
Color scheme for console output |
logRequestBody |
Boolean |
true |
Log request bodies (only at TRACE level) |
logResponseBody |
Boolean |
true |
Log response bodies (only at TRACE level) |
sanitizeBody |
String => String |
identity |
Transform function applied to body strings before logging |
Built-in sensitive sets:
- Headers (from http4s
Headers.SensitiveHeaders):Authorization,Cookie,Set-Cookie - Query parameters (
LogConfig.SensitiveQueryParams):api_key,apikey,api-key,token,access_token,access-token,refresh_token,refresh-token,password,passwd,secret,client_secret,client-secret,private_key,private-key
When
sensitiveHeadersorsensitiveQueryParamsare non-empty, they replace the built-in set entirely. They are not additive. To extend the built-in set, include all desired names.
Color scheme configuration for log output.
| Parameter | Default | Description |
|---|---|---|
httpVersion |
White | Color for HTTP version (e.g., HTTP/1.1) |
safeMethod |
Green | Color for GET, HEAD, OPTIONS |
unsafeMethod |
Yellow | Color for POST, PUT, DELETE, etc. |
uri |
Magenta + Bold | Color for request URI |
headers |
Blue | Color for headers section |
body |
White | Color for body content |
successStatus |
Green | Color for 1xx, 2xx, 3xx responses |
clientErrorStatus |
Yellow | Color for 4xx responses |
serverErrorStatus |
Red | Color for 5xx responses |
Presets:
LogColors.default- Full ANSI color supportLogColors.noColors- Plain text output
h4sbl adjusts its output based on the configured log level:
| Log Level | Behavior |
|---|---|
| TRACE | Full output: method, URI, headers, and bodies |
| DEBUG | Method, URI, and headers (no bodies) |
| INFO+ | Minimal or no logging |
Create a custom color scheme for your logs:
import scala.Console.*
val customColors =
LogColors(
httpVersion = CYAN,
safeMethod = BLUE,
unsafeMethod = RED,
uri = s"$WHITE$BOLD",
headers = YELLOW,
body = WHITE,
successStatus = GREEN,
clientErrorStatus = MAGENTA,
serverErrorStatus = s"$RED$BOLD",
reset = RESET,
)
val config = LogConfig(colors = customColors)
val loggingClient = ClientLogger.withConfig(config)(httpClient)Configure your logback.xml to control logging verbosity:
<configuration>
<!-- Show full details including bodies -->
<logger name="com.colofabrix.scala.http4s.middleware.betterlogger" level="TRACE"/>
<!-- Or just headers, no bodies -->
<logger name="com.colofabrix.scala.http4s.middleware.betterlogger" level="DEBUG"/>
</configuration>Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
h4sbl is released under the MIT license. See LICENSE for details.
- http4s - Typeful, functional HTTP for Scala
- Cats Effect - The pure asynchronous runtime for Scala
- log4cats - Logging for Cats Effect