Initial implementation for workflow log tracing (#1176)

* Initial setup for workflow log tracing

Signed-off-by: Ryan Lettieri <ryanLettieri@microsoft.com>

* Created log sink for E2E tests using serilog

Signed-off-by: Ryan Lettieri <ryanLettieri@microsoft.com>

* Formatting

Signed-off-by: Ryan Lettieri <ryanLettieri@microsoft.com>

* Addressing feedback on review

Signed-off-by: Ryan Lettieri <ryanLettieri@microsoft.com>

* Addressing some review comments

Signed-off-by: Ryan Lettieri <ryanLettieri@microsoft.com>

* Addressing more feedbck in the workflow e2e test

Signed-off-by: Ryan Lettieri <ryanLettieri@microsoft.com>

---------

Signed-off-by: Ryan Lettieri <ryanLettieri@microsoft.com>
This commit is contained in:
Ryan Lettieri 2023-11-14 13:17:17 -07:00 committed by GitHub
parent 2332388155
commit 39a38f6e9b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 176 additions and 18 deletions

View File

@ -0,0 +1,75 @@
// ------------------------------------------------------------------------
// Copyright 2022 The Dapr Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------------
namespace Dapr.Workflow
{
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Configuration;
/// <summary>
/// Defines runtime options for workflows.
/// </summary>
internal sealed class WorkflowLoggingService : IHostedService
{
private readonly ILogger<WorkflowLoggingService> logger;
private static readonly HashSet<string> registeredWorkflows = new();
private static readonly HashSet<string> registeredActivities = new();
public WorkflowLoggingService(ILogger<WorkflowLoggingService> logger, IConfiguration configuration)
{
this.logger = logger;
}
public Task StartAsync(CancellationToken cancellationToken)
{
this.logger.Log(LogLevel.Information, "WorkflowLoggingService started");
this.logger.Log(LogLevel.Information, "List of registered workflows");
foreach (string item in registeredWorkflows)
{
this.logger.Log(LogLevel.Information, item);
}
this.logger.Log(LogLevel.Information, "List of registered activities:");
foreach (string item in registeredActivities)
{
this.logger.Log(LogLevel.Information, item);
}
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
this.logger.Log(LogLevel.Information, "WorkflowLoggingService stopped");
return Task.CompletedTask;
}
public static void LogWorkflowName(string workflowName)
{
registeredWorkflows.Add(workflowName);
}
public static void LogActivityName(string activityName)
{
registeredActivities.Add(activityName);
}
}
}

View File

@ -54,6 +54,7 @@ namespace Dapr.Workflow
WorkflowContext workflowContext = new DaprWorkflowContext(innerContext);
return implementation(workflowContext, input);
});
WorkflowLoggingService.LogWorkflowName(name);
});
}
@ -73,6 +74,7 @@ namespace Dapr.Workflow
TWorkflow workflow = Activator.CreateInstance<TWorkflow>();
return new OrchestratorWrapper(workflow);
});
WorkflowLoggingService.LogWorkflowName(name);
});
}
@ -91,6 +93,7 @@ namespace Dapr.Workflow
WorkflowActivityContext activityContext = new(innerContext);
return implementation(activityContext, input);
});
WorkflowLoggingService.LogActivityName(name);
});
}
@ -111,6 +114,7 @@ namespace Dapr.Workflow
TActivity activity = ActivatorUtilities.CreateInstance<TActivity>(serviceProvider);
return new ActivityWrapper(activity);
});
WorkflowLoggingService.LogActivityName(name);
});
}

View File

@ -47,7 +47,7 @@ namespace Dapr.Workflow
#pragma warning disable CS0618 // Type or member is obsolete - keeping around temporarily - replaced by DaprWorkflowClient
serviceCollection.TryAddSingleton<WorkflowEngineClient>();
#pragma warning restore CS0618 // Type or member is obsolete
serviceCollection.AddHostedService<WorkflowLoggingService>();
serviceCollection.TryAddSingleton<DaprWorkflowClient>();
serviceCollection.AddDaprClient();
serviceCollection.AddDaprWorkflowClient();

View File

