feat: add logging on provider state transitions (#1444)

* NOISSUE add logging on provider state transitions

Signed-off-by: christian.lutnik <christian.lutnik@dynatrace.com>

* fix npe

Signed-off-by: christian.lutnik <christian.lutnik@dynatrace.com>

* fix failing test

Signed-off-by: christian.lutnik <christian.lutnik@dynatrace.com>

* fix failing test

Signed-off-by: christian.lutnik <christian.lutnik@dynatrace.com>

* format

Signed-off-by: christian.lutnik <christian.lutnik@dynatrace.com>

---------

Signed-off-by: christian.lutnik <christian.lutnik@dynatrace.com>
Co-authored-by: Simon Schrottner <simon.schrottner@dynatrace.com>
This commit is contained in:
chrfwow 2025-05-14 11:11:36 +02:00 committed by GitHub
parent bc10bacb5a
commit e2813b2e5d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 32 additions and 14 deletions

View File

@ -2,14 +2,14 @@ package dev.openfeature.sdk;
import dev.openfeature.sdk.exceptions.OpenFeatureError; import dev.openfeature.sdk.exceptions.OpenFeatureError;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import lombok.Getter; import java.util.concurrent.atomic.AtomicReference;
import lombok.extern.slf4j.Slf4j;
@Slf4j
class FeatureProviderStateManager implements EventProviderListener { class FeatureProviderStateManager implements EventProviderListener {
private final FeatureProvider delegate; private final FeatureProvider delegate;
private final AtomicBoolean isInitialized = new AtomicBoolean(); private final AtomicBoolean isInitialized = new AtomicBoolean();
private final AtomicReference<ProviderState> state = new AtomicReference<>(ProviderState.NOT_READY);
@Getter
private ProviderState state = ProviderState.NOT_READY;
public FeatureProviderStateManager(FeatureProvider delegate) { public FeatureProviderStateManager(FeatureProvider delegate) {
this.delegate = delegate; this.delegate = delegate;
@ -24,17 +24,17 @@ class FeatureProviderStateManager implements EventProviderListener {
} }
try { try {
delegate.initialize(evaluationContext); delegate.initialize(evaluationContext);
state = ProviderState.READY; setState(ProviderState.READY);
} catch (OpenFeatureError openFeatureError) { } catch (OpenFeatureError openFeatureError) {
if (ErrorCode.PROVIDER_FATAL.equals(openFeatureError.getErrorCode())) { if (ErrorCode.PROVIDER_FATAL.equals(openFeatureError.getErrorCode())) {
state = ProviderState.FATAL; setState(ProviderState.FATAL);
} else { } else {
state = ProviderState.ERROR; setState(ProviderState.ERROR);
} }
isInitialized.set(false); isInitialized.set(false);
throw openFeatureError; throw openFeatureError;
} catch (Exception e) { } catch (Exception e) {
state = ProviderState.ERROR; setState(ProviderState.ERROR);
isInitialized.set(false); isInitialized.set(false);
throw e; throw e;
} }
@ -42,7 +42,7 @@ class FeatureProviderStateManager implements EventProviderListener {
public void shutdown() { public void shutdown() {
delegate.shutdown(); delegate.shutdown();
state = ProviderState.NOT_READY; setState(ProviderState.NOT_READY);
isInitialized.set(false); isInitialized.set(false);
} }
@ -50,17 +50,34 @@ class FeatureProviderStateManager implements EventProviderListener {
public void onEmit(ProviderEvent event, ProviderEventDetails details) { public void onEmit(ProviderEvent event, ProviderEventDetails details) {
if (ProviderEvent.PROVIDER_ERROR.equals(event)) { if (ProviderEvent.PROVIDER_ERROR.equals(event)) {
if (details != null && details.getErrorCode() == ErrorCode.PROVIDER_FATAL) { if (details != null && details.getErrorCode() == ErrorCode.PROVIDER_FATAL) {
state = ProviderState.FATAL; setState(ProviderState.FATAL);
} else { } else {
state = ProviderState.ERROR; setState(ProviderState.ERROR);
} }
} else if (ProviderEvent.PROVIDER_STALE.equals(event)) { } else if (ProviderEvent.PROVIDER_STALE.equals(event)) {
state = ProviderState.STALE; setState(ProviderState.STALE);
} else if (ProviderEvent.PROVIDER_READY.equals(event)) { } else if (ProviderEvent.PROVIDER_READY.equals(event)) {
state = ProviderState.READY; setState(ProviderState.READY);
} }
} }
private void setState(ProviderState state) {
ProviderState oldState = this.state.getAndSet(state);
if (oldState != state) {
String providerName;
if (delegate.getMetadata() == null || delegate.getMetadata().getName() == null) {
providerName = "unknown";
} else {
providerName = delegate.getMetadata().getName();
}
log.info("Provider {} transitioned from state {} to state {}", providerName, oldState, state);
}
}
public ProviderState getState() {
return state.get();
}
FeatureProvider getProvider() { FeatureProvider getProvider() {
return delegate; return delegate;
} }

View File

@ -5,6 +5,7 @@ import static org.assertj.core.api.Assertions.assertThatCode;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import dev.openfeature.sdk.providers.memory.InMemoryProvider; import dev.openfeature.sdk.providers.memory.InMemoryProvider;
@ -112,7 +113,7 @@ class OpenFeatureAPITest {
api.getClient().track("track-event", new ImmutableContext(), new MutableTrackingEventDetails(22.2f)); api.getClient().track("track-event", new ImmutableContext(), new MutableTrackingEventDetails(22.2f));
verify(featureProvider).initialize(any()); verify(featureProvider).initialize(any());
verify(featureProvider).getMetadata(); verify(featureProvider, times(2)).getMetadata();
verify(featureProvider).track(any(), any(), any()); verify(featureProvider).track(any(), any(), any());
} }
} }