diff --git a/src/Dapr.Actors.AspNetCore/Dapr.Actors.AspNetCore.csproj b/src/Dapr.Actors.AspNetCore/Dapr.Actors.AspNetCore.csproj index 1ce4b776..a9f6e881 100644 --- a/src/Dapr.Actors.AspNetCore/Dapr.Actors.AspNetCore.csproj +++ b/src/Dapr.Actors.AspNetCore/Dapr.Actors.AspNetCore.csproj @@ -4,7 +4,6 @@ - ActorManager. private readonly Dictionary actorManagers = new Dictionary(); private ActorSettings actorSettings; - internal ActorRuntime() + private readonly ILogger logger; + + internal ActorRuntime(ActorRuntimeOptions options, ILoggerFactory loggerFactory) { this.actorSettings = new ActorSettings(); + this.logger = loggerFactory.CreateLogger(this.GetType()); + + // Create ActorManagers, override existing entry if registered again. + foreach(var actorServiceFunc in options.actorServicesFunc) + { + var actorServiceFactory = actorServiceFunc.Value ?? ((type) => new ActorService(type, loggerFactory)); + var actorService = actorServiceFactory.Invoke(actorServiceFunc.Key); + + this.actorManagers[actorServiceFunc.Key.ActorTypeName] = new ActorManager(actorService, loggerFactory); + } } /// @@ -37,29 +48,6 @@ namespace Dapr.Actors.Runtime internal static IDaprInteractor DaprInteractor => new DaprHttpInteractor(); - /// - /// Registers an actor with the runtime. - /// - /// Type of actor. - /// An optional delegate to create actor service. This can be used for dependency injection into actors. - public void RegisterActor(Func actorServiceFactory = null) - where TActor : Actor - { - var actorTypeInfo = ActorTypeInformation.Get(typeof(TActor)); - - ActorService actorService; - if (actorServiceFactory != null) - { - actorService = actorServiceFactory.Invoke(actorTypeInfo); - } - else - { - actorService = new ActorService(actorTypeInfo); - } - - // Create ActorManagers, override existing entry if registered again. - this.actorManagers[actorTypeInfo.ActorTypeName] = new ActorManager(actorService); - } /// /// Allows configuration of this app's actor configuration. @@ -118,7 +106,10 @@ namespace Dapr.Actors.Runtime /// A representing the result of the asynchronous operation. internal async Task DeactivateAsync(string actorTypeName, string actorId) { - await GetActorManager(actorTypeName).DeactivateActor(new ActorId(actorId)); + using(this.logger.BeginScope("ActorType: {ActorType}, ActorId: {ActorId}", actorTypeName, actorId)) + { + await GetActorManager(actorTypeName).DeactivateActor(new ActorId(actorId)); + } } /// @@ -133,7 +124,10 @@ namespace Dapr.Actors.Runtime /// A representing the result of the asynchronous operation. internal Task> DispatchWithRemotingAsync(string actorTypeName, string actorId, string actorMethodName, string daprActorheader, Stream data, CancellationToken cancellationToken = default) { - return GetActorManager(actorTypeName).DispatchWithRemotingAsync(new ActorId(actorId), actorMethodName, daprActorheader, data, cancellationToken); + using(this.logger.BeginScope("ActorType: {ActorType}, ActorId: {ActorId}, MethodName: {Reminder}", actorTypeName, actorId, actorMethodName)) + { + return GetActorManager(actorTypeName).DispatchWithRemotingAsync(new ActorId(actorId), actorMethodName, daprActorheader, data, cancellationToken); + } } /// @@ -162,7 +156,10 @@ namespace Dapr.Actors.Runtime /// A representing the result of the asynchronous operation. internal Task FireReminderAsync(string actorTypeName, string actorId, string reminderName, Stream requestBodyStream, CancellationToken cancellationToken = default) { - return GetActorManager(actorTypeName).FireReminderAsync(new ActorId(actorId), reminderName, requestBodyStream, cancellationToken); + using(this.logger.BeginScope("ActorType: {ActorType}, ActorId: {ActorId}, ReminderName: {Reminder}", actorTypeName, actorId, reminderName)) + { + return GetActorManager(actorTypeName).FireReminderAsync(new ActorId(actorId), reminderName, requestBodyStream, cancellationToken); + } } /// @@ -175,7 +172,10 @@ namespace Dapr.Actors.Runtime /// A representing the result of the asynchronous operation. internal Task FireTimerAsync(string actorTypeName, string actorId, string timerName, CancellationToken cancellationToken = default) { - return GetActorManager(actorTypeName).FireTimerAsync(new ActorId(actorId), timerName, cancellationToken); + using(this.logger.BeginScope("ActorType: {ActorType}, ActorId: {ActorId}, TimerName: {Timer}", actorTypeName, actorId, timerName)) + { + return GetActorManager(actorTypeName).FireTimerAsync(new ActorId(actorId), timerName, cancellationToken); + } } private ActorManager GetActorManager(string actorTypeName) @@ -183,7 +183,6 @@ namespace Dapr.Actors.Runtime if (!this.actorManagers.TryGetValue(actorTypeName, out var actorManager)) { var errorMsg = $"Actor type {actorTypeName} is not registered with Actor runtime."; - ActorTrace.Instance.WriteError(TraceType, errorMsg); throw new InvalidOperationException(errorMsg); } diff --git a/src/Dapr.Actors/Runtime/ActorRuntimeOptions.cs b/src/Dapr.Actors/Runtime/ActorRuntimeOptions.cs new file mode 100644 index 00000000..9069ece9 --- /dev/null +++ b/src/Dapr.Actors/Runtime/ActorRuntimeOptions.cs @@ -0,0 +1,31 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// ------------------------------------------------------------ + +namespace Dapr.Actors.Runtime +{ + using System; + using System.Collections.Generic; + + /// + /// Represents the Dapr runtime options + /// + public class ActorRuntimeOptions + { + // Map of ActorType --> ActorService factory. + internal readonly Dictionary> actorServicesFunc = new Dictionary>(); + + /// + /// Registers an actor with the runtime. + /// + /// Type of actor. + /// An optional delegate to create actor service. This can be used for dependency injection into actors. + public void RegisterActor(Func actorServiceFactory = null) + where TActor : Actor + { + var actorTypeInfo = ActorTypeInformation.Get(typeof(TActor)); + this.actorServicesFunc.Add(actorTypeInfo, actorServiceFactory); + } + } +} diff --git a/src/Dapr.Actors/Runtime/ActorService.cs b/src/Dapr.Actors/Runtime/ActorService.cs index 7dd36f5f..e43cd5c0 100644 --- a/src/Dapr.Actors/Runtime/ActorService.cs +++ b/src/Dapr.Actors/Runtime/ActorService.cs @@ -6,6 +6,7 @@ namespace Dapr.Actors.Runtime { using System; + using Microsoft.Extensions.Logging; /// /// Represents a host for an actor type within the actor runtime. @@ -18,14 +19,17 @@ namespace Dapr.Actors.Runtime /// Initializes a new instance of the class. /// /// The type information of the Actor. + /// The logger factory. /// The factory method to create Actor objects. public ActorService( ActorTypeInformation actorTypeInfo, + ILoggerFactory loggerFactory, Func actorFactory = null) { this.ActorTypeInfo = actorTypeInfo; this.actorFactory = actorFactory ?? this.DefaultActorFactory; this.StateProvider = new DaprStateProvider(); + this.LoggerFactory = loggerFactory; } /// @@ -35,6 +39,11 @@ namespace Dapr.Actors.Runtime internal DaprStateProvider StateProvider { get; } + /// + /// Gets the LoggerFactory for actor service + /// + public ILoggerFactory LoggerFactory { get; private set; } + internal Actor CreateActor(ActorId actorId) { return this.actorFactory.Invoke(this, actorId); diff --git a/src/Dapr.AspNetCore/DaprEndpointRouteBuilderExtensions.cs b/src/Dapr.AspNetCore/DaprEndpointRouteBuilderExtensions.cs index e987df15..932942c4 100644 --- a/src/Dapr.AspNetCore/DaprEndpointRouteBuilderExtensions.cs +++ b/src/Dapr.AspNetCore/DaprEndpointRouteBuilderExtensions.cs @@ -55,7 +55,7 @@ namespace Microsoft.AspNetCore.Builder { if (logger != null) { - logger.LogWarning("Topic subscription doesn't support route with parameters. Subscription for topic {name} is removed.", entry.Name); + logger.LogError("Topic subscription doesn't support route with parameters. Subscription for topic {name} is removed.", entry.Name); } continue; diff --git a/test/Dapr.Actors.Test/ActorMethodInvocationExceptionTests.cs b/test/Dapr.Actors.Test/ActorMethodInvocationExceptionTests.cs index 4eb8688e..bc280815 100644 --- a/test/Dapr.Actors.Test/ActorMethodInvocationExceptionTests.cs +++ b/test/Dapr.Actors.Test/ActorMethodInvocationExceptionTests.cs @@ -29,7 +29,7 @@ namespace Dapr.Actors.Test var message = "Remote Actor Exception"; // Create Serialized Exception - var serializedException = RemoteException.FromException(new InvalidOperationException()); + (var serializedException, _) = RemoteException.FromException(new InvalidOperationException()); // De Serialize Exception var isDeserialzied = RemoteException.ToException( diff --git a/test/Dapr.Actors.Test/Dapr.Actors.Test.csproj b/test/Dapr.Actors.Test/Dapr.Actors.Test.csproj index 25af9bf8..a91a94fc 100644 --- a/test/Dapr.Actors.Test/Dapr.Actors.Test.csproj +++ b/test/Dapr.Actors.Test/Dapr.Actors.Test.csproj @@ -9,6 +9,7 @@ all + diff --git a/test/Dapr.Actors.Test/Runtime/ActorRuntimeOptionsTests.cs b/test/Dapr.Actors.Test/Runtime/ActorRuntimeOptionsTests.cs new file mode 100644 index 00000000..14702874 --- /dev/null +++ b/test/Dapr.Actors.Test/Runtime/ActorRuntimeOptionsTests.cs @@ -0,0 +1,42 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// ------------------------------------------------------------ + +namespace Dapr.Actors.Test.Runtime +{ + using System; + using System.Threading; + using Dapr.Actors.Runtime; + using FluentAssertions; + using Moq; + using Xunit; + using Microsoft.Extensions.Logging; + using System.Linq; + + public sealed class ActorRuntimeOptionsTests + { + [Fact] + public void TestRegisterActor_SavesActorServiceFactory() + { + var actorType = typeof(TestActor); + var actorTypeInformation = ActorTypeInformation.Get(actorType); + var actorService = new ActorService(actorTypeInformation, new LoggerFactory()); + Func actorServiceFactory = (actorTypeInfo) => NewActorServiceFactory(actorTypeInfo); + + Func actorFactory = (service, id) => + new TestActor(service, id, null); + + var actorRuntimeOptions = new ActorRuntimeOptions(); + actorRuntimeOptions.RegisterActor(actorServiceFactory); + Assert.True(actorRuntimeOptions.actorServicesFunc.Count.Equals(1)); + var key = actorRuntimeOptions.actorServicesFunc.Keys.First(); + Assert.True(key.ActorTypeName.Equals(ActorTypeInformation.Get(actorType).ActorTypeName)); + } + + private ActorService NewActorServiceFactory(ActorTypeInformation actorTypeInfo) + { + return new ActorService(actorTypeInfo, null, null); + } + } +} \ No newline at end of file diff --git a/test/Dapr.Actors.Test/Runtime/ActorRuntimeTests.cs b/test/Dapr.Actors.Test/Runtime/ActorRuntimeTests.cs index e0c531fd..640aa634 100644 --- a/test/Dapr.Actors.Test/Runtime/ActorRuntimeTests.cs +++ b/test/Dapr.Actors.Test/Runtime/ActorRuntimeTests.cs @@ -14,10 +14,13 @@ namespace Dapr.Actors.Test using Dapr.Actors; using Dapr.Actors.Runtime; using Xunit; + using Microsoft.Extensions.Logging; public sealed class ActorRuntimeTests { private const string RenamedActorTypeName = "MyRenamedActor"; + private ActorRuntimeOptions options = new ActorRuntimeOptions(); + private ILoggerFactory loggerFactory = new LoggerFactory(); private interface ITestActor : IActor { @@ -27,11 +30,9 @@ namespace Dapr.Actors.Test public void TestInferredActorType() { var actorType = typeof(TestActor); - var actorRuntime = new ActorRuntime(); - - Assert.Empty(actorRuntime.RegisteredActorTypes); - - actorRuntime.RegisterActor(); + + options.RegisterActor(); + var actorRuntime = new ActorRuntime(options, loggerFactory); Assert.Contains(actorType.Name, actorRuntime.RegisteredActorTypes, StringComparer.InvariantCulture); } @@ -40,14 +41,10 @@ namespace Dapr.Actors.Test public void TestExplicitActorType() { var actorType = typeof(RenamedActor); - var actorRuntime = new ActorRuntime(); + options.RegisterActor(); + var actorRuntime = new ActorRuntime(options, loggerFactory); Assert.NotEqual(RenamedActorTypeName, actorType.Name); - - Assert.Empty(actorRuntime.RegisteredActorTypes); - - actorRuntime.RegisterActor(); - Assert.Contains(RenamedActorTypeName, actorRuntime.RegisteredActorTypes, StringComparer.InvariantCulture); } @@ -57,8 +54,8 @@ namespace Dapr.Actors.Test { var actorType = typeof(MyActor); - var runtime = new ActorRuntime(); - runtime.RegisterActor(); + options.RegisterActor(); + var runtime = new ActorRuntime(options, loggerFactory); var output = new MemoryStream(); await runtime.DispatchWithoutRemotingAsync("MyActor", "abc", "MyMethod", new MemoryStream(), output); @@ -73,9 +70,8 @@ namespace Dapr.Actors.Test public void TestActorSettings() { var actorType = typeof(TestActor); - var actorRuntime = new ActorRuntime(); - - Assert.Empty(actorRuntime.RegisteredActorTypes); + options.RegisterActor(); + var actorRuntime = new ActorRuntime(options, loggerFactory); actorRuntime.ConfigureActorSettings(a => { @@ -85,8 +81,6 @@ namespace Dapr.Actors.Test a.DrainRebalancedActors = true; }); - actorRuntime.RegisterActor(); - Assert.Contains(actorType.Name, actorRuntime.RegisteredActorTypes, StringComparer.InvariantCulture); ArrayBufferWriter writer = new ArrayBufferWriter(); diff --git a/test/Dapr.Actors.Test/Runtime/ActorTests.cs b/test/Dapr.Actors.Test/Runtime/ActorTests.cs index 12de60b9..feca573e 100644 --- a/test/Dapr.Actors.Test/Runtime/ActorTests.cs +++ b/test/Dapr.Actors.Test/Runtime/ActorTests.cs @@ -11,6 +11,7 @@ namespace Dapr.Actors.Test.Runtime using FluentAssertions; using Moq; using Xunit; + using Microsoft.Extensions.Logging; public sealed class ActorTests { @@ -54,7 +55,8 @@ namespace Dapr.Actors.Test.Runtime var actorTypeInformation = ActorTypeInformation.Get(typeof(TestActor)); Func actorFactory = (service, id) => new TestActor(service, id, actorStateManager); - var actorService = new ActorService(actorTypeInformation, actorFactory); + var loggerFactory = new LoggerFactory(); + var actorService = new ActorService(actorTypeInformation, loggerFactory, actorFactory); var testActor = actorFactory.Invoke(actorService, ActorId.CreateRandom()); return testActor; }