Skip to content

Commit

Permalink
OpenTelemetry (#245)
Browse files Browse the repository at this point in the history
* add OpenTelemetry support to Example application

* add docs about OpenTelemetry
  • Loading branch information
jenschude authored Aug 28, 2023
1 parent 86b5665 commit b2ed1d4
Show file tree
Hide file tree
Showing 21 changed files with 7,286 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using System;

// <copyright file="Instrumentation.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>

namespace commercetools.Api.CheckoutApp
{
using System.Diagnostics;
using System.Diagnostics.Metrics;

/// <summary>
/// It is recommended to use a custom type to hold references for
/// ActivitySource and Instruments. This avoids possible type collisions
/// with other components in the DI container.
/// </summary>
public class Instrumentation : IDisposable
{
internal const string ActivitySourceName = "Commercetools.CheckoutApp";
internal const string MeterName = "Commercetools.CheckoutApp";
private readonly Meter meter;

public Instrumentation()
{
string? version = typeof(Instrumentation).Assembly.GetName().Version?.ToString();
this.ActivitySource = new ActivitySource(ActivitySourceName, version);
this.meter = new Meter(MeterName, version);
}

public ActivitySource ActivitySource { get; }

public void Dispose()
{
this.ActivitySource.Dispose();
this.meter.Dispose();
}
}
}

Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
using System;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using OpenTelemetry.Logs;
using OpenTelemetry.Resources;

namespace commercetools.Api.CheckoutApp
{
Expand All @@ -12,6 +17,34 @@ public static void Main(string[] args)

public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureLogging((context, logging) =>
{
var configuration = context.Configuration;
var logExporter = configuration.GetValue("UseLogExporter", defaultValue: "console")!.ToLowerInvariant();

logging.ClearProviders();
logging.AddOpenTelemetry(options =>
{
// Note: See appsettings.json Logging:OpenTelemetry section for configuration.
var resourceBuilder = ResourceBuilder.CreateDefault();
Startup.ConfigureResource(resourceBuilder);
options.SetResourceBuilder(resourceBuilder);

switch (logExporter)
{
case "otlp":
options.AddOtlpExporter(otlpOptions =>
{
// Use IConfiguration directly for Otlp exporter endpoint option.
otlpOptions.Endpoint = new Uri(configuration.GetValue("Otlp:Endpoint", defaultValue: "http://localhost:4317")!);
});
break;
default:
options.AddConsoleExporter();
break;
}
});
})
.ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });
}
}
25 changes: 25 additions & 0 deletions commercetools.Sdk/Examples/commercetools.Api.CheckoutApp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,28 @@ This example application demonstrates how the ME endpoints can be used with the
1. Open the folder **\Examples\commercetools.Api.CheckoutApp**
2. Start the app using **Run** > **Start Debugging** or by pressing **F5**.
3. A new web browser window opens and displays the Checkout app. A list of Products should appear.

## OpenTelemetry

For experimenting with OpenTelemetry the example Checkout application is configured for exporting logs, metrics and traces
using OpenTelemetry. By default the data will be reported to the console.

To send the data to an OpenTelemetry endpoint you have to configure the
[appsettings.json](../commercetools.Sdk/Examples/commercetools.Api.CheckoutApp/commercetools.Api.CheckoutApp.csproj) file.

```json
{
"UseLogExporter": "otlp",
"UseTracingExporter": "otlp",
"UseMetricExporter": "otlp"
}
```

Setting the exporter options to `otlp` will send the data to a local OTLP endpoint at `http://localhost:4317`. Please
make sure to have a OpenTelemetry collector listening. You can override the endpoint address by configuring
`Otlp:Endpoint` in your appsettings.json file.

This repository includes a [docker compose setup](../../../../otel/README.md) for starting a local OpenTelemetry
collector in combination with Prometheus, Grafana, JaegerUI and NewRelic as exporter target.


