diff --git a/CHANGELOG.md b/CHANGELOG.md
index cf26215..1cf34e9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -58,4 +58,9 @@
### Notes
- Enable debug logs with `logging.level.xyz.tcheeric=DEBUG` to see detailed request and configuration traces.
+## 0.1.4 — 2025-09-13
+
+### Enhancements
+- Add default logging configuration for SLF4J Simple in phoenixd-rest (`simplelogger.properties`).
+- Document logging usage and configuration.
diff --git a/README.md b/README.md
index b2bd441..0da11a1 100644
--- a/README.md
+++ b/README.md
@@ -46,3 +46,4 @@ version and `latest`, allowing consumers to pull the most recent build without s
- [Docker](docs/how-to/docker.md)
- [Code coverage](docs/how-to/coverage.md)
- [Release](docs/how-to/release.md)
+ - [Logging](docs/reference/logging.md)
diff --git a/docs/reference/logging.md b/docs/reference/logging.md
new file mode 100644
index 0000000..7f53e7a
--- /dev/null
+++ b/docs/reference/logging.md
@@ -0,0 +1,57 @@
+# Logging
+
+phoenixd-rest and phoenixd-base use SLF4J for logging with Lombok's `@Slf4j`.
+This lets your application choose the logging backend (Logback, Log4j2, slf4j-simple, etc.).
+
+## Quick Start (slf4j-simple)
+
+If your app does not already have a logging backend, add:
+
+```xml
+
+ org.slf4j
+ slf4j-simple
+ ${slf4j.version}
+
+```
+
+The module ships a `simplelogger.properties` that sets:
+
+- `org.slf4j.simpleLogger.defaultLogLevel=info`
+- `org.slf4j.simpleLogger.log.xyz.tcheeric=debug`
+- date/time and thread name output
+
+Override levels at runtime with system properties, for example:
+
+```bash
+JAVA_TOOL_OPTIONS="-Dorg.slf4j.simpleLogger.defaultLogLevel=info \
+ -Dorg.slf4j.simpleLogger.log.xyz.tcheeric=debug"
+```
+
+## Spring Boot (Logback)
+
+Add to `application.properties`:
+
+```
+logging.level.xyz.tcheeric=DEBUG
+```
+
+Or to `logback-spring.xml`:
+
+```xml
+
+
+
+
+
+
+```
+
+## What’s Logged
+
+- HTTP requests: method, URI, timeout, status; error responses include a truncated body.
+- Configuration resolution: whether values come from ENV, system properties, or `app.properties` (base module).
+- Request factory decisions: Lightning address vs BOLT11 detection.
+
+Sensitive values (e.g. Authorization) are redacted.
+
diff --git a/phoenixd-base/pom.xml b/phoenixd-base/pom.xml
index aed2dfd..26e1432 100644
--- a/phoenixd-base/pom.xml
+++ b/phoenixd-base/pom.xml
@@ -4,7 +4,7 @@
xyz.tcheeric
phoenixd-java
- 0.1.3
+ 0.1.4
phoenixd-base
diff --git a/phoenixd-mock/pom.xml b/phoenixd-mock/pom.xml
index ff6adc3..abb112f 100644
--- a/phoenixd-mock/pom.xml
+++ b/phoenixd-mock/pom.xml
@@ -6,7 +6,7 @@
xyz.tcheeric
phoenixd-java
- 0.1.3
+ 0.1.4
phoenixd-mock
diff --git a/phoenixd-model/pom.xml b/phoenixd-model/pom.xml
index a1fa00e..866dfc2 100644
--- a/phoenixd-model/pom.xml
+++ b/phoenixd-model/pom.xml
@@ -4,7 +4,7 @@
xyz.tcheeric
phoenixd-java
- 0.1.3
+ 0.1.4
phoenixd-model
diff --git a/phoenixd-rest/pom.xml b/phoenixd-rest/pom.xml
index c852ee6..3adf419 100644
--- a/phoenixd-rest/pom.xml
+++ b/phoenixd-rest/pom.xml
@@ -4,12 +4,12 @@
xyz.tcheeric
phoenixd-java
- 0.1.3
+ 0.1.4
phoenixd-rest
jar
- 0.1.3
+ 0.1.4
phoenixd-rest
https://maven.apache.org
diff --git a/phoenixd-rest/src/main/java/xyz/tcheeric/phoenixd/operation/impl/DeleteOperation.java b/phoenixd-rest/src/main/java/xyz/tcheeric/phoenixd/operation/impl/DeleteOperation.java
index c8cb934..b280483 100644
--- a/phoenixd-rest/src/main/java/xyz/tcheeric/phoenixd/operation/impl/DeleteOperation.java
+++ b/phoenixd-rest/src/main/java/xyz/tcheeric/phoenixd/operation/impl/DeleteOperation.java
@@ -2,6 +2,7 @@
import lombok.NonNull;
import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
import xyz.tcheeric.phoenixd.common.rest.Operation;
import xyz.tcheeric.phoenixd.common.rest.Request;
import xyz.tcheeric.phoenixd.common.rest.util.Constants;
@@ -11,6 +12,7 @@
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.Builder;
+@Slf4j
public class DeleteOperation extends AbstractOperation {
public DeleteOperation(HttpRequest httpRequest) {
@@ -20,11 +22,13 @@ public DeleteOperation(HttpRequest httpRequest) {
@SneakyThrows
public DeleteOperation(@NonNull String path) {
super(Constants.HTTP_DELETE_METHOD, path, null);
+ if (log.isDebugEnabled()) log.debug("Initialized DELETE operation for path={}", path);
}
@SneakyThrows
public DeleteOperation(@NonNull String path, @NonNull Request.Param requestParam) {
super(Constants.HTTP_DELETE_METHOD, path, requestParam, null);
+ if (log.isDebugEnabled()) log.debug("Initialized DELETE operation for path={} with params", path);
}
@Override
@@ -40,6 +44,7 @@ public Operation removeHeader(@NonNull String key) {
});
this.httpRequest = requestBuilder.build();
+ if (log.isDebugEnabled()) log.debug("Removed header '{}' from DELETE request {} {}", key, httpRequest.method(), httpRequest.uri());
return this;
}
@@ -47,4 +52,4 @@ public Operation removeHeader(@NonNull String key) {
public Operation addHeader(String key, String value) {
throw new UnsupportedOperationException("Not supported yet.");
}
-}
\ No newline at end of file
+}
diff --git a/phoenixd-rest/src/main/java/xyz/tcheeric/phoenixd/operation/impl/GetOperation.java b/phoenixd-rest/src/main/java/xyz/tcheeric/phoenixd/operation/impl/GetOperation.java
index 5213736..a5a12e7 100644
--- a/phoenixd-rest/src/main/java/xyz/tcheeric/phoenixd/operation/impl/GetOperation.java
+++ b/phoenixd-rest/src/main/java/xyz/tcheeric/phoenixd/operation/impl/GetOperation.java
@@ -2,12 +2,14 @@
import lombok.NonNull;
import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
import xyz.tcheeric.phoenixd.common.rest.Request;
import xyz.tcheeric.phoenixd.common.rest.util.Constants;
import xyz.tcheeric.phoenixd.operation.AbstractOperation;
import java.net.http.HttpRequest;
+@Slf4j
public class GetOperation extends AbstractOperation {
public GetOperation(HttpRequest httpRequest) {
@@ -17,10 +19,12 @@ public GetOperation(HttpRequest httpRequest) {
@SneakyThrows
public GetOperation(@NonNull String path) {
super(Constants.HTTP_GET_METHOD, path, null);
+ if (log.isDebugEnabled()) log.debug("Initialized GET operation for path={}", path);
}
@SneakyThrows
public GetOperation(@NonNull String path, @NonNull Request.Param requestParam) {
super(Constants.HTTP_GET_METHOD, path, requestParam, null);
+ if (log.isDebugEnabled()) log.debug("Initialized GET operation for path={} with params", path);
}
}
diff --git a/phoenixd-rest/src/main/java/xyz/tcheeric/phoenixd/operation/impl/PatchOperation.java b/phoenixd-rest/src/main/java/xyz/tcheeric/phoenixd/operation/impl/PatchOperation.java
index 6109100..1b5c594 100644
--- a/phoenixd-rest/src/main/java/xyz/tcheeric/phoenixd/operation/impl/PatchOperation.java
+++ b/phoenixd-rest/src/main/java/xyz/tcheeric/phoenixd/operation/impl/PatchOperation.java
@@ -2,21 +2,25 @@
import lombok.NonNull;
import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
import xyz.tcheeric.phoenixd.common.rest.Operation;
import xyz.tcheeric.phoenixd.common.rest.Request;
import xyz.tcheeric.phoenixd.common.rest.util.Constants;
import xyz.tcheeric.phoenixd.operation.AbstractOperation;
+@Slf4j
public class PatchOperation extends AbstractOperation {
@SneakyThrows
public PatchOperation(@NonNull String path) {
super(Constants.HTTP_PATCH_METHOD, path, null);
+ if (log.isDebugEnabled()) log.debug("Initialized PATCH operation for path={}", path);
}
@SneakyThrows
public PatchOperation(@NonNull String path, @NonNull Request.Param param) {
super(Constants.HTTP_PATCH_METHOD, path, param, null);
+ if (log.isDebugEnabled()) log.debug("Initialized PATCH operation for path={} with params", path);
}
@Override
@@ -26,6 +30,7 @@ public Operation removeHeader(String key) {
@Override
public Operation addHeader(String key, String value) {
+ if (log.isDebugEnabled()) log.debug("Adding header on PATCH operation: {}=", key, key.equalsIgnoreCase("Authorization") ? "yes" : "no");
return super.addHeader(key, value);
}
diff --git a/phoenixd-rest/src/main/java/xyz/tcheeric/phoenixd/operation/impl/PostOperation.java b/phoenixd-rest/src/main/java/xyz/tcheeric/phoenixd/operation/impl/PostOperation.java
index 22a835a..a8e002f 100644
--- a/phoenixd-rest/src/main/java/xyz/tcheeric/phoenixd/operation/impl/PostOperation.java
+++ b/phoenixd-rest/src/main/java/xyz/tcheeric/phoenixd/operation/impl/PostOperation.java
@@ -2,12 +2,14 @@
import lombok.NonNull;
import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
import xyz.tcheeric.phoenixd.common.rest.Request;
import xyz.tcheeric.phoenixd.common.rest.util.Constants;
import xyz.tcheeric.phoenixd.operation.AbstractOperation;
import java.net.http.HttpRequest;
+@Slf4j
public class PostOperation extends AbstractOperation {
public PostOperation(HttpRequest httpRequest) {
@@ -17,11 +19,13 @@ public PostOperation(HttpRequest httpRequest) {
public PostOperation(@NonNull String path, @NonNull String data) {
super(Constants.HTTP_POST_METHOD, path, data);
this.addHeader("Content-Type", "application/x-www-form-urlencoded");
+ if (log.isDebugEnabled()) log.debug("Initialized POST operation for path={} with body size={}", path, data.length());
}
@SneakyThrows
public PostOperation(@NonNull String path, @NonNull Request.Param param, @NonNull String data) {
super(Constants.HTTP_POST_METHOD, path, param, data);
this.addHeader("Content-Type", "application/x-www-form-urlencoded");
+ if (log.isDebugEnabled()) log.debug("Initialized POST operation for path={} with params and body size={}", path, data.length());
}
}
diff --git a/phoenixd-rest/src/main/java/xyz/tcheeric/phoenixd/request/AbstractRequest.java b/phoenixd-rest/src/main/java/xyz/tcheeric/phoenixd/request/AbstractRequest.java
index fbb68aa..e8631de 100644
--- a/phoenixd-rest/src/main/java/xyz/tcheeric/phoenixd/request/AbstractRequest.java
+++ b/phoenixd-rest/src/main/java/xyz/tcheeric/phoenixd/request/AbstractRequest.java
@@ -4,6 +4,7 @@
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
import xyz.tcheeric.phoenixd.common.rest.Operation;
import xyz.tcheeric.phoenixd.common.rest.Request;
import xyz.tcheeric.phoenixd.common.rest.Response;
@@ -14,6 +15,7 @@
@Data
@RequiredArgsConstructor
+@Slf4j
public abstract class AbstractRequest implements Request {
private final String path;
private final T param;
@@ -23,14 +25,23 @@ public abstract class AbstractRequest responseType = getResponseType();
+ if (log.isDebugEnabled()) {
+ log.debug("Executing request: path={}, paramClass={}, responseType={}", path, param != null ? param.getClass().getSimpleName() : "", responseType.getSimpleName());
+ }
if (VoidResponse.class.isAssignableFrom(responseType)) {
+ if (log.isDebugEnabled()) log.debug("VoidResponse detected; skipping network call for path={}", path);
return (U) new VoidResponse();
}
String body = this.getOperation().execute().getResponseBody();
+ if (log.isDebugEnabled()) {
+ String preview = body == null ? "" : (body.length() > 256 ? body.substring(0, 256) + "..." : body);
+ log.debug("Received response body (truncated): {}", preview);
+ }
if(GetLightningAddressResponse.class.equals(responseType)) {
- // Remove the ₿ prefix
- if(body.charAt(0) == '₿') {
+ // Hack: Remove the ₿ prefix
+ if(body != null && !body.isEmpty() && body.charAt(0) == '₿') {
+ if (log.isDebugEnabled()) log.debug("Stripping leading '₿' from lightning address response");
return (U) new GetLightningAddressResponse(body.substring(1));
} else {
return (U) new GetLightningAddressResponse(body);
@@ -38,6 +49,7 @@ public U getResponse() {
}
ObjectMapper objectMapper = new ObjectMapper();
+ if (log.isDebugEnabled()) log.debug("Deserializing JSON into {}", responseType.getSimpleName());
return objectMapper.readValue(body, responseType);
}
@@ -45,4 +57,4 @@ private Class getResponseType() {
ParameterizedType superClass = (ParameterizedType) getClass().getGenericSuperclass();
return (Class) superClass.getActualTypeArguments()[1];
}
-}
\ No newline at end of file
+}
diff --git a/phoenixd-rest/src/main/java/xyz/tcheeric/phoenixd/request/impl/rest/PayRequestFactory.java b/phoenixd-rest/src/main/java/xyz/tcheeric/phoenixd/request/impl/rest/PayRequestFactory.java
index 812c450..7b5e947 100644
--- a/phoenixd-rest/src/main/java/xyz/tcheeric/phoenixd/request/impl/rest/PayRequestFactory.java
+++ b/phoenixd-rest/src/main/java/xyz/tcheeric/phoenixd/request/impl/rest/PayRequestFactory.java
@@ -1,11 +1,13 @@
package xyz.tcheeric.phoenixd.request.impl.rest;
import lombok.NonNull;
+import lombok.extern.slf4j.Slf4j;
import xyz.tcheeric.phoenixd.model.param.PayBolt11InvoiceParam;
import xyz.tcheeric.phoenixd.model.param.PayLightningAddressParam;
import java.util.regex.Pattern;
+@Slf4j
public class PayRequestFactory {
/**
@@ -16,17 +18,20 @@ public class PayRequestFactory {
Pattern.CASE_INSENSITIVE);
public static BasePayRequest, ?> createPayRequest(@NonNull String request) {
-
+ if (log.isDebugEnabled()) log.debug("Creating pay request for input of length {}", request.length());
if (request.contains("@")) {
PayLightningAddressParam param = new PayLightningAddressParam();
param.setAddress(request);
+ if (log.isDebugEnabled()) log.debug("Detected Lightning Address format");
return new PayLightningAddressRequest(param);
} else if (BOLT11_PATTERN.matcher(request).matches()) {
PayBolt11InvoiceParam param = new PayBolt11InvoiceParam();
param.setInvoice(request);
+ if (log.isDebugEnabled()) log.debug("Detected BOLT11 invoice format");
return new PayBolt11InvoiceRequest(param);
} else {
+ log.warn("Invalid pay request format; input begins with: {}", request.length() > 8 ? request.substring(0, 8) + "..." : request);
throw new IllegalArgumentException("Invalid request format");
}
}
-}
\ No newline at end of file
+}
diff --git a/phoenixd-rest/src/main/resources/simplelogger.properties b/phoenixd-rest/src/main/resources/simplelogger.properties
new file mode 100644
index 0000000..c58c518
--- /dev/null
+++ b/phoenixd-rest/src/main/resources/simplelogger.properties
@@ -0,0 +1,13 @@
+# SLF4J SimpleLogger configuration for phoenixd-rest
+# This file is only used when slf4j-simple is on the classpath.
+# It does not affect applications that use Logback, Log4j2, etc.
+
+org.slf4j.simpleLogger.showDateTime=true
+org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss.SSS
+org.slf4j.simpleLogger.showThreadName=true
+org.slf4j.simpleLogger.showLogName=false
+org.slf4j.simpleLogger.defaultLogLevel=info
+
+# Turn on detailed logs for our packages
+org.slf4j.simpleLogger.log.xyz.tcheeric=debug
+
diff --git a/phoenixd-test/pom.xml b/phoenixd-test/pom.xml
index 46d5b1e..9dda937 100644
--- a/phoenixd-test/pom.xml
+++ b/phoenixd-test/pom.xml
@@ -4,7 +4,7 @@
xyz.tcheeric
phoenixd-java
- 0.1.3
+ 0.1.4
phoenixd-test
diff --git a/pom.xml b/pom.xml
index e67abc7..45a964b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
4.0.0
xyz.tcheeric
phoenixd-java
- 0.1.3
+ 0.1.4
pom