diff --git a/src/Dapr.Workflow/WorkflowLoggingService.cs b/src/Dapr.Workflow/WorkflowLoggingService.cs new file mode 100644 index 000000000..482d95b97 --- /dev/null +++ b/src/Dapr.Workflow/WorkflowLoggingService.cs @@ -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; + + /// + /// Defines runtime options for workflows. + /// + internal sealed class WorkflowLoggingService : IHostedService + { + private readonly ILogger logger; + private static readonly HashSet registeredWorkflows = new(); + private static readonly HashSet registeredActivities = new(); + + public WorkflowLoggingService(ILogger 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); + } + + } +} diff --git a/src/Dapr.Workflow/WorkflowRuntimeOptions.cs b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs index 4dd202b1a..adc925777 100644 --- a/src/Dapr.Workflow/WorkflowRuntimeOptions.cs +++ b/src/Dapr.Workflow/WorkflowRuntimeOptions.cs @@ -54,6 +54,7 @@ public void RegisterWorkflow(string name, Func(string name, Func(); return new OrchestratorWrapper(workflow); }); + WorkflowLoggingService.LogWorkflowName(name); }); } @@ -91,6 +93,7 @@ public void RegisterActivity(string name, Func() where TActivity : class, IWorkflowActi TActivity activity = ActivatorUtilities.CreateInstance(serviceProvider); return new ActivityWrapper(activity); }); + WorkflowLoggingService.LogActivityName(name); }); } diff --git a/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs b/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs index 50880ab24..ca514f221 100644 --- a/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs +++ b/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs @@ -47,7 +47,7 @@ public static IServiceCollection AddDaprWorkflow( #pragma warning disable CS0618 // Type or member is obsolete - keeping around temporarily - replaced by DaprWorkflowClient serviceCollection.TryAddSingleton(); #pragma warning restore CS0618 // Type or member is obsolete - + serviceCollection.AddHostedService(); serviceCollection.TryAddSingleton(); serviceCollection.AddDaprClient(); serviceCollection.AddDaprWorkflowClient(); diff --git a/test/Dapr.E2E.Test.App/Dapr.E2E.Test.App.csproj b/test/Dapr.E2E.Test.App/Dapr.E2E.Test.App.csproj index 30d47e7c2..5c81557b8 100644 --- a/test/Dapr.E2E.Test.App/Dapr.E2E.Test.App.csproj +++ b/test/Dapr.E2E.Test.App/Dapr.E2E.Test.App.csproj @@ -10,4 +10,11 @@ + + + + + + + diff --git a/test/Dapr.E2E.Test.App/Program.cs b/test/Dapr.E2E.Test.App/Program.cs index e617558bf..5713129d5 100644 --- a/test/Dapr.E2E.Test.App/Program.cs +++ b/test/Dapr.E2E.Test.App/Program.cs @@ -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 @@ public static IHostBuilder CreateHostBuilder(string[] args) => .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); - }); + }).UseSerilog(); } } diff --git a/test/Dapr.E2E.Test.App/Startup.cs b/test/Dapr.E2E.Test.App/Startup.cs index d1f291bf9..8207c5883 100644 --- a/test/Dapr.E2E.Test.App/Startup.cs +++ b/test/Dapr.E2E.Test.App/Startup.cs @@ -29,6 +29,8 @@ namespace Dapr.E2E.Test using Microsoft.Extensions.Hosting; using System.Threading.Tasks; using System; + using Microsoft.Extensions.Logging; + using Serilog; /// /// Startup class. @@ -61,6 +63,10 @@ public void ConfigureServices(IServiceCollection services) 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 @@ public void ConfigureServices(IServiceCollection services) /// Webhost environment. 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(); diff --git a/test/Dapr.E2E.Test/Workflows/WorkflowTest.cs b/test/Dapr.E2E.Test/Workflows/WorkflowTest.cs index 971d04098..979c136da 100644 --- a/test/Dapr.E2E.Test/Workflows/WorkflowTest.cs +++ b/test/Dapr.E2E.Test/Workflows/WorkflowTest.cs @@ -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 logStrings = new Dictionary(); + 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 workflowOptions = new Dictionary(); workflowOptions.Add("task_queue", "testQueue");