Observability - .NET SDK feature guide
This page covers features related to viewing the state of the application, including:
The observability feature guide covers the many ways to view the current state of your Temporal Application. This includes the ways to view which Workflow Executions are tracked by the Temporal Platform and the state of any specified Workflow Execution, either currently or at points of an execution.
Emit metrics
How to emit metrics using the Temporal .NET SDK
Each Temporal SDK is capable of emitting an optional set of metrics from either the Client or the Worker process. For a complete list of metrics capable of being emitted, see the SDK metrics reference.
Metrics can be scraped and stored in time series databases, such as:
Temporal also provides a dashboard you can integrate with graphing services like Grafana. For more information, see:
- Temporal's implementation of the Grafana dashboard
- How to export metrics in Grafana
Metrics in .NET are configured on the Metrics
property of the Telemetry
property on the TemporalRuntime
. That object should be created globally and should be used for all clients; therefore, you should configure this before any other Temporal code.
Set a Prometheus endpoint
How to set a Prometheus endpoint using the .NET SDK
The following example exposes a Prometheus endpoint on port 9000
.
using Temporalio.Client;
using Temporalio.Runtime;
var runtime = new TemporalRuntime(new()
{
Telemetry = new() { Metrics = new() { Prometheus = new("0.0.0.0:9000") } },
});
var client = await Temporalio.ConnectAsync(new("localhost:7233") { Runtime = runtime });
Set a custom metric meter
How to reuse the .NET metric meter using the Temporal .NET SDK
A custom metric meter can be set on the telemetry options to handle metrics programmatically. The Temporalio.Extensions.DiagnosticSource extension provides a custom metric meter implementation that sends all metrics to a System.Diagnostics.Metrics.Meter instance.
using System.Diagnostics.Metrics;
using Temporalio.Client;
using Temporalio.Extensions.DiagnosticSource;
using Temporalio.Runtime;
// Create .NET meter
using var meter = new Meter("My.Meter");
// Can create MeterListener or OTel meter provider here...
// Create Temporal runtime with a custom metric meter for that meter
var runtime = new TemporalRuntime(new()
{
Telemetry = new()
{
Metrics = new() { CustomMetricMeter = new CustomMetricMeter(meter) },
},
});
var client = await Temporalio.ConnectAsync(new("localhost:7233") { Runtime = runtime });
Setup Tracing
How to configure tracing using the Temporal .NET SDK
Tracing allows you to view the call graph of a Workflow along with its Activities and any Child Workflows.
To configure OpenTelemetry tracing in .NET, use the Temporalio.Extensions.OpenTelemetry extension.
The Temporalio.Extensions.OpenTelemetry.TracingInterceptor
class can be set as an interceptor in the client options.
When your Client is connected, spans are created for all Client calls, Activities, and Workflow invocations on the Worker. Spans are created and serialized through the server to give one trace for a Workflow Execution.
Log from a Workflow
How to log from a Workflow to Temporal .NET SDK
Send logs and errors to a logging service, so that when things go wrong, you can see what happened.
The SDK core uses WARN
for its default logging level.
Logging uses the .NET standard logging APIs.
The LoggerFactory
can be set in the client.
The following example shows logging on the console and sets the level to Information
.
var client = await TemporalClient.ConnectAsync(new("localhost:7233")
{
LoggerFactory = LoggerFactory.Create(builder =>
builder.
AddSimpleConsole(options => options.TimestampFormat = "[HH:mm:ss] ").
SetMinimumLevel(LogLevel.Information)),
});
You can log from a Workflow using Workflow.Logger
which is an instance of .NET's ILogger
.
Workflow.Logger.LogInformation("Given name: {Name}", name);
Use Visibility APIs
How to use Visibility APIs using the Temporal .NET SDK
The term Visibility, within the Temporal Platform, refers to the subsystems and APIs that enable an operator to view Workflow Executions that currently exist within a Temporal Service.
Use Search Attributes
How to use Search Attributes using the Temporal .NET SDK
The typical method of retrieving a Workflow Execution is by its Workflow Id.
However, sometimes you'll want to retrieve one or more Workflow Executions based on another property. For example, imagine you want to get all Workflow Executions of a certain type that have failed within a time range, so that you can start new ones with the same arguments.
You can do this with Search Attributes.
- Default Search Attributes like
WorkflowType
,StartTime
andExecutionStatus
are automatically added to Workflow Executions. - Custom Search Attributes can contain their own domain-specific data (like
customerId
ornumItems
). - A few generic Custom Search Attributes like
CustomKeywordField
andCustomIntField
are created by default in Temporal's Docker Compose.
The steps to using custom Search Attributes are:
- Create a new Search Attribute in your Temporal Service in the CLI or Web UI.
- For example:
temporal operator search-attribute create --name CustomKeywordField --type Text
- Replace
CustomKeywordField
with the name of your Search Attribute. - Replace
Text
with a type value associated with your Search Attribute:Text
|Keyword
|Int
|Double
|Bool
|Datetime
|KeywordList
- Replace
- For example:
- Set the value of the Search Attribute for a Workflow Execution:
- On the Client by including it as an option when starting the Execution.
- In the Workflow by calling
UpsertTypedSearchAttributes
.
- Read the value of the Search Attribute:
- On the Client by calling
Describe
on aWorkflowHandle
. - In the Workflow by looking at
WorkflowInfo
.
- On the Client by calling
- Query Workflow Executions by the Search Attribute using a List Filter:
- In the Temporal CLI
- In code by calling
ListWorkflowsAsync
.
List Workflow Executions
How to list Workflow Executions using the .NET SDK
Use the ListWorkflowsAsync() method on the Client and pass a List Filter as an argument to filter the listed Workflows. The result is an async enumerable.
await foreach (var wf in client.ListWorkflowsAsync("WorkflowType='GreetingWorkflow'"))
{
Console.WriteLine("Workflow: {0}", wf.Id);
}
Set Custom Search Attributes
How to use custom Search Attributes using the Temporal .NET SDK
After you've created custom Search Attributes in your Temporal Service (using temporal operator search-attribute create
or the Cloud UI), you can set the values of the custom Search Attributes when starting a Workflow.
To set custom Search Attributes, use the TypedSearchAttributes
property on WorkflowOptions
for StartWorkflowAsync
or ExecuteWorkflowAsync
.
Typed search attributes are a SearchAttributeCollection
created with a builder.
// This only needs to be created once, so it is common to make it a static readonly even though we
// create inline here for demonstration
var myKeywordAttributeKey = SearchAttributeKey.CreateKeyword("MyKeywordAttribute");
// Start workflow with the search attribute collection
var handle = await client.StartWorkflowAsync(
(MyWorkflow wf) => wf.RunAsync(),
new(id: "my-workflow-id", taskQueue: "my-task-queue")
{
TypedSearchAttributes = new SearchAttributeCollection.Builder().
Set(myKeywordAttributeKey, "SomeKeywordValue").
ToSearchAttributeCollection(),
});
Upsert Search Attributes
How to upsert custom Search Attributes using the Temporal .NET SDK
You can upsert Search Attributes to add, update, or remove Search Attributes from within Workflow code.
To upsert custom Search Attributes, use the UpsertTypedSearchAttributes()
method with a set of updates.
Keys can be predefined for reuse.
// These only need to be created once, so it is common to make them static readonly even though we
// create inline here for demonstration
var myKeywordAttributeKey = SearchAttributeKey.CreateKeyword("MyKeywordAttribute");
var myTextAttributeKey = SearchAttributeKey.CreateText("MyTextAttribute");
// Add/Update the keyword one and remove the text one
Workflow.UpsertTypedSearchAttributes(
myKeywordAttributeKey.ValueSet("SomeKeywordValue"),
myTextAttrbiuteKey.ValueUnset());