Repository: open-telemetry/opentelemetry-java
File: exporters/common/src/main/java/io/opentelemetry/exporter/internal/ExporterMetrics.java (Java)

Code before:
    this.meterProviderSupplier = meterProviderSupplier;
    this.exporterName = exporterName;
    this.transportName = transportName;
    this.seenAttrs = Attributes.builder().put(ATTRIBUTE_KEY_TYPE, type).build();
    this.successAttrs = this.seenAttrs.toBuilder().put(ATTRIBUTE_KEY_SUCCESS, true).build();
    this.failedAttrs = this.seenAttrs.toBuilder().put(ATTRIBUTE_KEY_SUCCESS, false).build();
  }

  /** Record number of records seen. */
  public void addSeen(long value) {
    seen().add(value, seenAttrs);
  }

  /** Record number of records which successfully exported. */
  public void addSuccess(long value) {
    exported().add(value, successAttrs);
  }

  /** Record number of records which failed to export. */
  public void addFailed(long value) {
    exported().add(value, failedAttrs);
  }

  private LongCounter seen() {
    LongCounter seen = this.seen;
    if (seen == null || isNoop(seen)) {
      seen = meter().counterBuilder(exporterName + ".exporter.seen").build();
      this.seen = seen;
    }
    return seen;
  }

  private LongCounter exported() {
    LongCounter exported = this.exported;
    if (exported == null || isNoop(exported)) {
      exported = meter().counterBuilder(exporterName + ".exporter.exported").build();
      this.exported = exported;
    }
    return exported;
  }

  private Meter meter() {
    return meterProviderSupplier
        .get()
        .get("io.opentelemetry.exporters." + exporterName + "-" + transportName);
  }

  private static boolean isNoop(LongCounter counter) {
    return counter.getClass().getSimpleName().startsWith("Noop");
  }


Review comment (performance):
The upside of this is that it provides a simple way to get around the race of `ExporterMetrics` possibly initializing before the `Supplier<MeterProvider>` is able to resolve a concrete `SdkMeterProvider` instance.

The downside is:
- Doing extra work every time we record a measurement
- When a noop is installed, we'll be doing extra work to call `meter().counterBuilder(exporterName + ".exporter.seen").build()` on every export for the lifetime of the app

Still, I think the benefits are worthwhile since in the grand scheme of things, the extra work being done is a drop in the bucket compared to the work to send batches of telemetry over the network.

Code after:
    this.meterProviderSupplier = meterProviderSupplier;
    this.exporterName = exporterName;
    this.transportName = transportName;
    this.seenAttrs = Attributes.builder().put(ATTRIBUTE_KEY_TYPE, type).build();
    this.successAttrs = this.seenAttrs.toBuilder().put(ATTRIBUTE_KEY_SUCCESS, true).build();
    this.failedAttrs = this.seenAttrs.toBuilder().put(ATTRIBUTE_KEY_SUCCESS, false).build();
  }

  /** Record number of records seen. */
  public void addSeen(long value) {
    seen().add(value, seenAttrs);
  }

  /** Record number of records which successfully exported. */
  public void addSuccess(long value) {
    exported().add(value, successAttrs);
  }

  /** Record number of records which failed to export. */
  public void addFailed(long value) {
    exported().add(value, failedAttrs);
  }

  private LongCounter seen() {
    LongCounter seen = this.seen;
    if (seen == null || isNoop(seen)) {
      seen = meter().counterBuilder(exporterName + ".exporter.seen").build();
      this.seen = seen;
    }
    return seen;
  }

  private LongCounter exported() {
    LongCounter exported = this.exported;
    if (exported == null || isNoop(exported)) {
      exported = meter().counterBuilder(exporterName + ".exporter.exported").build();
      this.exported = exported;
    }
    return exported;
  }

  private Meter meter() {
    return meterProviderSupplier
        .get()
        .get("io.opentelemetry.exporters." + exporterName + "-" + transportName);
  }

  private static boolean isNoop(LongCounter counter) {
    // This is a poor way to identify a Noop implementation, but the API doesn't provide a better
    // way. Perhaps we could add a common "Noop" interface to allow for an instanceof check?
    return counter.getClass().getSimpleName().startsWith("Noop");