@ -10,4 +10,11 @@
<ProjectReference Include="..\Dapr.E2E.Test.Actors\Dapr.E2E.Test.Actors.csproj" />
<ProjectReference Include="..\..\src\Dapr.Workflow\Dapr.Workflow.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Serilog" Version="3.0.1" />
<PackageReference Include="Serilog.AspNetCore" Version="7.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
</ItemGroup>
</Project>

View File

@ -1,23 +1,25 @@
// ------------------------------------------------------------------------
// Copyright 2021 The Dapr Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------------
// ------------------------------------------------------------------------
// Copyright 2021 The Dapr Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------------
namespace Dapr.E2E.Test
{
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Serilog;
public class Program
{
public static void Main(string[] args)
{
Log.Logger = new LoggerConfiguration().WriteTo.File("log.txt").CreateLogger();
CreateHostBuilder(args).Build().Run();
}
@ -26,6 +28,6 @@ namespace Dapr.E2E.Test
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}).UseSerilog();
}
}

View File

@ -29,6 +29,8 @@ namespace Dapr.E2E.Test
using Microsoft.Extensions.Hosting;
using System.Threading.Tasks;
using System;
using Microsoft.Extensions.Logging;
using Serilog;
/// <summary>
/// Startup class.
@ -61,6 +63,10 @@ namespace Dapr.E2E.Test
services.AddAuthentication().AddDapr();
services.AddAuthorization(o => o.AddDapr());
services.AddControllers().AddDapr();
services.AddLogging(builder =>
{
builder.AddConsole();
});
// Register a workflow and associated activity
services.AddDaprWorkflow(options =>
{
@ -108,11 +114,19 @@ namespace Dapr.E2E.Test
/// <param name="env">Webhost environment.</param>
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
var logger = new LoggerConfiguration().WriteTo.File("log.txt").CreateLogger();
var loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddSerilog(logger);
});
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseSerilogRequestLogging();
app.UseRouting();
app.UseAuthentication();

View File

@ -11,25 +11,81 @@
// limitations under the License.
// ------------------------------------------------------------------------
using System;
using System.IO;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Dapr.Client;
using FluentAssertions;
using Xunit;
using System.Linq;
using System.Diagnostics;
namespace Dapr.E2E.Test
{
[Obsolete]
public partial class E2ETests
{
[Fact]
public async Task TestWorkflowLogging()
{
// This test starts the daprclient and searches through the logfile to ensure the
// workflow logger is correctly logging the registered workflow(s) and activity(s)
Dictionary<string, bool> logStrings = new Dictionary<string, bool>();
logStrings["PlaceOrder"] = false;
logStrings["ShipProduct"] = false;
var logFilePath = "../../../../../test/Dapr.E2E.Test.App/log.txt";
var allLogsFound = false;
var timeout = 30; // 30s
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeout));
using var daprClient = new DaprClientBuilder().UseGrpcEndpoint(this.GrpcEndpoint).UseHttpEndpoint(this.HttpEndpoint).Build();
var health = await daprClient.CheckHealthAsync();
health.Should().Be(true, "DaprClient is not healthy");
var searchTask = Task.Run(async() =>
{
using (StreamReader reader = new StreamReader(logFilePath))
{
string line;
while ((line = await reader.ReadLineAsync().WaitAsync(cts.Token)) != null)
{
foreach (var entry in logStrings)
{
if (line.Contains(entry.Key))
{
logStrings[entry.Key] = true;
}
}
allLogsFound = logStrings.All(k => k.Value);
if (allLogsFound)
{
break;
}
}
}
}, cts.Token);
try
{
await searchTask;
}
finally
{
File.Delete(logFilePath);
}
if (!allLogsFound)
{
Assert.True(false, "The logs were not able to found within the timeout");
}
}
[Fact]
public async Task TestWorkflows()
{
string instanceId = "testInstanceId";
string instanceId2 = "EventRaiseId";
string workflowComponent = "dapr";
string workflowName = "PlaceOrder";
var instanceId = "testInstanceId";
var instanceId2 = "EventRaiseId";
var workflowComponent = "dapr";
var workflowName = "PlaceOrder";
object input = "paperclips";
Dictionary<string, string> workflowOptions = new Dictionary<string, string>();
workflowOptions.Add("task_queue", "testQueue");