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

io.opentelemetry.sdk.metrics.SdkMeter Maven / Gradle / Ivy

The newest version!
/*
 * Copyright The OpenTelemetry Authors
 * SPDX-License-Identifier: Apache-2.0
 */

package io.opentelemetry.sdk.metrics;

import static java.util.stream.Collectors.toMap;

import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.internal.GuardedBy;
import io.opentelemetry.api.metrics.BatchCallback;
import io.opentelemetry.api.metrics.DoubleGaugeBuilder;
import io.opentelemetry.api.metrics.DoubleHistogramBuilder;
import io.opentelemetry.api.metrics.LongCounterBuilder;
import io.opentelemetry.api.metrics.LongUpDownCounterBuilder;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.api.metrics.ObservableMeasurement;
import io.opentelemetry.context.Context;
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
import io.opentelemetry.sdk.metrics.data.MetricData;
import io.opentelemetry.sdk.metrics.internal.MeterConfig;
import io.opentelemetry.sdk.metrics.internal.descriptor.InstrumentDescriptor;
import io.opentelemetry.sdk.metrics.internal.export.RegisteredReader;
import io.opentelemetry.sdk.metrics.internal.state.AsynchronousMetricStorage;
import io.opentelemetry.sdk.metrics.internal.state.CallbackRegistration;
import io.opentelemetry.sdk.metrics.internal.state.MeterProviderSharedState;
import io.opentelemetry.sdk.metrics.internal.state.MetricStorage;
import io.opentelemetry.sdk.metrics.internal.state.MetricStorageRegistry;
import io.opentelemetry.sdk.metrics.internal.state.SdkObservableMeasurement;
import io.opentelemetry.sdk.metrics.internal.state.SynchronousMetricStorage;
import io.opentelemetry.sdk.metrics.internal.state.WriteableMetricStorage;
import io.opentelemetry.sdk.metrics.internal.view.RegisteredView;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;

/** {@link SdkMeter} is SDK implementation of {@link Meter}. */
final class SdkMeter implements Meter {

  private static final Logger logger = Logger.getLogger(SdkMeter.class.getName());

