Skip to content

Commit

Permalink
Merge branch 'main' into mfogliatto/add-otlplogexporter-tests
Browse files Browse the repository at this point in the history
  • Loading branch information
mfogliatto authored Mar 8, 2023
2 parents 283f7a4 + 6c580b3 commit aa64c70
Show file tree
Hide file tree
Showing 87 changed files with 3,345 additions and 797 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -345,3 +345,6 @@ ASALocalRun/
/.sonarqube

/src/LastMajorVersionBinaries

# Tempo files
tempo-data/
4 changes: 0 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,6 @@ of Windows.
* Visual Studio 2022+ or Visual Studio Code
* .NET Framework 4.6.2+

> **Note**
> Visual Studio 2022 preview is **recommended** due to projects needing
to target .NET preview versions.

### Public API

It is critical to keep public API surface small and clean. This repository is
Expand Down
7 changes: 7 additions & 0 deletions OpenTelemetry.sln
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "getting-started-console", "
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "getting-started-jaeger", "docs\trace\getting-started-jaeger\getting-started-jaeger.csproj", "{A0C0B77C-6C7B-4EC2-AC61-EA1F489811B9}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "stratified-sampling-example", "docs\trace\advanced\stratified-sampling-example\stratified-sampling-example.csproj", "{9C99621C-343E-479C-A943-332DB6129B71}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -523,6 +525,10 @@ Global
{A0C0B77C-6C7B-4EC2-AC61-EA1F489811B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A0C0B77C-6C7B-4EC2-AC61-EA1F489811B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A0C0B77C-6C7B-4EC2-AC61-EA1F489811B9}.Release|Any CPU.Build.0 = Release|Any CPU
{9C99621C-343E-479C-A943-332DB6129B71}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9C99621C-343E-479C-A943-332DB6129B71}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9C99621C-343E-479C-A943-332DB6129B71}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9C99621C-343E-479C-A943-332DB6129B71}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -562,6 +568,7 @@ Global
{DEDE8442-03CA-48CF-99B9-EA224D89D148} = {5B7FB835-3FFF-4BC2-99C5-A5B5FAE3C818}
{EF4F6280-14D1-49D4-8095-1AC36E169AA8} = {5B7FB835-3FFF-4BC2-99C5-A5B5FAE3C818}
{A0C0B77C-6C7B-4EC2-AC61-EA1F489811B9} = {5B7FB835-3FFF-4BC2-99C5-A5B5FAE3C818}
{9C99621C-343E-479C-A943-332DB6129B71} = {5B7FB835-3FFF-4BC2-99C5-A5B5FAE3C818}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {55639B5C-0770-4A22-AB56-859604650521}
Expand Down
1 change: 1 addition & 0 deletions build/Common.prod.props
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>

<PackageReference Include="Microsoft.CodeAnalysis.PublicApiAnalyzers" Version="$(MicrosoftCodeAnalysisAnalyzersPkgVer)" Condition=" $(OS) == 'Windows_NT'">
<PrivateAssets>All</PrivateAssets>
</PackageReference>
Expand Down
92 changes: 92 additions & 0 deletions docs/metrics/customizing-the-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,98 @@ AnotherFruitCounter.Add(4, new("name", "mango"), new("color", "yellow")); // Not
streams. There is no ability to apply different limits for each instrument at
this moment.

### Exemplars

Exemplars are example data points for aggregated data. They provide access to
the raw measurement value, time stamp when measurement was made, and trace
context, if any. It also provides "Filtered Tags", which are attributes (Tags)
that are [dropped by a view](#select-specific-tags). Exemplars are an opt-in
feature, and allow customization via ExemplarFilter and ExemplarReservoir.

Exemplar collection in OpenTelemetry .NET is done automatically (once Exemplar
feature itself is enabled on `MeterProvider`). There is no separate API
to report exemplar data. If an app is already using existing Metrics API
(manually or via instrumentation libraries), exemplars can be configured/enabled
without requiring instrumentation changes.

While the SDK is capable of producing exemplars automatically, the exporters
(and the backends) must also support them in order to be useful. OTLP Metric
Exporter has support for this today, and this [end-to-end
tutorial](../exemplars/README.md) demonstrates how to use exemplars to achieve
correlation from metrics to traces, which is one of the primary use cases for
exemplars.

#### ExemplarFilter

`ExemplarFilter` determines which measurements are eligible to become an
Exemplar. i.e. `ExemplarFilter` determines which measurements are offered to
`ExemplarReservoir`, which makes the final decision about whether the offered
measurement gets stored as an exemplar. They can be used to control the noise
and overhead associated with Exemplar collection.

OpenTelemetry SDK comes with the following Filters:

* `AlwaysOnExemplarFilter` - makes all measurements eligible for being an Exemplar.
* `AlwaysOffExemplarFilter` - makes no measurements eligible for being an
Exemplar. Using this is as good as turning off Exemplar feature, and is the current
default.
* `TraceBasedExemplarFilter` - makes those measurements eligible for being an
Exemplar, which are recorded in the context of a sampled parent `Activity`
(span).

`SetExemplarFilter` method on `MeterProviderBuilder` can be used to set the
desired `ExemplarFilter`.

The snippet below shows how to set `ExemplarFilter`.

```csharp
using OpenTelemetry;
using OpenTelemetry.Metrics;

using var meterProvider = Sdk.CreateMeterProviderBuilder()
// rest of config not shown
.SetExemplarFilter(new TraceBasedExemplarFilter())
.Build();
```

> **Note**
> As of today, there is no separate toggle for enable/disable Exemplar feature.
Exemplars can be disabled by setting filter as `AlwaysOffExemplarFilter`, which
is also the default (i.e Exemplar feature is disabled by default). Users can
enable the feature by setting filter to anything other than
`AlwaysOffExemplarFilter`. For example: `.SetExemplarFilter(new TraceBasedExemplarFilter())`.

If the built-in `ExemplarFilter`s are not meeting the needs, one may author
custom `ExemplarFilter` as shown
[here](../extending-the-sdk/README.md#exemplarfilter). A custom filter, which
eliminates all un-interesting measurements from becoming Exemplar is a
recommended way to control performance overhead associated with collecting
Exemplars. See
[benchmark](../../../test/Benchmarks/Metrics/ExemplarBenchmarks.cs) to see how
much impact can `ExemplarFilter` have on performance.

#### ExemplarReservoir

`ExemplarReservoir` receives the measurements sampled in by the `ExemplarFilter`
and is responsible for storing Exemplars. `ExemplarReservoir` ultimately decides
which measurements get stored as exemplars. The following are the default
reservoirs:

* `AlignedHistogramBucketExemplarReservoir` is the default reservoir used for
Histograms with buckets, and it stores at most one exemplar per histogram
bucket. The exemplar stored is the last measurement recorded - i.e. any new
measurement overwrites the previous one in that bucket.

`SimpleExemplarReservoir` is the default reservoir used for all metrics except
Histograms with buckets. It has a fixed reservoir pool, and implements the
equivalent of [naive
reservoir](https://en.wikipedia.org/wiki/Reservoir_sampling). The reservoir pool
size (currently defaulting to 10) determines the maximum number of exemplars
stored.

> **Note**
> Currently there is no ability to change or configure Reservoir.
### Instrumentation

// TODO
Expand Down
99 changes: 99 additions & 0 deletions docs/metrics/exemplars/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Using Exemplars in OpenTelemetry .NET

Exemplars are example data points for aggregated data. They provide specific
context to otherwise general aggregations. One common use case is to gain
ability to correlate metrics to traces (and logs). While OpenTelemetry .NET
supports Exemplars, it is only useful if the telemetry backend also supports the
capabilities. This tutorial uses well known open source backends to demonstrate
the concept. The following are the components involved:

* Test App - We use existing example app from the repo. This app is already
instrumented with OpenTelemetry for logs, metrics and traces, and is configured
to export them to the configured OTLP end point.
* OpenTelemetry Collector - An instance of collector is run, which receives
telemetry from the above app using OTLP. The collector then exports metrics to
Prometheus, traces to Tempo.
* Prometheus - Prometheus is used as the Metric backend.
* Tempo - Tempo is used as the Tracing backend.
* Grafana - UI to query metrics from Prometheus, traces from Tempo, and to
navigate between metrics and traces using Exemplar.

All these components except the test app require additional configuration to
enable Exemplar feature. To make it easy for users, these components are
pre-configured to enable Exemplars, and a docker-compose is provided to spun
them all up, in the required configurations.

## Pre-requisite

Install docker: <https://docs.docker.com/get-docker/>

## Setup

As mentioned in the intro, this tutorial uses OTel Collector, Prometheus, Tempo,
and Grafana, and they must be up and running before proceeding. The following
spins all of them with the correct configurations to support Exemplars.

Navigate to current directory and run the following:

```sh
docker-compose up -d
```

If the above step succeeds, all dependencies would be spun up and ready now. To
test, navigate to Grafana running at: "http://localhost:3000/".

## Run test app

Now that the required dependencies are ready, lets run the demo app.
This tutorial is using the existing ASP.NET Core app from the repo.

Navigate to [Example Asp.Net Core App](../../../examples/AspNetCore/Program.cs)
directory and run the following command:

```sh
dotnet run
```

Once the application is running, navigate to
[http://localhost:5000/weatherforecast]("http://localhost:5000/weatherforecast")
from a web browser. You may use the following Powershell script to generate load
to the application.

```powershell
while($true)
{
Invoke-WebRequest http://localhost:5000/weatherforecast
Start-Sleep -Milliseconds 500
}
```

## Use Exemplars to navigate from Metrics to Traces

The application sends metrics (with exemplars), and traces to the OTel
Collector, which export metrics and traces to Prometheus and Tempo
respectively.

Please wait for 2 minutes before continuing so that enough data is generated
and exported.

Open Grafana, select Explore, and select Prometheus as the source. Select the
metric named "http_server_duration_bucket", and plot the chart. Toggle on the
"Exemplar" option from the UI and hit refresh.

![Enable Exemplar](https://user-images.githubusercontent.com/16979322/218627781-9886f837-11ae-4d52-94d3-f1821503209c.png)

The Exemplars appear as special "diamond shaped dots" along with the metric
charts in the UI. Select any Exemplar to see the exemplar data, which includes
the timestamp when the measurement was recorded, the raw value, and trace
context when the recording was done. The "trace_id" enables jumping to the
tracing backed (tempo). Click on the "Query with Tempo" button next to the
"trace_id" field to open the corresponding `Trace` in Tempo.

![Navigate to trace with exemplar](https://user-images.githubusercontent.com/16979322/218629999-1d1cd6ba-2385-4683-975a-d4797df8361a.png)

## References

* [Exemplar specification](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#exemplar)
* [Exemplars in Prometheus](https://prometheus.io/docs/prometheus/latest/feature_flags/#exemplars-storage)
* [Exemplars in Grafana](https://grafana.com/docs/grafana/latest/fundamentals/exemplars/)
* [Tempo](https://github.com/grafana/tempo)
51 changes: 51 additions & 0 deletions docs/metrics/exemplars/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
version: "3"
services:

# OTEL Collector to receive logs, metrics and traces from the application
otel-collector:
image: otel/opentelemetry-collector:0.70.0
command: [ "--config=/etc/otel-collector.yaml" ]
volumes:
- ./otel-collector.yaml:/etc/otel-collector.yaml
ports:
- "4317:4317"
- "4318:4318"
- "9201:9201"

# Exports Traces to Tempo
tempo:
image: grafana/tempo:latest
command: [ "-config.file=/etc/tempo.yaml" ]
volumes:
- ./tempo.yaml:/etc/tempo.yaml
- ./tempo-data:/tmp/tempo
ports:
- "3200" # tempo
- "4317" # otlp grpc
- "4318" # otlp http

# Exports Metrics to Prometheus
prometheus:
image: prom/prometheus:latest
command:
- --config.file=/etc/prometheus.yaml
- --web.enable-remote-write-receiver
- --enable-feature=exemplar-storage
volumes:
- ./prometheus.yaml:/etc/prometheus.yaml
ports:
- "9090:9090"

# UI to query traces and metrics
grafana:
image: grafana/grafana:9.3.2
volumes:
- ./grafana-datasources.yaml:/etc/grafana/provisioning/datasources/datasources.yaml
environment:
- GF_AUTH_ANONYMOUS_ENABLED=true
- GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
- GF_AUTH_DISABLE_LOGIN_FORM=true
- GF_FEATURE_TOGGLES_ENABLE=traceqlEditor
ports:
- "3000:3000"

33 changes: 33 additions & 0 deletions docs/metrics/exemplars/grafana-datasources.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
apiVersion: 1

datasources:
- name: Prometheus
type: prometheus
uid: prometheus
access: proxy
orgId: 1
url: http://prometheus:9090
basicAuth: false
isDefault: true
version: 1
editable: false
jsonData:
httpMethod: GET
exemplarTraceIdDestinations:
- name: trace_id
datasourceUid: Tempo
- name: Tempo
type: tempo
access: proxy
orgId: 1
url: http://tempo:3200
basicAuth: false
isDefault: false
version: 1
editable: false
apiVersion: 1
uid: tempo
jsonData:
httpMethod: GET
serviceMap:
datasourceUid: prometheus
30 changes: 30 additions & 0 deletions docs/metrics/exemplars/otel-collector.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
receivers:
otlp:
protocols:
grpc:
http:

exporters:
logging:
loglevel: debug
prometheus:
endpoint: ":9201"
send_timestamps: true
metric_expiration: 180m
enable_open_metrics: true
otlp:
endpoint: tempo:4317
tls:
insecure: true

service:
pipelines:
traces:
receivers: [otlp]
exporters: [logging,otlp]
metrics:
receivers: [otlp]
exporters: [logging,prometheus]
logs:
receivers: [otlp]
exporters: [logging]
8 changes: 8 additions & 0 deletions docs/metrics/exemplars/prometheus.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
global:
scrape_interval: 15s
evaluation_interval: 15s

scrape_configs:
- job_name: 'otel'
static_configs:
- targets: [ 'otel-collector:9201' ]
17 changes: 17 additions & 0 deletions docs/metrics/exemplars/tempo.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
server:
http_listen_port: 3200

distributor:
receivers:
otlp:
protocols:
http:
grpc:

storage:
trace:
backend: local
wal:
path: /tmp/tempo/wal
local:
path: /tmp/tempo/blocks
Loading

0 comments on commit aa64c70

Please sign in to comment.