Build:
dotnet build OpenTelemetry.slnx --configuration ReleaseTest all:
dotnet testTest 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 net462):
dotnet test --framework 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). net462tests only run on Windows.
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 plusnet462on 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.