106 changes: 105 additions & 1 deletion commercetools.Sdk/Examples/commercetools.Api.CheckoutApp/Startup.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System;
using System.Diagnostics.Metrics;
using commercetools.Api.CheckoutApp.Extensions;
using commercetools.Api.CheckoutApp.Services;
using commercetools.Base.Client;
Expand All @@ -6,6 +8,11 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using OpenTelemetry.Exporter;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
using OpenTelemetry.Instrumentation.AspNetCore;
using OpenTelemetry.Metrics;

namespace commercetools.Api.CheckoutApp
{
Expand All @@ -20,10 +27,12 @@ public Startup(IConfiguration configuration)
Settings.ProjectKey = clientConfiguration.ProjectKey;
Settings.DefaultCurrency = configuration.GetSection("DefaultCurrency").Value;
}

// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{

services.UseCommercetoolsScopedClient(configuration, "SPA-Client");
services.AddScoped<InCookiesStoreManager>();
services.AddScoped<InSessionStoreManager>();
Expand All @@ -37,8 +46,104 @@ public void ConfigureServices(IServiceCollection services)
services.AddControllersWithViews();
services.AddMvc();
services.AddHttpContextAccessor();

// Note: Switch between Zipkin/OTLP/Console by setting UseTracingExporter in appsettings.json.
var tracingExporter = configuration.GetValue("UseTracingExporter", defaultValue: "console")!.ToLowerInvariant();

// Note: Switch between Prometheus/OTLP/Console by setting UseMetricsExporter in appsettings.json.
var metricsExporter = configuration.GetValue("UseMetricsExporter", defaultValue: "console")!.ToLowerInvariant();

// Note: Switch between Explicit/Exponential by setting HistogramAggregation in appsettings.json
var histogramAggregation = configuration.GetValue("HistogramAggregation", defaultValue: "explicit")!.ToLowerInvariant();

services.AddSingleton<Instrumentation>();

services.AddOpenTelemetry()
.ConfigureResource(ConfigureResource)
.WithTracing(builder =>
{
// Tracing

// Ensure the TracerProvider subscribes to any custom ActivitySources.
builder
.AddSource(Instrumentation.ActivitySourceName)
.SetSampler(new AlwaysOnSampler())
.AddHttpClientInstrumentation()
.AddAspNetCoreInstrumentation();

// Use IConfiguration binding for AspNetCore instrumentation options.
services.Configure<AspNetCoreInstrumentationOptions>(
configuration.GetSection("AspNetCoreInstrumentation"));

switch (tracingExporter)
{
case "zipkin":
builder.AddZipkinExporter();

builder.ConfigureServices(services =>
{
// Use IConfiguration binding for Zipkin exporter options.
services.Configure<ZipkinExporterOptions>(configuration.GetSection("Zipkin"));
});
break;

case "otlp":
builder.AddOtlpExporter(otlpOptions =>
{
// Use IConfiguration directly for Otlp exporter endpoint option.
otlpOptions.Endpoint = new Uri(configuration.GetValue("Otlp:Endpoint",
defaultValue: "http://localhost:4317")!);
});
break;

default:
builder.AddConsoleExporter();
break;
}
}
)
.WithMetrics(builder =>
{
// Metrics

// Ensure the MeterProvider subscribes to any custom Meters.
builder
.AddMeter(Instrumentation.MeterName)
.AddRuntimeInstrumentation()
.AddHttpClientInstrumentation()
.AddAspNetCoreInstrumentation();

switch (histogramAggregation)
{
case "exponential":
builder.AddView(instrument => instrument.GetType().GetGenericTypeDefinition() == typeof(Histogram<>)
? new Base2ExponentialBucketHistogramConfiguration()
: null);
break;
default:
// Explicit bounds histogram is the default.
// No additional configuration necessary.
break;
}

switch (metricsExporter)
{
case "otlp":
builder.AddOtlpExporter(otlpOptions =>
{
// Use IConfiguration directly for Otlp exporter endpoint option.
otlpOptions.Endpoint = new Uri(configuration.GetValue("Otlp:Endpoint", defaultValue: "http://localhost:4317")!);
});
break;
default:
builder.AddConsoleExporter();
break;
}
});
}

