When an error rate rises after a deployment, the first useful question is often not when did we deploy? It is which build handled the failing requests?
During rolling deployments, canary releases, and partial rollbacks, old and new instances can run at the same time. A deployment timestamp cannot tell you which code produced a specific trace, log, or measurement.
Attach one immutable build identity to the OpenTelemetry resource for the process. OpenTelemetry defines service.version for this purpose. Because resource attributes describe the service producing the telemetry, you configure the value once instead of attaching it individually to spans, logs, and measurements.
In .NET, the assembly informational version is a practical source when the build pipeline stamps it with a release version and source revision:
using System.Reflection;
using OpenTelemetry.Resources;
var entryAssembly = Assembly.GetEntryAssembly()
?? throw new InvalidOperationException("Entry assembly is unavailable.");
var buildIdentity = entryAssembly
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?
.InformationalVersion;
if (string.IsNullOrWhiteSpace(buildIdentity))
{
throw new InvalidOperationException(
"The build must provide an informational version.");
}
builder.Services.AddOpenTelemetry()
.ConfigureResource(resource => resource.AddService(
serviceName: builder.Environment.ApplicationName,
serviceVersion: buildIdentity));
For SDK-style projects, InformationalVersion can contain a value such as 1.8.0+a01dbef8. When you build with the .NET 8 SDK or later, the SDK includes SourceRevisionId in InformationalVersion by default. You can disable this behavior with IncludeSourceRevisionInInformationalVersion=false. Verify the published artifact in your own pipeline instead of assuming every build configuration produces the same metadata.
Pick a value that identifies the artifact rather than the deployment environment. A release version plus commit hash, a CI build ID, or an immutable container image digest can all work. Preserve that value when the same artifact moves from staging to production. Branch names, slot names, deployment times, and replica IDs describe something else.
Keep those concepts separate:
service.versionidentifies the build shared by all instances of that release.service.instance.ididentifies one running instance or replica.- the deployment environment identifies where the instance is running.
Once those identities are separate, mixed-version failures become easy to query. Group failures by service.version, compare latency across releases, or confirm that a rollback removed the bad build. A dashboard that records only the service and environment can still leave you unable to find the code that emitted the signal.
Use this for deployable services and workers. Local throwaway runs rarely need it. Keep trace IDs, release records, and deployment events too; they answer different questions. When a production trace carries service.version=1.8.0+a01dbef8, you know which artifact to inspect.