diff --git a/CHANGELOG.md b/CHANGELOG.md
index e461adb..079f6d7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+## [1.3.0] - 2026-05-06
+
+### Added
+- **Log4j2 appender (optional)** — `JObsLog4j2Appender` mirrors `JObsLogAppender` (Logback) and captures log events into the J-Obs log repository when applications use Log4j2 instead of Logback. Extracts `traceId`/`spanId` from MDC with fallback to the current OpenTelemetry `SpanContext`, applies `LogSanitizer` to message/MDC/stack trace, and skips J-Obs internal loggers to prevent circular logging.
+- **Auto-configuration `Log4j2Configuration`** — registers the Log4j2 appender bean and attaches it to the Log4j2 root logger. Activates when `org.apache.logging.log4j.core.appender.AbstractAppender` is on the classpath and Logback (`ch.qos.logback.classic.Logger`) is not. When Log4j2 is bridged to SLF4J, the bean is still created but the root-logger attach step is skipped (defensive `LoggerContext` instance check), so the appender doesn't crash on hybrid setups.
+- **Tests** — `JObsLogAutoConfigurationTest` now verifies that Logback wins when both frameworks are present, and that the Log4j2 appender activates when Logback is filtered out via `FilteredClassLoader`.
+
+### Changed
+- `j-obs-spring-boot-starter/pom.xml` declares `org.apache.logging.log4j:log4j-core` as an `true` dependency (version inherited from `spring-boot-dependencies` BOM, no transitive impact for users on Logback).
+
## [1.2.0] - 2026-04-17
### Added
diff --git a/README.md b/README.md
index 6f137a2..e8c273f 100644
--- a/README.md
+++ b/README.md
@@ -153,7 +153,7 @@ sequenceDiagram
io.github.johnpitter
j-obs-spring-boot-starter
- 1.2.0
+ 1.3.0
```
@@ -166,28 +166,28 @@ J-Obs offers **modular starters** so you can include only the features you need,
io.github.johnpitter
j-obs-starter-tracing
- 1.2.0
+ 1.3.0
io.github.johnpitter
j-obs-starter-metrics
- 1.2.0
+ 1.3.0
io.github.johnpitter
j-obs-starter-logging
- 1.2.0
+ 1.3.0
io.github.johnpitter
j-obs-starter-profiling
- 1.2.0
+ 1.3.0
```
@@ -198,12 +198,12 @@ You can combine multiple starters:
io.github.johnpitter
j-obs-starter-tracing
- 1.2.0
+ 1.3.0
io.github.johnpitter
j-obs-starter-metrics
- 1.2.0
+ 1.3.0
```
@@ -211,10 +211,10 @@ You can combine multiple starters:
| Starter | Dependencies Added | Features Enabled |
|---|---|---|
-| `j-obs-spring-boot-starter` | All (OpenTelemetry, Micrometer, Logback, WebSocket, Actuator) | Everything |
+| `j-obs-spring-boot-starter` | All (OpenTelemetry, Micrometer, Logback or Log4j2, WebSocket, Actuator) | Everything |
| `j-obs-starter-tracing` | OpenTelemetry API + SDK | Traces, waterfall timeline, span details, SQL analyzer, anomaly detection, service map |
| `j-obs-starter-metrics` | Micrometer + Actuator | Metrics dashboard, latency percentiles, JVM stats, health monitoring, SLO/SLI |
-| `j-obs-starter-logging` | Logback + WebSocket | Real-time log streaming, level/logger filters, trace correlation |
+| `j-obs-starter-logging` | Logback or Log4j2 + WebSocket | Real-time log streaming, level/logger filters, trace correlation |
| `j-obs-starter-profiling` | None (uses JVM MXBeans) | CPU sampling, heap snapshots, thread dumps |
> **Note:** All starters include the base dashboard UI. Features auto-activate based on classpath — only the sections for your chosen starters appear in the dashboard.
diff --git a/j-obs-benchmarks/pom.xml b/j-obs-benchmarks/pom.xml
index d2106a7..86d247f 100644
--- a/j-obs-benchmarks/pom.xml
+++ b/j-obs-benchmarks/pom.xml
@@ -7,7 +7,7 @@
io.github.johnpitter
j-obs-parent
- 1.2.0
+ 1.3.0
j-obs-benchmarks
diff --git a/j-obs-bom/pom.xml b/j-obs-bom/pom.xml
index 8f7ee31..79e0a1d 100644
--- a/j-obs-bom/pom.xml
+++ b/j-obs-bom/pom.xml
@@ -7,7 +7,7 @@
io.github.johnpitter
j-obs-bom
- 1.2.0
+ 1.3.0
pom
J-Obs BOM
@@ -43,7 +43,7 @@
- 1.2.0
+ 1.3.0
diff --git a/j-obs-core/pom.xml b/j-obs-core/pom.xml
index a6dfe1c..ac4d606 100644
--- a/j-obs-core/pom.xml
+++ b/j-obs-core/pom.xml
@@ -8,7 +8,7 @@
io.github.johnpitter
j-obs-parent
- 1.2.0
+ 1.3.0
j-obs-core
diff --git a/j-obs-sample/pom.xml b/j-obs-sample/pom.xml
index b7f9be7..a6351f4 100644
--- a/j-obs-sample/pom.xml
+++ b/j-obs-sample/pom.xml
@@ -8,7 +8,7 @@
io.github.johnpitter
j-obs-parent
- 1.2.0
+ 1.3.0
../pom.xml
diff --git a/j-obs-spring-boot-starter/pom.xml b/j-obs-spring-boot-starter/pom.xml
index 572124f..350ea19 100644
--- a/j-obs-spring-boot-starter/pom.xml
+++ b/j-obs-spring-boot-starter/pom.xml
@@ -8,7 +8,7 @@
io.github.johnpitter
j-obs-parent
- 1.2.0
+ 1.3.0
j-obs-spring-boot-starter
@@ -119,6 +119,13 @@
true
+
+
+ org.apache.logging.log4j
+ log4j-core
+ true
+
+
diff --git a/j-obs-spring-boot-starter/src/main/java/io/github/jobs/spring/autoconfigure/JObsLogAutoConfiguration.java b/j-obs-spring-boot-starter/src/main/java/io/github/jobs/spring/autoconfigure/JObsLogAutoConfiguration.java
index 5dfa43b..34daeff 100644
--- a/j-obs-spring-boot-starter/src/main/java/io/github/jobs/spring/autoconfigure/JObsLogAutoConfiguration.java
+++ b/j-obs-spring-boot-starter/src/main/java/io/github/jobs/spring/autoconfigure/JObsLogAutoConfiguration.java
@@ -4,16 +4,19 @@
import ch.qos.logback.classic.LoggerContext;
import io.github.jobs.application.LogRepository;
import io.github.jobs.infrastructure.InMemoryLogRepository;
+import io.github.jobs.spring.log.JObsLog4j2Appender;
import io.github.jobs.spring.log.JObsLogAppender;
import io.github.jobs.spring.log.LogEntryFactory;
import io.github.jobs.spring.web.LogApiController;
import io.github.jobs.spring.web.LogController;
import io.github.jobs.spring.web.template.TemplateService;
import io.github.jobs.spring.websocket.LogWebSocketHandler;
+import org.apache.logging.log4j.LogManager;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
@@ -97,8 +100,8 @@ public LogEntryFactory logEntryFactory() {
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = {
- "org.springframework.web.socket.config.annotation.WebSocketConfigurer",
- "jakarta.websocket.server.ServerContainer"
+ "org.springframework.web.socket.config.annotation.WebSocketConfigurer",
+ "jakarta.websocket.server.ServerContainer"
})
@EnableWebSocket
static class WebSocketConfiguration implements WebSocketConfigurer {
@@ -148,6 +151,43 @@ public JObsLogAppender jObsLogAppender(LogRepository logRepository, LogEntryFact
Logger rootLogger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
rootLogger.addAppender(appender);
+ return appender;
+ }
+ }
+ /**
+ * Configuration for Log4j2 integration (optional).
+ * Only loaded when Log4j2 Core is on the classpath AND Logback is NOT present.
+ * When both are on the classpath, Logback takes priority (Spring Boot default).
+ *
+ * Uses @ConditionalOnMissingClass instead of @ConditionalOnMissingBean because
+ * @ConditionalOnMissingBean on a @Configuration class is evaluated before any beans
+ * are created, making it unreliable for inter-configuration ordering.
+ */
+ @Configuration(proxyBeanMethods = false)
+ @ConditionalOnClass(name = "org.apache.logging.log4j.core.appender.AbstractAppender")
+ @ConditionalOnMissingClass("ch.qos.logback.classic.Logger")
+ static class Log4j2Configuration {
+
+ @Bean
+ @ConditionalOnMissingBean
+ public JObsLog4j2Appender jObsLog4j2Appender(LogRepository logRepository, LogEntryFactory logEntryFactory) {
+ JObsLog4j2Appender appender = new JObsLog4j2Appender("J-OBS");
+ appender.setLogRepository(logRepository);
+ appender.setLogEntryFactory(logEntryFactory);
+ appender.start();
+
+ // Attach to root logger only when Log4j2 is the real logging backend.
+ // When Log4j2 is bridged to SLF4J (log4j-to-slf4j), getContext() returns
+ // an SLF4J-backed context that is not a Log4j2 LoggerContext — safe to skip.
+ try {
+ org.apache.logging.log4j.spi.LoggerContext ctx = LogManager.getContext(false);
+ if (ctx instanceof org.apache.logging.log4j.core.LoggerContext log4j2Ctx) {
+ log4j2Ctx.getRootLogger().addAppender(appender);
+ }
+ } catch (Exception | Error ignored) {
+ // Log4j2 context unavailable or bridged — appender bean is still valid
+ }
+
return appender;
}
}
diff --git a/j-obs-spring-boot-starter/src/main/java/io/github/jobs/spring/log/JObsLog4j2Appender.java b/j-obs-spring-boot-starter/src/main/java/io/github/jobs/spring/log/JObsLog4j2Appender.java
new file mode 100644
index 0000000..739f3cb
--- /dev/null
+++ b/j-obs-spring-boot-starter/src/main/java/io/github/jobs/spring/log/JObsLog4j2Appender.java
@@ -0,0 +1,160 @@
+package io.github.jobs.spring.log;
+
+import io.github.jobs.application.LogRepository;
+import io.github.jobs.domain.log.LogEntry;
+import io.github.jobs.domain.log.LogLevel;
+import io.github.jobs.spring.security.LogSanitizer;
+import io.opentelemetry.api.trace.Span;
+import io.opentelemetry.api.trace.SpanContext;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import org.apache.logging.log4j.core.config.Property;
+
+import java.time.Instant;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * Log4j2 appender that captures log events and stores them in the LogRepository.
+ * Mirror of JObsLogAppender for Log4j2 support.
+ */
+public class JObsLog4j2Appender extends AbstractAppender {
+
+ private volatile LogRepository logRepository;
+ private volatile LogEntryFactory logEntryFactory;
+ private volatile LogSanitizer logSanitizer;
+
+ public JObsLog4j2Appender(String name) {
+ super(name, null, null, true, Property.EMPTY_ARRAY);
+ }
+
+ public void setLogRepository(LogRepository logRepository) {
+ this.logRepository = logRepository;
+ }
+
+ public void setLogEntryFactory(LogEntryFactory logEntryFactory) {
+ this.logEntryFactory = logEntryFactory;
+ }
+
+ public void setLogSanitizer(LogSanitizer logSanitizer) {
+ this.logSanitizer = logSanitizer;
+ }
+
+ @Override
+ public void append(LogEvent event) {
+ LogRepository repo = this.logRepository;
+ if (repo == null) {
+ return;
+ }
+
+ // Skip logs from J-Obs library itself to avoid circular logging
+ String loggerName = event.getLoggerName();
+ if (loggerName.startsWith("io.github.jobs.spring") ||
+ loggerName.startsWith("io.github.jobs.application") ||
+ loggerName.startsWith("io.github.jobs.domain") ||
+ loggerName.startsWith("io.github.jobs.infrastructure")) {
+ return;
+ }
+
+ LogEntryFactory factory = this.logEntryFactory;
+ if (factory == null) {
+ synchronized (this) {
+ factory = this.logEntryFactory;
+ if (factory == null) {
+ factory = new LogEntryFactory();
+ this.logEntryFactory = factory;
+ }
+ }
+ }
+
+ LogSanitizer sanitizer = this.logSanitizer;
+ if (sanitizer == null) {
+ synchronized (this) {
+ sanitizer = this.logSanitizer;
+ if (sanitizer == null) {
+ sanitizer = new LogSanitizer();
+ this.logSanitizer = sanitizer;
+ }
+ }
+ }
+
+ String message = event.getMessage() != null ? event.getMessage().getFormattedMessage() : "";
+ String sanitizedMessage = sanitizer.sanitize(message);
+ String sanitizedStackTrace = sanitizer.sanitizeStackTrace(formatThrowable(event));
+ Map mdcMap = event.getContextData() != null
+ ? event.getContextData().toMap().entrySet().stream()
+ .collect(Collectors.toMap(Map.Entry::getKey, e -> String.valueOf(e.getValue())))
+ : Map.of();
+ Map sanitizedMdc = sanitizer.sanitizeMdc(mdcMap);
+
+ LogEntry entry = factory.create(
+ Instant.ofEpochMilli(event.getTimeMillis()),
+ convertLevel(event.getLevel()),
+ loggerName,
+ sanitizedMessage,
+ event.getThreadName(),
+ extractTraceId(mdcMap),
+ extractSpanId(mdcMap),
+ sanitizedStackTrace,
+ sanitizedMdc != null ? Map.copyOf(sanitizedMdc) : Map.of()
+ );
+
+ repo.add(entry);
+ }
+
+ private LogLevel convertLevel(Level level) {
+ if (level == null) {
+ return LogLevel.INFO;
+ }
+ if (level.isMoreSpecificThan(Level.ERROR)) return LogLevel.ERROR;
+ if (level.isMoreSpecificThan(Level.WARN)) return LogLevel.WARN;
+ if (level.isMoreSpecificThan(Level.INFO)) return LogLevel.INFO;
+ if (level.isMoreSpecificThan(Level.DEBUG)) return LogLevel.DEBUG;
+ return LogLevel.TRACE;
+ }
+
+ private String extractTraceId(Map mdc) {
+ String traceId = mdc.get("traceId");
+ if (traceId == null) traceId = mdc.get("trace_id");
+ if (traceId == null) traceId = mdc.get("X-B3-TraceId");
+ if (traceId == null) {
+ SpanContext ctx = getOtelSpanContext();
+ if (ctx != null) traceId = ctx.getTraceId();
+ }
+ return traceId;
+ }
+
+ private String extractSpanId(Map mdc) {
+ String spanId = mdc.get("spanId");
+ if (spanId == null) spanId = mdc.get("span_id");
+ if (spanId == null) spanId = mdc.get("X-B3-SpanId");
+ if (spanId == null) {
+ SpanContext ctx = getOtelSpanContext();
+ if (ctx != null) spanId = ctx.getSpanId();
+ }
+ return spanId;
+ }
+
+ private SpanContext getOtelSpanContext() {
+ try {
+ SpanContext ctx = Span.current().getSpanContext();
+ return ctx.isValid() ? ctx : null;
+ } catch (NoClassDefFoundError | Exception e) {
+ return null;
+ }
+ }
+
+ private String formatThrowable(LogEvent event) {
+ if (event.getThrown() == null) {
+ return null;
+ }
+ StringBuilder sb = new StringBuilder();
+ Throwable t = event.getThrown();
+ sb.append(t.toString());
+ for (StackTraceElement el : t.getStackTrace()) {
+ sb.append("\n\tat ").append(el);
+ }
+ return sb.toString();
+ }
+}
diff --git a/j-obs-spring-boot-starter/src/test/java/io/github/jobs/spring/autoconfigure/JObsLogAutoConfigurationTest.java b/j-obs-spring-boot-starter/src/test/java/io/github/jobs/spring/autoconfigure/JObsLogAutoConfigurationTest.java
index dc88922..1d43dc6 100644
--- a/j-obs-spring-boot-starter/src/test/java/io/github/jobs/spring/autoconfigure/JObsLogAutoConfigurationTest.java
+++ b/j-obs-spring-boot-starter/src/test/java/io/github/jobs/spring/autoconfigure/JObsLogAutoConfigurationTest.java
@@ -1,6 +1,8 @@
package io.github.jobs.spring.autoconfigure;
import io.github.jobs.application.LogRepository;
+import io.github.jobs.spring.log.JObsLog4j2Appender;
+import io.github.jobs.spring.log.JObsLogAppender;
import io.github.jobs.spring.web.LogApiController;
import io.github.jobs.spring.web.LogController;
import io.github.jobs.spring.websocket.LogWebSocketHandler;
@@ -95,4 +97,31 @@ void shouldConfigureMaxEntries() {
assertThat(props.getLogs().getMaxEntries()).isEqualTo(5000);
});
}
+
+ /**
+ * When Logback is on the classpath (default Spring Boot setup), Logback takes priority.
+ * The Log4j2 appender must NOT be created, preventing duplicate log capture.
+ */
+ @Test
+ void shouldUseLogbackWhenBothLoggingFrameworksPresent() {
+ contextRunner.run(context -> {
+ assertThat(context).hasSingleBean(JObsLogAppender.class);
+ assertThat(context).doesNotHaveBean(JObsLog4j2Appender.class);
+ });
+ }
+
+ /**
+ * When Logback is absent, Log4j2 appender should activate automatically.
+ * Uses FilteredClassLoader to simulate a Log4j2-only environment.
+ */
+ @Test
+ void shouldUseLog4j2AppenderWhenLogbackAbsent() {
+ contextRunner
+ .withClassLoader(new FilteredClassLoader(ch.qos.logback.classic.Logger.class))
+ .run(context -> {
+ assertThat(context).hasSingleBean(LogRepository.class);
+ assertThat(context).hasSingleBean(JObsLog4j2Appender.class);
+ assertThat(context).doesNotHaveBean(JObsLogAppender.class);
+ });
+ }
}
diff --git a/j-obs-starter-logging/pom.xml b/j-obs-starter-logging/pom.xml
index 5fc89a2..e0076ac 100644
--- a/j-obs-starter-logging/pom.xml
+++ b/j-obs-starter-logging/pom.xml
@@ -8,7 +8,7 @@
io.github.johnpitter
j-obs-parent
- 1.2.0
+ 1.3.0
j-obs-starter-logging
diff --git a/j-obs-starter-metrics/pom.xml b/j-obs-starter-metrics/pom.xml
index 2eff826..8f06f87 100644
--- a/j-obs-starter-metrics/pom.xml
+++ b/j-obs-starter-metrics/pom.xml
@@ -8,7 +8,7 @@
io.github.johnpitter
j-obs-parent
- 1.2.0
+ 1.3.0
j-obs-starter-metrics
diff --git a/j-obs-starter-profiling/pom.xml b/j-obs-starter-profiling/pom.xml
index 6df0a97..49dd0a5 100644
--- a/j-obs-starter-profiling/pom.xml
+++ b/j-obs-starter-profiling/pom.xml
@@ -8,7 +8,7 @@
io.github.johnpitter
j-obs-parent
- 1.2.0
+ 1.3.0
j-obs-starter-profiling
diff --git a/j-obs-starter-tracing/pom.xml b/j-obs-starter-tracing/pom.xml
index 8a23457..98c3b89 100644
--- a/j-obs-starter-tracing/pom.xml
+++ b/j-obs-starter-tracing/pom.xml
@@ -8,7 +8,7 @@
io.github.johnpitter
j-obs-parent
- 1.2.0
+ 1.3.0
j-obs-starter-tracing
diff --git a/pom.xml b/pom.xml
index e8365a1..a239c9d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,7 +7,7 @@
io.github.johnpitter
j-obs-parent
- 1.2.0
+ 1.3.0
pom
J-Obs Parent
diff --git a/samples/j-obs-alerts/pom.xml b/samples/j-obs-alerts/pom.xml
index 4b81f1e..c893c8f 100644
--- a/samples/j-obs-alerts/pom.xml
+++ b/samples/j-obs-alerts/pom.xml
@@ -8,7 +8,7 @@
io.github.johnpitter
j-obs-samples
- 1.2.0
+ 1.3.0
../pom.xml
diff --git a/samples/j-obs-database/pom.xml b/samples/j-obs-database/pom.xml
index 3f59d0e..f78cb2f 100644
--- a/samples/j-obs-database/pom.xml
+++ b/samples/j-obs-database/pom.xml
@@ -8,7 +8,7 @@
io.github.johnpitter
j-obs-samples
- 1.2.0
+ 1.3.0
../pom.xml
diff --git a/samples/j-obs-microservices/api-gateway/pom.xml b/samples/j-obs-microservices/api-gateway/pom.xml
index 26d18f9..ba1fd87 100644
--- a/samples/j-obs-microservices/api-gateway/pom.xml
+++ b/samples/j-obs-microservices/api-gateway/pom.xml
@@ -8,7 +8,7 @@
io.github.johnpitter
j-obs-microservices
- 1.2.0
+ 1.3.0
../pom.xml
diff --git a/samples/j-obs-microservices/inventory-service/pom.xml b/samples/j-obs-microservices/inventory-service/pom.xml
index 4b789bf..c241166 100644
--- a/samples/j-obs-microservices/inventory-service/pom.xml
+++ b/samples/j-obs-microservices/inventory-service/pom.xml
@@ -8,7 +8,7 @@
io.github.johnpitter
j-obs-microservices
- 1.2.0
+ 1.3.0
../pom.xml
diff --git a/samples/j-obs-microservices/order-service/pom.xml b/samples/j-obs-microservices/order-service/pom.xml
index 00f00df..59aafe9 100644
--- a/samples/j-obs-microservices/order-service/pom.xml
+++ b/samples/j-obs-microservices/order-service/pom.xml
@@ -8,7 +8,7 @@
io.github.johnpitter
j-obs-microservices
- 1.2.0
+ 1.3.0
../pom.xml
diff --git a/samples/j-obs-microservices/pom.xml b/samples/j-obs-microservices/pom.xml
index a83f518..d848ed2 100644
--- a/samples/j-obs-microservices/pom.xml
+++ b/samples/j-obs-microservices/pom.xml
@@ -8,7 +8,7 @@
io.github.johnpitter
j-obs-samples
- 1.2.0
+ 1.3.0
../pom.xml
diff --git a/samples/j-obs-minimal/pom.xml b/samples/j-obs-minimal/pom.xml
index 640752c..58ae75f 100644
--- a/samples/j-obs-minimal/pom.xml
+++ b/samples/j-obs-minimal/pom.xml
@@ -8,7 +8,7 @@
io.github.johnpitter
j-obs-samples
- 1.2.0
+ 1.3.0
../pom.xml
diff --git a/samples/j-obs-security/pom.xml b/samples/j-obs-security/pom.xml
index 17e072e..9f4a200 100644
--- a/samples/j-obs-security/pom.xml
+++ b/samples/j-obs-security/pom.xml
@@ -8,7 +8,7 @@
io.github.johnpitter
j-obs-samples
- 1.2.0
+ 1.3.0
../pom.xml
diff --git a/samples/j-obs-slo/pom.xml b/samples/j-obs-slo/pom.xml
index b053c56..ce5ed29 100644
--- a/samples/j-obs-slo/pom.xml
+++ b/samples/j-obs-slo/pom.xml
@@ -8,7 +8,7 @@
io.github.johnpitter
j-obs-samples
- 1.2.0
+ 1.3.0
../pom.xml
diff --git a/samples/j-obs-webflux/pom.xml b/samples/j-obs-webflux/pom.xml
index 672b2b4..3b023f6 100644
--- a/samples/j-obs-webflux/pom.xml
+++ b/samples/j-obs-webflux/pom.xml
@@ -8,7 +8,7 @@
io.github.johnpitter
j-obs-samples
- 1.2.0
+ 1.3.0
../pom.xml
diff --git a/samples/pom.xml b/samples/pom.xml
index 0875f4c..3bbc30f 100644
--- a/samples/pom.xml
+++ b/samples/pom.xml
@@ -8,7 +8,7 @@
io.github.johnpitter
j-obs-parent
- 1.2.0
+ 1.3.0
../pom.xml