  /**
   * Instrument names MUST conform to the following syntax.
   *
   * 
    *
  • They are not null or empty strings. *
  • They are case-insensitive, ASCII strings. *
  • The first character must be an alphabetic character. *
  • Subsequent characters must belong to the alphanumeric characters, '_', '.', '/', and '-'. *
  • They can have a maximum length of 255 characters. *
*/ private static final Pattern VALID_INSTRUMENT_NAME_PATTERN = Pattern.compile("([A-Za-z]){1}([A-Za-z0-9\\_\\-\\./]){0,254}"); private static final Meter NOOP_METER = MeterProvider.noop().get("noop"); private static final String NOOP_INSTRUMENT_NAME = "noop"; private final Object collectLock = new Object(); private final Object callbackLock = new Object(); @GuardedBy("callbackLock") private final List callbackRegistrations = new ArrayList<>(); private final MeterProviderSharedState meterProviderSharedState; private final InstrumentationScopeInfo instrumentationScopeInfo; private final Map readerStorageRegistries; private final boolean meterEnabled; SdkMeter( MeterProviderSharedState meterProviderSharedState, InstrumentationScopeInfo instrumentationScopeInfo, List registeredReaders, MeterConfig meterConfig) { this.instrumentationScopeInfo = instrumentationScopeInfo; this.meterProviderSharedState = meterProviderSharedState; this.readerStorageRegistries = registeredReaders.stream() .collect(toMap(Function.identity(), unused -> new MetricStorageRegistry())); this.meterEnabled = meterConfig.isEnabled(); } // Visible for testing InstrumentationScopeInfo getInstrumentationScopeInfo() { return instrumentationScopeInfo; } /** Collect all metrics for the meter. */ Collection collectAll(RegisteredReader registeredReader, long epochNanos) { // Short circuit collection process if meter is disabled if (!meterEnabled) { return Collections.emptyList(); } List currentRegisteredCallbacks; synchronized (callbackLock) { currentRegisteredCallbacks = new ArrayList<>(callbackRegistrations); } // Collections across all readers are sequential synchronized (collectLock) { for (CallbackRegistration callbackRegistration : currentRegisteredCallbacks) { callbackRegistration.invokeCallback( registeredReader, meterProviderSharedState.getStartEpochNanos(), epochNanos); } Collection storages = Objects.requireNonNull(readerStorageRegistries.get(registeredReader)).getStorages(); List result = new ArrayList<>(storages.size()); for (MetricStorage storage : storages) { MetricData current = storage.collect( meterProviderSharedState.getResource(), getInstrumentationScopeInfo(), meterProviderSharedState.getStartEpochNanos(), epochNanos); // Ignore if the metric data doesn't have any data points, for example when aggregation is // Aggregation#drop() if (!current.isEmpty()) { result.add(current); } } return Collections.unmodifiableList(result); } } /** Reset the meter, clearing all registered callbacks and storages. */ void resetForTest() { synchronized (collectLock) { synchronized (callbackLock) { callbackRegistrations.clear(); } this.readerStorageRegistries.values().forEach(MetricStorageRegistry::resetForTest); } } @Override public LongCounterBuilder counterBuilder(String name) { return checkValidInstrumentName(name) ? new SdkLongCounter.SdkLongCounterBuilder(this, name) : NOOP_METER.counterBuilder(NOOP_INSTRUMENT_NAME); } @Override public LongUpDownCounterBuilder upDownCounterBuilder(String name) { return checkValidInstrumentName(name) ? new SdkLongUpDownCounter.SdkLongUpDownCounterBuilder(this, name) : NOOP_METER.upDownCounterBuilder(NOOP_INSTRUMENT_NAME); } @Override public DoubleHistogramBuilder histogramBuilder(String name) { return checkValidInstrumentName(name) ? new SdkDoubleHistogram.SdkDoubleHistogramBuilder(this, name) : NOOP_METER.histogramBuilder(NOOP_INSTRUMENT_NAME); } @Override public DoubleGaugeBuilder gaugeBuilder(String name) { return checkValidInstrumentName(name) ? new SdkDoubleGauge.SdkDoubleGaugeBuilder(this, name) : NOOP_METER.gaugeBuilder(NOOP_INSTRUMENT_NAME); } @Override public BatchCallback batchCallback( Runnable callback, ObservableMeasurement observableMeasurement, ObservableMeasurement... additionalMeasurements) { Set measurements = new HashSet<>(); measurements.add(observableMeasurement); Collections.addAll(measurements, additionalMeasurements); List sdkMeasurements = new ArrayList<>(); for (ObservableMeasurement measurement : measurements) { if (!(measurement instanceof SdkObservableMeasurement)) { logger.log( Level.WARNING, "batchCallback called with instruments that were not created by the SDK."); continue; } SdkObservableMeasurement sdkMeasurement = (SdkObservableMeasurement) measurement; if (!instrumentationScopeInfo.equals(sdkMeasurement.getInstrumentationScopeInfo())) { logger.log( Level.WARNING, "batchCallback called with instruments that belong to a different Meter."); continue; } sdkMeasurements.add(sdkMeasurement); } CallbackRegistration callbackRegistration = CallbackRegistration.create(sdkMeasurements, callback); registerCallback(callbackRegistration); return new SdkObservableInstrument(this, callbackRegistration); } /** * Unregister the callback. * *

Callbacks are originally registered via {@link #registerCallback(CallbackRegistration)}. */ void removeCallback(CallbackRegistration callbackRegistration) { synchronized (callbackLock) { this.callbackRegistrations.remove(callbackRegistration); } } /** * Register the callback. * *

The callback will be invoked once per collection until unregistered via {@link * #removeCallback(CallbackRegistration)}. */ void registerCallback(CallbackRegistration callbackRegistration) { synchronized (callbackLock) { callbackRegistrations.add(callbackRegistration); } } /** Returns {@code true} if the {@link MeterConfig#enabled()} of the meter is {@code true}. */ boolean isMeterEnabled() { return meterEnabled; } /** Registers new synchronous storage associated with a given instrument. */ WriteableMetricStorage registerSynchronousMetricStorage(InstrumentDescriptor instrument) { List registeredStorages = new ArrayList<>(); for (Map.Entry entry : readerStorageRegistries.entrySet()) { RegisteredReader reader = entry.getKey(); MetricStorageRegistry registry = entry.getValue(); for (RegisteredView registeredView : reader.getViewRegistry().findViews(instrument, getInstrumentationScopeInfo())) { if (Aggregation.drop() == registeredView.getView().getAggregation()) { continue; } registeredStorages.add( registry.register( SynchronousMetricStorage.create( reader, registeredView, instrument, meterProviderSharedState.getExemplarFilter()))); } } if (registeredStorages.size() == 1) { return registeredStorages.get(0); } return new MultiWritableMetricStorage(registeredStorages); } /** Register new asynchronous storage associated with a given instrument. */ SdkObservableMeasurement registerObservableMeasurement( InstrumentDescriptor instrumentDescriptor) { List> registeredStorages = new ArrayList<>(); for (Map.Entry entry : readerStorageRegistries.entrySet()) { RegisteredReader reader = entry.getKey(); MetricStorageRegistry registry = entry.getValue(); for (RegisteredView registeredView : reader.getViewRegistry().findViews(instrumentDescriptor, getInstrumentationScopeInfo())) { if (Aggregation.drop() == registeredView.getView().getAggregation()) { continue; } registeredStorages.add( registry.register( AsynchronousMetricStorage.create(reader, registeredView, instrumentDescriptor))); } } return SdkObservableMeasurement.create( instrumentationScopeInfo, instrumentDescriptor, registeredStorages); } @Override public String toString() { return "SdkMeter{instrumentationScopeInfo=" + instrumentationScopeInfo + "}"; } /** Check if the instrument name is valid. If invalid, log a warning. */ // Visible for testing static boolean checkValidInstrumentName(String name) { if (name != null && VALID_INSTRUMENT_NAME_PATTERN.matcher(name).matches()) { return true; } if (logger.isLoggable(Level.WARNING)) { logger.log( Level.WARNING, "Instrument name \"" + name + "\" is invalid, returning noop instrument. Instrument names must consist of 255 or fewer characters including alphanumeric, _, ., -, /, and start with a letter.", new AssertionError()); } return false; } private static class MultiWritableMetricStorage implements WriteableMetricStorage { private final List storages; private MultiWritableMetricStorage(List storages) { this.storages = storages; } @Override public void recordLong(long value, Attributes attributes, Context context) { for (WriteableMetricStorage storage : storages) { storage.recordLong(value, attributes, context); } } @Override public void recordDouble(double value, Attributes attributes, Context context) { for (WriteableMetricStorage storage : storages) { storage.recordDouble(value, attributes, context); } } @Override public boolean isEnabled() { for (WriteableMetricStorage storage : storages) { if (storage.isEnabled()) { return true; } } return false; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy