diff --git a/dependencyManagement/build.gradle.kts b/dependencyManagement/build.gradle.kts
index 3fd2f5fc38..3c4fed3dc3 100644
--- a/dependencyManagement/build.gradle.kts
+++ b/dependencyManagement/build.gradle.kts
@@ -21,7 +21,8 @@ val DEPENDENCY_BOMS = listOf(
"io.zipkin.brave:brave-bom:5.13.7",
"io.zipkin.reporter2:zipkin-reporter-bom:2.16.3",
"org.junit:junit-bom:5.8.2",
- "org.testcontainers:testcontainers-bom:1.16.3"
+ "org.testcontainers:testcontainers-bom:1.16.3",
+ "org.yaml:snakeyaml:1.30"
)
val DEPENDENCY_SETS = listOf(
diff --git a/sdk-extensions/metric-incubator/README.md b/sdk-extensions/metric-incubator/README.md
new file mode 100644
index 0000000000..36d750ae79
--- /dev/null
+++ b/sdk-extensions/metric-incubator/README.md
@@ -0,0 +1,83 @@
+# OpenTelemetry Metric Incubator
+
+[![Javadocs][javadoc-image]][javadoc-url]
+
+This artifact contains experimental code related to metrics.
+
+## View File Configuration
+
+Adds support for file based YAML configuration of Metric SDK Views.
+
+For example, suppose `/Users/user123/view.yaml` has the following content:
+
+```yaml
+- selector:
+ instrument_name: my-instrument
+ instrument_type: COUNTER
+ meter_name: my-meter
+ meter_version: 1.0.0
+ meter_schema_url: http://example.com
+ view:
+ name: new-instrument-name
+ description: new-description
+ aggregation: histogram
+ attribute_keys:
+ - foo
+ - bar
+```
+
+The equivalent view configuration would be:
+
+```
+SdkMeterProvider.builder()
+ .registerView(
+ InstrumentSelector.builder()
+ .setInstrumentName("my-instrument")
+ .setInstrumentType(InstrumentType.COUNTER)
+ .setMeterSelector(
+ MeterSelector.builder()
+ .setName("my-meter")
+ .setVersion("1.0.0")
+ .setSchemaUrl("http://example.com")
+ .build())
+ .build(),
+ View.builder()
+ .setName("new-instrument")
+ .setDescription("new-description")
+ .setAggregation(Aggregation.histogram())
+ .filterAttributes(key -> new HashSet<>(Arrays.asList("foo", "bar")).contains(key))
+ .build());
+```
+
+If using [autoconfigure](../autoconfigure) with this artifact on your classpath, it will automatically load a list of view config files specified via environment variable or system property:
+
+| System property | Environment variable | Purpose |
+|---------------------------------------|---------------------------------------|------------------------------------------------------|
+| otel.experimental.metrics.view-config | OTEL_EXPERIMENTAL_METRICS_VIEW_CONFIG | List of files containing view configuration YAML [1] |
+
+**[1]** In addition to absolute paths, resources on the classpath packaged with a jar can be loaded.
+For example, `otel.experimental.metrics.view-config=classpath:/my-view.yaml` loads the
+resource `/my-view.yaml`.
+
+If not using autoconfigure, a file can be used to configure views as follows:
+
+```
+SdkMeterProviderBuilder builder = SdkMeterProvider.builder();
+try (FileInputStream fileInputStream = new FileInputStream("/Users/user123/view.yaml")) {
+ ViewConfig.registerViews(builder, fileInputStream);
+}
+```
+
+Notes on usage:
+
+- Many view configurations can live in one file. The YAML is parsed as an array of view
+ configurations.
+- At least one selection field is required, but including all is not necessary. Any omitted fields
+ will result in the default from `InstrumentSelector` being used.
+- At least one view field is required, but including all is not required. Any omitted fields will
+ result in the default from `View` being used.
+- Advanced selection criteria, like regular expressions, is not yet supported.
+
+[javadoc-image]: https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-sdk-extension-metric-incubator
+
+[javadoc-url]: https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-sdk-extension-metric-incubator
diff --git a/sdk-extensions/metric-incubator/build.gradle.kts b/sdk-extensions/metric-incubator/build.gradle.kts
new file mode 100644
index 0000000000..e1eb309d4a
--- /dev/null
+++ b/sdk-extensions/metric-incubator/build.gradle.kts
@@ -0,0 +1,22 @@
+plugins {
+ id("otel.java-conventions")
+ id("otel.publish-conventions")
+}
+
+description = "OpenTelemetry SDK Metric Incubator"
+otelJava.moduleName.set("io.opentelemetry.sdk.extension.metric.incubator")
+
+dependencies {
+ implementation(project(":sdk:metrics"))
+ implementation(project(":sdk-extensions:autoconfigure-spi"))
+
+ implementation("org.yaml:snakeyaml")
+
+ annotationProcessor("com.google.auto.value:auto-value")
+
+ testImplementation(project(":sdk:testing"))
+ testImplementation(project(":sdk-extensions:autoconfigure"))
+ testImplementation(project(":sdk:metrics-testing"))
+
+ testImplementation("com.google.guava:guava")
+}
diff --git a/sdk-extensions/metric-incubator/gradle.properties b/sdk-extensions/metric-incubator/gradle.properties
new file mode 100644
index 0000000000..4476ae57e3
--- /dev/null
+++ b/sdk-extensions/metric-incubator/gradle.properties
@@ -0,0 +1 @@
+otel.release=alpha
diff --git a/sdk-extensions/metric-incubator/src/main/java/io/opentelemetry/sdk/viewconfig/SelectorSpecification.java b/sdk-extensions/metric-incubator/src/main/java/io/opentelemetry/sdk/viewconfig/SelectorSpecification.java
new file mode 100644
index 0000000000..1ff1d06d0d
--- /dev/null
+++ b/sdk-extensions/metric-incubator/src/main/java/io/opentelemetry/sdk/viewconfig/SelectorSpecification.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.sdk.viewconfig;
+
+import com.google.auto.value.AutoValue;
+import io.opentelemetry.sdk.metrics.common.InstrumentType;
+import javax.annotation.Nullable;
+
+@AutoValue
+abstract class SelectorSpecification {
+
+ static AutoValue_SelectorSpecification.Builder builder() {
+ return new AutoValue_SelectorSpecification.Builder();
+ }
+
+ @Nullable
+ abstract String getInstrumentName();
+
+ @Nullable
+ abstract InstrumentType getInstrumentType();
+
+ @Nullable
+ abstract String getMeterName();
+
+ @Nullable
+ abstract String getMeterVersion();
+
+ @Nullable
+ abstract String getMeterSchemaUrl();
+
+ @AutoValue.Builder
+ interface Builder {
+ Builder instrumentName(@Nullable String instrumentName);
+
+ Builder instrumentType(@Nullable InstrumentType instrumentType);
+
+ Builder meterName(@Nullable String meterName);
+
+ Builder meterVersion(@Nullable String meterVersion);
+
+ Builder meterSchemaUrl(@Nullable String meterSchemaUrl);
+
+ SelectorSpecification build();
+ }
+}
diff --git a/sdk-extensions/metric-incubator/src/main/java/io/opentelemetry/sdk/viewconfig/ViewConfig.java b/sdk-extensions/metric-incubator/src/main/java/io/opentelemetry/sdk/viewconfig/ViewConfig.java
new file mode 100644
index 0000000000..d5af5b317c
--- /dev/null
+++ b/sdk-extensions/metric-incubator/src/main/java/io/opentelemetry/sdk/viewconfig/ViewConfig.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.sdk.viewconfig;
+
+import static java.util.Objects.requireNonNull;
+import static java.util.stream.Collectors.toList;
+
+import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
+import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder;
+import io.opentelemetry.sdk.metrics.common.InstrumentType;
+import io.opentelemetry.sdk.metrics.view.Aggregation;
+import io.opentelemetry.sdk.metrics.view.InstrumentSelector;
+import io.opentelemetry.sdk.metrics.view.MeterSelector;
+import io.opentelemetry.sdk.metrics.view.View;
+import io.opentelemetry.sdk.metrics.view.ViewBuilder;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import javax.annotation.Nullable;
+import org.yaml.snakeyaml.Yaml;
+
+/**
+ * Enables file based YAML configuration of Metric SDK {@link View}s.
+ *
+ *
For example, a YAML file with the following content:
+ *
+ *
+ * - selector:
+ * instrument_name: my-instrument
+ * instrument_type: COUNTER
+ * meter_name: my-meter
+ * meter_version: 1.0.0
+ * meter_schema_url: http://example.com
+ * view:
+ * name: new-instrument-name
+ * description: new-description
+ * aggregation: histogram
+ * attribute_keys:
+ * - foo
+ * - bar
+ *
+ *
+ * Is equivalent to the following configuration:
+ *
+ *
{@code
+ * SdkMeterProvider.builder()
+ * .registerView(
+ * InstrumentSelector.builder()
+ * .setInstrumentName("my-instrument")
+ * .setInstrumentType(InstrumentType.COUNTER)
+ * .setMeterSelector(
+ * MeterSelector.builder()
+ * .setName("my-meter")
+ * .setVersion("1.0.0")
+ * .setSchemaUrl("http://example.com")
+ * .build())
+ * .build(),
+ * View.builder()
+ * .setName("new-instrument")
+ * .setDescription("new-description")
+ * .setAggregation(Aggregation.histogram())
+ * .filterAttributes(key -> new HashSet<>(Arrays.asList("foo", "bar")).contains(key))
+ * .build());
+ * }
+ */
+public final class ViewConfig {
+
+ private ViewConfig() {}
+
+ /**
+ * Load the view configuration YAML from the {@code inputStream} and apply it to the {@link
+ * SdkMeterProviderBuilder}.
+ *
+ * @throws ConfigurationException if unable to interpret {@code inputStream} contents
+ */
+ public static void registerViews(
+ SdkMeterProviderBuilder meterProviderBuilder, InputStream inputStream) {
+ List viewConfigSpecs = loadViewConfig(inputStream);
+
+ for (ViewConfigSpecification viewConfigSpec : viewConfigSpecs) {
+ meterProviderBuilder.registerView(
+ toInstrumentSelector(viewConfigSpec.getSelectorSpecification()),
+ toView(viewConfigSpec.getViewSpecification()));
+ }
+ }
+
+ // Visible for testing
+ @SuppressWarnings("unchecked")
+ static List loadViewConfig(InputStream inputStream) {
+ Yaml yaml = new Yaml();
+ try {
+ List result = new ArrayList<>();
+ List