[java-identity-provider] branch main updated: IDP-1731 - Add metric to detect connectors in failure state

Scott Cantor cantor.2 at osu.edu
Wed Dec 30 18:45:06 UTC 2020


This is an automated email from the git hooks/post-receive script.

scantor pushed a commit to branch main
in repository java-identity-provider.

View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=f69e8afa6f9680c8c701ec260b34a8bbaf4887ed

The following commit(s) were added to refs/heads/main by this push:
       new  f69e8afa6 IDP-1731 - Add metric to detect connectors in failure state
f69e8afa6 is described below

commit f69e8afa6f9680c8c701ec260b34a8bbaf4887ed
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Wed Dec 30 13:45:03 2020 -0500

    IDP-1731 - Add metric to detect connectors in failure state
    
    https://issues.shibboleth.net/jira/browse/IDP-1731
---
 .../attribute/resolver/AbstractDataConnector.java  | 59 ++++++++++++++--------
 .../idp/attribute/resolver/DataConnector.java      | 13 +++++
 .../impl/AttributeResolverServiceGaugeSet.java     | 44 ++++++++++++++--
 idp-war/src/main/webapp/WEB-INF/jsp/status.jsp     | 25 ++++++---
 4 files changed, 109 insertions(+), 32 deletions(-)

diff --git a/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/AbstractDataConnector.java b/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/AbstractDataConnector.java
index a8c6425be..22627e740 100644
--- a/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/AbstractDataConnector.java
+++ b/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/AbstractDataConnector.java
@@ -55,6 +55,9 @@ public abstract class AbstractDataConnector extends AbstractResolverPlugin<Map<S
     /** cache for the log prefix - to save multiple recalculations. */
     @Nullable private String logPrefix;
 
+    /** When did this connector last work? */
+    @Nullable private Instant lastSuccess;
+
     /** When did this connector last fail? */
     @Nullable private Instant lastFail;
 
@@ -81,7 +84,7 @@ public abstract class AbstractDataConnector extends AbstractResolverPlugin<Map<S
      * @return ID of the {@link AbstractDataConnector} whose values will be used in the event that this data connector
      *         experiences an error
      */
-    @Override @Nullable public String getFailoverDataConnectorId() {
+    @Nullable public String getFailoverDataConnectorId() {
         return failoverDataConnectorId;
     }
 
@@ -99,6 +102,24 @@ public abstract class AbstractDataConnector extends AbstractResolverPlugin<Map<S
         failoverDataConnectorId = StringSupport.trimOrNull(id);
     }
 