public static void ConfigureResource(ResourceBuilder r) => r.AddService(serviceName: "commercetools-checkout-demo", serviceVersion: typeof(Program).Assembly.GetName().Version?.ToString() ?? "unknown", serviceInstanceId: Environment.MachineName);

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
Expand All @@ -52,7 +157,6 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
app.UseRouting();

app.UseAuthorization();

app.UseSession();
app.UseEndpoints(endpoints =>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@
},
"AllowedHosts": "*",
"DefaultCurrency": "EUR",

"UseLogExporter": "console",
"UseTracingExporter": "console",
"UseMetricExporter": "console",
"HistogramAggregation": "explicit",
"SPA-Client": {
"ProjectKey": "{projectKey}",
"ClientSecret": "{clientSecret}",
"ClientId": "{clientID}",
"AuthorizationBaseAddress": "https://auth.{region}.commercetools.com/",
"ApiBaseAddress": "https://api.{region}.commercetools.com/",
"Scope": "{scope}"
"ApiBaseAddress": "https://api.{region}.commercetools.com/"
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<TargetFramework>net7.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\commercetools.Sdk.Api\commercetools.Sdk.Api.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="OpenTelemetry.AutoInstrumentation" Version="1.0.0-rc.2" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.5.1-beta.1" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.5.1-beta.1" />
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.5.1" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.5.1" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.5.1-beta.1" />
<PackageReference Include="OpenTelemetry.Instrumentation.GrpcNetClient" Version="1.5.1-beta.1" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.5.1-beta.1" />
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.5.0" />
<PackageReference Include="OpenTelemetry.ResourceDetectors.Container" Version="1.0.0-beta.4" />
</ItemGroup>

</Project>
41 changes: 41 additions & 0 deletions otel/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@


# Images
IMAGE_VERSION=1.4.0
IMAGE_NAME=ghcr.io/open-telemetry/demo

# Demo Platform
ENV_PLATFORM=local

# OpenTelemetry Collector
OTEL_COLLECTOR_HOST=otelcol
OTEL_COLLECTOR_PORT_GRPC=4317
OTEL_COLLECTOR_PORT_HTTP=4318
OTEL_EXPORTER_OTLP_ENDPOINT=http://${OTEL_COLLECTOR_HOST}:${OTEL_COLLECTOR_PORT_GRPC}
PUBLIC_OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:8080/otlp-http/v1/traces

# OpenTelemetry Resource Definitions
OTEL_RESOURCE_ATTRIBUTES="service.namespace=opentelemetry-demo"

# Metrics Temporality
OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE=cumulative

# ********************
# Telemetry Components
# ********************
# Grafana
GRAFANA_SERVICE_PORT=3000
GRAFANA_SERVICE_HOST=grafana

# Jaeger
JAEGER_SERVICE_PORT=16686
JAEGER_SERVICE_HOST=jaeger

# Prometheus
PROMETHEUS_SERVICE_PORT=9090
PROMETHEUS_SERVICE_HOST=prometheus
PROMETHEUS_ADDR=${PROMETHEUS_SERVICE_HOST}:${PROMETHEUS_SERVICE_PORT}

# NewRelic
NEW_RELIC_OTLP_ENDPOINT=https://otlp.eu01.nr-data.net:4317
NEW_RELIC_LICENSE_KEY=
15 changes: 15 additions & 0 deletions otel/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# OpenTelemetry

This folder contains a preconfigured docker compose file for local experiments
with OpenTelemetry. It includes a OpenTelemetry collector, a Jaeger UI, Prometheus, Grafana, and
NewRelic as export target.

To start it use the following command:

```bash
docker-compose --env-file .env up --no-build
```

The collector is preconfigured to export metric, traces and log entries to NewRelic.
In order to activate it add your NewRelic api key to the `.env` file and restart the
containers.
Loading

0 comments on commit b2ed1d4

Please sign in to comment.