Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
46 changes: 31 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,52 @@

## Purpose of this Repo

This is to show several features of temporal in a way that is easy to understand and run. All you need is docker as everything will run in docker containers.
This is to show several features of Temporal in a way that is easy to understand and run. All you
need is Docker as everything will run in Docker containers.

It will run a swagger page that allows you to start workflows using a rest api.
It will run a [Swagger page](http://localhost:8080/swagger/index.html) that allows you to start
workflows and send signals to paused workflows via a REST API.

## Running the project - you'll need Docker

Make sure your terminal is in the temporal-examples folder and then run docker compose up. This should also run the temporal workers as well. If you ever need to debug the temporal workflows you can debug via a docker container by installing the "Container Tools" extension in VS Code. There is also an option to do the debugging through Visual Studio tooling as well.
Make sure your terminal is in the temporal-examples folder and then run `docker compose up`. This
will start all services. If you ever need to debug the Temporal workflows you can debug via a Docker
container by installing the "Container Tools" extension in VS Code. There is also an option to do
the debugging through Visual Studio tooling as well.

This set of docker containers actually installs the temporal cli to a container. If you want to execute commands using it you should either exec into it or access exec in Docker Desktop. The container is named temporal-cli. If you want examples of this go to the temporal-examples/scripts folder. There is also the documentation here: https://docs.temporal.io/cli#command-set
This set of Docker containers actually installs the Temporal CLI to an isolated. If you want to
execute commands using it you should either exec into it or access exec in Docker Desktop. The
container is named `temporal-cli`. If you want examples of this go to the `temporal-examples/scripts`
folder. There is also the documentation [here](https://docs.temporal.io/cli#command-set)

## The Examples
## Examples

We have several different examples of what Temporal can do in the Workflows files which are in the workflows project.
We have several different examples of what Temporal can do in the Workflows files which are in the
Workflows project.

**Examples.workflow.cs**- shows a default Temporal workflow with a generic activity
**Examples.workflow.cs** - shows a default Temporal workflow with a generic activity

**ExampleWithChildren.workflow.cs** - shows you how you can compose workflows together. This creates 1-10 child workflows and waits for them to finish.
**ExampleWithChildren.workflow.cs** - shows you how you can compose workflows together. This creates
1-10 child workflows and waits for them to finish.

**WaitingSignal.workflow.cs** - shows how you can pause a workflow indefinitely or with a timeout to wait for a "signal". This signal in our example is caused by a rest api. While paused the workflow takes up very few resources. The SignalController inside the rest-controller project is where we send the signal. THis also shows how you can search the Temporal Server for specific workflows. In this case we are using the Workflow ID.
**WaitingSignal.workflow.cs** - shows how you can pause a workflow indefinitely or with a timeout to
wait for a "signal". This signal in our example is caused by a REST API. While paused the workflow
takes up very few resources. The SignalController inside the rest-controller project is where we send
the signal. This also shows how you can search the Temporal Server for specific workflows. In this
case we are using the Workflow ID.

## Monitoring and Metrics

The docker compose file contains [Prometheus](https://prometheus.io/) and [Grafana](https://grafana.com/) services.
Both the Temporal service and all worker services expose metrics to Prometheus, which are collected based on the
settings in `prometheus.yml`. Prometheus can be manually added to Grafana as a data source.
The docker compose file contains [Prometheus](https://prometheus.io/) and
[Grafana](https://grafana.com/) services. Both the Temporal service and all worker services expose
metrics to Prometheus, which are collected based on the settings in `prometheus.yml`. Prometheus can
be manually added to Grafana as a data source.

The Prometheus UI can be accessed at [query page](http://localhost:9090/query), and details of the different scraped metrics
endpoints can be seen at [targets page](http://localhost:9090/targets).
The Prometheus UI can be accessed at [query page](http://localhost:9090/query), and details of the
different scraped metrics endpoints can be seen at [targets page](http://localhost:9090/targets).

The Grafana UI can be accessed at [Grafana login](http://localhost:3000/login) - Login details are admin/admin.
The Grafana UI can be accessed at [Grafana login](http://localhost:3000/login) - Login details are
admin/admin.

## License

Expand Down
7 changes: 6 additions & 1 deletion temporal-examples/Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<Project>
<PropertyGroup>
<!-- Enable central package management, https://learn.microsoft.com/en-us/nuget/consume-packages/Central-Package-Management -->
<!-- Enable central package management,
https://learn.microsoft.com/en-us/nuget/consume-packages/Central-Package-Management -->
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
Expand All @@ -10,6 +11,10 @@
<PackageVersion Include="microsoft.aspnetcore.openapi" Version="9.0.7" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.7" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.8" />
<PackageVersion Include="Serilog" Version="4.3.0" />
<PackageVersion Include="Serilog.Extensions.Logging" Version="9.0.2" />
<PackageVersion Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageVersion Include="Serilog.Sinks.File" Version="7.0.0" />
<PackageVersion Include="swashbuckle.aspnetcore" Version="9.0.3" />
<PackageVersion Include="swashbuckle.aspnetcore.annotations" Version="9.0.3" />
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="9.0.7" />
Expand Down
2 changes: 1 addition & 1 deletion temporal-examples/RestController/MicroserviceController.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Temporalio.Client;
using workflows;
using Workflows;

namespace RestController
{
Expand Down
2 changes: 1 addition & 1 deletion temporal-examples/RestController/SignalController.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Temporalio.Client;
using workflows;
using Workflows;

namespace RestController
{
Expand Down
15 changes: 10 additions & 5 deletions temporal-examples/RestController/appsettings.Development.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"Temporal": {
"ClientTargetHost": "host.docker.internal:7233",
"Namespace": "default",
"TaskQueue": "example"
}
}
}
63 changes: 30 additions & 33 deletions temporal-examples/Shared/AutoFac/Modules/LoggingModule.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
using Autofac;
using Autofac.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Serilog;
using Serilog.Extensions.Logging;
using Serilog.Formatting.Json;

namespace Shared.AutoFac.Modules
{
Expand All @@ -8,48 +14,39 @@ namespace Shared.AutoFac.Modules
/// </summary>
public class LoggingModule : Module
{
private readonly LogLevel _minimumLevel;
private readonly IConfiguration _configuration;

/// <summary>
/// Creates a new instance of the logging module with the specified minimum log level
/// </summary>
/// <param name="minimumLevel">Minimum log level to display</param>
public LoggingModule(LogLevel minimumLevel = LogLevel.Information)
public LoggingModule(IConfiguration configuration)
{
_minimumLevel = minimumLevel;
_configuration = configuration;
}

/// <summary>
/// Configure the logging services with Autofac
/// </summary>
protected override void Load(ContainerBuilder builder)
{
builder.RegisterGeneric(typeof(Logger<>)).As(typeof(ILogger<>)).SingleInstance();
var loggerConfig = new LoggerConfiguration()
.Enrich.WithProperty(
"Environment",
Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"
)
.WriteTo.Console(
outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}"
)
.WriteTo.File(
new JsonFormatter(renderMessage: true),
"logs/log-.json",
rollingInterval: RollingInterval.Day,
shared: true
);

// Register factory for creating loggers
builder
.Register(c =>
{
var factory = LoggerFactory.Create(loggingBuilder =>
{
loggingBuilder
.SetMinimumLevel(_minimumLevel)
.AddSimpleConsole(options =>
{
options.SingleLine = false;
options.TimestampFormat = "[yyyy-MM-dd HH:mm:ss] ";
options.UseUtcTimestamp = true;
});
// You can add additional providers here as needed
});
return factory;
})
.As<ILoggerFactory>()
.SingleInstance();
var logger = loggerConfig.CreateLogger();

Log.Logger = logger;

builder.RegisterInstance(logger).As<Serilog.ILogger>().SingleInstance();

// Register a filter to respect minimum level
builder
.Register(c => new LoggerFilterOptions { MinLevel = _minimumLevel })
.Register<ILoggerFactory>(c => new SerilogLoggerFactory(logger, true))
.As<ILoggerFactory>()
.SingleInstance();
}
}
Expand Down
4 changes: 4 additions & 0 deletions temporal-examples/Shared/Shared.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Autofac" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" />
<PackageReference Include="Microsoft.Extensions.Hosting" />
<PackageReference Include="Microsoft.Extensions.Logging" />
<PackageReference Include="Serilog.Extensions.Logging" />
<PackageReference Include="Serilog.Sinks.Console" />
<PackageReference Include="Serilog.Sinks.File" />
<PackageReference Include="Temporalio" />
</ItemGroup>
<ItemGroup>
Expand Down
2 changes: 2 additions & 0 deletions temporal-examples/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ services:
dockerfile: temporal-worker/Dockerfile
environment:
- Temporal__ClientUrl=temporal:7233
volumes:
- ./logs:/app/logs
depends_on:
temporal:
condition: service_healthy
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
using Microsoft.Extensions.DependencyInjection;
using Temporalio.Extensions.Hosting;
using Temporalio.Runtime;
using workflows;
using Workflows;

namespace TemporalWorker.AutoFac.Modules
{
Expand Down
2 changes: 1 addition & 1 deletion temporal-examples/microservice-worker/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ private static async Task Main(string[] args)
.ConfigureContainer<ContainerBuilder>(
(context, builder) =>
{
builder.RegisterModule(new LoggingModule(LogLevel.Information));
builder.RegisterModule(new LoggingModule(context.Configuration));
builder.RegisterModule(
new TemporalWorkerConfigurationModule(context.Configuration)
);
Expand Down
1 change: 1 addition & 0 deletions temporal-examples/temporal-examples.sln
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "docker-co
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8EC462FD-D22E-90A8-E5CE-7E832BA40C5D}"
ProjectSection(SolutionItems) = preProject
..\LICENSE.md = ..\LICENSE.md
..\README.md = ..\README.md
EndProjectSection
EndProject
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
using Microsoft.Extensions.DependencyInjection;
using Temporalio.Extensions.Hosting;
using Temporalio.Runtime;
using workflows;
using Workflows;
using Workflows;

Check warning on line 8 in temporal-examples/temporal-worker/AutoFac/Modules/TemporalWorkerConfigurationModule.cs

View workflow job for this annotation

GitHub Actions / build-and-test

The using directive for 'Workflows' appeared previously in this namespace

Check warning on line 8 in temporal-examples/temporal-worker/AutoFac/Modules/TemporalWorkerConfigurationModule.cs

View workflow job for this annotation

GitHub Actions / build-and-test

The using directive for 'Workflows' appeared previously in this namespace

namespace TemporalWorker.AutoFac.Modules
{
Expand All @@ -14,8 +15,7 @@

public TemporalWorkerConfigurationModule(IConfiguration configuration)
{
_configuration =
configuration ?? throw new ArgumentNullException(nameof(configuration));
_configuration = configuration;
}

protected override void Load(ContainerBuilder builder)
Expand Down
3 changes: 1 addition & 2 deletions temporal-examples/temporal-worker/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using Autofac.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Shared.AutoFac.Modules;
using TemporalWorker.AutoFac.Modules;

Expand All @@ -17,10 +16,10 @@ private static async Task Main(string[] args)
.ConfigureContainer<ContainerBuilder>(
(context, builder) =>
{
builder.RegisterModule(new LoggingModule(LogLevel.Information));
builder.RegisterModule(
new TemporalWorkerConfigurationModule(context.Configuration)
);
builder.RegisterModule(new LoggingModule(context.Configuration));
}
)
.Build();
Expand Down
4 changes: 4 additions & 0 deletions temporal-examples/temporal-worker/temporal-worker.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" />
<PackageReference Include="prometheus-net" />
<PackageReference Include="Serilog" />
<PackageReference Include="Serilog.Extensions.Logging" />
<PackageReference Include="Serilog.Sinks.Console" />
<PackageReference Include="Serilog.Sinks.File" />
<PackageReference Include="Temporalio" />
<PackageReference Include="Temporalio.Extensions.Hosting" />
</ItemGroup>
Expand Down
37 changes: 28 additions & 9 deletions temporal-examples/workflows/ExampleActivities.cs
Original file line number Diff line number Diff line change
@@ -1,39 +1,58 @@
using Microsoft.Extensions.Logging;
using Temporalio.Activities;

namespace workflows;
namespace Workflows;

public class ExampleActivities
{
private readonly ILogger<ExampleActivities> _logger;

public ExampleActivities(ILogger<ExampleActivities> logger)
{
_logger = logger;
}

[Activity]
public static async Task<string> GenericTask()
public async Task<string> GenericTask()
{
await Task.Delay(2000);
var duration = 2000;
_logger.LogInformation("Starting delay: {Duration}", duration);
await Task.Delay(duration);
_logger.LogInformation("Finished delay");
return $"generic-task-{DateTime.Now}";
}

[Activity]
public static List<string> GenerateChildWorkflowsName()
public List<string> GenerateChildWorkflowsName()
{
int numberOfChildWorkflows = new Random().Next(1, 10);
List<string> childWorkflowsInfo = [];
for (int index = 0; index < numberOfChildWorkflows; index++)
{
childWorkflowsInfo.Add($"child-{index}-workflow-{DateTime.Now}");
var name = $"child-{index}-workflow-{DateTime.Now}";
_logger.LogInformation("Creating child workflow {Name}", name);
childWorkflowsInfo.Add(name);
}
return childWorkflowsInfo;
}

[Activity]
public static async Task<string> TaskTriggeredBySignal()
public async Task<string> TaskTriggeredBySignal()
{
await Task.Delay(2000);
var duration = 2000;
_logger.LogInformation("Starting delay: {Duration}", duration);
await Task.Delay(duration);
_logger.LogInformation("Finished delay");
return $"task-triggered-by-signal-{DateTime.Now}";
}

[Activity]
public static async Task<string> TaskTriggeredByTimeout()
public async Task<string> TaskTriggeredByTimeout()
{
await Task.Delay(2000);
var duration = 2000;
_logger.LogInformation("Starting delay: {Duration}", duration);
await Task.Delay(duration);
_logger.LogInformation("Finished delay");
return $"task-triggered-by-timeout-{DateTime.Now}";
}
}
Loading