+    /**
+     * Set the time when this connector last worked.
+     *
+     * @param time what to set
+     * 
+     * @since 4.1.0
+     */
+    public void setLastSuccess(@Nullable final Instant time) {
+        lastSuccess = time;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Nullable public Instant getLastSuccess() {
+        return lastSuccess;
+    }
+
     /**
      * Set the time when this connector last failed.
      *
@@ -111,7 +132,7 @@ public abstract class AbstractDataConnector extends AbstractResolverPlugin<Map<S
     /**
      * {@inheritDoc}
      */
-    @Override @Nullable public Instant getLastFail() {
+    @Nullable public Instant getLastFail() {
         return lastFail;
     }
 
@@ -121,11 +142,13 @@ public abstract class AbstractDataConnector extends AbstractResolverPlugin<Map<S
      * @param delay what to set
      */
     public void setNoRetryDelay(@Nonnull final Duration delay) {
+        ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
         noRetryDelay = delay;
     }
 
     /** {@inheritDoc} */
-    @Override @Nonnull public Duration getNoRetryDelay() {
+    @Nonnull public Duration getNoRetryDelay() {
         return noRetryDelay;
     }
 
@@ -141,11 +164,9 @@ public abstract class AbstractDataConnector extends AbstractResolverPlugin<Map<S
         exportAllAttributes = what;
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     @Deprecated(since = "4.1.0", forRemoval = true)
-    @Override public boolean isExportAllAttributes() {
+    public boolean isExportAllAttributes() {
         return exportAllAttributes;
     }
 
@@ -160,13 +181,19 @@ public abstract class AbstractDataConnector extends AbstractResolverPlugin<Map<S
         exportAttributes = Set.copyOf(what);
     }
 
-    /**
-     * {@inheritDoc}
-     */
-    @Override @Nonnull @NonnullElements @Unmodifiable public Collection<String> getExportAttributes() {
+    /** {@inheritDoc} */
+    @Nonnull @NonnullElements @Unmodifiable public Collection<String> getExportAttributes() {
         return exportAttributes;
     }
 
+    /** {@inheritDoc} */
+    @Override protected void doInitialize() throws ComponentInitializationException {
+        super.doInitialize();
+
+        // The Id is now definitive. Just in case it was used prior to that, reset the getPrefixCache
+        logPrefix = null;
+    }
+
     /**
      * {@inheritDoc}
      * 
@@ -182,6 +209,7 @@ public abstract class AbstractDataConnector extends AbstractResolverPlugin<Map<S
         final Map<String, IdPAttribute> result;
         try {
             result = doDataConnectorResolve(resolutionContext, workContext);
+            setLastSuccess(Instant.now());
         } catch (final NoResultAnErrorResolutionException | MultipleResultAnErrorResolutionException e) {
             // Do not record these failures, they are 'expected'
             throw e;
@@ -204,15 +232,6 @@ public abstract class AbstractDataConnector extends AbstractResolverPlugin<Map<S
         return result;
     }
 
-    /** {@inheritDoc} */
-    @Override protected void doInitialize() throws ComponentInitializationException {
-
-        super.doInitialize();
-
-        // The Id is now definitive. Just in case it was used prior to that, reset the getPrefixCache
-        logPrefix = null;
-    }
-
     /**
      * Retrieves a collection of attributes from some data source.
      * 
diff --git a/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/DataConnector.java b/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/DataConnector.java
index ca85576d0..ecbc30972 100644
--- a/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/DataConnector.java
+++ b/idp-attribute-resolver-api/src/main/java/net/shibboleth/idp/attribute/resolver/DataConnector.java
@@ -43,6 +43,19 @@ public interface DataConnector extends ResolverPlugin<Map<String, IdPAttribute>>
      */
      @Nonnull Duration getNoRetryDelay();
 
+     /**
+      * Get the time when this connector last succeeded.
+      *
+      * TODO: Remove default in V5.
+      *
+      * @return when it last succeeded
+      * 
+      * @since 4.1.0
+      */
+     @Nullable default Instant getLastSuccess() {
+         return Instant.now();
+     }
+
      /**
       * Get the time when this connector last failed. This will be set for any exception regardless of the setting of
       * {@link #isPropagateResolutionExceptions()}
diff --git a/idp-attribute-resolver-impl/src/main/java/net/shibboleth/idp/attribute/resolver/impl/AttributeResolverServiceGaugeSet.java b/idp-attribute-resolver-impl/src/main/java/net/shibboleth/idp/attribute/resolver/impl/AttributeResolverServiceGaugeSet.java
index 3a42f381c..3544ce9df 100644
--- a/idp-attribute-resolver-impl/src/main/java/net/shibboleth/idp/attribute/resolver/impl/AttributeResolverServiceGaugeSet.java
+++ b/idp-attribute-resolver-impl/src/main/java/net/shibboleth/idp/attribute/resolver/impl/AttributeResolverServiceGaugeSet.java
@@ -53,11 +53,45 @@ public class AttributeResolverServiceGaugeSet extends ReloadableServiceGaugeSet<
      * 
      * @param metricName name to include in metric names produced by this set
      */
+// Checkstyle: AnonInnerLength|MethodLength OFF
     public AttributeResolverServiceGaugeSet(
             @Nonnull @NotEmpty @ParameterName(name="metricName") final String metricName) {
         super(metricName);
         
-// Checkstyle: AnonInnerLength OFF
+        getMetricMap().put(
+                MetricRegistry.name(DEFAULT_METRIC_NAME, metricName, "success"),
+                new Gauge<Map<String,Instant>>() {
+                    public Map<String,Instant> getValue() {
+                        final Map<String,Instant> mapBuilder = new HashMap<>();
+                        final ServiceableComponent<AttributeResolver> component =
+                                getService().getServiceableComponent();
+                        if (component != null) {
+                            try {                                
+                                final Object resolver = component.getComponent();
+                                if (resolver instanceof AttributeResolverImpl) {
+                                    final Collection<DataConnector> connectors =
+                                            ((AttributeResolverImpl) resolver).getDataConnectors().values();
+                                    for (final DataConnector connector: connectors) {
+                                        if (connector.getLastSuccess() != null) {
+                                            mapBuilder.put(connector.getId(), connector.getLastSuccess());
+                                        }
+                                    }
+                                } else if (resolver instanceof AttributeResolver) {
+                                   log.debug("{}: Cannot get Data Connector success " +
+                                           " information from unsupported class type {}",
+                                           getLogPrefix(), resolver.getClass());
+                                } else {
+                                    log.warn("{}: Injected Service was not for an AttributeResolver ({})",
+                                            getLogPrefix(), resolver.getClass());
+                                }
+                            } finally {
+                                component.unpinComponent();
+                            }
+                        }
+                        return Map.copyOf(mapBuilder);
+                    }
+                });
+
         getMetricMap().put(
                 MetricRegistry.name(DEFAULT_METRIC_NAME, metricName, "failure"),
                 new Gauge<Map<String,Instant>>() {
@@ -77,11 +111,11 @@ public class AttributeResolverServiceGaugeSet extends ReloadableServiceGaugeSet<
                                         }
                                     }
                                 } else if (resolver instanceof AttributeResolver) {
-                                   log.debug("{} : Cannot get Data Connector failure " +
+                                   log.debug("{}: Cannot get Data Connector failure " +
                                            " information from unsupported class type {}",
                                            getLogPrefix(), resolver.getClass());
                                 } else {
-                                    log.warn("{} : Injected Service was not for an AttributeResolver ({})",
+                                    log.warn("{}: Injected Service was not for an AttributeResolver ({})",
                                             getLogPrefix(), resolver.getClass());
                                 }
                             } finally {
@@ -91,9 +125,9 @@ public class AttributeResolverServiceGaugeSet extends ReloadableServiceGaugeSet<
                         return Map.copyOf(mapBuilder);
                     }
                 });
-// Checkstyle: AnonInnerLength ON
         
     }
+// Checkstyle: AnonInnerLength|MethodLength ON
 
     /** {@inheritDoc} */
     @Override
@@ -106,7 +140,7 @@ public class AttributeResolverServiceGaugeSet extends ReloadableServiceGaugeSet<
                 if (component.getComponent() instanceof AttributeResolver) {
                     return;
                 }
-                log.error("{} : Injected service was not for an AttributeResolver ({})",
+                log.error("{}: Injected service was not for an AttributeResolver ({})",
                         getLogPrefix(), component.getClass());
                 throw new ComponentInitializationException("Injected service was not for an AttributeResolver");
             } finally {
diff --git a/idp-war/src/main/webapp/WEB-INF/jsp/status.jsp b/idp-war/src/main/webapp/WEB-INF/jsp/status.jsp
index 055a93ad6..3fbd94fbd 100644
--- a/idp-war/src/main/webapp/WEB-INF/jsp/status.jsp
+++ b/idp-war/src/main/webapp/WEB-INF/jsp/status.jsp
@@ -137,19 +137,30 @@ for (final ReloadableService service : (Collection<ReloadableService>) request.g
     		out.println("No Attribute Resolver Gauge Set Found");
     		continue;
     	}
-    	final Gauge<Map<String,Instant>> gauge = (Gauge<Map<String,Instant>>) metrics.getMetrics().get("net.shibboleth.idp.attribute.resolver.failure");
-    	Set<Entry<String, Instant>> entrySet = gauge.getValue().entrySet();
-    	if (entrySet.isEmpty()) {
+    	final Gauge<Map<String,Instant>> failGauge =
+    	        (Gauge<Map<String,Instant>>) metrics.getMetrics().get("net.shibboleth.idp.attribute.resolver.failure");
+    	final Set<Entry<String,Instant>> failSet = failGauge.getValue().entrySet();
+    	if (failSet.isEmpty()) {
 	    	out.println("\tNo Data Connector has ever failed");
 			out.println();
 			continue;
 		}
-    	for (final Entry<String, Instant> en : entrySet) {
-			final Instant lastFail = en.getValue();
+        final Gauge<Map<String,Instant>> successGauge =
+                (Gauge<Map<String,Instant>>) metrics.getMetrics().get("net.shibboleth.idp.attribute.resolver.success");
+        final Map<String,Instant> successMap = successGauge.getValue();
+        final ArrayList<String> failingConnectors = new ArrayList<>();
+    	for (final Entry<String, Instant> en : failSet) {
 			final String connectorId = en.getKey();
+            final Instant lastFail = en.getValue();
 			out.println("\tDataConnector " +  connectorId + ": last failed at " + dateTimeFormatter.format(lastFail));
             out.println();
-        }   
-    }    
+            final Instant lastSuccess = successMap.get(connectorId);
+            if (lastSuccess == null || lastSuccess.isBefore(lastFail)) {
+                failingConnectors.add(connectorId);
+            }
+        }
+        out.println("\tCurrently failing: " + failingConnectors);
+        out.println();
+    }
 }
 %>

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list