diff --git a/dev/docker-compose.yaml b/dev/docker-compose.yaml index e21f27be9b..2d8b9a2149 100644 --- a/dev/docker-compose.yaml +++ b/dev/docker-compose.yaml @@ -5,24 +5,34 @@ services: image: jaegertracing/all-in-one:latest ports: - "16686:16686" - - "14268:14268" - - "14250:14250" + + prometheus: + container_name: prometheus + image: prom/prometheus:latest + volumes: + - ./prometheus.yaml:/etc/prometheus/prometheus.yml + ports: + - "9090:9090" otel-collector: - image: otel/opentelemetry-collector-contrib:0.48.0 + image: otel/opentelemetry-collector-contrib:0.51.0 volumes: - ./otel-config.yaml:/etc/otel/config.yaml command: --config /etc/otel/config.yaml environment: - JAEGER_ENDPOINT=jaeger:14250 ports: - - "1777:1777" # pprof extension - "4317:4317" # OTLP gRPC receiver - "4318:4318" # OTLP http/protobuf receiver - - "8888:8888" # Prometheus metrics exposed by the collector - - "8889:8889" # Prometheus exporter metrics - - "9411:9411" # zipkin receiver + - "6831:6831" # Jaeger thrift compact receiver + - "6832:6832" # Jaeger thrift binary receiver + - "8888:8888" # Prometheus metrics scrape endpoint with collector telemetry + - "8889:8889" # Prometheus metrics exporter (scrape endpoint) + - "9411:9411" # Zipkin receiver - "13133:13133" # health_check extension - - "55679:55679" # zpages extension + - "14250:14250" # Jaeger grpc receiver + - "14268:14268" # Jaeger thrift http receiver + - "55679:55679" # ZPages extension depends_on: - jaeger + - prometheus diff --git a/dev/otel-config.yaml b/dev/otel-config.yaml index af782f86f1..35f0cc8bc2 100644 --- a/dev/otel-config.yaml +++ b/dev/otel-config.yaml @@ -1,7 +1,5 @@ extensions: health_check: - pprof: - endpoint: :1777 zpages: endpoint: :55679 @@ -10,7 +8,6 @@ receivers: protocols: grpc: http: - opencensus: jaeger: protocols: grpc: @@ -24,26 +21,22 @@ processors: exporters: logging: - logLevel: debug + # logLevel: debug jaeger: endpoint: "${JAEGER_ENDPOINT}" tls: insecure: true prometheus: endpoint: "0.0.0.0:8889" - namespace: promexample - const_labels: - label1: value1 service: pipelines: traces: - receivers: [otlp, opencensus, jaeger, zipkin] + receivers: [otlp, jaeger, zipkin] processors: [batch] - exporters: [logging, jaeger] + exporters: [jaeger, logging] metrics: - receivers: [otlp, opencensus] + receivers: [otlp] processors: [batch] - exporters: [prometheus] - #exporters: [logging] - extensions: [health_check, pprof, zpages] + exporters: [prometheus, logging] + extensions: [health_check, zpages] diff --git a/dev/prometheus.yaml b/dev/prometheus.yaml new file mode 100644 index 0000000000..58eb918959 --- /dev/null +++ b/dev/prometheus.yaml @@ -0,0 +1,9 @@ +scrape_configs: + - job_name: 'otel-collector-telemetry' + scrape_interval: 10s + static_configs: + - targets: ['otel-collector:8888'] + - job_name: 'otel-collector-exporter' + scrape_interval: 10s + static_configs: + - targets: ['otel-collector:8889'] diff --git a/examples/README.md b/examples/README.md index 3a102c26e7..0caa0c9275 100644 --- a/examples/README.md +++ b/examples/README.md @@ -15,10 +15,11 @@ with the following names: to the `aspnet-server` and some public websites. The exact name is equal to `exampleApp` value. -The script creates Docker containers for MongoDB, Redis, Jaeger, +The script creates Docker containers for MongoDB, Redis, Jaeger, Prometheus, and the [OpenTelemetry Collector](https://opentelemetry.io/docs/collector/). -You can see the traces generated by the example in the Jaeger container. -The number and organization of traces generated depends on the `http-client` +You can see the traces generated by the example in the Jaeger container, +while the metrics are available on the Prometheus container. +The number and organization of traces generated depends on the `exampleApp` used in the specific run. The script waits for your input before stopping the containers. ## How to run the examples diff --git a/run-example.sh b/run-example.sh index 592687a6f3..b851563fd8 100755 --- a/run-example.sh +++ b/run-example.sh @@ -52,7 +52,7 @@ docker-compose -f ./dev/docker-compose.yaml -f ./examples/docker-compose.yaml up # instrument and run HTTP server app in background export OTEL_DOTNET_AUTO_TRACES_INSTRUMENTATION_PLUGINS="Examples.AspNetCoreMvc.OtelSdkPlugin, Examples.AspNetCoreMvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null:Examples.Vendor.Distro.Plugin, Examples.Vendor.Distro, Version=0.0.1.0, Culture=neutral, PublicKeyToken=null" -ENABLE_PROFILING=${enableProfiling} OTEL_DOTNET_AUTO_TRACES_ENABLED_INSTRUMENTATIONS="AspNet,SqlClient" OTEL_SERVICE_NAME="aspnet-server" OTEL_TRACES_EXPORTER=${exporter} ./dev/instrument.sh ASPNETCORE_URLS="http://127.0.0.1:8080/" dotnet ./examples/AspNetCoreMvc/bin/${configuration}/${aspNetAppTargetFramework}/Examples.AspNetCoreMvc.dll & +ENABLE_PROFILING=${enableProfiling} OTEL_DOTNET_AUTO_TRACES_ENABLED_INSTRUMENTATIONS="AspNet,MongoDb,SqlClient" OTEL_DOTNET_AUTO_METRICS_ADDITIONAL_SOURCES="MyCompany.MyProduct.MyLibrary" OTEL_DOTNET_AUTO_METRICS_ENABLED_INSTRUMENTATIONS="NetRuntime" OTEL_SERVICE_NAME="aspnet-server" OTEL_TRACES_EXPORTER=${exporter} ./dev/instrument.sh ASPNETCORE_URLS="http://127.0.0.1:8080/" dotnet ./examples/AspNetCoreMvc/bin/${configuration}/${aspNetAppTargetFramework}/Examples.AspNetCoreMvc.dll & unset OTEL_DOTNET_AUTO_TRACES_INSTRUMENTATION_PLUGINS ./dev/wait-local-port.sh 8080 @@ -60,4 +60,9 @@ unset OTEL_DOTNET_AUTO_TRACES_INSTRUMENTATION_PLUGINS ENABLE_PROFILING=${enableProfiling} OTEL_DOTNET_AUTO_TRACES_ENABLED_INSTRUMENTATIONS="HttpClient" OTEL_SERVICE_NAME=${exampleApp} OTEL_TRACES_EXPORTER=${exporter} OTEL_DOTNET_AUTO_LOAD_TRACER_AT_STARTUP=${exampleAppInjectSDK} ./dev/instrument.sh $exampleAppDotnetCli ./examples/${exampleApp}/bin/$configuration/${exampleAppTargetFramework}/Examples.${exampleApp}.${exampleAppExt} # verify if it works -read -p "Check traces under: http://localhost:16686/search. Press enter to close containers and stop example apps" +{ + echo "Check traces at http://localhost:16686/search" + echo "Check metrics at http://localhost:9090" + echo "Press enter to close containers and stop example apps" + read +} 2> /dev/null diff --git a/src/OpenTelemetry.AutoInstrumentation/Configuration/EnvironmentConfigurationMetricHelper.cs b/src/OpenTelemetry.AutoInstrumentation/Configuration/EnvironmentConfigurationMetricHelper.cs index 14667b9bc8..98abd2f2b5 100644 --- a/src/OpenTelemetry.AutoInstrumentation/Configuration/EnvironmentConfigurationMetricHelper.cs +++ b/src/OpenTelemetry.AutoInstrumentation/Configuration/EnvironmentConfigurationMetricHelper.cs @@ -52,6 +52,34 @@ private static MeterProviderBuilder SetExporter(this MeterProviderBuilder builde builder.AddConsoleExporter(); } + switch (settings.MetricExporter) + { + case MetricsExporter.Prometheus: + throw new NotSupportedException("Prometheus is not supported yet."); + case MetricsExporter.Otlp: +#if NETCOREAPP3_1 + if (settings.Http2UnencryptedSupportEnabled) + { + // Adding the OtlpExporter creates a GrpcChannel. + // This switch must be set before creating a GrpcChannel/HttpClient when calling an insecure gRPC service. + // See: https://docs.microsoft.com/aspnet/core/grpc/troubleshoot#call-insecure-grpc-services-with-net-core-client + AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true); + } +#endif + builder.AddOtlpExporter(options => + { + if (settings.OtlpExportProtocol.HasValue) + { + options.Protocol = settings.OtlpExportProtocol.Value; + } + }); + break; + case MetricsExporter.None: + break; + default: + throw new ArgumentOutOfRangeException($"Metrics exporter '{settings.MetricExporter}' is incorrect"); + } + return builder; } } diff --git a/test/OpenTelemetry.AutoInstrumentation.Tests/Configuration/SettingsTests.cs b/test/OpenTelemetry.AutoInstrumentation.Tests/Configuration/SettingsTests.cs index d3a277bc7c..574da87851 100644 --- a/test/OpenTelemetry.AutoInstrumentation.Tests/Configuration/SettingsTests.cs +++ b/test/OpenTelemetry.AutoInstrumentation.Tests/Configuration/SettingsTests.cs @@ -31,13 +31,19 @@ public SettingsTests() ClearEnvVars(); } + public static IEnumerable ExporterEnvVarAndLoadSettingsAction() + { + yield return new object[] { ConfigurationKeys.Traces.Exporter, void () => TracerSettings.FromDefaultSources() }; + yield return new object[] { ConfigurationKeys.Metrics.Exporter, void () => MeterSettings.FromDefaultSources() }; + } + public void Dispose() { ClearEnvVars(); } [Fact] - public void DefaultValues() + public void TracerSettings_DefaultValues() { var settings = TracerSettings.FromDefaultSources(); @@ -58,6 +64,26 @@ public void DefaultValues() } } + [Fact] + public void MeterSettings_DefaultValues() + { + var settings = MeterSettings.FromDefaultSources(); + + using (new AssertionScope()) + { + settings.MetricsEnabled.Should().BeTrue(); + settings.LoadMetricsAtStartup.Should().BeTrue(); + settings.MetricExporter.Should().Be(MetricsExporter.Otlp); + settings.OtlpExportProtocol.Should().Be(OtlpExportProtocol.HttpProtobuf); + settings.ConsoleExporterEnabled.Should().BeFalse(); + settings.EnabledInstrumentation.Should().BeEmpty(); + settings.MetricPlugins.Should().BeEmpty(); + settings.Meters.Should().BeEmpty(); + settings.Http2UnencryptedSupportEnabled.Should().BeFalse(); + settings.FlushOnUnhandledException.Should().BeFalse(); + } + } + [Theory] [InlineData("none", TracesExporter.None)] [InlineData("jaeger", TracesExporter.Jaeger)] @@ -73,15 +99,24 @@ public void TracesExporter_SupportedValues(string tracesExporter, TracesExporter } [Theory] - [InlineData("not-existing")] - [InlineData("prometheus")] - public void TracesExporter_UnsupportedValues(string tracesExporter) + [InlineData("none", MetricsExporter.None)] + [InlineData("otlp", MetricsExporter.Otlp)] + [InlineData("prometheus", MetricsExporter.Prometheus)] + public void MetricExporter_SupportedValues(string metricExporter, MetricsExporter expectedMetricsExporter) { - Environment.SetEnvironmentVariable(ConfigurationKeys.Traces.Exporter, tracesExporter); + Environment.SetEnvironmentVariable(ConfigurationKeys.Metrics.Exporter, metricExporter); + + var settings = MeterSettings.FromDefaultSources(); - Action act = () => TracerSettings.FromDefaultSources(); + settings.MetricExporter.Should().Be(expectedMetricsExporter); + } - act.Should().Throw(); + [Theory] + [MemberData(nameof(ExporterEnvVarAndLoadSettingsAction))] + public void UnsupportedExporterValues(string exporterEnvVar, Action loadSettingsAction) + { + Environment.SetEnvironmentVariable(exporterEnvVar, "not-existing"); + loadSettingsAction.Should().Throw(); } [Theory] @@ -128,6 +163,7 @@ public void FlushOnUnhandledException_DependsOnCorrespondingEnvVariable(string f private static void ClearEnvVars() { + Environment.SetEnvironmentVariable(ConfigurationKeys.Metrics.Exporter, null); Environment.SetEnvironmentVariable(ConfigurationKeys.Traces.Exporter, null); Environment.SetEnvironmentVariable(ConfigurationKeys.ExporterOtlpProtocol, null); Environment.SetEnvironmentVariable(ConfigurationKeys.Http2UnencryptedSupportEnabled, null);