kubectl describe trainer fpommerening
{
name: "Frank Pommerening",
employer: {},
skills: [
"Senior-Softwareentwickler",
"Consultant",
"Trainer"
],
communication:{
email : "frank@pommerening-consulting.de",
twitter: "@fpommerening"
}
}
C++ | .NET | Erlang/Elixir | GO |
Java | JavaScript | PHP | Python |
Ruby | Rust | Swift |
ASP.NET | ASP.NET Core | HTTP clients |
Grpc.Net.Client | Redis Client | MS SQL Client |
services.AddOpenTelemetry().ConfigureResource(rb =>
rb.AddEnvironmentVariableDetector().AddService(ServiceName))
.WithMetrics(metricsBuilder =>
{
metricsBuilder.AddAspNetCoreInstrumentation();
metricsBuilder.AddHttpClientInstrumentation();
metricsBuilder.AddOtlpExporter(otlpOptions =>{ otlpOptions.Endpoint = new Uri(...);}));
})
.WithTracing(WithTracing =>
{
traceBuilder.SetSampler(new AlwaysOnSampler());
traceBuilder.AddAspNetCoreInstrumentation();
traceBuilder.AddHttpClientInstrumentation();
traceBuilder.AddOtlpExporter(otlpOptions =>{ otlpOptions.Endpoint = new Uri(...);}));
});
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
.ConfigureResource(res => res.AddService(ServiceName))
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddOtlpExporter(otlpOptions =>{ otlpOptions.Endpoint = new Uri(...);
.Build();
using var meterProvider = Sdk.CreateMeterProviderBuilder()
.ConfigureResource(res => res.AddService(ServiceName))
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddOtlpExporter(otlpOptions =>{ otlpOptions.Endpoint = new Uri(...);
.Build();
ActivitySource ActivitySource = new ActivitySource(ActivitySourceName, ...)
using var activity = ActivitySource.StartActivity(ActivityName);
Meter Meter = new Meter(MeterName, ...)
var counter = Meter.CreateCounter<int>(CounterName, Unit, Description);
var gauge = Meter.CreateObservableGauge<int>(CounterName, ()=>GetValue(), Unit, Description);
receivers:
otlp:
protocols:
grpc:{}
http:{}
exporters:
jaeger:
endpoint: jaeger.fqdn:14250
extensions:
health_check: {}
processors:
batch: {}
service:
extensions:
- health_check
pipelines:
traces:
exporters:
- jaeger
processors:
- batch
receivers:
- otlp
receivers:
otlp:
protocols:
grpc: {}
processors:
resource:
attributes:
- action: insert
key: loki.resource.labels
value: service.name
exporters:
otlp/jeager:
endpoint: jaeger-collector.jaeger:4317
tls:
insecure: true
prometheus:
endpoint: "${MY_POD_IP}:9090"
send_timestamps: true
metric_expiration: 15m
resource_to_telemetry_conversion:
enabled: true
loki:
endpoint: http://loki-write.loki:3100/loki/api/v1/push
otlp/aspecto:
endpoint: otelcol.aspecto.io:4317
headers:
Authorization: not-my-key
service:
pipelines:
logs:
receivers:
- otlp
processors:
- resource
exporters:
- loki
metrics:
receivers:
- otlp
exporters:
- prometheus
traces:
receivers:
- otlp
exporters:
- otlp/jeager
- otlp/aspecto
Lokale Anwendungen GitHub Services.AddOpenTelemetry()
.WithMetrics(mb => mb.AddOtlpExporter(opt => opt.Endpoint = new Uri(...)));
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenTelemetry().WithMetrics(mb => mb.AddPrometheusExporter());
var app = builder.Build();
app.UseOpenTelemetryPrometheusScrapingEndpoint();
var meterProvider = Sdk.CreateMeterProviderBuilder()
.AddMeter(MyMeter.Name)
.AddPrometheusHttpListener(options => options.UriPrefixes =
new string[] { "http://localhost:9090/" })
.Build();
public const string MeterName = "fp.monitoring.workshop.instrumentationlibrary";
using Meter meter = new Meter(MeterName);
Services.AddOpenTelemetry().WithMetrics(mb => mb.AddMeter(MeterName));
Counter<int> executionCounter = meter.CreateCounter<int>("executions");
executionCounter.Add(1);
Vergleichbar mit der Counter-Metrik, hält die ObservableCounter-Metrik den State (Wert) nicht selbst, sondern erhält ihn über eine Funktion.ObservableCounter<int> executionCounter =
meter.CreateCounter<int>("executions", ()=> QueryExecutions());
UpDownCounter-Metriken sind mit den Counter-Metriken vergleich. Abweichend sind Inkrementierung und Dekrementierung vorgesehen.
Beispiel: Länge einer Warteschlange
UpDownCounter<int> queryItemCounter = meter.CreateUpDownCounter<int>("query.lenght");
queryItemCounter.Add(5);
queryItemCounter.Add(-1);
Die ObservableUpDownCounter-Metriken unterstützen ObservableUpDownCounter<int> queryLength =
meter.CreateObservableUpDownCounter<int>("query.lenght", ()=> MyQuery.Lenght);
Einige Exporter haben keine native Unterstützung für UpDownCounter-Metriken. Sie werden dann meist als Gauge-Metrik exportiert.Gauge-Metriken können beliebige Zahlenwerte annehmen. Die Ermittlung des aktuellen Wertes erfolgt bei der Veröffentlichung der Metriken.
Je nach Aufwand ist ein Cache zu empfehlen.
Beispiele: aktueller RAM-Verbrauch, Anzahl Objekte im Lager.
meter.CreateObservableGauge("my_sample_gauge", observeValue: () => ObserveMyValue());
Histogram-Metriken stellen die Verteilung von Zahlenwerten in Buckets dar.
Beispiel: Verarbeitungszeit je Anfrage.
Histogram<double> mySampleHistogram = meter.CreateHistogram<double>("my_sample_histogram");
mySampleHistogram.Record(14.5d);
mySampleHistogram.Record(67.1d);
OpenTelemetry liefert einen Default für die Bucketgrenzen der Histogramme. Eigene Werte können ggf. in diesen Grenzen unzureichend abgebildet werden. Die Konfiguration eines View definiert eigene Bucket-Boundaries.
Services.AddOpenTelemetry().WithMetrics(mb =>{ ...
mb.AddView("my_sample_histogram",
new ExplicitBucketHistogramConfiguration
{
Boundaries = new double[] {1, 2, 5, 10, 50, 100}
});
});
Einheiten (Units) und Beschreibung (Descriptions) sind optionale Metadaten. Diese werden bei der Erstellung der Metriken angegeben.
UpDownCounter<int> queryLength =
meter.CreateUpDownCounter<int>("query.lenght","wi", "count of unprocessed work items");
meter.CreateObservableGauge("system.ram.free",
()=>> querySystemRam(), "MiB", "amount of free system memory");
Labels sind optionale Metadaten, die den jeweiligen Metrik-Wert näher beschreiben. Die Angabe erfolgt als 1 .. n Key-Value-Pair.
MyIntSampleCounter.Add(2, KeyValuePair.Create<string, object>("label1", value));
MySampleHistogram.Record(14.5d, new TagList{{"label1", 42}});
meter.CreateObservableGauge("my.sample.gauge",
observeValue: () => new Measurement<int>(42,
KeyValuePair.Create<string, object>("label2", "value2")));
Services.AddOpenTelemetry()
.WithTracing(tb =>
{
tb.SetSampler(SAMPLER);
tb.AddOtlpExporter(opt => opt.Endpoint = new Uri(...));
});
Services.AddOpenTelemetry()
.WithTracing(tb =>
{
...
tb.AddAspNetCoreInstrumentation();
tb.AddHttpClientInstrumentation();
tb.AddGrpcClientInstrumentation();
...
});
public const string ActivitySourceName = "fp.monitoring.workshop.instrumentationlibrary";
using myActivitySource = new ActivitySource(ActivitySourceName);
Der Name ist wichtig für die Registrierung bei OpenTelemetry.Services.AddOpenTelemetry()
.WithTracing(tb =>
{ tb.AddSource(ActivitySourceName); });
using var activity = ActivitySource.StartActivity(ActivityName);
Die Methode CreateActivity erstellt eine neue Aktivität. Sie überschreibt nicht Activity.Current. In der Praxis spielt diese Funktion meist keine Rolle. Activity.Current?.SetTag("myKey", myValue)
var value = Activity.Current?.GetTagItem("myKey");
Activity.Current?.SetStatus(ActivityStatusCode.Ok);
var status = Activity.Current?.GetStatus()
var activityEvent = new ActivityEvent("Something is happen...");
Activity.Current?.AddEvent(activityEvent);
Activity.Current?.RecordException(EXCEPTION);
var builder = WebApplication.CreateBuilder(args);
builder.Logging.ClearProviders();
builder.Logging.AddOpenTelemetry(options =>
{
options.SetResourceBuilder(ResourceBuilder.CreateDefault().AddService(ServiceName));
otlpOptions.Endpoint = new Uri(...);
}
Die ASP.NET Core Umgebung gibt standardmäßig Logs auf die Konsole aus. public class MyClass()
{
private ILogger<MyClass> _logger;
public MyClass(ILogger<MyClass> logger)
{
_logger = logger;
}
public void DoSomething()
{
_logger.LogInformation("INFO");
}
}