All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opentripplanner.standalone.server.AlertMetrics Maven / Gradle / Ivy

The newest version!
package org.opentripplanner.standalone.server;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMultimap;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.MultiGauge;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Tags;
import io.micrometer.core.instrument.binder.MeterBinder;
import java.util.ArrayList;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import java.util.stream.Stream;
import org.opentripplanner.framework.application.ApplicationShutdownSupport;
import org.opentripplanner.routing.alertpatch.AlertCause;
import org.opentripplanner.routing.alertpatch.AlertEffect;
import org.opentripplanner.routing.alertpatch.AlertSeverity;
import org.opentripplanner.routing.alertpatch.TransitAlert;
import org.opentripplanner.routing.services.TransitAlertService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A binder that creates metrics about the alerts present in the system. The metrics are read from
 * the alert service twice a minute by a background thread.
 */
public class AlertMetrics implements MeterBinder {

  private static final Logger LOG = LoggerFactory.getLogger(AlertMetrics.class);
  private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
  private final Supplier serviceSupplier;
  private MultiGauge statuses;

  public AlertMetrics(Supplier serviceSupplier) {
    this.serviceSupplier = serviceSupplier;
    scheduler.scheduleWithFixedDelay(this::recordMetrics, 0, 30, TimeUnit.SECONDS);
  }

  @Override
  public void bindTo(MeterRegistry meterRegistry) {
    this.statuses = MultiGauge.builder("alerts")
      .description("Total number of alerts (sourced from GTFS-Alerts and SIRI-SX) in the system.")
      .register(meterRegistry);
    ApplicationShutdownSupport.addShutdownHook("alert-metrics-shutdown", scheduler::shutdownNow);
  }

  /**
   * Creates a {@link MultiGauge} for the alerts and publishes ("registers" in Micrometer language)
   * to the repository.
   */
  @VisibleForTesting
  void recordMetrics() {
    try {
      // during construction of the app the service can be null so we must check for this case.
      var transitAlertService = serviceSupplier.get();
      if (transitAlertService != null && statuses != null) {
        var rows = summarizeAlerts(transitAlertService);
        statuses.register(rows, true);
      }
    } catch (Exception e) {
      LOG.error("Error building alerts metrics", e);
    }
  }

  private Iterable> summarizeAlerts(TransitAlertService alertService) {
    var alerts = alertService.getAllAlerts();

    ImmutableMultimap taggedAlerts = alerts
      .stream()
      .collect(
        ImmutableListMultimap.<
            TransitAlert,
            AlertTags,
            TransitAlert
          >flatteningToImmutableListMultimap(AlertTags::of, Stream::of)
      );
    return taggedAlerts
      .keySet()
      .stream()
      .map(alertTags -> {
        var tags = alertTags.toTags();
        var count = taggedAlerts.get(alertTags).size();
        return MultiGauge.Row.of(tags, count);
      })
      .toList();
  }

  /**
   * The tags that can be exported for an alert.
   */
  record AlertTags(
    String feedId,
    String siriCodespace,
    AlertSeverity severity,
    AlertEffect effect,
    AlertCause cause
  ) {
    static AlertTags of(TransitAlert a) {
      return new AlertTags(
        a.getId().getFeedId(),
        a.siriCodespace(),
        a.severity(),
        a.effect(),
        a.cause()
      );
    }

    Tags toTags() {
      var tags = new ArrayList(5);
      tags.add(Tag.of("feedId", feedId));
      if (siriCodespace != null) {
        tags.add(Tag.of("siriCodespace", siriCodespace));
      }
      if (severity != null) {
        tags.add(Tag.of("severity", severity.name()));
      }
      if (effect != null) {
        tags.add(Tag.of("effect", effect.name()));
      }
      if (cause != null) {
        tags.add(Tag.of("cause", cause.name()));
      }
      return Tags.of(tags);
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy