Build:
dotnet build OpenTelemetry.slnx --configuration ReleaseTest all:
dotnet test OpenTelemetry.slnxTest a single project:
dotnet test test/OpenTelemetry.Tests/OpenTelemetry.Tests.csprojTest a single test by name:
dotnet test test/OpenTelemetry.Tests/OpenTelemetry.Tests.csproj --filter "FullyQualifiedName~MyTestName"Test for a specific TFM (Windows also supports net472):
dotnet test test/OpenTelemetry.Tests/OpenTelemetry.Tests.csproj --framework net10.0Test all projects for a specific TFM (as CI does, skipping the projects which do not target it):
dotnet build ./build/OpenTelemetry.proj -t:Test -p:Configuration=Release -p:TargetFramework=net10.0Check code formatting:
dotnet format OpenTelemetry.slnx --no-restore --verify-no-changesApply code formatting:
dotnet format OpenTelemetry.slnxLint Markdown (requires markdownlint-cli):
markdownlint .Check for non-ASCII characters and trailing whitespace (requires python3):
CI runs a sanity check that rejects non-ASCII characters (e.g. smart quotes, em dashes) and trailing whitespace. To check locally:
python3 ./build/scripts/sanitycheck.py- The .NET SDK version specified in
global.json(or newer) is required. TreatWarningsAsErrorsis active in Release builds; StyleCop and nullable violations fail the build.- Tests run serially by default
(
build/xunit.runner.json:maxParallelThreads: 1). net472tests only run on Windows.- Tests use Microsoft Testing Platform (the
test.runnersetting inglobal.json), not VSTest. The test executables can also be run directly (e.g.dotnet OpenTelemetry.Tests.dll --filter "..."). dotnet test ... --framework <tfm>fails if any test project does not target<tfm>; use theTesttarget shown above instead.
The codebase is structured around three OpenTelemetry signals (Traces, Metrics, Logs) with a strict three-layer design:
OpenTelemetry.Api - no-op API layer (no SDK dependency)
OpenTelemetry - SDK: provider implementations, processors, samplers
OpenTelemetry.Exporter.* - signal-specific export backends
OpenTelemetry.Extensions.* - DI/hosting integration, propagators
API layer (src/OpenTelemetry.Api) - Defines the programming model
(TracerProvider, MeterProvider, TelemetrySpan, Baggage, etc.) with no-op
defaults. Libraries instrument against this layer only.
SDK layer (src/OpenTelemetry) - Implements the provider pipeline:
TracerProviderSdk, MeterProviderSdk, LoggerProviderSdk. Each signal has its
own Trace/, Metrics/, Logs/ subdirectory. Contains base classes
BaseExporter<T>, BaseProcessor<T>, Sampler, and Resource.
Exporters - Each exporter project targets one or more signals. They extend
BaseExporter<T> where T is Activity, Metric, or LogRecord.
Shared code (src/Shared) - Utility files (e.g., SemanticConventions.cs,
ActivityHelperExtensions.cs) that are linked (not referenced) into consuming
projects via <Compile Include="..." Link="Includes/..." /> in .csproj files.
Do not add Shared/ to a project reference; use the link pattern.
Provider builder pattern - All providers are configured fluently:
Sdk.CreateTracerProviderBuilder()
.AddSource("MyLibrary")
.SetSampler(new AlwaysOnSampler())
.AddConsoleExporter()
.Build();
// Or via Microsoft.Extensions.Hosting:
services.AddOpenTelemetry()
.WithTracing(b => b.AddConsoleExporter())
.WithMetrics(b => b.AddConsoleExporter());- Versioning is done via MinVer (git-tag based). Tags follow the pattern
core-{version}orcoreunstable-{version}. - Central package management is enabled (
Directory.Packages.props). Never specifyVersion=on a<PackageReference>inside a project file; version belongs only inDirectory.Packages.props. - Match
Microsoft.Extensions.*package major version to the target .NET major version (see the version conditions inDirectory.Packages.props). - The current latest stable version constant is
OTelLatestStableVerinDirectory.Packages.props.
- Production libraries use
$(TargetFrameworksForLibraries)- All supported versions of .NET plusnetstandard2.0andnet462. - Tests use
$(TargetFrameworksForTests)= All supported versions of .NET plusnet472on Windows. - Set these via the shared MSBuild properties rather than hardcoding in
.csproj.
- Experimental public API is gated by the
EXPOSE_EXPERIMENTAL_FEATUREScompiler constant, which is set whenExposeExperimentalFeatures=true(the default for pre-release builds). - Experimental APIs carry
[Experimental("OTEL####")]and are tracked in.publicApi/Experimental/PublicAPI.Unshipped.txt. [Obsolete]warnings for experimental APIs (OTEL1000-OTEL1004) are suppressed project-wide.
Every production project has a .publicApi/ folder.
When you add or change a public API:
- Use the IDE IntelliSense "Fix" to update
PublicAPI.Unshipped.txt(do not edit manually). - If APIs differ per framework, place the shared portion in root files and
overrides in per-framework subdirectories (e.g.,
.publicApi/net462/). - Never modify
PublicAPI.Shipped.txt- only maintainers do this during releases. - For experimental APIs use
.publicApi/Stable/and.publicApi/Experimental/subdirectories.
Extend BaseExporter<T> and override Export:
public sealed class MyExporter : BaseExporter<Activity>
{
public override ExportResult Export(in Batch<Activity> batch)
{
foreach (var activity in batch) { /* ... */ }
return ExportResult.Success;
}
}Register via an Add* extension method on TracerProviderBuilder /
MeterProviderBuilder / LoggerProviderBuilder. Follow the
ConsoleExporterHelperExtensions.cs pattern.
Extend Sampler and override ShouldSample:
public sealed class MySampler : Sampler
{
public MySampler() => this.Description = nameof(MySampler);
public override SamplingResult ShouldSample(in SamplingParameters samplingParameters)
=> new SamplingResult(SamplingDecision.RecordAndSample);
}Extend BaseProcessor<T> and override OnStart/OnEnd. For processors that
wrap an exporter, use SimpleExportProcessor<T> or BatchExportProcessor<T>
instead of writing your own batching logic.
Instrumentation libraries create a static ActivitySource and check for null
before using the returned Activity:
private static readonly ActivitySource MySource = new("MyLibrary", "1.0.0");
using var activity = MySource.StartActivity("OperationName");
activity?.SetTag("key", "value");-
LangVersionis set tolatest; use current C# features freely. -
Nullable reference types are mandatory - never disable
#nullable. -
ImplicitUsingsis enabled globally. -
All source files must start with the Apache-2.0 SPDX header:
// Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0
-
StyleCop enforces ordering and documentation; XML doc comments are required on all public members (
GenerateDocumentationFile=true).
- Use
$(TargetFrameworksForLibraries)(or the appropriate shared property) as<TargetFrameworks>. - Import
build/Common.prod.propsimplicitly viaDirectory.Build.props- do not overrideNullable,LangVersion,EnforceCodeStyleInBuild, orImplicitUsings. - Add
.publicApi/PublicAPI.Shipped.txt(empty) and.publicApi/PublicAPI.Unshipped.txt. - Add
<MinVerTagPrefix>matching the component category (core-orcoreunstable-). - Add an
<InternalsVisibleTo>entry in the source project.csprojfor the companion test project. - Shared utilities from
src/Shared/must be included via<Compile ... Link="..." />, not as project references.
- Tests live in
test/OpenTelemetry.<Component>.Tests/mirroring the source structure. - Reusable test helpers (mock exporters, processors, samplers) live in
test/OpenTelemetry.Tests/Shared/and are linked into other test projects. - Use
InMemoryExporter<T>for verifying telemetry output in unit tests. - Build the provider inside the test, not in a shared constructor/fixture, to keep tests isolated.
Every behavioural change needs a CHANGELOG entry in the affected component's
CHANGELOG.md (under src/<Component>/CHANGELOG.md) in the ## Unreleased
section. Format:
* Description of the change (sentence case, ends with a period).
([#NNNN](https://github.com/open-telemetry/opentelemetry-dotnet/pull/NNNN))- New entries go at the end of
## Unreleased, not the top. - Use the pull request URL - not the issue URL - in the link.
- Do not add a new version header - that is added automatically during release.
- No CHANGELOG entry is needed for pure infrastructure changes (CI workflows, Renovate config, editor config, test-only changes).
- When a PR touches multiple components, add an entry to each component's
CHANGELOG.md.
Several APIs are banned in production code via build/BannedSymbols.txt and
will cause a build error if used:
- Use
string.Equals(a, b, StringComparison)(static), not the instance methoda.Equals(b, StringComparison). - All
TryParseoverloads on numeric/date types must passCultureInfo.InvariantCulture. - Do not use
Task<T>.Result- useawaitorGetAwaiter().GetResult(). - Do not use
System.Runtime.CompilerServices.UnsafeorSystem.Runtime.InteropServices.MemoryMarshal- unsafe code is banned. - Use
ActivitySource(ActivitySourceOptions)not the otherActivitySourceconstructors. - Use
Meter(MeterOptions)not the otherMeterconstructors. - Use
HttpClientHelpersmethods instead ofHttpClient.GetByteArrayAsync,HttpClient.GetStringAsync,HttpContent.ReadAsByteArrayAsync, orHttpContent.ReadAsStringAsync.
- Use
<para/>as the paragraph break inside///XML doc comments - not a blank line. - For property setters, the
paramNamein anyArgumentExceptionshould benameof(value), not the property name.
- Use
[Obsolete("...")]on a test method that exercises an obsolete API rather than suppressing the compiler warning with#pragma. - New bug fixes should include a regression test.
See REVIEW.md for guidance used by automated code review agents
when reviewing pull requests in this repository.
-
Pull request descriptions must follow the pull request template.
-
Pull request descriptions authored by an agent must disclose which agent was used to create the pull request. Add a note at the bottom of the description, for example:
> [!NOTE] > This PR description was generated with GitHub Copilot.
-
The same applies to PR reviews, PR comments and issue comments posted by an agent under a user's own account, rather than a dedicated bot account or app.
-
Replies to pull requests, comments, and issues must be made by the end-user themselves, not autonomously by an agent.
Agent skills for common tasks in this repository are in .github/skills:
code-review- review a pull request or code change against this repository's conventions.performance-benchmark- write and run BenchmarkDotNet benchmarks to validate the performance impact of a change.