diff --git a/.gitignore b/.gitignore index b94e9a479..69800e3b7 100644 --- a/.gitignore +++ b/.gitignore @@ -26,7 +26,7 @@ bld/ # Visual Studio 2015 cache/options directory .vs/ -# Files created by bundling and minification on startup +# .js files created on build: src/Web/WebMVC/wwwroot/js/site* # Uncomment if you have tasks that create the project's static files in wwwroot diff --git a/deploy/az/servicebus/readme.md b/deploy/az/servicebus/readme.md index 16da4c7b2..886b3ec60 100644 --- a/deploy/az/servicebus/readme.md +++ b/deploy/az/servicebus/readme.md @@ -8,7 +8,7 @@ The ARM template `sbusdeploy.json` and its parameter file (`sbusdeploy.parameter ## Editing sbusdeploy.parameters.json file -You can edit the `sbusdeploy.parameters.parameters.json` file to set your values, but is not needed. The only parameter than can +You can edit the `sbusdeploy.parameters.json` file to set your values, but is not needed. The only parameter than can be set is: 1. `namespaceprefix` is a string that is used to create the namespace. ARM script creates unique values by appending a unique string to this parameter value, so you can leave the default value. @@ -21,4 +21,4 @@ i. e. if you are in windows, to deploy servicebus in a new resourcegroup located ``` create-resources.cmd servicebus\sbusdeploy newResourceGroup -c westus -``` \ No newline at end of file +``` diff --git a/docker-compose.override.yml b/docker-compose.override.yml index f8d608a27..7353c744c 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -7,6 +7,12 @@ version: '3.4' # An external IP or DNS name has to be used (instead localhost and the 10.0.75.1 IP) when testing the Web apps and the Xamarin apps from remote machines/devices using the same WiFi, for instance. services: + seq: + environment: + - ACCEPT_EULA=Y + ports: + - "5340:80" + sql.data: environment: - SA_PASSWORD=Pass@word diff --git a/docker-compose.yml b/docker-compose.yml index 194d7e06c..55d5b10e6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,9 @@ version: '3.4' services: + seq: + image: datalust/seq:latest + sql.data: image: microsoft/mssql-server-linux:2017-latest diff --git a/src/ApiGateways/ApiGw-Base/OcelotApiGw.csproj b/src/ApiGateways/ApiGw-Base/OcelotApiGw.csproj index f19e84335..6ad9e78d1 100644 --- a/src/ApiGateways/ApiGw-Base/OcelotApiGw.csproj +++ b/src/ApiGateways/ApiGw-Base/OcelotApiGw.csproj @@ -6,7 +6,7 @@ - + diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj index e0426f3bc..83c6ff356 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj @@ -12,7 +12,7 @@ - + diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Startup.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Startup.cs index eeb58ac3b..924b5b1aa 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Startup.cs +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Startup.cs @@ -57,7 +57,7 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator if (!string.IsNullOrEmpty(pathBase)) { - loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'"); + loggerFactory.CreateLogger().LogDebug("Using PATH BASE '{pathBase}'", pathBase); app.UsePathBase(pathBase); } diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Startup.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Startup.cs index 0fcd13a41..6d3da29b7 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Startup.cs +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Startup.cs @@ -57,7 +57,7 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator var pathBase = Configuration["PATH_BASE"]; if (!string.IsNullOrEmpty(pathBase)) { - loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'"); + loggerFactory.CreateLogger().LogDebug("Using PATH BASE '{pathBase}'", pathBase); app.UsePathBase(pathBase); } diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj b/src/ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj index 7309593a8..970d01bf8 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj @@ -12,7 +12,7 @@ - + diff --git a/src/BuildingBlocks/EventBus/EventBus/EventBus.csproj b/src/BuildingBlocks/EventBus/EventBus/EventBus.csproj index 9704f6ff5..17c5a8293 100644 --- a/src/BuildingBlocks/EventBus/EventBus/EventBus.csproj +++ b/src/BuildingBlocks/EventBus/EventBus/EventBus.csproj @@ -6,7 +6,7 @@ - + \ No newline at end of file diff --git a/src/BuildingBlocks/EventBus/EventBus/Extensions/GenericTypeExtensions.cs b/src/BuildingBlocks/EventBus/EventBus/Extensions/GenericTypeExtensions.cs new file mode 100644 index 000000000..de5a2cb79 --- /dev/null +++ b/src/BuildingBlocks/EventBus/EventBus/Extensions/GenericTypeExtensions.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions +{ + public static class GenericTypeExtensions + { + public static string GetGenericTypeName(this Type type) + { + var typeName = string.Empty; + + if (type.IsGenericType) + { + var genericTypes = string.Join(",", type.GetGenericArguments().Select(t => t.Name).ToArray()); + typeName = $"{type.Name.Remove(type.Name.IndexOf('`'))}<{genericTypes}>"; + } + else + { + typeName = type.Name; + } + + return typeName; + } + + public static string GetGenericTypeName(this object @object) + { + return @object.GetType().GetGenericTypeName(); + } + } +} diff --git a/src/BuildingBlocks/EventBus/EventBusRabbitMQ/DefaultRabbitMQPersisterConnection.cs b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/DefaultRabbitMQPersistentConnection.cs similarity index 91% rename from src/BuildingBlocks/EventBus/EventBusRabbitMQ/DefaultRabbitMQPersisterConnection.cs rename to src/BuildingBlocks/EventBus/EventBusRabbitMQ/DefaultRabbitMQPersistentConnection.cs index 2e0555e61..93e5b2917 100644 --- a/src/BuildingBlocks/EventBus/EventBusRabbitMQ/DefaultRabbitMQPersisterConnection.cs +++ b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/DefaultRabbitMQPersistentConnection.cs @@ -72,7 +72,7 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ .Or() .WaitAndRetry(_retryCount, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), (ex, time) => { - _logger.LogWarning(ex.ToString()); + _logger.LogWarning(ex, "RabbitMQ Client could not connect after {TimeOut}s ({ExceptionMessage})", $"{time.TotalSeconds:n1}", ex.Message); } ); @@ -88,7 +88,7 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ _connection.CallbackException += OnCallbackException; _connection.ConnectionBlocked += OnConnectionBlocked; - _logger.LogInformation($"RabbitMQ persistent connection acquired a connection {_connection.Endpoint.HostName} and is subscribed to failure events"); + _logger.LogInformation("RabbitMQ Client acquired a persistent connection to '{HostName}' and is subscribed to failure events", _connection.Endpoint.HostName); return true; } diff --git a/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs index a3b6437ef..ac379d50a 100644 --- a/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs +++ b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs @@ -2,6 +2,7 @@ using Microsoft.eShopOnContainers.BuildingBlocks.EventBus; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -76,7 +77,7 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ .Or() .WaitAndRetry(_retryCount, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), (ex, time) => { - _logger.LogWarning(ex.ToString()); + _logger.LogWarning(ex, "Could not publish event: {EventId} after {Timeout}s ({ExceptionMessage})", @event.Id, $"{time.TotalSeconds:n1}", ex.Message); }); using (var channel = _persistentConnection.CreateModel()) @@ -107,6 +108,8 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ public void SubscribeDynamic(string eventName) where TH : IDynamicIntegrationEventHandler { + _logger.LogInformation("Subscribing to dynamic event {EventName} with {EventHandler}", eventName, typeof(TH).GetGenericTypeName()); + DoInternalSubscription(eventName); _subsManager.AddDynamicSubscription(eventName); } @@ -117,6 +120,9 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ { var eventName = _subsManager.GetEventKey(); DoInternalSubscription(eventName); + + _logger.LogInformation("Subscribing to event {EventName} with {EventHandler}", eventName, typeof(TH).GetGenericTypeName()); + _subsManager.AddSubscription(); } @@ -140,9 +146,13 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ } public void Unsubscribe() - where TH : IIntegrationEventHandler where T : IntegrationEvent + where TH : IIntegrationEventHandler { + var eventName = _subsManager.GetEventKey(); + + _logger.LogInformation("Unsubscribing from event {EventName}", eventName); + _subsManager.RemoveSubscription(); } @@ -215,7 +225,7 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ foreach (var subscription in subscriptions) { if (subscription.IsDynamic) - { + { var handler = scope.ResolveOptional(subscription.HandlerType) as IDynamicIntegrationEventHandler; if (handler == null) continue; dynamic eventData = JObject.Parse(message); diff --git a/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj index cc16b89e1..827eb8b11 100644 --- a/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj +++ b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/BuildingBlocks/EventBus/EventBusRabbitMQ/IRabbitMQPersisterConnection.cs b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/IRabbitMQPersistentConnection.cs similarity index 100% rename from src/BuildingBlocks/EventBus/EventBusRabbitMQ/IRabbitMQPersisterConnection.cs rename to src/BuildingBlocks/EventBus/EventBusRabbitMQ/IRabbitMQPersistentConnection.cs diff --git a/src/BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.cs b/src/BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.cs index d16eb4625..cd2dc557f 100644 --- a/src/BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.cs +++ b/src/BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.cs @@ -27,7 +27,7 @@ ILifetimeScope autofac) { _serviceBusPersisterConnection = serviceBusPersisterConnection; - _logger = logger; + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _subsManager = subsManager ?? new InMemoryEventBusSubscriptionsManager(); _subscriptionClient = new SubscriptionClient(serviceBusPersisterConnection.ServiceBusConnectionStringBuilder, @@ -61,6 +61,8 @@ public void SubscribeDynamic(string eventName) where TH : IDynamicIntegrationEventHandler { + _logger.LogInformation("Subscribing to dynamic event {EventName} with {EventHandler}", eventName, nameof(TH)); + _subsManager.AddDynamicSubscription(eventName); } @@ -83,10 +85,12 @@ } catch (ServiceBusException) { - _logger.LogInformation($"The messaging entity {eventName} already exists."); + _logger.LogWarning("The messaging entity {eventName} already exists.", eventName); } } + _logger.LogInformation("Subscribing to event {EventName} with {EventHandler}", eventName, nameof(TH)); + _subsManager.AddSubscription(); } @@ -105,15 +109,19 @@ } catch (MessagingEntityNotFoundException) { - _logger.LogInformation($"The messaging entity {eventName} Could not be found."); + _logger.LogWarning("The messaging entity {eventName} Could not be found.", eventName); } + _logger.LogInformation("Unsubscribing from event {EventName}", eventName); + _subsManager.RemoveSubscription(); } public void UnsubscribeDynamic(string eventName) where TH : IDynamicIntegrationEventHandler { + _logger.LogInformation("Unsubscribing from dynamic event {EventName}", eventName); + _subsManager.RemoveDynamicSubscription(eventName); } @@ -136,17 +144,16 @@ await _subscriptionClient.CompleteAsync(message.SystemProperties.LockToken); } }, - new MessageHandlerOptions(ExceptionReceivedHandler) { MaxConcurrentCalls = 10, AutoComplete = false }); + new MessageHandlerOptions(ExceptionReceivedHandler) { MaxConcurrentCalls = 10, AutoComplete = false }); } private Task ExceptionReceivedHandler(ExceptionReceivedEventArgs exceptionReceivedEventArgs) { - Console.WriteLine($"Message handler encountered an exception {exceptionReceivedEventArgs.Exception}."); + var ex = exceptionReceivedEventArgs.Exception; var context = exceptionReceivedEventArgs.ExceptionReceivedContext; - Console.WriteLine("Exception context for troubleshooting:"); - Console.WriteLine($"- Endpoint: {context.Endpoint}"); - Console.WriteLine($"- Entity Path: {context.EntityPath}"); - Console.WriteLine($"- Executing Action: {context.Action}"); + + _logger.LogError(ex, "ERROR handling message: {ExceptionMessage} - Context: {@ExceptionContext}", ex.Message, context); + return Task.CompletedTask; } @@ -172,7 +179,7 @@ var handler = scope.ResolveOptional(subscription.HandlerType); if (handler == null) continue; var eventType = _subsManager.GetEventTypeByName(eventName); - var integrationEvent = JsonConvert.DeserializeObject(message, eventType); + var integrationEvent = JsonConvert.DeserializeObject(message, eventType); var concreteType = typeof(IIntegrationEventHandler<>).MakeGenericType(eventType); await (Task)concreteType.GetMethod("Handle").Invoke(handler, new object[] { integrationEvent }); } @@ -194,7 +201,7 @@ } catch (MessagingEntityNotFoundException) { - _logger.LogInformation($"The messaging entity { RuleDescription.DefaultRuleName } Could not be found."); + _logger.LogWarning("The messaging entity {DefaultRuleName} Could not be found.", RuleDescription.DefaultRuleName); } } } diff --git a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj index ef3463cca..601d337b0 100644 --- a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj +++ b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj @@ -10,7 +10,7 @@ - + diff --git a/src/BuildingBlocks/WebHostCustomization/WebHost.Customization/WebHostExtensions.cs b/src/BuildingBlocks/WebHostCustomization/WebHost.Customization/WebHostExtensions.cs index 0da18f581..fa06e0a0b 100644 --- a/src/BuildingBlocks/WebHostCustomization/WebHost.Customization/WebHostExtensions.cs +++ b/src/BuildingBlocks/WebHostCustomization/WebHost.Customization/WebHostExtensions.cs @@ -32,7 +32,7 @@ namespace Microsoft.AspNetCore.Hosting try { - logger.LogInformation($"Migrating database associated with context {typeof(TContext).Name}"); + logger.LogInformation("Migrating database associated with context {DbContextName}", typeof(TContext).Name); if (underK8s) { @@ -55,11 +55,11 @@ namespace Microsoft.AspNetCore.Hosting retry.Execute(() => InvokeSeeder(seeder, context, services)); } - logger.LogInformation($"Migrated database associated with context {typeof(TContext).Name}"); + logger.LogInformation("Migrated database associated with context {DbContextName}", typeof(TContext).Name); } catch (Exception ex) { - logger.LogError(ex, $"An error occurred while migrating the database used on context {typeof(TContext).Name}"); + logger.LogError(ex, "An error occurred while migrating the database used on context {DbContextName}", typeof(TContext).Name); if (underK8s) { throw; // Rethrow under k8s because we rely on k8s to re-run the pod diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.TestRunner.iOS/eShopOnContainers.TestRunner.iOS.csproj.bak b/src/Mobile/eShopOnContainers/eShopOnContainers.TestRunner.iOS/eShopOnContainers.TestRunner.iOS.csproj.bak deleted file mode 100644 index 0d45fd280..000000000 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.TestRunner.iOS/eShopOnContainers.TestRunner.iOS.csproj.bak +++ /dev/null @@ -1,175 +0,0 @@ - - - - Debug - iPhoneSimulator - {B68C2B56-7581-46AE-B55D-D25DDFD3BFE3} - {FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - Exe - eShopOnContainers.TestRunner.iOS - Resources - eShopOnContainers.TestRunner.iOS - - - - - true - full - false - bin\iPhoneSimulator\Debug - DEBUG - prompt - 4 - false - x86_64 - SdkOnly - True - 10.1 - False - False - False - False - False - False - False - True - Default - HttpClientHandler - False - - - none - true - bin\iPhoneSimulator\Release - prompt - 4 - None - x86_64 - false - - - true - full - false - bin\iPhone\Debug - DEBUG - prompt - 4 - false - ARMv7, ARM64 - Entitlements.plist - iPhone Developer - true - - - none - true - bin\iPhone\Release - prompt - 4 - Entitlements.plist - ARMv7, ARM64 - false - iPhone Developer - - - none - True - bin\iPhone\Ad-Hoc - prompt - 4 - False - ARMv7, ARM64 - Entitlements.plist - True - Automatic:AdHoc - iPhone Distribution - - - none - True - bin\iPhone\AppStore - prompt - 4 - False - ARMv7, ARM64 - Entitlements.plist - Automatic:AppStore - iPhone Distribution - - - - - - - - - - - - - - - - ..\..\packages\Xamarin.Forms.2.3.3.166-pre4\lib\Xamarin.iOS10\Xamarin.Forms.Core.dll - True - - - ..\..\packages\Xamarin.Forms.2.3.3.166-pre4\lib\Xamarin.iOS10\Xamarin.Forms.Platform.dll - True - - - ..\..\packages\Xamarin.Forms.2.3.3.166-pre4\lib\Xamarin.iOS10\Xamarin.Forms.Platform.iOS.dll - True - - - ..\..\packages\Xamarin.Forms.2.3.3.166-pre4\lib\Xamarin.iOS10\Xamarin.Forms.Xaml.dll - True - - - - ..\..\packages\xunit.abstractions.2.0.1\lib\netstandard1.0\xunit.abstractions.dll - True - - - ..\..\packages\xunit.assert.2.2.0-beta4-build3444\lib\netstandard1.0\xunit.assert.dll - True - - - ..\..\packages\xunit.extensibility.core.2.2.0-beta4-build3444\lib\netstandard1.0\xunit.core.dll - True - - - ..\..\packages\xunit.extensibility.execution.2.2.0-beta4-build3444\lib\netstandard1.0\xunit.execution.dotnet.dll - True - - - ..\..\packages\xunit.runner.devices.2.1.0\lib\Xamarin.iOS\xunit.runner.devices.dll - True - - - ..\..\packages\xunit.runner.utility.2.2.0-beta4-build3444\lib\netstandard1.1\xunit.runner.utility.dotnet.dll - True - - - - - - - - - {f7b6a162-bc4d-4924-b16a-713f9b0344e7} - eShopOnContainers.UnitTests - - - - - - - Este proyecto hace referencia a los paquetes NuGet que faltan en este equipo. Use la restauración de paquetes NuGet para descargarlos. Para obtener más información, consulte http://go.microsoft.com/fwlink/?LinkID=322105. El archivo que falta es {0}. - - - - - - \ No newline at end of file diff --git a/src/Services/Basket/Basket.API/Basket.API.csproj b/src/Services/Basket/Basket.API/Basket.API.csproj index 77d58aff1..ab1184ae1 100644 --- a/src/Services/Basket/Basket.API/Basket.API.csproj +++ b/src/Services/Basket/Basket.API/Basket.API.csproj @@ -13,25 +13,25 @@ + - - - - - + + + + + + - - - - + + + - diff --git a/src/Services/Basket/Basket.API/Controllers/BasketController.cs b/src/Services/Basket/Basket.API/Controllers/BasketController.cs index 0e15e65dd..7bab4e969 100644 --- a/src/Services/Basket/Basket.API/Controllers/BasketController.cs +++ b/src/Services/Basket/Basket.API/Controllers/BasketController.cs @@ -5,6 +5,8 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; using Microsoft.eShopOnContainers.Services.Basket.API.Model; using Microsoft.eShopOnContainers.Services.Basket.API.Services; +using Microsoft.Extensions.Logging; +using Serilog.Context; using System; using System.Net; using System.Threading.Tasks; @@ -19,9 +21,15 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Controllers private readonly IBasketRepository _repository; private readonly IIdentityService _identityService; private readonly IEventBus _eventBus; + private readonly ILogger _logger; - public BasketController(IBasketRepository repository, IIdentityService identityService, IEventBus eventBus) + public BasketController( + ILogger logger, + IBasketRepository repository, + IIdentityService identityService, + IEventBus eventBus) { + _logger = logger; _repository = repository; _identityService = identityService; _eventBus = eventBus; @@ -50,7 +58,7 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Controllers public async Task CheckoutAsync([FromBody]BasketCheckout basketCheckout, [FromHeader(Name = "x-requestid")] string requestId) { var userId = _identityService.GetUserIdentity(); - + basketCheckout.RequestId = (Guid.TryParse(requestId, out Guid guid) && guid != Guid.Empty) ? guid : basketCheckout.RequestId; @@ -70,7 +78,18 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Controllers // Once basket is checkout, sends an integration event to // ordering.api to convert basket to order and proceeds with // order creation process - _eventBus.Publish(eventMessage); + try + { + _logger.LogInformation("----- Publishing integration event: {IntegrationEventId} from {AppName} - ({@IntegrationEvent})", eventMessage.Id, Program.AppName, eventMessage); + + _eventBus.Publish(eventMessage); + } + catch (Exception ex) + { + _logger.LogError(ex, "ERROR Publishing integration event: {IntegrationEventId} from {AppName}", eventMessage.Id, Program.AppName); + + throw; + } return Accepted(); } diff --git a/src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/OrderStartedIntegrationEventHandler.cs b/src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/OrderStartedIntegrationEventHandler.cs index 19ae1b594..cb7b6a2d6 100644 --- a/src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/OrderStartedIntegrationEventHandler.cs +++ b/src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/OrderStartedIntegrationEventHandler.cs @@ -1,6 +1,9 @@ using Basket.API.IntegrationEvents.Events; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; +using Microsoft.eShopOnContainers.Services.Basket.API; using Microsoft.eShopOnContainers.Services.Basket.API.Model; +using Microsoft.Extensions.Logging; +using Serilog.Context; using System; using System.Threading.Tasks; @@ -9,15 +12,24 @@ namespace Basket.API.IntegrationEvents.EventHandling public class OrderStartedIntegrationEventHandler : IIntegrationEventHandler { private readonly IBasketRepository _repository; + private readonly ILogger _logger; - public OrderStartedIntegrationEventHandler(IBasketRepository repository) + public OrderStartedIntegrationEventHandler( + IBasketRepository repository, + ILogger logger) { _repository = repository ?? throw new ArgumentNullException(nameof(repository)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public async Task Handle(OrderStartedIntegrationEvent @event) { - await _repository.DeleteBasketAsync(@event.UserId.ToString()); + using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) + { + _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); + + await _repository.DeleteBasketAsync(@event.UserId.ToString()); + } } } } diff --git a/src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/ProductPriceChangedIntegrationEventHandler.cs b/src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/ProductPriceChangedIntegrationEventHandler.cs index 546483b40..c27200e6f 100644 --- a/src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/ProductPriceChangedIntegrationEventHandler.cs +++ b/src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/ProductPriceChangedIntegrationEventHandler.cs @@ -1,6 +1,8 @@ using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; using Microsoft.eShopOnContainers.Services.Basket.API.IntegrationEvents.Events; using Microsoft.eShopOnContainers.Services.Basket.API.Model; +using Microsoft.Extensions.Logging; +using Serilog.Context; using System; using System.Linq; using System.Threading.Tasks; @@ -9,22 +11,31 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.IntegrationEvents.Even { public class ProductPriceChangedIntegrationEventHandler : IIntegrationEventHandler { + private readonly ILogger _logger; private readonly IBasketRepository _repository; - public ProductPriceChangedIntegrationEventHandler(IBasketRepository repository) + public ProductPriceChangedIntegrationEventHandler( + ILogger logger, + IBasketRepository repository) { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _repository = repository ?? throw new ArgumentNullException(nameof(repository)); } public async Task Handle(ProductPriceChangedIntegrationEvent @event) { - var userIds = _repository.GetUsers(); - - foreach (var id in userIds) + using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) { - var basket = await _repository.GetBasketAsync(id); + _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); - await UpdatePriceInBasketItems(@event.ProductId, @event.NewPrice, @event.OldPrice, basket); + var userIds = _repository.GetUsers(); + + foreach (var id in userIds) + { + var basket = await _repository.GetBasketAsync(id); + + await UpdatePriceInBasketItems(@event.ProductId, @event.NewPrice, @event.OldPrice, basket); + } } } @@ -35,17 +46,19 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.IntegrationEvents.Even if (itemsToUpdate != null) { + _logger.LogInformation("----- ProductPriceChangedIntegrationEventHandler - Updating items in basket for user: {BuyerId} ({@Items})", basket.BuyerId, itemsToUpdate); + foreach (var item in itemsToUpdate) { - if(item.UnitPrice == oldPrice) - { + if (item.UnitPrice == oldPrice) + { var originalPrice = item.UnitPrice; item.UnitPrice = newPrice; item.OldUnitPrice = originalPrice; } } await _repository.UpdateBasketAsync(basket); - } + } } } } diff --git a/src/Services/Basket/Basket.API/Program.cs b/src/Services/Basket/Basket.API/Program.cs index cf53c1b13..893a52400 100644 --- a/src/Services/Basket/Basket.API/Program.cs +++ b/src/Services/Basket/Basket.API/Program.cs @@ -12,52 +12,80 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API { public class Program { - public static void Main(string[] args) + public static readonly string Namespace = typeof(Program).Namespace; + public static readonly string AppName = Namespace.Substring(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1); + + public static int Main(string[] args) { - BuildWebHost(args).Run(); + var configuration = GetConfiguration(); + + Log.Logger = CreateSerilogLogger(configuration); + + try + { + Log.Information("Configuring web host ({ApplicationContext})...", AppName); + var host = BuildWebHost(configuration, args); + + Log.Information("Starting web host ({ApplicationContext})...", AppName); + host.Run(); + + return 0; + } + catch (Exception ex) + { + Log.Fatal(ex, "Program terminated unexpectedly ({ApplicationContext})!", AppName); + return 1; + } + finally + { + Log.CloseAndFlush(); + } } - public static IWebHost BuildWebHost(string[] args) => + private static IWebHost BuildWebHost(IConfiguration configuration, string[] args) => WebHost.CreateDefaultBuilder(args) + .CaptureStartupErrors(false) .UseFailing(options => - { - options.ConfigPath = "/Failing"; - }) - .UseContentRoot(Directory.GetCurrentDirectory()) + options.ConfigPath = "/Failing") .UseStartup() - .ConfigureAppConfiguration((builderContext, config) => - { - var builtConfig = config.Build(); - - var configurationBuilder = new ConfigurationBuilder(); - - if (Convert.ToBoolean(builtConfig["UseVault"])) - { - configurationBuilder.AddAzureKeyVault( - $"https://{builtConfig["Vault:Name"]}.vault.azure.net/", - builtConfig["Vault:ClientId"], - builtConfig["Vault:ClientSecret"]); - } - - configurationBuilder.AddEnvironmentVariables(); - - config.AddConfiguration(configurationBuilder.Build()); - }) - .ConfigureLogging((hostingContext, builder) => - { - builder.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); - builder.AddConsole(); - builder.AddDebug(); - builder.AddAzureWebAppDiagnostics(); - }) - .UseSerilog((builderContext, config) => - { - config - .MinimumLevel.Information() - .Enrich.FromLogContext() - .WriteTo.Console(); - }) .UseApplicationInsights() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseConfiguration(configuration) + .UseSerilog() .Build(); + + private static Serilog.ILogger CreateSerilogLogger(IConfiguration configuration) + { + var seqServerUrl = configuration["Serilog:SeqServerUrl"]; + + return new LoggerConfiguration() + .MinimumLevel.Verbose() + .Enrich.WithProperty("ApplicationContext", AppName) + .Enrich.FromLogContext() + .WriteTo.Console() + .WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl) + .ReadFrom.Configuration(configuration) + .CreateLogger(); + } + + private static IConfiguration GetConfiguration() + { + var builder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .AddEnvironmentVariables(); + + var config = builder.Build(); + + if (config.GetValue("UseVault", false)) + { + builder.AddAzureKeyVault( + $"https://{config["Vault:Name"]}.vault.azure.net/", + config["Vault:ClientId"], + config["Vault:ClientSecret"]); + } + + return builder.Build(); + } } } diff --git a/src/Services/Basket/Basket.API/Startup.cs b/src/Services/Basket/Basket.API/Startup.cs index 4b9d65cc7..235b787d0 100644 --- a/src/Services/Basket/Basket.API/Startup.cs +++ b/src/Services/Basket/Basket.API/Startup.cs @@ -50,7 +50,7 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API // This method gets called by the runtime. Use this method to add services to the container. public IServiceProvider ConfigureServices(IServiceCollection services) { - RegisterAppInsights(services); + RegisterAppInsights(services); // Add framework services. services.AddMvc(options => @@ -66,7 +66,7 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API services.AddCustomHealthCheck(Configuration); - services.Configure(Configuration); + services.Configure(Configuration); //By connecting here we are making sure that our service //cannot start until redis is ready. This might slow down startup, @@ -179,9 +179,10 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) + public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { - loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); + //loggerFactory.AddAzureWebAppDiagnostics(); + //loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); var pathBase = Configuration["PATH_BASE"]; if (!string.IsNullOrEmpty(pathBase)) diff --git a/src/Services/Basket/Basket.API/appsettings.json b/src/Services/Basket/Basket.API/appsettings.json index 4bff4d70d..33f1c299f 100644 --- a/src/Services/Basket/Basket.API/appsettings.json +++ b/src/Services/Basket/Basket.API/appsettings.json @@ -1,10 +1,13 @@ { - "Logging": { - "IncludeScopes": false, - "LogLevel": { - "Default": "Debug", - "System": "Information", - "Microsoft": "Information" + "Serilog": { + "SeqServerUrl": null, + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Warning", + "Microsoft.eShopOnContainers": "Information", + "System": "Warning" + } } }, "IdentityUrl": "http://localhost:5105", diff --git a/src/Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj b/src/Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj index 294d3bcc3..4d422efc8 100644 --- a/src/Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj +++ b/src/Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj @@ -17,7 +17,7 @@ - + diff --git a/src/Services/Basket/Basket.FunctionalTests/BasketScenarios.cs b/src/Services/Basket/Basket.FunctionalTests/BasketScenarios.cs index 142cc51f1..f010eac9f 100644 --- a/src/Services/Basket/Basket.FunctionalTests/BasketScenarios.cs +++ b/src/Services/Basket/Basket.FunctionalTests/BasketScenarios.cs @@ -73,7 +73,6 @@ namespace Basket.FunctionalTests string BuildCheckout() { - var checkoutBasket = new { City = "city", diff --git a/src/Services/Basket/Basket.UnitTests/Application/BasketWebApiTest.cs b/src/Services/Basket/Basket.UnitTests/Application/BasketWebApiTest.cs index a2e382b92..0045ce4aa 100644 --- a/src/Services/Basket/Basket.UnitTests/Application/BasketWebApiTest.cs +++ b/src/Services/Basket/Basket.UnitTests/Application/BasketWebApiTest.cs @@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; using Microsoft.eShopOnContainers.Services.Basket.API.Controllers; using Microsoft.eShopOnContainers.Services.Basket.API.Model; +using Microsoft.Extensions.Logging; using Moq; using System; using System.Collections.Generic; @@ -20,12 +21,14 @@ namespace UnitTest.Basket.Application private readonly Mock _basketRepositoryMock; private readonly Mock _identityServiceMock; private readonly Mock _serviceBusMock; + private readonly Mock> _loggerMock; public BasketWebApiTest() { _basketRepositoryMock = new Mock(); _identityServiceMock = new Mock(); _serviceBusMock = new Mock(); + _loggerMock = new Mock>(); } [Fact] @@ -43,7 +46,11 @@ namespace UnitTest.Basket.Application //Act var basketController = new BasketController( - _basketRepositoryMock.Object, _identityServiceMock.Object, _serviceBusMock.Object); + _loggerMock.Object, + _basketRepositoryMock.Object, + _identityServiceMock.Object, + _serviceBusMock.Object); + var actionResult = await basketController.GetBasketByIdAsync(fakeCustomerId); //Assert @@ -65,14 +72,17 @@ namespace UnitTest.Basket.Application //Act var basketController = new BasketController( - _basketRepositoryMock.Object, _identityServiceMock.Object, _serviceBusMock.Object); + _loggerMock.Object, + _basketRepositoryMock.Object, + _identityServiceMock.Object, + _serviceBusMock.Object); var actionResult = await basketController.UpdateBasketAsync(fakeCustomerBasket); //Assert Assert.Equal((actionResult.Result as OkObjectResult).StatusCode, (int)System.Net.HttpStatusCode.OK); Assert.Equal(((CustomerBasket)actionResult.Value).BuyerId, fakeCustomerId); - } + } [Fact] public async Task Doing_Checkout_Without_Basket_Should_Return_Bad_Request() @@ -84,7 +94,10 @@ namespace UnitTest.Basket.Application //Act var basketController = new BasketController( - _basketRepositoryMock.Object, _identityServiceMock.Object, _serviceBusMock.Object); + _loggerMock.Object, + _basketRepositoryMock.Object, + _identityServiceMock.Object, + _serviceBusMock.Object); var result = await basketController.CheckoutAsync(new BasketCheckout(), Guid.NewGuid().ToString()) as BadRequestResult; Assert.NotNull(result); @@ -102,7 +115,10 @@ namespace UnitTest.Basket.Application _identityServiceMock.Setup(x => x.GetUserIdentity()).Returns(fakeCustomerId); var basketController = new BasketController( - _basketRepositoryMock.Object, _identityServiceMock.Object, _serviceBusMock.Object); + _loggerMock.Object, + _basketRepositoryMock.Object, + _identityServiceMock.Object, + _serviceBusMock.Object); basketController.ControllerContext = new ControllerContext() { @@ -122,7 +138,7 @@ namespace UnitTest.Basket.Application } private CustomerBasket GetCustomerBasketFake(string fakeCustomerId) - { + { return new CustomerBasket(fakeCustomerId) { Items = new List() diff --git a/src/Services/Basket/Basket.UnitTests/Basket.UnitTests.csproj b/src/Services/Basket/Basket.UnitTests/Basket.UnitTests.csproj index d76680291..555bf8e30 100644 --- a/src/Services/Basket/Basket.UnitTests/Basket.UnitTests.csproj +++ b/src/Services/Basket/Basket.UnitTests/Basket.UnitTests.csproj @@ -7,7 +7,7 @@ - + all diff --git a/src/Services/Catalog/Catalog.API/Catalog.API.csproj b/src/Services/Catalog/Catalog.API/Catalog.API.csproj index af93035ec..dfd58311f 100644 --- a/src/Services/Catalog/Catalog.API/Catalog.API.csproj +++ b/src/Services/Catalog/Catalog.API/Catalog.API.csproj @@ -35,13 +35,13 @@ - - - + + + - - + + @@ -51,7 +51,10 @@ + + + diff --git a/src/Services/Catalog/Catalog.API/Infrastructure/CatalogContextSeed.cs b/src/Services/Catalog/Catalog.API/Infrastructure/CatalogContextSeed.cs index 5d0e38bbf..833841049 100644 --- a/src/Services/Catalog/Catalog.API/Infrastructure/CatalogContextSeed.cs +++ b/src/Services/Catalog/Catalog.API/Infrastructure/CatalogContextSeed.cs @@ -77,14 +77,14 @@ } catch (Exception ex) { - logger.LogError(ex.Message); + logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); return GetPreconfiguredCatalogBrands(); } return File.ReadAllLines(csvFileCatalogBrands) .Skip(1) // skip header row .SelectTry(x => CreateCatalogBrand(x)) - .OnCaughtException(ex => { logger.LogError(ex.Message); return null; }) + .OnCaughtException(ex => { logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); return null; }) .Where(x => x != null); } @@ -132,14 +132,14 @@ } catch (Exception ex) { - logger.LogError(ex.Message); + logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); return GetPreconfiguredCatalogTypes(); } return File.ReadAllLines(csvFileCatalogTypes) .Skip(1) // skip header row .SelectTry(x => CreateCatalogType(x)) - .OnCaughtException(ex => { logger.LogError(ex.Message); return null; }) + .OnCaughtException(ex => { logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); return null; }) .Where(x => x != null); } @@ -187,7 +187,7 @@ } catch (Exception ex) { - logger.LogError(ex.Message); + logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); return GetPreconfiguredItems(); } @@ -198,7 +198,7 @@ .Skip(1) // skip header row .Select(row => Regex.Split(row, ",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)") ) .SelectTry(column => CreateCatalogItem(column, csvheaders, catalogTypeIdLookup, catalogBrandIdLookup)) - .OnCaughtException(ex => { logger.LogError(ex.Message); return null; }) + .OnCaughtException(ex => { logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); return null; }) .Where(x => x != null); } @@ -378,7 +378,7 @@ sleepDurationProvider: retry => TimeSpan.FromSeconds(5), onRetry: (exception, timeSpan, retry, ctx) => { - logger.LogTrace($"[{prefix}] Exception {exception.GetType().Name} with message ${exception.Message} detected on attempt {retry} of {retries}"); + logger.LogWarning(exception, "[{prefix}] Exception {ExceptionType} with message {Message} detected on attempt {retry} of {retries}", prefix, exception.GetType().Name, exception.Message, retry, retries); } ); } diff --git a/src/Services/Catalog/Catalog.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs b/src/Services/Catalog/Catalog.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs index 4c4c746a6..1c1dfd45f 100644 --- a/src/Services/Catalog/Catalog.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs +++ b/src/Services/Catalog/Catalog.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs @@ -49,7 +49,7 @@ namespace Catalog.API.Infrastructure.Filters if (env.IsDevelopment()) { - json.DeveloperMeesage = context.Exception; + json.DeveloperMessage = context.Exception; } context.Result = new InternalServerErrorObjectResult(json); @@ -62,7 +62,7 @@ namespace Catalog.API.Infrastructure.Filters { public string[] Messages { get; set; } - public object DeveloperMeesage { get; set; } + public object DeveloperMessage { get; set; } } } } diff --git a/src/Services/Catalog/Catalog.API/IntegrationEvents/CatalogIntegrationEventService.cs b/src/Services/Catalog/Catalog.API/IntegrationEvents/CatalogIntegrationEventService.cs index 8c550bf27..3b9476b9f 100644 --- a/src/Services/Catalog/Catalog.API/IntegrationEvents/CatalogIntegrationEventService.cs +++ b/src/Services/Catalog/Catalog.API/IntegrationEvents/CatalogIntegrationEventService.cs @@ -4,7 +4,10 @@ using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services; using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Utilities; +using Microsoft.eShopOnContainers.Services.Catalog.API; using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; +using Microsoft.Extensions.Logging; +using Serilog.Context; using System; using System.Data.Common; using System.Threading.Tasks; @@ -17,10 +20,15 @@ namespace Catalog.API.IntegrationEvents private readonly IEventBus _eventBus; private readonly CatalogContext _catalogContext; private readonly IIntegrationEventLogService _eventLogService; + private readonly ILogger _logger; - public CatalogIntegrationEventService(IEventBus eventBus, CatalogContext catalogContext, - Func integrationEventLogServiceFactory) + public CatalogIntegrationEventService( + ILogger logger, + IEventBus eventBus, + CatalogContext catalogContext, + Func integrationEventLogServiceFactory) { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _catalogContext = catalogContext ?? throw new ArgumentNullException(nameof(catalogContext)); _integrationEventLogServiceFactory = integrationEventLogServiceFactory ?? throw new ArgumentNullException(nameof(integrationEventLogServiceFactory)); _eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus)); @@ -31,26 +39,31 @@ namespace Catalog.API.IntegrationEvents { try { + _logger.LogInformation("----- Publishing integration event: {IntegrationEventId_published} from {AppName} - ({@IntegrationEvent})", evt.Id, Program.AppName, evt); + await _eventLogService.MarkEventAsInProgressAsync(evt.Id); _eventBus.Publish(evt); await _eventLogService.MarkEventAsPublishedAsync(evt.Id); } - catch (Exception) + catch (Exception ex) { + _logger.LogError(ex, "ERROR Publishing integration event: {IntegrationEventId} from {AppName} - ({@IntegrationEvent})", evt.Id, Program.AppName, evt); await _eventLogService.MarkEventAsFailedAsync(evt.Id); - } + } } public async Task SaveEventAndCatalogContextChangesAsync(IntegrationEvent evt) { + _logger.LogInformation("----- CatalogIntegrationEventService - Saving changes and integrationEvent: {IntegrationEventId}", evt.Id); + //Use of an EF Core resiliency strategy when using multiple DbContexts within an explicit BeginTransaction(): //See: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency - await ResilientTransaction.New(_catalogContext) - .ExecuteAsync(async () => { - // Achieving atomicity between original catalog database operation and the IntegrationEventLog thanks to a local transaction - await _catalogContext.SaveChangesAsync(); - await _eventLogService.SaveEventAsync(evt, _catalogContext.Database.CurrentTransaction.GetDbTransaction()); - }); + await ResilientTransaction.New(_catalogContext).ExecuteAsync(async () => + { + // Achieving atomicity between original catalog database operation and the IntegrationEventLog thanks to a local transaction + await _catalogContext.SaveChangesAsync(); + await _eventLogService.SaveEventAsync(evt, _catalogContext.Database.CurrentTransaction.GetDbTransaction()); + }); } } } diff --git a/src/Services/Catalog/Catalog.API/IntegrationEvents/EventHandling/OrderStatusChangedToAwaitingValidationIntegrationEventHandler.cs b/src/Services/Catalog/Catalog.API/IntegrationEvents/EventHandling/OrderStatusChangedToAwaitingValidationIntegrationEventHandler.cs index 0f30a3e0a..493a271cc 100644 --- a/src/Services/Catalog/Catalog.API/IntegrationEvents/EventHandling/OrderStatusChangedToAwaitingValidationIntegrationEventHandler.cs +++ b/src/Services/Catalog/Catalog.API/IntegrationEvents/EventHandling/OrderStatusChangedToAwaitingValidationIntegrationEventHandler.cs @@ -8,39 +8,51 @@ using System.Linq; using global::Catalog.API.IntegrationEvents; using IntegrationEvents.Events; + using Serilog.Context; + using Microsoft.Extensions.Logging; public class OrderStatusChangedToAwaitingValidationIntegrationEventHandler : IIntegrationEventHandler { private readonly CatalogContext _catalogContext; private readonly ICatalogIntegrationEventService _catalogIntegrationEventService; + private readonly ILogger _logger; - public OrderStatusChangedToAwaitingValidationIntegrationEventHandler(CatalogContext catalogContext, - ICatalogIntegrationEventService catalogIntegrationEventService) + public OrderStatusChangedToAwaitingValidationIntegrationEventHandler( + CatalogContext catalogContext, + ICatalogIntegrationEventService catalogIntegrationEventService, + ILogger logger) { _catalogContext = catalogContext; _catalogIntegrationEventService = catalogIntegrationEventService; + _logger = logger ?? throw new System.ArgumentNullException(nameof(logger)); } - public async Task Handle(OrderStatusChangedToAwaitingValidationIntegrationEvent command) + public async Task Handle(OrderStatusChangedToAwaitingValidationIntegrationEvent @event) { - var confirmedOrderStockItems = new List(); - - foreach (var orderStockItem in command.OrderStockItems) + using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) { - var catalogItem = _catalogContext.CatalogItems.Find(orderStockItem.ProductId); - var hasStock = catalogItem.AvailableStock >= orderStockItem.Units; - var confirmedOrderStockItem = new ConfirmedOrderStockItem(catalogItem.Id, hasStock); + _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); - confirmedOrderStockItems.Add(confirmedOrderStockItem); - } + var confirmedOrderStockItems = new List(); + + foreach (var orderStockItem in @event.OrderStockItems) + { + var catalogItem = _catalogContext.CatalogItems.Find(orderStockItem.ProductId); + var hasStock = catalogItem.AvailableStock >= orderStockItem.Units; + var confirmedOrderStockItem = new ConfirmedOrderStockItem(catalogItem.Id, hasStock); - var confirmedIntegrationEvent = confirmedOrderStockItems.Any(c => !c.HasStock) - ? (IntegrationEvent) new OrderStockRejectedIntegrationEvent(command.OrderId, confirmedOrderStockItems) - : new OrderStockConfirmedIntegrationEvent(command.OrderId); + confirmedOrderStockItems.Add(confirmedOrderStockItem); + } - await _catalogIntegrationEventService.SaveEventAndCatalogContextChangesAsync(confirmedIntegrationEvent); - await _catalogIntegrationEventService.PublishThroughEventBusAsync(confirmedIntegrationEvent); + var confirmedIntegrationEvent = confirmedOrderStockItems.Any(c => !c.HasStock) + ? (IntegrationEvent)new OrderStockRejectedIntegrationEvent(@event.OrderId, confirmedOrderStockItems) + : new OrderStockConfirmedIntegrationEvent(@event.OrderId); + + await _catalogIntegrationEventService.SaveEventAndCatalogContextChangesAsync(confirmedIntegrationEvent); + await _catalogIntegrationEventService.PublishThroughEventBusAsync(confirmedIntegrationEvent); + + } } } } \ No newline at end of file diff --git a/src/Services/Catalog/Catalog.API/IntegrationEvents/EventHandling/OrderStatusChangedToPaidIntegrationEventHandler.cs b/src/Services/Catalog/Catalog.API/IntegrationEvents/EventHandling/OrderStatusChangedToPaidIntegrationEventHandler.cs index 0a45547f9..7d383254f 100644 --- a/src/Services/Catalog/Catalog.API/IntegrationEvents/EventHandling/OrderStatusChangedToPaidIntegrationEventHandler.cs +++ b/src/Services/Catalog/Catalog.API/IntegrationEvents/EventHandling/OrderStatusChangedToPaidIntegrationEventHandler.cs @@ -4,28 +4,40 @@ using System.Threading.Tasks; using Infrastructure; using Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events; + using Microsoft.Extensions.Logging; + using Serilog.Context; public class OrderStatusChangedToPaidIntegrationEventHandler : IIntegrationEventHandler { private readonly CatalogContext _catalogContext; + private readonly ILogger _logger; - public OrderStatusChangedToPaidIntegrationEventHandler(CatalogContext catalogContext) + public OrderStatusChangedToPaidIntegrationEventHandler( + CatalogContext catalogContext, + ILogger logger) { _catalogContext = catalogContext; + _logger = logger ?? throw new System.ArgumentNullException(nameof(logger)); } - public async Task Handle(OrderStatusChangedToPaidIntegrationEvent command) + public async Task Handle(OrderStatusChangedToPaidIntegrationEvent @event) { - //we're not blocking stock/inventory - foreach (var orderStockItem in command.OrderStockItems) + using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) { - var catalogItem = _catalogContext.CatalogItems.Find(orderStockItem.ProductId); + _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); - catalogItem.RemoveStock(orderStockItem.Units); - } + //we're not blocking stock/inventory + foreach (var orderStockItem in @event.OrderStockItems) + { + var catalogItem = _catalogContext.CatalogItems.Find(orderStockItem.ProductId); + + catalogItem.RemoveStock(orderStockItem.Units); + } - await _catalogContext.SaveChangesAsync(); + await _catalogContext.SaveChangesAsync(); + + } } } } \ No newline at end of file diff --git a/src/Services/Catalog/Catalog.API/Program.cs b/src/Services/Catalog/Catalog.API/Program.cs index c4ebb6183..c75b56c39 100644 --- a/src/Services/Catalog/Catalog.API/Program.cs +++ b/src/Services/Catalog/Catalog.API/Program.cs @@ -9,66 +9,97 @@ using Microsoft.Extensions.Options; using Serilog; using System; using System.IO; + namespace Microsoft.eShopOnContainers.Services.Catalog.API { public class Program { - public static void Main(string[] args) + public static readonly string Namespace = typeof(Program).Namespace; + public static readonly string AppName = Namespace.Substring(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1); + + public static int Main(string[] args) { - BuildWebHost(args) - .MigrateDbContext((context,services)=> + var configuration = GetConfiguration(); + + Log.Logger = CreateSerilogLogger(configuration); + + try + { + Log.Information("Configuring web host ({ApplicationContext})...", AppName); + var host = BuildWebHost(configuration, args); + + Log.Information("Applying migrations ({ApplicationContext})...", AppName); + host.MigrateDbContext((context, services) => { var env = services.GetService(); var settings = services.GetService>(); var logger = services.GetService>(); new CatalogContextSeed() - .SeedAsync(context,env,settings,logger) - .Wait(); - + .SeedAsync(context, env, settings, logger) + .Wait(); }) - .MigrateDbContext((_,__)=> { }) - .Run(); + .MigrateDbContext((_, __) => { }); + + Log.Information("Starting web host ({ApplicationContext})...", AppName); + host.Run(); + + return 0; + } + catch (Exception ex) + { + Log.Fatal(ex, "Program terminated unexpectedly ({ApplicationContext})!", AppName); + return 1; + } + finally + { + Log.CloseAndFlush(); + } } - public static IWebHost BuildWebHost(string[] args) => + private static IWebHost BuildWebHost(IConfiguration configuration, string[] args) => WebHost.CreateDefaultBuilder(args) - .UseStartup() + .CaptureStartupErrors(false) + .UseStartup() .UseApplicationInsights() .UseContentRoot(Directory.GetCurrentDirectory()) .UseWebRoot("Pics") - .ConfigureAppConfiguration((builderContext, config) => - { - var builtConfig = config.Build(); + .UseConfiguration(configuration) + .UseSerilog() + .Build(); + + private static Serilog.ILogger CreateSerilogLogger(IConfiguration configuration) + { + var seqServerUrl = configuration["Serilog:SeqServerUrl"]; - var configurationBuilder = new ConfigurationBuilder(); + return new LoggerConfiguration() + .MinimumLevel.Verbose() + .Enrich.WithProperty("ApplicationContext", AppName) + .Enrich.FromLogContext() + .WriteTo.Console() + .WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl) + .ReadFrom.Configuration(configuration) + .CreateLogger(); + } - if (Convert.ToBoolean(builtConfig["UseVault"])) - { - configurationBuilder.AddAzureKeyVault( - $"https://{builtConfig["Vault:Name"]}.vault.azure.net/", - builtConfig["Vault:ClientId"], - builtConfig["Vault:ClientSecret"]); - } + private static IConfiguration GetConfiguration() + { + var builder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .AddEnvironmentVariables(); - configurationBuilder.AddEnvironmentVariables(); + var config = builder.Build(); - config.AddConfiguration(configurationBuilder.Build()); - }) - .ConfigureLogging((hostingContext, builder) => - { - builder.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); - builder.AddConsole(); - builder.AddDebug(); - builder.AddAzureWebAppDiagnostics(); - }) - .UseSerilog((builderContext, config) => - { - config - .MinimumLevel.Information() - .Enrich.FromLogContext() - .WriteTo.Console(); - }) - .Build(); + if (config.GetValue("UseVault", false)) + { + builder.AddAzureKeyVault( + $"https://{config["Vault:Name"]}.vault.azure.net/", + config["Vault:ClientId"], + config["Vault:ClientSecret"]); + } + + return builder.Build(); + } } } \ No newline at end of file diff --git a/src/Services/Catalog/Catalog.API/Properties/launchSettings.json b/src/Services/Catalog/Catalog.API/Properties/launchSettings.json index 2b21ca280..8f2cde4db 100644 --- a/src/Services/Catalog/Catalog.API/Properties/launchSettings.json +++ b/src/Services/Catalog/Catalog.API/Properties/launchSettings.json @@ -13,7 +13,10 @@ "launchBrowser": true, "launchUrl": "/swagger", "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" + "ConnectionString": "server=localhost,5433;Database=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word", + "ASPNETCORE_ENVIRONMENT": "Development", + "EventBusConnection": "localhost", + "Serilog:SeqServerUrl": "http://locahost:5340" } }, "Microsoft.eShopOnContainers.Services.Catalog.API": { diff --git a/src/Services/Catalog/Catalog.API/Startup.cs b/src/Services/Catalog/Catalog.API/Startup.cs index 9c91a743e..0258a0a98 100644 --- a/src/Services/Catalog/Catalog.API/Startup.cs +++ b/src/Services/Catalog/Catalog.API/Startup.cs @@ -60,17 +60,18 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API } - public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) + public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { //Configure logs - loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); + //loggerFactory.AddAzureWebAppDiagnostics(); + //loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); var pathBase = Configuration["PATH_BASE"]; if (!string.IsNullOrEmpty(pathBase)) { - loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'"); + loggerFactory.CreateLogger().LogDebug("Using PATH BASE '{pathBase}'", pathBase); app.UsePathBase(pathBase); } diff --git a/src/Services/Catalog/Catalog.API/appsettings.json b/src/Services/Catalog/Catalog.API/appsettings.json index 0bf489699..b26f63bff 100644 --- a/src/Services/Catalog/Catalog.API/appsettings.json +++ b/src/Services/Catalog/Catalog.API/appsettings.json @@ -2,12 +2,15 @@ "ConnectionString": "Server=tcp:127.0.0.1,5433;Initial Catalog=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word", "PicBaseUrl": "http://localhost:5101/api/v1/catalog/items/[0]/pic/", "UseCustomizationData": false, - "Logging": { - "IncludeScopes": false, - "LogLevel": { - "Default": "Trace", - "System": "Information", - "Microsoft": "Information" + "Serilog": { + "SeqServerUrl": null, + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Warning", + "Microsoft.eShopOnContainers": "Information", + "System": "Warning" + } } }, "AzureServiceBusEnabled": false, diff --git a/src/Services/Catalog/Catalog.API/web.config b/src/Services/Catalog/Catalog.API/web.config index e04a0397b..2157aef31 100644 --- a/src/Services/Catalog/Catalog.API/web.config +++ b/src/Services/Catalog/Catalog.API/web.config @@ -2,8 +2,10 @@ - + - + + + \ No newline at end of file diff --git a/src/Services/Catalog/Catalog.FunctionalTests/Catalog.FunctionalTests.csproj b/src/Services/Catalog/Catalog.FunctionalTests/Catalog.FunctionalTests.csproj index 04aeeb8ef..4cc9f337c 100644 --- a/src/Services/Catalog/Catalog.FunctionalTests/Catalog.FunctionalTests.csproj +++ b/src/Services/Catalog/Catalog.FunctionalTests/Catalog.FunctionalTests.csproj @@ -33,7 +33,7 @@ - + diff --git a/src/Services/Catalog/Catalog.UnitTests/Catalog.UnitTests.csproj b/src/Services/Catalog/Catalog.UnitTests/Catalog.UnitTests.csproj index e69d785b1..02b12502b 100644 --- a/src/Services/Catalog/Catalog.UnitTests/Catalog.UnitTests.csproj +++ b/src/Services/Catalog/Catalog.UnitTests/Catalog.UnitTests.csproj @@ -7,7 +7,7 @@ - + all diff --git a/src/Services/Identity/Identity.API/Controllers/AccountController.cs b/src/Services/Identity/Identity.API/Controllers/AccountController.cs index 7a1fea312..6e9bbce16 100644 --- a/src/Services/Identity/Identity.API/Controllers/AccountController.cs +++ b/src/Services/Identity/Identity.API/Controllers/AccountController.cs @@ -20,7 +20,7 @@ using Microsoft.Extensions.Logging; namespace Microsoft.eShopOnContainers.Services.Identity.API.Controllers { /// - /// This sample controller implements a typical login/logout/provision workflow for local and external accounts. + /// This sample controller implements a typical login/logout/provision workflow for local accounts. /// The login service encapsulates the interactions with the user data store. This data store is in-memory only and cannot be used for production! /// The interaction service provides a way for the UI to communicate with identityserver for validation and context retrieval /// @@ -58,8 +58,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Controllers var context = await _interaction.GetAuthorizationContextAsync(returnUrl); if (context?.IdP != null) { - // if IdP is passed, then bypass showing the login screen - return ExternalLogin(context.IdP, returnUrl); + throw new NotImplementedException("External login is not implemented!"); } var vm = await BuildLoginViewModelAsync(returnUrl, context); @@ -209,7 +208,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Controllers } catch (Exception ex) { - _logger.LogCritical(ex.Message); + _logger.LogError(ex, "LOGOUT ERROR: {ExceptionMessage}", ex.Message); } } @@ -238,28 +237,6 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Controllers return Redirect(redirectUrl); } - /// - /// initiate roundtrip to external authentication provider - /// - [HttpGet] - public IActionResult ExternalLogin(string provider, string returnUrl) - { - if (returnUrl != null) - { - returnUrl = UrlEncoder.Default.Encode(returnUrl); - } - returnUrl = "/account/externallogincallback?returnUrl=" + returnUrl; - - // start challenge and roundtrip the return URL - var props = new AuthenticationProperties - { - RedirectUri = returnUrl, - Items = { { "scheme", provider } } - }; - return new ChallengeResult(provider, props); - } - - // GET: /Account/Register [HttpGet] [AllowAnonymous] diff --git a/src/Services/Identity/Identity.API/Data/ApplicationDbContextSeed.cs b/src/Services/Identity/Identity.API/Data/ApplicationDbContextSeed.cs index 3dbf28171..a1fda6455 100644 --- a/src/Services/Identity/Identity.API/Data/ApplicationDbContextSeed.cs +++ b/src/Services/Identity/Identity.API/Data/ApplicationDbContextSeed.cs @@ -51,7 +51,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Data { retryForAvaiability++; - logger.LogError(ex.Message,$"There is an error migrating data for ApplicationDbContext"); + logger.LogError(ex, "EXCEPTION ERROR while migrating {DbContextName}", nameof(ApplicationDbContext)); await SeedAsync(context,env,logger,settings, retryForAvaiability); } @@ -80,7 +80,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Data } catch (Exception ex) { - logger.LogError(ex.Message); + logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); return GetDefaultUser(); } @@ -89,7 +89,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Data .Skip(1) // skip header column .Select(row => Regex.Split(row, ",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)") ) .SelectTry(column => CreateApplicationUser(column, csvheaders)) - .OnCaughtException(ex => { logger.LogError(ex.Message); return null; }) + .OnCaughtException(ex => { logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); return null; }) .Where(x => x != null) .ToList(); @@ -199,7 +199,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Data string imagesZipFile = Path.Combine(contentRootPath, "Setup", "images.zip"); if (!File.Exists(imagesZipFile)) { - logger.LogError($" zip file '{imagesZipFile}' does not exists."); + logger.LogError("Zip file '{ZipFileName}' does not exists.", imagesZipFile); return; } @@ -221,14 +221,14 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Data } else { - logger.LogWarning($"Skip file '{entry.Name}' in zipfile '{imagesZipFile}'"); + logger.LogWarning("Skipped file '{FileName}' in zipfile '{ZipFileName}'", entry.Name, imagesZipFile); } } } } catch (Exception ex) { - logger.LogError($"Exception in method GetPreconfiguredImages WebMVC. Exception Message={ex.Message}"); + logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); ; } } } diff --git a/src/Services/Identity/Identity.API/Identity.API.csproj b/src/Services/Identity/Identity.API/Identity.API.csproj index a6dea0d4b..d9acbfa09 100644 --- a/src/Services/Identity/Identity.API/Identity.API.csproj +++ b/src/Services/Identity/Identity.API/Identity.API.csproj @@ -17,18 +17,24 @@ - - + + - + + + + + + + diff --git a/src/Services/Identity/Identity.API/Models/AccountViewModels/ExternalLoginConfirmationViewModel.cs b/src/Services/Identity/Identity.API/Models/AccountViewModels/ExternalLoginConfirmationViewModel.cs deleted file mode 100644 index 5d4f597a2..000000000 --- a/src/Services/Identity/Identity.API/Models/AccountViewModels/ExternalLoginConfirmationViewModel.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace Microsoft.eShopOnContainers.Services.Identity.API.Models.AccountViewModels -{ - public class ExternalLoginConfirmationViewModel - { - [Required] - [EmailAddress] - public string Email { get; set; } - } -} diff --git a/src/Services/Identity/Identity.API/Models/ManageViewModels/ManageLoginsViewModel.cs b/src/Services/Identity/Identity.API/Models/ManageViewModels/ManageLoginsViewModel.cs deleted file mode 100644 index 0b24c3b7c..000000000 --- a/src/Services/Identity/Identity.API/Models/ManageViewModels/ManageLoginsViewModel.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Microsoft.AspNetCore.Http.Authentication; -using Microsoft.AspNetCore.Identity; -using System.Collections.Generic; - -namespace Microsoft.eShopOnContainers.Services.Identity.API.Models.ManageViewModels -{ - public class ManageLoginsViewModel - { - public IList CurrentLogins { get; set; } - - public IList OtherLogins { get; set; } - } -} diff --git a/src/Services/Identity/Identity.API/Models/ManageViewModels/RemoveLoginViewModel.cs b/src/Services/Identity/Identity.API/Models/ManageViewModels/RemoveLoginViewModel.cs deleted file mode 100644 index c9171fcf3..000000000 --- a/src/Services/Identity/Identity.API/Models/ManageViewModels/RemoveLoginViewModel.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Microsoft.eShopOnContainers.Services.Identity.API.Models.ManageViewModels -{ - public class RemoveLoginViewModel - { - public string LoginProvider { get; set; } - public string ProviderKey { get; set; } - } -} diff --git a/src/Services/Identity/Identity.API/Program.cs b/src/Services/Identity/Identity.API/Program.cs index 401a2b8a5..8204a1ebf 100644 --- a/src/Services/Identity/Identity.API/Program.cs +++ b/src/Services/Identity/Identity.API/Program.cs @@ -14,70 +14,99 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API { public class Program { - public static void Main(string[] args) + public static readonly string Namespace = typeof(Program).Namespace; + public static readonly string AppName = Namespace.Substring(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1); + + public static int Main(string[] args) { - BuildWebHost(args) - .MigrateDbContext((_, __) => { }) - .MigrateDbContext((context, services) => - { - var env = services.GetService(); - var logger = services.GetService>(); - var settings = services.GetService>(); - - new ApplicationDbContextSeed() - .SeedAsync(context, env, logger, settings) - .Wait(); - }) - .MigrateDbContext((context,services)=> - { - var configuration = services.GetService(); - - new ConfigurationDbContextSeed() - .SeedAsync(context, configuration) - .Wait(); - }).Run(); - } + var configuration = GetConfiguration(); - public static IWebHost BuildWebHost(string[] args) => - WebHost.CreateDefaultBuilder(args) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .UseStartup() - .ConfigureAppConfiguration((builderContext, config) => - { - var builtConfig = config.Build(); + Log.Logger = CreateSerilogLogger(configuration); + + try + { + Log.Information("Configuring web host ({ApplicationContext})...", AppName); + var host = BuildWebHost(configuration, args); - var configurationBuilder = new ConfigurationBuilder(); + Log.Information("Applying migrations ({ApplicationContext})...", AppName); + host.MigrateDbContext((_, __) => { }) + .MigrateDbContext((context, services) => + { + var env = services.GetService(); + var logger = services.GetService>(); + var settings = services.GetService>(); - if (Convert.ToBoolean(builtConfig["UseVault"])) + new ApplicationDbContextSeed() + .SeedAsync(context, env, logger, settings) + .Wait(); + }) + .MigrateDbContext((context, services) => { - configurationBuilder.AddAzureKeyVault( - $"https://{builtConfig["Vault:Name"]}.vault.azure.net/", - builtConfig["Vault:ClientId"], - builtConfig["Vault:ClientSecret"]); - } - - configurationBuilder.AddEnvironmentVariables(); - - config.AddConfiguration(configurationBuilder.Build()); - }) - .ConfigureLogging((hostingContext, builder) => - { - builder.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); - builder.AddConsole(); - builder.AddDebug(); - builder.AddAzureWebAppDiagnostics(); - }) + new ConfigurationDbContextSeed() + .SeedAsync(context, configuration) + .Wait(); + }); + + Log.Information("Starting web host ({ApplicationContext})...", AppName); + host.Run(); + + return 0; + } + catch (Exception ex) + { + Log.Fatal(ex, "Program terminated unexpectedly ({ApplicationContext})!", AppName); + return 1; + } + finally + { + Log.CloseAndFlush(); + } + } + + private static IWebHost BuildWebHost(IConfiguration configuration, string[] args) => + WebHost.CreateDefaultBuilder(args) + .CaptureStartupErrors(false) + .UseStartup() .UseApplicationInsights() - .UseSerilog((builderContext, config) => - { - config - .MinimumLevel.Information() - .Enrich.FromLogContext() - .WriteTo.Console(); - }) + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseConfiguration(configuration) + .UseSerilog() .Build(); + + private static Serilog.ILogger CreateSerilogLogger(IConfiguration configuration) + { + var seqServerUrl = configuration["Serilog:SeqServerUrl"]; + + return new LoggerConfiguration() + .MinimumLevel.Verbose() + .Enrich.WithProperty("ApplicationContext", AppName) + .Enrich.FromLogContext() + .WriteTo.Console() + .WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl) + .ReadFrom.Configuration(configuration) + .CreateLogger(); + } + + private static IConfiguration GetConfiguration() + { + var builder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .AddEnvironmentVariables(); + + var config = builder.Build(); + + if (config.GetValue("UseVault", false)) + { + builder.AddAzureKeyVault( + $"https://{config["Vault:Name"]}.vault.azure.net/", + config["Vault:ClientId"], + config["Vault:ClientSecret"]); + } + + return builder.Build(); + } + } } diff --git a/src/Services/Identity/Identity.API/Startup.cs b/src/Services/Identity/Identity.API/Startup.cs index b6e0d1390..e672bab60 100644 --- a/src/Services/Identity/Identity.API/Startup.cs +++ b/src/Services/Identity/Identity.API/Startup.cs @@ -42,12 +42,12 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API // Add framework services. services.AddDbContext(options => options.UseSqlServer(Configuration["ConnectionString"], - sqlServerOptionsAction: sqlOptions => - { - sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name); - //Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency - sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); - })); + sqlServerOptionsAction: sqlOptions => + { + sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name); + //Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency + sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); + })); services.AddIdentity() .AddEntityFrameworkStores() @@ -90,22 +90,22 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API .AddConfigurationStore(options => { options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString, - sqlServerOptionsAction: sqlOptions => - { - sqlOptions.MigrationsAssembly(migrationsAssembly); - //Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency - sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); - }); + sqlServerOptionsAction: sqlOptions => + { + sqlOptions.MigrationsAssembly(migrationsAssembly); + //Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency + sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); + }); }) .AddOperationalStore(options => { options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString, - sqlServerOptionsAction: sqlOptions => - { - sqlOptions.MigrationsAssembly(migrationsAssembly); - //Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency - sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); - }); + sqlServerOptionsAction: sqlOptions => + { + sqlOptions.MigrationsAssembly(migrationsAssembly); + //Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency + sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); + }); }) .Services.AddTransient(); @@ -117,8 +117,11 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) - { - loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); + { + //loggerFactory.AddConsole(Configuration.GetSection("Logging")); + //loggerFactory.AddDebug(); + //loggerFactory.AddAzureWebAppDiagnostics(); + //loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); if (env.IsDevelopment()) { @@ -133,7 +136,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API var pathBase = Configuration["PATH_BASE"]; if (!string.IsNullOrEmpty(pathBase)) { - loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'"); + loggerFactory.CreateLogger().LogDebug("Using PATH BASE '{pathBase}'", pathBase); app.UsePathBase(pathBase); } diff --git a/src/Services/Identity/Identity.API/appsettings.json b/src/Services/Identity/Identity.API/appsettings.json index c5a109218..74d8e826c 100644 --- a/src/Services/Identity/Identity.API/appsettings.json +++ b/src/Services/Identity/Identity.API/appsettings.json @@ -5,12 +5,15 @@ "SpaClient": "http://localhost:5104", "XamarinCallback": "http://localhost:5105/xamarincallback", "UseCustomizationData": false, - "Logging": { - "IncludeScopes": false, - "LogLevel": { - "Default": "Trace", - "System": "Information", - "Microsoft": "Information" + "Serilog": { + "SeqServerUrl": null, + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Warning", + "Microsoft.eShopOnContainers": "Information", + "System": "Warning" + } } }, "ApplicationInsights": { diff --git a/src/Services/Location/Locations.API/Infrastructure/Services/LocationsService.cs b/src/Services/Location/Locations.API/Infrastructure/Services/LocationsService.cs index e9c299d23..f6b9ed708 100644 --- a/src/Services/Location/Locations.API/Infrastructure/Services/LocationsService.cs +++ b/src/Services/Location/Locations.API/Infrastructure/Services/LocationsService.cs @@ -6,6 +6,7 @@ using Microsoft.eShopOnContainers.Services.Locations.API.IntegrationEvents.Events; using Microsoft.eShopOnContainers.Services.Locations.API.Model; using Microsoft.eShopOnContainers.Services.Locations.API.ViewModel; + using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -14,11 +15,16 @@ { private readonly ILocationsRepository _locationsRepository; private readonly IEventBus _eventBus; + private readonly ILogger _logger; - public LocationsService(ILocationsRepository locationsRepository, IEventBus eventBus) + public LocationsService( + ILocationsRepository locationsRepository, + IEventBus eventBus, + ILogger logger) { _locationsRepository = locationsRepository ?? throw new ArgumentNullException(nameof(locationsRepository)); _eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public async Task GetLocationAsync(int locationId) @@ -37,11 +43,11 @@ } public async Task AddOrUpdateUserLocationAsync(string userId, LocationRequest currentPosition) - { + { // Get the list of ordered regions the user currently is within var currentUserAreaLocationList = await _locationsRepository.GetCurrentUserRegionsListAsync(currentPosition); - - if(currentUserAreaLocationList is null) + + if (currentUserAreaLocationList is null) { throw new LocationDomainException("User current area not found"); } @@ -66,13 +72,17 @@ { var newUserLocations = MapUserLocationDetails(newLocations); var @event = new UserLocationUpdatedIntegrationEvent(userId, newUserLocations); + + _logger.LogInformation("----- Publishing integration event: {IntegrationEventId} from {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); + _eventBus.Publish(@event); } private List MapUserLocationDetails(List newLocations) { var result = new List(); - newLocations.ForEach(location => { + newLocations.ForEach(location => + { result.Add(new UserLocationDetails() { LocationId = location.LocationId, diff --git a/src/Services/Location/Locations.API/Locations.API.csproj b/src/Services/Location/Locations.API/Locations.API.csproj index f7f0ff3c8..0233fbcbf 100644 --- a/src/Services/Location/Locations.API/Locations.API.csproj +++ b/src/Services/Location/Locations.API/Locations.API.csproj @@ -7,12 +7,12 @@ - - - - - + + + + + @@ -25,7 +25,10 @@ + + + diff --git a/src/Services/Location/Locations.API/Program.cs b/src/Services/Location/Locations.API/Program.cs index 11be8d68c..c13df985f 100644 --- a/src/Services/Location/Locations.API/Program.cs +++ b/src/Services/Location/Locations.API/Program.cs @@ -11,48 +11,78 @@ namespace Microsoft.eShopOnContainers.Services.Locations.API { public class Program { - public static void Main(string[] args) + public static readonly string Namespace = typeof(Program).Namespace; + public static readonly string AppName = Namespace.Substring(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1); + + public static int Main(string[] args) { - BuildWebHost(args).Run(); + var configuration = GetConfiguration(); + + Log.Logger = CreateSerilogLogger(configuration); + + try + { + Log.Information("Configuring web host ({ApplicationContext})...", AppName); + var host = BuildWebHost(configuration, args); + + Log.Information("Starting web host ({ApplicationContext})...", AppName); + host.Run(); + + return 0; + } + catch (Exception ex) + { + Log.Fatal(ex, "Program terminated unexpectedly ({ApplicationContext})!", AppName); + return 1; + } + finally + { + Log.CloseAndFlush(); + } } - public static IWebHost BuildWebHost(string[] args) => + private static IWebHost BuildWebHost(IConfiguration configuration, string[] args) => WebHost.CreateDefaultBuilder(args) - .UseContentRoot(Directory.GetCurrentDirectory()) + .CaptureStartupErrors(false) .UseStartup() - .ConfigureAppConfiguration((builderContext, config) => - { - var builtConfig = config.Build(); - - var configurationBuilder = new ConfigurationBuilder(); - - if (Convert.ToBoolean(builtConfig["UseVault"])) - { - configurationBuilder.AddAzureKeyVault( - $"https://{builtConfig["Vault:Name"]}.vault.azure.net/", - builtConfig["Vault:ClientId"], - builtConfig["Vault:ClientSecret"]); - } - - configurationBuilder.AddEnvironmentVariables(); - - config.AddConfiguration(configurationBuilder.Build()); - }) - .ConfigureLogging((hostingContext, builder) => - { - builder.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); - builder.AddConsole(); - builder.AddDebug(); - builder.AddAzureWebAppDiagnostics(); - }) .UseApplicationInsights() - .UseSerilog((builderContext, config) => - { - config - .MinimumLevel.Information() - .Enrich.FromLogContext() - .WriteTo.Console(); - }) + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseConfiguration(configuration) + .UseSerilog() .Build(); + + private static Serilog.ILogger CreateSerilogLogger(IConfiguration configuration) + { + var seqServerUrl = configuration["Serilog:SeqServerUrl"]; + + return new LoggerConfiguration() + .MinimumLevel.Verbose() + .Enrich.WithProperty("ApplicationContext", AppName) + .Enrich.FromLogContext() + .WriteTo.Console() + .WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl) + .ReadFrom.Configuration(configuration) + .CreateLogger(); + } + + private static IConfiguration GetConfiguration() + { + var builder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .AddEnvironmentVariables(); + + var config = builder.Build(); + + if (config.GetValue("UseVault", false)) + { + builder.AddAzureKeyVault( + $"https://{config["Vault:Name"]}.vault.azure.net/", + config["Vault:ClientId"], + config["Vault:ClientSecret"]); + } + + return builder.Build(); + } } -} +} \ No newline at end of file diff --git a/src/Services/Location/Locations.API/Startup.cs b/src/Services/Location/Locations.API/Startup.cs index d18769fcc..5ef9d195d 100644 --- a/src/Services/Location/Locations.API/Startup.cs +++ b/src/Services/Location/Locations.API/Startup.cs @@ -153,9 +153,10 @@ namespace Microsoft.eShopOnContainers.Services.Locations.API } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) + public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { - loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); + //loggerFactory.AddAzureWebAppDiagnostics(); + //loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); var pathBase = Configuration["PATH_BASE"]; if (!string.IsNullOrEmpty(pathBase)) diff --git a/src/Services/Location/Locations.API/appsettings.json b/src/Services/Location/Locations.API/appsettings.json index cd4166bb0..43fe53873 100644 --- a/src/Services/Location/Locations.API/appsettings.json +++ b/src/Services/Location/Locations.API/appsettings.json @@ -2,12 +2,15 @@ "ConnectionString": "mongodb://nosql.data", "Database": "LocationsDb", "IdentityUrl": "http://localhost:5105", - "Logging": { - "IncludeScopes": false, - "LogLevel": { - "Default": "Trace", - "System": "Information", - "Microsoft": "Information" + "Serilog": { + "SeqServerUrl": null, + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Warning", + "Microsoft.eShopOnContainers": "Information", + "System": "Warning" + } } }, "AzureServiceBusEnabled": false, diff --git a/src/Services/Location/Locations.FunctionalTests/Locations.FunctionalTests.csproj b/src/Services/Location/Locations.FunctionalTests/Locations.FunctionalTests.csproj index a1ad058cb..5de852a32 100644 --- a/src/Services/Location/Locations.FunctionalTests/Locations.FunctionalTests.csproj +++ b/src/Services/Location/Locations.FunctionalTests/Locations.FunctionalTests.csproj @@ -17,7 +17,7 @@ - + diff --git a/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContextSeed.cs b/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContextSeed.cs index 8d7091804..ac800909e 100644 --- a/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContextSeed.cs +++ b/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContextSeed.cs @@ -77,7 +77,7 @@ sleepDurationProvider: retry => TimeSpan.FromSeconds(5), onRetry: (exception, timeSpan, retry, ctx) => { - logger.LogTrace($"[{prefix}] Exception {exception.GetType().Name} with message ${exception.Message} detected on attempt {retry} of {retries}"); + logger.LogWarning(exception, "[{prefix}] Exception {ExceptionType} with message {Message} detected on attempt {retry} of {retries}", prefix, exception.GetType().Name, exception.Message, retry, retries); } ); } diff --git a/src/Services/Marketing/Marketing.API/IntegrationEvents/Handlers/UserLocationUpdatedIntegrationEventHandler.cs b/src/Services/Marketing/Marketing.API/IntegrationEvents/Handlers/UserLocationUpdatedIntegrationEventHandler.cs index 77574c0e5..3d5e62e45 100644 --- a/src/Services/Marketing/Marketing.API/IntegrationEvents/Handlers/UserLocationUpdatedIntegrationEventHandler.cs +++ b/src/Services/Marketing/Marketing.API/IntegrationEvents/Handlers/UserLocationUpdatedIntegrationEventHandler.cs @@ -4,6 +4,8 @@ using Marketing.API.Model; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; using Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.Repositories; + using Microsoft.Extensions.Logging; + using Serilog.Context; using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -12,20 +14,29 @@ : IIntegrationEventHandler { private readonly IMarketingDataRepository _marketingDataRepository; + private readonly ILogger _logger; - public UserLocationUpdatedIntegrationEventHandler(IMarketingDataRepository repository) + public UserLocationUpdatedIntegrationEventHandler( + IMarketingDataRepository repository, + ILogger logger) { _marketingDataRepository = repository ?? throw new ArgumentNullException(nameof(repository)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public async Task Handle(UserLocationUpdatedIntegrationEvent @event) { - var userMarketingData = await _marketingDataRepository.GetAsync(@event.UserId); - userMarketingData = userMarketingData ?? - new MarketingData() { UserId = @event.UserId }; + using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) + { + _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); - userMarketingData.Locations = MapUpdatedUserLocations(@event.LocationList); - await _marketingDataRepository.UpdateLocationAsync(userMarketingData); + var userMarketingData = await _marketingDataRepository.GetAsync(@event.UserId); + userMarketingData = userMarketingData ?? + new MarketingData() { UserId = @event.UserId }; + + userMarketingData.Locations = MapUpdatedUserLocations(@event.LocationList); + await _marketingDataRepository.UpdateLocationAsync(userMarketingData); + } } private List MapUpdatedUserLocations(List newUserLocations) diff --git a/src/Services/Marketing/Marketing.API/Marketing.API.csproj b/src/Services/Marketing/Marketing.API/Marketing.API.csproj index 8efd65ef2..f9aff6e38 100644 --- a/src/Services/Marketing/Marketing.API/Marketing.API.csproj +++ b/src/Services/Marketing/Marketing.API/Marketing.API.csproj @@ -20,15 +20,15 @@ - + - - - + + + - - + + @@ -41,7 +41,10 @@ + + + diff --git a/src/Services/Marketing/Marketing.API/Program.cs b/src/Services/Marketing/Marketing.API/Program.cs index cca7f4d79..ae26c9786 100644 --- a/src/Services/Marketing/Marketing.API/Program.cs +++ b/src/Services/Marketing/Marketing.API/Program.cs @@ -12,59 +12,89 @@ public class Program { - public static void Main(string[] args) + public static readonly string Namespace = typeof(Program).Namespace; + public static readonly string AppName = Namespace.Substring(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1); + + public static int Main(string[] args) { - BuildWebHost(args) - .MigrateDbContext((context, services) => + var configuration = GetConfiguration(); + + Log.Logger = CreateSerilogLogger(configuration); + + try { - var logger = services.GetService>(); + Log.Information("Configuring web host ({ApplicationContext})...", AppName); + var host = BuildWebHost(configuration, args); + + Log.Information("Applying migrations ({ApplicationContext})...", AppName); + host.MigrateDbContext((context, services) => + { + var logger = services.GetService>(); + + new MarketingContextSeed() + .SeedAsync(context, logger) + .Wait(); + }); - new MarketingContextSeed() - .SeedAsync(context,logger) - .Wait(); + Log.Information("Starting web host ({ApplicationContext})...", AppName); + host.Run(); - }).Run(); + return 0; + } + catch (Exception ex) + { + Log.Fatal(ex, "Program terminated unexpectedly ({ApplicationContext})!", AppName); + return 1; + } + finally + { + Log.CloseAndFlush(); + } } - public static IWebHost BuildWebHost(string[] args) => + private static IWebHost BuildWebHost(IConfiguration configuration, string[] args) => WebHost.CreateDefaultBuilder(args) + .CaptureStartupErrors(false) + .UseStartup() .UseApplicationInsights() .UseContentRoot(Directory.GetCurrentDirectory()) - .UseStartup() .UseWebRoot("Pics") - .ConfigureAppConfiguration((builderContext, config) => - { - var builtConfig = config.Build(); + .UseConfiguration(configuration) + .UseSerilog() + .Build(); - var configurationBuilder = new ConfigurationBuilder(); + private static Serilog.ILogger CreateSerilogLogger(IConfiguration configuration) + { + var seqServerUrl = configuration["Serilog:SeqServerUrl"]; - if (Convert.ToBoolean(builtConfig["UseVault"])) - { - configurationBuilder.AddAzureKeyVault( - $"https://{builtConfig["Vault:Name"]}.vault.azure.net/", - builtConfig["Vault:ClientId"], - builtConfig["Vault:ClientSecret"]); - } + return new LoggerConfiguration() + .MinimumLevel.Verbose() + .Enrich.WithProperty("ApplicationContext", AppName) + .Enrich.FromLogContext() + .WriteTo.Console() + .WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl) + .ReadFrom.Configuration(configuration) + .CreateLogger(); + } - configurationBuilder.AddEnvironmentVariables(); + private static IConfiguration GetConfiguration() + { + var builder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .AddEnvironmentVariables(); - config.AddConfiguration(configurationBuilder.Build()); - }) - .ConfigureLogging((hostingContext, builder) => - { - builder.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); - builder.AddConsole(); - builder.AddDebug(); - builder.AddAzureWebAppDiagnostics(); - }) - .UseApplicationInsights() - .UseSerilog((builderContext, config) => - { - config - .MinimumLevel.Information() - .Enrich.FromLogContext() - .WriteTo.Console(); - }) - .Build(); + var config = builder.Build(); + + if (config.GetValue("UseVault", false)) + { + builder.AddAzureKeyVault( + $"https://{config["Vault:Name"]}.vault.azure.net/", + config["Vault:ClientId"], + config["Vault:ClientSecret"]); + } + + return builder.Build(); + } } } diff --git a/src/Services/Marketing/Marketing.API/Startup.cs b/src/Services/Marketing/Marketing.API/Startup.cs index 276a82fa9..75c749452 100644 --- a/src/Services/Marketing/Marketing.API/Startup.cs +++ b/src/Services/Marketing/Marketing.API/Startup.cs @@ -179,7 +179,8 @@ // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env,ILoggerFactory loggerFactory) { - loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); + //loggerFactory.AddAzureWebAppDiagnostics(); + //loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); var pathBase = Configuration["PATH_BASE"]; diff --git a/src/Services/Marketing/Marketing.API/appsettings.json b/src/Services/Marketing/Marketing.API/appsettings.json index 2af660446..c16978dce 100644 --- a/src/Services/Marketing/Marketing.API/appsettings.json +++ b/src/Services/Marketing/Marketing.API/appsettings.json @@ -1,8 +1,13 @@ { - "Logging": { - "IncludeScopes": false, - "LogLevel": { - "Default": "Trace" + "Serilog": { + "SeqServerUrl": null, + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Warning", + "Microsoft.eShopOnContainers": "Information", + "System": "Warning" + } } }, "ConnectionString": "Server=tcp:127.0.0.1,5433;Initial Catalog=Microsoft.eShopOnContainers.Services.MarketingDb;User Id=sa;Password=Pass@word", diff --git a/src/Services/Marketing/Marketing.FunctionalTests/Marketing.FunctionalTests.csproj b/src/Services/Marketing/Marketing.FunctionalTests/Marketing.FunctionalTests.csproj index 743f2fb1d..13f91249a 100644 --- a/src/Services/Marketing/Marketing.FunctionalTests/Marketing.FunctionalTests.csproj +++ b/src/Services/Marketing/Marketing.FunctionalTests/Marketing.FunctionalTests.csproj @@ -17,7 +17,7 @@ - + diff --git a/src/Services/Ordering/Ordering.API/Application/Behaviors/LoggingBehavior.cs b/src/Services/Ordering/Ordering.API/Application/Behaviors/LoggingBehavior.cs index 2708db8a9..35023b297 100644 --- a/src/Services/Ordering/Ordering.API/Application/Behaviors/LoggingBehavior.cs +++ b/src/Services/Ordering/Ordering.API/Application/Behaviors/LoggingBehavior.cs @@ -1,9 +1,11 @@ using MediatR; using Microsoft.Extensions.Logging; +using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; -namespace Ordering.API.Infrastructure.Behaviors +namespace Ordering.API.Application.Behaviors { public class LoggingBehavior : IPipelineBehavior { @@ -12,9 +14,10 @@ namespace Ordering.API.Infrastructure.Behaviors public async Task Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate next) { - _logger.LogInformation($"Handling {typeof(TRequest).Name}"); + _logger.LogInformation("----- Handling command {CommandName} ({@Command})", request.GetGenericTypeName(), request); var response = await next(); - _logger.LogInformation($"Handled {typeof(TResponse).Name}"); + _logger.LogInformation("----- Command {CommandName} handled - response: {@Response}", request.GetGenericTypeName(), response); + return response; } } diff --git a/src/Services/Ordering/Ordering.API/Application/Behaviors/TransactionBehaviour.cs b/src/Services/Ordering/Ordering.API/Application/Behaviors/TransactionBehaviour.cs index 6f9aed9e5..d9d3e0b0a 100644 --- a/src/Services/Ordering/Ordering.API/Application/Behaviors/TransactionBehaviour.cs +++ b/src/Services/Ordering/Ordering.API/Application/Behaviors/TransactionBehaviour.cs @@ -1,11 +1,11 @@ using MediatR; using Microsoft.EntityFrameworkCore; +using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure; using Microsoft.Extensions.Logging; using Ordering.API.Application.IntegrationEvents; +using Serilog.Context; using System; -using System.Collections.Generic; -using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -28,33 +28,41 @@ namespace Ordering.API.Application.Behaviors public async Task Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate next) { - TResponse response = default(TResponse); + var response = default(TResponse); + var typeName = request.GetGenericTypeName(); try { - var strategy = _dbContext.Database.CreateExecutionStrategy(); - await strategy.ExecuteAsync(async () => + if (_dbContext.HasActiveTransaction) { - _logger.LogInformation($"Begin transaction {typeof(TRequest).Name}"); + return await next(); + } + + var strategy = _dbContext.Database.CreateExecutionStrategy(); - await _dbContext.BeginTransactionAsync(); + await strategy.ExecuteAsync(async () => + { + using (var transaction = await _dbContext.BeginTransactionAsync()) + using (LogContext.PushProperty("TransactionContext", transaction.TransactionId)) + { + _logger.LogInformation("----- Begin transaction {TransactionId} for {CommandName} ({@Command})", transaction.TransactionId, typeName, request); - response = await next(); + response = await next(); - await _dbContext.CommitTransactionAsync(); + _logger.LogInformation("----- Commit transaction {TransactionId} for {CommandName}", transaction.TransactionId, typeName); - _logger.LogInformation($"Committed transaction {typeof(TRequest).Name}"); + await _dbContext.CommitTransactionAsync(transaction); + } await _orderingIntegrationEventService.PublishEventsThroughEventBusAsync(); }); return response; } - catch (Exception) + catch (Exception ex) { - _logger.LogInformation($"Rollback transaction executed {typeof(TRequest).Name}"); + _logger.LogError(ex, "ERROR Handling transaction for {CommandName} ({@Command})", typeName, request); - _dbContext.RollbackTransaction(); throw; } } diff --git a/src/Services/Ordering/Ordering.API/Application/Behaviors/ValidatorBehavior.cs b/src/Services/Ordering/Ordering.API/Application/Behaviors/ValidatorBehavior.cs index de0a2ba4b..6fc12258b 100644 --- a/src/Services/Ordering/Ordering.API/Application/Behaviors/ValidatorBehavior.cs +++ b/src/Services/Ordering/Ordering.API/Application/Behaviors/ValidatorBehavior.cs @@ -1,20 +1,31 @@ using FluentValidation; using MediatR; +using Microsoft.Extensions.Logging; using Ordering.Domain.Exceptions; -using System; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; -namespace Ordering.API.Infrastructure.Behaviors +namespace Ordering.API.Application.Behaviors { public class ValidatorBehavior : IPipelineBehavior { + private readonly ILogger> _logger; private readonly IValidator[] _validators; - public ValidatorBehavior(IValidator[] validators) => _validators = validators; + + public ValidatorBehavior(IValidator[] validators, ILogger> logger) + { + _validators = validators; + _logger = logger; + } public async Task Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate next) { + var typeName = request.GetGenericTypeName(); + + _logger.LogInformation("----- Validating command {CommandType}", typeName); + var failures = _validators .Select(v => v.Validate(request)) .SelectMany(result => result.Errors) @@ -23,12 +34,13 @@ namespace Ordering.API.Infrastructure.Behaviors if (failures.Any()) { + _logger.LogWarning("Validation errors - {CommandType} - Command: {@Command} - Errors: {@ValidationErrors}", typeName, request, failures); + throw new OrderingDomainException( $"Command Validation Errors for type {typeof(TRequest).Name}", new ValidationException("Validation exception", failures)); } - var response = await next(); - return response; + return await next(); } } -} +} \ No newline at end of file diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/CancelOrderCommandHandler.cs b/src/Services/Ordering/Ordering.API/Application/Commands/CancelOrderCommandHandler.cs index a4c4facb8..85858486c 100644 --- a/src/Services/Ordering/Ordering.API/Application/Commands/CancelOrderCommandHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/Commands/CancelOrderCommandHandler.cs @@ -2,6 +2,7 @@ using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; +using Microsoft.Extensions.Logging; using System.Threading; using System.Threading.Tasks; @@ -40,7 +41,11 @@ namespace Ordering.API.Application.Commands // Use for Idempotency in Command process public class CancelOrderIdentifiedCommandHandler : IdentifiedCommandHandler { - public CancelOrderIdentifiedCommandHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager) + public CancelOrderIdentifiedCommandHandler( + IMediator mediator, + IRequestManager requestManager, + ILogger> logger) + : base(mediator, requestManager, logger) { } diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommandHandler.cs b/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommandHandler.cs index 9a3035d5c..00a088c09 100644 --- a/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommandHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommandHandler.cs @@ -6,6 +6,7 @@ using MediatR; using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services; using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; + using Microsoft.Extensions.Logging; using System; using System.Threading; using System.Threading.Tasks; @@ -18,17 +19,20 @@ private readonly IIdentityService _identityService; private readonly IMediator _mediator; private readonly IOrderingIntegrationEventService _orderingIntegrationEventService; + private readonly ILogger _logger; // Using DI to inject infrastructure persistence Repositories public CreateOrderCommandHandler(IMediator mediator, IOrderingIntegrationEventService orderingIntegrationEventService, - IOrderRepository orderRepository, - IIdentityService identityService) + IOrderRepository orderRepository, + IIdentityService identityService, + ILogger logger) { _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); _identityService = identityService ?? throw new ArgumentNullException(nameof(identityService)); _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); _orderingIntegrationEventService = orderingIntegrationEventService ?? throw new ArgumentNullException(nameof(orderingIntegrationEventService)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public async Task Handle(CreateOrderCommand message, CancellationToken cancellationToken) @@ -36,20 +40,22 @@ // Add Integration event to clean the basket var orderStartedIntegrationEvent = new OrderStartedIntegrationEvent(message.UserId); await _orderingIntegrationEventService.AddAndSaveEventAsync(orderStartedIntegrationEvent); - + // Add/Update the Buyer AggregateRoot // DDD patterns comment: Add child entities and value-objects through the Order Aggregate-Root // methods and constructor so validations, invariants and business logic // make sure that consistency is preserved across the whole aggregate var address = new Address(message.Street, message.City, message.State, message.Country, message.ZipCode); var order = new Order(message.UserId, message.UserName, address, message.CardTypeId, message.CardNumber, message.CardSecurityNumber, message.CardHolderName, message.CardExpiration); - + foreach (var item in message.OrderItems) { order.AddOrderItem(item.ProductId, item.ProductName, item.UnitPrice, item.Discount, item.PictureUrl, item.Units); } - _orderRepository.Add(order); + _logger.LogInformation("----- Creating Order - Order: {@Order}", order); + + _orderRepository.Add(order); return await _orderRepository.UnitOfWork .SaveEntitiesAsync(); @@ -60,7 +66,11 @@ // Use for Idempotency in Command process public class CreateOrderIdentifiedCommandHandler : IdentifiedCommandHandler { - public CreateOrderIdentifiedCommandHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager) + public CreateOrderIdentifiedCommandHandler( + IMediator mediator, + IRequestManager requestManager, + ILogger> logger) + : base(mediator, requestManager, logger) { } diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/IdentifiedCommandHandler.cs b/src/Services/Ordering/Ordering.API/Application/Commands/IdentifiedCommandHandler.cs index f00ea44c8..e389e3975 100644 --- a/src/Services/Ordering/Ordering.API/Application/Commands/IdentifiedCommandHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/Commands/IdentifiedCommandHandler.cs @@ -1,5 +1,10 @@ using MediatR; +using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; +using Microsoft.Extensions.Logging; +using Ordering.API.Application.Behaviors; +using Ordering.API.Application.Commands; +using System; using System.Threading; using System.Threading.Tasks; @@ -16,11 +21,16 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands { private readonly IMediator _mediator; private readonly IRequestManager _requestManager; + private readonly ILogger> _logger; - public IdentifiedCommandHandler(IMediator mediator, IRequestManager requestManager) + public IdentifiedCommandHandler( + IMediator mediator, + IRequestManager requestManager, + ILogger> logger) { _mediator = mediator; _requestManager = requestManager; + _logger = logger ?? throw new System.ArgumentNullException(nameof(logger)); } /// @@ -48,16 +58,60 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands else { await _requestManager.CreateRequestForCommandAsync(message.Id); - try - { - // Send the embeded business command to mediator so it runs its related CommandHandler - var result = await _mediator.Send(message.Command); - return result; - } - catch - { - return default(R); - } + try + { + var command = message.Command; + var commandName = command.GetGenericTypeName(); + var idProperty = string.Empty; + var commandId = string.Empty; + + switch (command) + { + case CreateOrderCommand createOrderCommand: + idProperty = nameof(createOrderCommand.UserId); + commandId = createOrderCommand.UserId; + break; + + case CancelOrderCommand cancelOrderCommand: + idProperty = nameof(cancelOrderCommand.OrderNumber); + commandId = $"{cancelOrderCommand.OrderNumber}"; + break; + + case ShipOrderCommand shipOrderCommand: + idProperty = nameof(shipOrderCommand.OrderNumber); + commandId = $"{shipOrderCommand.OrderNumber}"; + break; + + default: + idProperty = "Id?"; + commandId = "n/a"; + break; + } + + _logger.LogInformation( + "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", + commandName, + idProperty, + commandId, + command); + + // Send the embeded business command to mediator so it runs its related CommandHandler + var result = await _mediator.Send(command); + + _logger.LogInformation( + "----- Command result: {@Result} - {CommandName} - {IdProperty}: {CommandId} ({@Command})", + result, + commandName, + idProperty, + commandId, + command); + + return result; + } + catch + { + return default(R); + } } } } diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/SetAwaitingValidationOrderStatusCommandHandler.cs b/src/Services/Ordering/Ordering.API/Application/Commands/SetAwaitingValidationOrderStatusCommandHandler.cs index cee307ca2..c40ddb37f 100644 --- a/src/Services/Ordering/Ordering.API/Application/Commands/SetAwaitingValidationOrderStatusCommandHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/Commands/SetAwaitingValidationOrderStatusCommandHandler.cs @@ -2,6 +2,7 @@ using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; +using Microsoft.Extensions.Logging; using System.Threading; using System.Threading.Tasks; @@ -40,7 +41,11 @@ namespace Ordering.API.Application.Commands // Use for Idempotency in Command process public class SetAwaitingValidationIdentifiedOrderStatusCommandHandler : IdentifiedCommandHandler { - public SetAwaitingValidationIdentifiedOrderStatusCommandHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager) + public SetAwaitingValidationIdentifiedOrderStatusCommandHandler( + IMediator mediator, + IRequestManager requestManager, + ILogger> logger) + : base(mediator, requestManager, logger) { } diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/SetPaidOrderStatusCommandHandler.cs b/src/Services/Ordering/Ordering.API/Application/Commands/SetPaidOrderStatusCommandHandler.cs index 211e568cb..8282bf6ab 100644 --- a/src/Services/Ordering/Ordering.API/Application/Commands/SetPaidOrderStatusCommandHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/Commands/SetPaidOrderStatusCommandHandler.cs @@ -2,6 +2,7 @@ using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; +using Microsoft.Extensions.Logging; using System.Threading; using System.Threading.Tasks; @@ -43,7 +44,11 @@ namespace Ordering.API.Application.Commands // Use for Idempotency in Command process public class SetPaidIdentifiedOrderStatusCommandHandler : IdentifiedCommandHandler { - public SetPaidIdentifiedOrderStatusCommandHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager) + public SetPaidIdentifiedOrderStatusCommandHandler( + IMediator mediator, + IRequestManager requestManager, + ILogger> logger) + : base(mediator, requestManager, logger) { } diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/SetStockConfirmedOrderStatusCommandHandler.cs b/src/Services/Ordering/Ordering.API/Application/Commands/SetStockConfirmedOrderStatusCommandHandler.cs index 4e1bc6185..e9c3eb75c 100644 --- a/src/Services/Ordering/Ordering.API/Application/Commands/SetStockConfirmedOrderStatusCommandHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/Commands/SetStockConfirmedOrderStatusCommandHandler.cs @@ -2,6 +2,7 @@ using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; +using Microsoft.Extensions.Logging; using System.Threading; using System.Threading.Tasks; @@ -43,7 +44,11 @@ namespace Ordering.API.Application.Commands // Use for Idempotency in Command process public class SetStockConfirmedOrderStatusIdenfifiedCommandHandler : IdentifiedCommandHandler { - public SetStockConfirmedOrderStatusIdenfifiedCommandHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager) + public SetStockConfirmedOrderStatusIdenfifiedCommandHandler( + IMediator mediator, + IRequestManager requestManager, + ILogger> logger) + : base(mediator, requestManager, logger) { } diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/SetStockRejectedOrderStatusCommandHandler.cs b/src/Services/Ordering/Ordering.API/Application/Commands/SetStockRejectedOrderStatusCommandHandler.cs index 2b7a72526..2e6c794d2 100644 --- a/src/Services/Ordering/Ordering.API/Application/Commands/SetStockRejectedOrderStatusCommandHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/Commands/SetStockRejectedOrderStatusCommandHandler.cs @@ -2,6 +2,7 @@ using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; +using Microsoft.Extensions.Logging; using System.Threading; using System.Threading.Tasks; @@ -44,7 +45,11 @@ namespace Ordering.API.Application.Commands // Use for Idempotency in Command process public class SetStockRejectedOrderStatusIdenfifiedCommandHandler : IdentifiedCommandHandler { - public SetStockRejectedOrderStatusIdenfifiedCommandHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager) + public SetStockRejectedOrderStatusIdenfifiedCommandHandler( + IMediator mediator, + IRequestManager requestManager, + ILogger> logger) + : base(mediator, requestManager, logger) { } diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/ShipOrderCommandHandler.cs b/src/Services/Ordering/Ordering.API/Application/Commands/ShipOrderCommandHandler.cs index b4a6ac26d..fcac86ef9 100644 --- a/src/Services/Ordering/Ordering.API/Application/Commands/ShipOrderCommandHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/Commands/ShipOrderCommandHandler.cs @@ -2,6 +2,7 @@ using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; +using Microsoft.Extensions.Logging; using System.Threading; using System.Threading.Tasks; @@ -9,7 +10,7 @@ namespace Ordering.API.Application.Commands { // Regular CommandHandler public class ShipOrderCommandHandler : IRequestHandler - { + { private readonly IOrderRepository _orderRepository; public ShipOrderCommandHandler(IOrderRepository orderRepository) @@ -26,7 +27,7 @@ namespace Ordering.API.Application.Commands public async Task Handle(ShipOrderCommand command, CancellationToken cancellationToken) { var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber); - if(orderToUpdate == null) + if (orderToUpdate == null) { return false; } @@ -40,7 +41,11 @@ namespace Ordering.API.Application.Commands // Use for Idempotency in Command process public class ShipOrderIdentifiedCommandHandler : IdentifiedCommandHandler { - public ShipOrderIdentifiedCommandHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager) + public ShipOrderIdentifiedCommandHandler( + IMediator mediator, + IRequestManager requestManager, + ILogger> logger) + : base(mediator, requestManager, logger) { } diff --git a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/BuyerAndPaymentMethodVerified/UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler.cs b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/BuyerAndPaymentMethodVerified/UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler.cs index d738c07ee..bea8eaac5 100644 --- a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/BuyerAndPaymentMethodVerified/UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/BuyerAndPaymentMethodVerified/UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler.cs @@ -8,16 +8,16 @@ using System.Threading.Tasks; namespace Ordering.API.Application.DomainEventHandlers.BuyerAndPaymentMethodVerified { - public class UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler + public class UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler : INotificationHandler { - private readonly IOrderRepository _orderRepository; - private readonly ILoggerFactory _logger; + private readonly IOrderRepository _orderRepository; + private readonly ILoggerFactory _logger; public UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler( - IOrderRepository orderRepository, ILoggerFactory logger) + IOrderRepository orderRepository, ILoggerFactory logger) { - _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); + _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } @@ -28,10 +28,11 @@ namespace Ordering.API.Application.DomainEventHandlers.BuyerAndPaymentMethodVeri { var orderToUpdate = await _orderRepository.GetAsync(buyerPaymentMethodVerifiedEvent.OrderId); orderToUpdate.SetBuyerId(buyerPaymentMethodVerifiedEvent.Buyer.Id); - orderToUpdate.SetPaymentId(buyerPaymentMethodVerifiedEvent.Payment.Id); + orderToUpdate.SetPaymentId(buyerPaymentMethodVerifiedEvent.Payment.Id); - _logger.CreateLogger(nameof(UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler)) - .LogTrace($"Order with Id: {buyerPaymentMethodVerifiedEvent.OrderId} has been successfully updated with a payment method id: { buyerPaymentMethodVerifiedEvent.Payment.Id }"); + _logger.CreateLogger() + .LogTrace("Order with Id: {OrderId} has been successfully updated with a payment method {PaymentMethod} ({Id})", + buyerPaymentMethodVerifiedEvent.OrderId, nameof(buyerPaymentMethodVerifiedEvent.Payment), buyerPaymentMethodVerifiedEvent.Payment.Id); } - } + } } diff --git a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderCancelled/OrderCancelledDomainEventHandler.cs b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderCancelled/OrderCancelledDomainEventHandler.cs index 32967f6a7..f8fddf3a8 100644 --- a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderCancelled/OrderCancelledDomainEventHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderCancelled/OrderCancelledDomainEventHandler.cs @@ -33,9 +33,9 @@ namespace Ordering.API.Application.DomainEventHandlers.OrderCancelled public async Task Handle(OrderCancelledDomainEvent orderCancelledDomainEvent, CancellationToken cancellationToken) { - _logger.CreateLogger(nameof(OrderCancelledDomainEvent)) - .LogTrace($"Order with Id: {orderCancelledDomainEvent.Order.Id} has been successfully updated with " + - $"a status order id: {OrderStatus.Shipped.Id}"); + _logger.CreateLogger() + .LogTrace("Order with Id: {OrderId} has been successfully updated to status {Status} ({Id})", + orderCancelledDomainEvent.Order.Id, nameof(OrderStatus.Cancelled), OrderStatus.Cancelled.Id); var order = await _orderRepository.GetAsync(orderCancelledDomainEvent.Order.Id); var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value.ToString()); diff --git a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderGracePeriodConfirmed/OrderStatusChangedToAwaitingValidationDomainEventHandler.cs b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderGracePeriodConfirmed/OrderStatusChangedToAwaitingValidationDomainEventHandler.cs index e1c54af4f..edea66895 100644 --- a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderGracePeriodConfirmed/OrderStatusChangedToAwaitingValidationDomainEventHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderGracePeriodConfirmed/OrderStatusChangedToAwaitingValidationDomainEventHandler.cs @@ -33,9 +33,9 @@ public async Task Handle(OrderStatusChangedToAwaitingValidationDomainEvent orderStatusChangedToAwaitingValidationDomainEvent, CancellationToken cancellationToken) { - _logger.CreateLogger(nameof(OrderStatusChangedToAwaitingValidationDomainEvent)) - .LogTrace($"Order with Id: {orderStatusChangedToAwaitingValidationDomainEvent.OrderId} has been successfully updated with " + - $"a status order id: {OrderStatus.AwaitingValidation.Id}"); + _logger.CreateLogger() + .LogTrace("Order with Id: {OrderId} has been successfully updated to status {Status} ({Id})", + orderStatusChangedToAwaitingValidationDomainEvent.OrderId, nameof(OrderStatus.AwaitingValidation), OrderStatus.AwaitingValidation.Id); var order = await _orderRepository.GetAsync(orderStatusChangedToAwaitingValidationDomainEvent.OrderId); diff --git a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderPaid/OrderStatusChangedToPaidDomainEventHandler.cs b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderPaid/OrderStatusChangedToPaidDomainEventHandler.cs index d3dca202f..a934a621d 100644 --- a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderPaid/OrderStatusChangedToPaidDomainEventHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderPaid/OrderStatusChangedToPaidDomainEventHandler.cs @@ -35,9 +35,9 @@ public async Task Handle(OrderStatusChangedToPaidDomainEvent orderStatusChangedToPaidDomainEvent, CancellationToken cancellationToken) { - _logger.CreateLogger(nameof(OrderStatusChangedToPaidDomainEventHandler)) - .LogTrace($"Order with Id: {orderStatusChangedToPaidDomainEvent.OrderId} has been successfully updated with " + - $"a status order id: {OrderStatus.Paid.Id}"); + _logger.CreateLogger() + .LogTrace("Order with Id: {OrderId} has been successfully updated to status {Status} ({Id})", + orderStatusChangedToPaidDomainEvent.OrderId, nameof(OrderStatus.Paid), OrderStatus.Paid.Id); var order = await _orderRepository.GetAsync(orderStatusChangedToPaidDomainEvent.OrderId); var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value.ToString()); diff --git a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderShipped/OrderShippedDomainEventHandler.cs b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderShipped/OrderShippedDomainEventHandler.cs index 3be83a2ae..7f4754865 100644 --- a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderShipped/OrderShippedDomainEventHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderShipped/OrderShippedDomainEventHandler.cs @@ -33,9 +33,9 @@ namespace Ordering.API.Application.DomainEventHandlers.OrderShipped public async Task Handle(OrderShippedDomainEvent orderShippedDomainEvent, CancellationToken cancellationToken) { - _logger.CreateLogger(nameof(OrderShippedDomainEvent)) - .LogTrace($"Order with Id: {orderShippedDomainEvent.Order.Id} has been successfully updated with " + - $"a status order id: {OrderStatus.Shipped.Id}"); + _logger.CreateLogger() + .LogTrace("Order with Id: {OrderId} has been successfully updated to status {Status} ({Id})", + orderShippedDomainEvent.Order.Id, nameof(OrderStatus.Shipped), OrderStatus.Shipped.Id); var order = await _orderRepository.GetAsync(orderShippedDomainEvent.Order.Id); var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value.ToString()); diff --git a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderStartedEvent/ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler.cs b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderStartedEvent/ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler.cs index 99b2a21a0..e47192dcd 100644 --- a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderStartedEvent/ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderStartedEvent/ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler.cs @@ -61,7 +61,9 @@ namespace Ordering.API.Application.DomainEventHandlers.OrderStartedEvent var orderStatusChangedTosubmittedIntegrationEvent = new OrderStatusChangedToSubmittedIntegrationEvent(orderStartedEvent.Order.Id, orderStartedEvent.Order.OrderStatus.Name, buyer.Name); await _orderingIntegrationEventService.AddAndSaveEventAsync(orderStatusChangedTosubmittedIntegrationEvent); - _logger.CreateLogger(nameof(ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler)).LogTrace($"Buyer {buyerUpdated.Id} and related payment method were validated or updated for orderId: {orderStartedEvent.Order.Id}."); + _logger.CreateLogger() + .LogTrace("Buyer {BuyerId} and related payment method were validated or updated for orderId: {OrderId}.", + buyerUpdated.Id, orderStartedEvent.Order.Id); } } } diff --git a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderStockConfirmed/OrderStatusChangedToStockConfirmedDomainEventHandler.cs b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderStockConfirmed/OrderStatusChangedToStockConfirmedDomainEventHandler.cs index e910964e8..0e9ca4f08 100644 --- a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderStockConfirmed/OrderStatusChangedToStockConfirmedDomainEventHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderStockConfirmed/OrderStatusChangedToStockConfirmedDomainEventHandler.cs @@ -33,9 +33,9 @@ public async Task Handle(OrderStatusChangedToStockConfirmedDomainEvent orderStatusChangedToStockConfirmedDomainEvent, CancellationToken cancellationToken) { - _logger.CreateLogger(nameof(OrderStatusChangedToStockConfirmedDomainEventHandler)) - .LogTrace($"Order with Id: {orderStatusChangedToStockConfirmedDomainEvent.OrderId} has been successfully updated with " + - $"a status order id: {OrderStatus.StockConfirmed.Id}"); + _logger.CreateLogger() + .LogTrace("Order with Id: {OrderId} has been successfully updated to status {Status} ({Id})", + orderStatusChangedToStockConfirmedDomainEvent.OrderId, nameof(OrderStatus.StockConfirmed), OrderStatus.StockConfirmed.Id); var order = await _orderRepository.GetAsync(orderStatusChangedToStockConfirmedDomainEvent.OrderId); var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value.ToString()); diff --git a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/GracePeriodConfirmedIntegrationEventHandler.cs b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/GracePeriodConfirmedIntegrationEventHandler.cs index f8dcc6edb..2e003b322 100644 --- a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/GracePeriodConfirmedIntegrationEventHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/GracePeriodConfirmedIntegrationEventHandler.cs @@ -1,8 +1,13 @@ using MediatR; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; +using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; +using Microsoft.eShopOnContainers.Services.Ordering.API; using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; +using Microsoft.Extensions.Logging; +using Ordering.API.Application.Behaviors; using Ordering.API.Application.Commands; using Ordering.API.Application.IntegrationEvents.Events; +using Serilog.Context; using System.Threading.Tasks; namespace Ordering.API.Application.IntegrationEvents.EventHandling @@ -10,10 +15,14 @@ namespace Ordering.API.Application.IntegrationEvents.EventHandling public class GracePeriodConfirmedIntegrationEventHandler : IIntegrationEventHandler { private readonly IMediator _mediator; + private readonly ILogger _logger; - public GracePeriodConfirmedIntegrationEventHandler(IMediator mediator) + public GracePeriodConfirmedIntegrationEventHandler( + IMediator mediator, + ILogger logger) { _mediator = mediator; + _logger = logger ?? throw new System.ArgumentNullException(nameof(logger)); } /// @@ -26,8 +35,21 @@ namespace Ordering.API.Application.IntegrationEvents.EventHandling /// public async Task Handle(GracePeriodConfirmedIntegrationEvent @event) { - var command = new SetAwaitingValidationOrderStatusCommand(@event.OrderId); - await _mediator.Send(command); + using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) + { + _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); + + var command = new SetAwaitingValidationOrderStatusCommand(@event.OrderId); + + _logger.LogInformation( + "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", + command.GetGenericTypeName(), + nameof(command.OrderNumber), + command.OrderNumber, + command); + + await _mediator.Send(command); + } } } } diff --git a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderPaymentFailedIntegrationEventHandler.cs b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderPaymentFailedIntegrationEventHandler.cs index 5f4fc28e1..a123dd891 100644 --- a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderPaymentFailedIntegrationEventHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderPaymentFailedIntegrationEventHandler.cs @@ -2,26 +2,48 @@ { using MediatR; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; + using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; + using Microsoft.eShopOnContainers.Services.Ordering.API; using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; + using Microsoft.Extensions.Logging; + using Ordering.API.Application.Behaviors; using Ordering.API.Application.Commands; using Ordering.API.Application.IntegrationEvents.Events; - using System; + using Serilog.Context; using System.Threading.Tasks; + using System; public class OrderPaymentFailedIntegrationEventHandler : IIntegrationEventHandler { private readonly IMediator _mediator; + private readonly ILogger _logger; - public OrderPaymentFailedIntegrationEventHandler(IMediator mediator) + public OrderPaymentFailedIntegrationEventHandler( + IMediator mediator, + ILogger logger) { _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public async Task Handle(OrderPaymentFailedIntegrationEvent @event) { - var command = new CancelOrderCommand(@event.OrderId); - await _mediator.Send(command); + using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) + { + _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); + + var command = new CancelOrderCommand(@event.OrderId); + + _logger.LogInformation( + "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", + command.GetGenericTypeName(), + nameof(command.OrderNumber), + command.OrderNumber, + command); + + await _mediator.Send(command); + } } } } diff --git a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderPaymentSuccededIntegrationEventHandler.cs b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderPaymentSuccededIntegrationEventHandler.cs index 6c201d77e..9cc69e5e8 100644 --- a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderPaymentSuccededIntegrationEventHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderPaymentSuccededIntegrationEventHandler.cs @@ -2,9 +2,14 @@ { using MediatR; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; + using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; + using Microsoft.eShopOnContainers.Services.Ordering.API; using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; + using Microsoft.Extensions.Logging; + using Ordering.API.Application.Behaviors; using Ordering.API.Application.Commands; using Ordering.API.Application.IntegrationEvents.Events; + using Serilog.Context; using System; using System.Threading.Tasks; @@ -12,16 +17,33 @@ IIntegrationEventHandler { private readonly IMediator _mediator; + private readonly ILogger _logger; - public OrderPaymentSuccededIntegrationEventHandler(IMediator mediator) + public OrderPaymentSuccededIntegrationEventHandler( + IMediator mediator, + ILogger logger) { _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public async Task Handle(OrderPaymentSuccededIntegrationEvent @event) { - var command = new SetPaidOrderStatusCommand(@event.OrderId); - await _mediator.Send(command); + using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) + { + _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); + + var command = new SetPaidOrderStatusCommand(@event.OrderId); + + _logger.LogInformation( + "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", + command.GetGenericTypeName(), + nameof(command.OrderNumber), + command.OrderNumber, + command); + + await _mediator.Send(command); + } } } } \ No newline at end of file diff --git a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderStockConfirmedIntegrationEventHandler.cs b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderStockConfirmedIntegrationEventHandler.cs index c5561508b..6438b01d0 100644 --- a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderStockConfirmedIntegrationEventHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderStockConfirmedIntegrationEventHandler.cs @@ -1,27 +1,49 @@ namespace Ordering.API.Application.IntegrationEvents.EventHandling { using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; + using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; using System.Threading.Tasks; using Events; using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; using MediatR; using System; using Ordering.API.Application.Commands; + using Microsoft.Extensions.Logging; + using Serilog.Context; + using Microsoft.eShopOnContainers.Services.Ordering.API; + using Ordering.API.Application.Behaviors; - public class OrderStockConfirmedIntegrationEventHandler : + public class OrderStockConfirmedIntegrationEventHandler : IIntegrationEventHandler { private readonly IMediator _mediator; + private readonly ILogger _logger; - public OrderStockConfirmedIntegrationEventHandler(IMediator mediator) + public OrderStockConfirmedIntegrationEventHandler( + IMediator mediator, + ILogger logger) { _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public async Task Handle(OrderStockConfirmedIntegrationEvent @event) { - var command = new SetStockConfirmedOrderStatusCommand(@event.OrderId); - await _mediator.Send(command); + using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) + { + _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); + + var command = new SetStockConfirmedOrderStatusCommand(@event.OrderId); + + _logger.LogInformation( + "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", + command.GetGenericTypeName(), + nameof(command.OrderNumber), + command.OrderNumber, + command); + + await _mediator.Send(command); + } } } } \ No newline at end of file diff --git a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderStockRejectedIntegrationEventHandler.cs b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderStockRejectedIntegrationEventHandler.cs index af7d98f74..b457211ed 100644 --- a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderStockRejectedIntegrationEventHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderStockRejectedIntegrationEventHandler.cs @@ -1,31 +1,53 @@ namespace Ordering.API.Application.IntegrationEvents.EventHandling { using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; + using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; using System.Threading.Tasks; using Events; using System.Linq; using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; using MediatR; using Ordering.API.Application.Commands; + using Microsoft.Extensions.Logging; + using Serilog.Context; + using Microsoft.eShopOnContainers.Services.Ordering.API; + using Ordering.API.Application.Behaviors; public class OrderStockRejectedIntegrationEventHandler : IIntegrationEventHandler { private readonly IMediator _mediator; + private readonly ILogger _logger; - public OrderStockRejectedIntegrationEventHandler(IMediator mediator) + public OrderStockRejectedIntegrationEventHandler( + IMediator mediator, + ILogger logger) { _mediator = mediator; + _logger = logger ?? throw new System.ArgumentNullException(nameof(logger)); } public async Task Handle(OrderStockRejectedIntegrationEvent @event) { - var orderStockRejectedItems = @event.OrderStockItems - .FindAll(c => !c.HasStock) - .Select(c => c.ProductId) - .ToList(); + using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) + { + _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); - var command = new SetStockRejectedOrderStatusCommand(@event.OrderId, orderStockRejectedItems); - await _mediator.Send(command); + var orderStockRejectedItems = @event.OrderStockItems + .FindAll(c => !c.HasStock) + .Select(c => c.ProductId) + .ToList(); + + var command = new SetStockRejectedOrderStatusCommand(@event.OrderId, orderStockRejectedItems); + + _logger.LogInformation( + "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", + command.GetGenericTypeName(), + nameof(command.OrderNumber), + command.OrderNumber, + command); + + await _mediator.Send(command); + } } } } \ No newline at end of file diff --git a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/UserCheckoutAcceptedIntegrationEventHandler.cs b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/UserCheckoutAcceptedIntegrationEventHandler.cs index 33f327c6b..a1452b23c 100644 --- a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/UserCheckoutAcceptedIntegrationEventHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/UserCheckoutAcceptedIntegrationEventHandler.cs @@ -1,52 +1,82 @@ -using System; -using MediatR; +using MediatR; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; -using System.Threading.Tasks; +using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; +using Microsoft.eShopOnContainers.Services.Ordering.API; using Microsoft.Extensions.Logging; +using Ordering.API.Application.Behaviors; using Ordering.API.Application.IntegrationEvents.Events; +using Serilog.Context; +using System.Threading.Tasks; +using System; namespace Ordering.API.Application.IntegrationEvents.EventHandling { public class UserCheckoutAcceptedIntegrationEventHandler : IIntegrationEventHandler { private readonly IMediator _mediator; - private readonly ILoggerFactory _logger; + private readonly ILogger _logger; - public UserCheckoutAcceptedIntegrationEventHandler(IMediator mediator, - ILoggerFactory logger) + public UserCheckoutAcceptedIntegrationEventHandler( + IMediator mediator, + ILogger logger) { _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } /// /// Integration event handler which starts the create order process /// - /// + /// /// Integration event message which is sent by the /// basket.api once it has successfully process the /// order items. /// /// - public async Task Handle(UserCheckoutAcceptedIntegrationEvent eventMsg) + public async Task Handle(UserCheckoutAcceptedIntegrationEvent @event) { - var result = false; - - if (eventMsg.RequestId != Guid.Empty) + using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) { - var createOrderCommand = new CreateOrderCommand(eventMsg.Basket.Items, eventMsg.UserId, eventMsg.UserName, eventMsg.City, eventMsg.Street, - eventMsg.State, eventMsg.Country, eventMsg.ZipCode, - eventMsg.CardNumber, eventMsg.CardHolderName, eventMsg.CardExpiration, - eventMsg.CardSecurityNumber, eventMsg.CardTypeId); - - var requestCreateOrder = new IdentifiedCommand(createOrderCommand, eventMsg.RequestId); - result = await _mediator.Send(requestCreateOrder); - } - - _logger.CreateLogger(nameof(UserCheckoutAcceptedIntegrationEventHandler)) - .LogTrace(result ? $"UserCheckoutAccepted integration event has been received and a create new order process is started with requestId: {eventMsg.RequestId}" : - $"UserCheckoutAccepted integration event has been received but a new order process has failed with requestId: {eventMsg.RequestId}"); + _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); + + var result = false; + + if (@event.RequestId != Guid.Empty) + { + using (LogContext.PushProperty("IdentifiedCommandId", @event.RequestId)) + { + var createOrderCommand = new CreateOrderCommand(@event.Basket.Items, @event.UserId, @event.UserName, @event.City, @event.Street, + @event.State, @event.Country, @event.ZipCode, + @event.CardNumber, @event.CardHolderName, @event.CardExpiration, + @event.CardSecurityNumber, @event.CardTypeId); + + var requestCreateOrder = new IdentifiedCommand(createOrderCommand, @event.RequestId); + + _logger.LogInformation( + "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", + requestCreateOrder.GetGenericTypeName(), + nameof(requestCreateOrder.Id), + requestCreateOrder.Id, + requestCreateOrder); + + result = await _mediator.Send(requestCreateOrder); + + if (result) + { + _logger.LogInformation("----- CreateOrderCommand suceeded - RequestId: {RequestId}", @event.RequestId); + } + else + { + _logger.LogWarning("CreateOrderCommand failed - RequestId: {RequestId}", @event.RequestId); + } + } + } + else + { + _logger.LogWarning("Invalid IntegrationEvent - RequestId is missing - {@IntegrationEvent}", @event); + } + } } } } \ No newline at end of file diff --git a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/OrderingIntegrationEventService.cs b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/OrderingIntegrationEventService.cs index 9c1bd4e1b..9d85e2dd4 100644 --- a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/OrderingIntegrationEventService.cs +++ b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/OrderingIntegrationEventService.cs @@ -5,7 +5,9 @@ using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services; using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Utilities; +using Microsoft.eShopOnContainers.Services.Ordering.API; using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure; +using Microsoft.Extensions.Logging; using System; using System.Data.Common; using System.Diagnostics; @@ -21,39 +23,49 @@ namespace Ordering.API.Application.IntegrationEvents private readonly OrderingContext _orderingContext; private readonly IntegrationEventLogContext _eventLogContext; private readonly IIntegrationEventLogService _eventLogService; + private readonly ILogger _logger; - public OrderingIntegrationEventService(IEventBus eventBus, + public OrderingIntegrationEventService(IEventBus eventBus, OrderingContext orderingContext, IntegrationEventLogContext eventLogContext, - Func integrationEventLogServiceFactory) + Func integrationEventLogServiceFactory, + ILogger logger) { _orderingContext = orderingContext ?? throw new ArgumentNullException(nameof(orderingContext)); _eventLogContext = eventLogContext ?? throw new ArgumentNullException(nameof(eventLogContext)); _integrationEventLogServiceFactory = integrationEventLogServiceFactory ?? throw new ArgumentNullException(nameof(integrationEventLogServiceFactory)); _eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus)); _eventLogService = _integrationEventLogServiceFactory(_orderingContext.Database.GetDbConnection()); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public async Task PublishEventsThroughEventBusAsync() { var pendindLogEvents = await _eventLogService.RetrieveEventLogsPendingToPublishAsync(); + foreach (var logEvt in pendindLogEvents) { + _logger.LogInformation("----- Publishing integration event: {IntegrationEventId} from {AppName} - ({@IntegrationEvent})", logEvt.EventId, Program.AppName, logEvt.IntegrationEvent); + try { await _eventLogService.MarkEventAsInProgressAsync(logEvt.EventId); _eventBus.Publish(logEvt.IntegrationEvent); await _eventLogService.MarkEventAsPublishedAsync(logEvt.EventId); } - catch (Exception) + catch (Exception ex) { + _logger.LogError(ex, "ERROR publishing integration event: {IntegrationEventId} from {AppName}", logEvt.EventId, Program.AppName); + await _eventLogService.MarkEventAsFailedAsync(logEvt.EventId); - } + } } } public async Task AddAndSaveEventAsync(IntegrationEvent evt) { + _logger.LogInformation("----- Enqueuing integration event {IntegrationEventId} to repository ({@IntegrationEvent})", evt.Id, evt); + await _eventLogService.SaveEventAsync(evt, _orderingContext.GetCurrentTransaction.GetDbTransaction()); } } diff --git a/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs b/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs index e2c711b69..ae35b0377 100644 --- a/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs +++ b/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs @@ -1,9 +1,12 @@ using MediatR; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries; using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services; +using Microsoft.Extensions.Logging; +using Ordering.API.Application.Behaviors; using Ordering.API.Application.Commands; using System; using System.Collections.Generic; @@ -20,12 +23,18 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers private readonly IMediator _mediator; private readonly IOrderQueries _orderQueries; private readonly IIdentityService _identityService; + private readonly ILogger _logger; - public OrdersController(IMediator mediator, IOrderQueries orderQueries, IIdentityService identityService) + public OrdersController( + IMediator mediator, + IOrderQueries orderQueries, + IIdentityService identityService, + ILogger logger) { _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); _orderQueries = orderQueries ?? throw new ArgumentNullException(nameof(orderQueries)); _identityService = identityService ?? throw new ArgumentNullException(nameof(identityService)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } [Route("cancel")] @@ -39,6 +48,14 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers if (Guid.TryParse(requestId, out Guid guid) && guid != Guid.Empty) { var requestCancelOrder = new IdentifiedCommand(command, guid); + + _logger.LogInformation( + "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", + requestCancelOrder.GetGenericTypeName(), + nameof(requestCancelOrder.Command.OrderNumber), + requestCancelOrder.Command.OrderNumber, + requestCancelOrder); + commandResult = await _mediator.Send(requestCancelOrder); } @@ -61,6 +78,14 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers if (Guid.TryParse(requestId, out Guid guid) && guid != Guid.Empty) { var requestShipOrder = new IdentifiedCommand(command, guid); + + _logger.LogInformation( + "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", + requestShipOrder.GetGenericTypeName(), + nameof(requestShipOrder.Command.OrderNumber), + requestShipOrder.Command.OrderNumber, + requestShipOrder); + commandResult = await _mediator.Send(requestShipOrder); } @@ -114,6 +139,13 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers [HttpPost] public async Task> CreateOrderDraftFromBasketDataAsync([FromBody] CreateOrderDraftCommand createOrderDraftCommand) { + _logger.LogInformation( + "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", + createOrderDraftCommand.GetGenericTypeName(), + nameof(createOrderDraftCommand.BuyerId), + createOrderDraftCommand.BuyerId, + createOrderDraftCommand); + return await _mediator.Send(createOrderDraftCommand); } } diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/MediatorModule.cs b/src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/MediatorModule.cs index 99a413f9f..67275a587 100644 --- a/src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/MediatorModule.cs +++ b/src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/MediatorModule.cs @@ -7,7 +7,6 @@ using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; using Ordering.API.Application.Behaviors; using Ordering.API.Application.DomainEventHandlers.OrderStartedEvent; using Ordering.API.Application.Validations; -using Ordering.API.Infrastructure.Behaviors; namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.AutofacModules { diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/OrderingContextSeed.cs b/src/Services/Ordering/Ordering.API/Infrastructure/OrderingContextSeed.cs index 975fb9a6a..7dcbe194e 100644 --- a/src/Services/Ordering/Ordering.API/Infrastructure/OrderingContextSeed.cs +++ b/src/Services/Ordering/Ordering.API/Infrastructure/OrderingContextSeed.cs @@ -75,7 +75,7 @@ } catch (Exception ex) { - log.LogError(ex.Message); + log.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); return GetPredefinedCardTypes(); } @@ -83,7 +83,7 @@ return File.ReadAllLines(csvFileCardTypes) .Skip(1) // skip header column .SelectTry(x => CreateCardType(x, ref id)) - .OnCaughtException(ex => { log.LogError(ex.Message); return null; }) + .OnCaughtException(ex => { log.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); return null; }) .Where(x => x != null); } @@ -119,7 +119,7 @@ } catch (Exception ex) { - log.LogError(ex.Message); + log.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); return GetPredefinedOrderStatus(); } @@ -127,7 +127,7 @@ return File.ReadAllLines(csvFileOrderStatus) .Skip(1) // skip header row .SelectTry(x => CreateOrderStatus(x, ref id)) - .OnCaughtException(ex => { log.LogError(ex.Message); return null; }) + .OnCaughtException(ex => { log.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); return null; }) .Where(x => x != null); } @@ -183,7 +183,7 @@ sleepDurationProvider: retry => TimeSpan.FromSeconds(5), onRetry: (exception, timeSpan, retry, ctx) => { - logger.LogTrace($"[{prefix}] Exception {exception.GetType().Name} with message ${exception.Message} detected on attempt {retry} of {retries}"); + logger.LogWarning(exception, "[{prefix}] Exception {ExceptionType} with message {Message} detected on attempt {retry} of {retries}", prefix, exception.GetType().Name, exception.Message, retry, retries); } ); } diff --git a/src/Services/Ordering/Ordering.API/Ordering.API.csproj b/src/Services/Ordering/Ordering.API/Ordering.API.csproj index f69c6f673..f7443f9a4 100644 --- a/src/Services/Ordering/Ordering.API/Ordering.API.csproj +++ b/src/Services/Ordering/Ordering.API/Ordering.API.csproj @@ -5,6 +5,7 @@ aspnet-Ordering.API-20161122013547 $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; ..\..\..\..\docker-compose.dcproj + 7.1 @@ -29,13 +30,12 @@ - - - - + + - + + @@ -45,10 +45,13 @@ + + + - + diff --git a/src/Services/Ordering/Ordering.API/Program.cs b/src/Services/Ordering/Ordering.API/Program.cs index 8db59598c..8fc18c8bb 100644 --- a/src/Services/Ordering/Ordering.API/Program.cs +++ b/src/Services/Ordering/Ordering.API/Program.cs @@ -15,10 +15,22 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API { public class Program { - public static void Main(string[] args) + public static readonly string Namespace = typeof(Program).Namespace; + public static readonly string AppName = Namespace.Substring(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1); + + public static int Main(string[] args) { - BuildWebHost(args) - .MigrateDbContext((context, services) => + var configuration = GetConfiguration(); + + Log.Logger = CreateSerilogLogger(configuration); + + try + { + Log.Information("Configuring web host ({ApplicationContext})...", AppName); + var host = BuildWebHost(configuration, args); + + Log.Information("Applying migrations ({ApplicationContext})...", AppName); + host.MigrateDbContext((context, services) => { var env = services.GetService(); var settings = services.GetService>(); @@ -28,47 +40,66 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API .SeedAsync(context, env, settings, logger) .Wait(); }) - .MigrateDbContext((_,__)=>{}) - .Run(); + .MigrateDbContext((_, __) => { }); + + Log.Information("Starting web host ({ApplicationContext})...", AppName); + host.Run(); + + return 0; + } + catch (Exception ex) + { + Log.Fatal(ex, "Program terminated unexpectedly ({ApplicationContext})!", AppName); + return 1; + } + finally + { + Log.CloseAndFlush(); + } } - public static IWebHost BuildWebHost(string[] args) => - WebHost.CreateDefaultBuilder(args) + private static IWebHost BuildWebHost(IConfiguration configuration, string[] args) => + WebHost.CreateDefaultBuilder(args) + .CaptureStartupErrors(false) .UseStartup() + .UseApplicationInsights() .UseContentRoot(Directory.GetCurrentDirectory()) - .ConfigureAppConfiguration((builderContext, config) => - { - var builtConfig = config.Build(); + .UseConfiguration(configuration) + .UseSerilog() + .Build(); - var configurationBuilder = new ConfigurationBuilder(); - - if (Convert.ToBoolean(builtConfig["UseVault"])) - { - configurationBuilder.AddAzureKeyVault( - $"https://{builtConfig["Vault:Name"]}.vault.azure.net/", - builtConfig["Vault:ClientId"], - builtConfig["Vault:ClientSecret"]); - } + private static Serilog.ILogger CreateSerilogLogger(IConfiguration configuration) + { + var seqServerUrl = configuration["Serilog:SeqServerUrl"]; - configurationBuilder.AddEnvironmentVariables(); + return new LoggerConfiguration() + .MinimumLevel.Verbose() + .Enrich.WithProperty("ApplicationContext", AppName) + .Enrich.FromLogContext() + .WriteTo.Console() + .WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl) + .ReadFrom.Configuration(configuration) + .CreateLogger(); + } - config.AddConfiguration(configurationBuilder.Build()); - }) - .ConfigureLogging((hostingContext, builder) => - { - builder.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); - builder.AddConsole(); - builder.AddDebug(); - builder.AddAzureWebAppDiagnostics(); - }) - .UseApplicationInsights() - .UseSerilog((builderContext, config) => - { - config - .MinimumLevel.Information() - .Enrich.FromLogContext() - .WriteTo.Console(); - }) - .Build(); + private static IConfiguration GetConfiguration() + { + var builder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .AddEnvironmentVariables(); + + var config = builder.Build(); + + if (config.GetValue("UseVault", false)) + { + builder.AddAzureKeyVault( + $"https://{config["Vault:Name"]}.vault.azure.net/", + config["Vault:ClientId"], + config["Vault:ClientSecret"]); + } + + return builder.Build(); + } } -} +} \ No newline at end of file diff --git a/src/Services/Ordering/Ordering.API/Startup.cs b/src/Services/Ordering/Ordering.API/Startup.cs index 03e9b1d7f..cd34f98dd 100644 --- a/src/Services/Ordering/Ordering.API/Startup.cs +++ b/src/Services/Ordering/Ordering.API/Startup.cs @@ -74,14 +74,15 @@ public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) { - loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); + //loggerFactory.AddAzureWebAppDiagnostics(); + //loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); var pathBase = Configuration["PATH_BASE"]; if (!string.IsNullOrEmpty(pathBase)) { - loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'"); + loggerFactory.CreateLogger().LogDebug("Using PATH BASE '{pathBase}'", pathBase); app.UsePathBase(pathBase); - } + } app.UseCors("CorsPolicy"); @@ -121,10 +122,9 @@ eventBus.Subscribe>(); eventBus.Subscribe>(); eventBus.Subscribe>(); - eventBus.Subscribe>(); + eventBus.Subscribe>(); } - protected virtual void ConfigureAuth(IApplicationBuilder app) { if (Configuration.GetValue("UseLoadTest")) @@ -167,7 +167,7 @@ }) .SetCompatibilityVersion(CompatibilityVersion.Version_2_2) .AddControllersAsServices(); //Injecting Controllers themselves thru DI - //For further info see: http://docs.autofac.org/en/latest/integration/aspnetcore.html#controllers-as-services + //For further info see: http://docs.autofac.org/en/latest/integration/aspnetcore.html#controllers-as-services services.AddCors(options => { diff --git a/src/Services/Ordering/Ordering.API/appsettings.json b/src/Services/Ordering/Ordering.API/appsettings.json index 96dd74630..64b24a354 100644 --- a/src/Services/Ordering/Ordering.API/appsettings.json +++ b/src/Services/Ordering/Ordering.API/appsettings.json @@ -2,12 +2,15 @@ "ConnectionString": "Server=tcp:127.0.0.1,5433;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word;", "IdentityUrl": "http://localhost:5105", "UseCustomizationData": false, - "Logging": { - "IncludeScopes": false, - "LogLevel": { - "Default": "Trace", - "System": "Information", - "Microsoft": "Information" + "Serilog": { + "SeqServerUrl": null, + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Warning", + "Microsoft.eShopOnContainers": "Information", + "System": "Warning" + } } }, "AzureServiceBusEnabled": false, diff --git a/src/Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj b/src/Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj index b4ee75da7..b889f5453 100644 --- a/src/Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj +++ b/src/Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj @@ -15,16 +15,19 @@ - - + + - + + + + diff --git a/src/Services/Ordering/Ordering.BackgroundTasks/Program.cs b/src/Services/Ordering/Ordering.BackgroundTasks/Program.cs index 3c78efa10..8f8cf41e1 100644 --- a/src/Services/Ordering/Ordering.BackgroundTasks/Program.cs +++ b/src/Services/Ordering/Ordering.BackgroundTasks/Program.cs @@ -1,33 +1,75 @@ using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Serilog; +using System; +using System.IO; namespace Ordering.BackgroundTasks { public class Program { - public static void Main(string[] args) + public static readonly string Namespace = typeof(Program).Namespace; + public static readonly string AppName = Namespace; + + public static int Main(string[] args) { - BuildWebHost(args).Run(); + var configuration = GetConfiguration(); + + Log.Logger = CreateSerilogLogger(configuration); + + try + { + Log.Information("Configuring web host ({ApplicationContext})...", AppName); + var host = BuildWebHost(configuration, args); + + Log.Information("Starting web host ({ApplicationContext})...", AppName); + host.Run(); + + return 0; + } + catch (Exception ex) + { + Log.Fatal(ex, "Program terminated unexpectedly ({ApplicationContext})!", AppName); + return 1; + } + finally + { + Log.CloseAndFlush(); + } } - public static IWebHost BuildWebHost(string[] args) => + private static IWebHost BuildWebHost(IConfiguration configuration, string[] args) => WebHost.CreateDefaultBuilder(args) - .UseStartup() - .ConfigureLogging((hostingContext, builder) => - { - builder.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); - builder.AddDebug(); - builder.AddConsole(); - }) - .UseSerilog((builderContext, config) => - { - config - .MinimumLevel.Information() - .Enrich.FromLogContext() - .WriteTo.Console(); - }) + .CaptureStartupErrors(false) + .UseStartup() + .UseConfiguration(configuration) + .UseSerilog() .Build(); + + private static Serilog.ILogger CreateSerilogLogger(IConfiguration configuration) + { + var seqServerUrl = configuration["Serilog:SeqServerUrl"]; + + return new LoggerConfiguration() + .MinimumLevel.Verbose() + .Enrich.WithProperty("ApplicationContext", AppName) + .Enrich.FromLogContext() + .WriteTo.Console() + .WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl) + .ReadFrom.Configuration(configuration) + .CreateLogger(); + } + + private static IConfiguration GetConfiguration() + { + var builder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .AddEnvironmentVariables(); + + return builder.Build(); + } } } diff --git a/src/Services/Ordering/Ordering.BackgroundTasks/Tasks/GracePeriodManagerTask.cs b/src/Services/Ordering/Ordering.BackgroundTasks/Tasks/GracePeriodManagerTask.cs index fbf92f978..328fb95c4 100644 --- a/src/Services/Ordering/Ordering.BackgroundTasks/Tasks/GracePeriodManagerTask.cs +++ b/src/Services/Ordering/Ordering.BackgroundTasks/Tasks/GracePeriodManagerTask.cs @@ -20,9 +20,10 @@ namespace Ordering.BackgroundTasks.Tasks private readonly BackgroundTaskSettings _settings; private readonly IEventBus _eventBus; - public GracePeriodManagerService(IOptions settings, - IEventBus eventBus, - ILogger logger) + public GracePeriodManagerService( + IOptions settings, + IEventBus eventBus, + ILogger logger) { _settings = settings?.Value ?? throw new ArgumentNullException(nameof(settings)); _eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus)); @@ -32,27 +33,27 @@ namespace Ordering.BackgroundTasks.Tasks protected override async Task ExecuteAsync(CancellationToken stoppingToken) { - _logger.LogDebug($"GracePeriodManagerService is starting."); + _logger.LogDebug("GracePeriodManagerService is starting."); - stoppingToken.Register(() => _logger.LogDebug($"#1 GracePeriodManagerService background task is stopping.")); + stoppingToken.Register(() => _logger.LogDebug("#1 GracePeriodManagerService background task is stopping.")); while (!stoppingToken.IsCancellationRequested) { - _logger.LogDebug($"GracePeriodManagerService background task is doing background work."); + _logger.LogDebug("GracePeriodManagerService background task is doing background work."); CheckConfirmedGracePeriodOrders(); await Task.Delay(_settings.CheckUpdateTime, stoppingToken); } - _logger.LogDebug($"GracePeriodManagerService background task is stopping."); + _logger.LogDebug("GracePeriodManagerService background task is stopping."); await Task.CompletedTask; } private void CheckConfirmedGracePeriodOrders() { - _logger.LogDebug($"Checking confirmed grace period orders"); + _logger.LogDebug("Checking confirmed grace period orders"); var orderIds = GetConfirmedGracePeriodOrders(); @@ -60,6 +61,8 @@ namespace Ordering.BackgroundTasks.Tasks { var confirmGracePeriodEvent = new GracePeriodConfirmedIntegrationEvent(orderId); + _logger.LogInformation("----- Publishing integration event: {IntegrationEventId} from {AppName} - ({@IntegrationEvent})", confirmGracePeriodEvent.Id, Program.AppName, confirmGracePeriodEvent); + _eventBus.Publish(confirmGracePeriodEvent); } } @@ -81,7 +84,7 @@ namespace Ordering.BackgroundTasks.Tasks } catch (SqlException exception) { - _logger.LogCritical($"FATAL ERROR: Database connections could not be opened: {exception.Message}"); + _logger.LogCritical(exception, "FATAL ERROR: Database connections could not be opened: {Message}", exception.Message); } } diff --git a/src/Services/Ordering/Ordering.BackgroundTasks/appsettings.json b/src/Services/Ordering/Ordering.BackgroundTasks/appsettings.json index fd26645ab..3697c53a5 100644 --- a/src/Services/Ordering/Ordering.BackgroundTasks/appsettings.json +++ b/src/Services/Ordering/Ordering.BackgroundTasks/appsettings.json @@ -1,15 +1,13 @@ { "ConnectionString": "Server=tcp:127.0.0.1,5433;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word;", - "Logging": { - "IncludeScopes": false, - "Debug": { - "LogLevel": { - "Default": "Debug" - } - }, - "Console": { - "LogLevel": { - "Default": "Debug" + "Serilog": { + "SeqServerUrl": null, + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Warning", + "Microsoft.eShopOnContainers": "Information", + "System": "Warning" } } }, diff --git a/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/Buyer.cs b/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/Buyer.cs index 2fd52917e..1269fd259 100644 --- a/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/Buyer.cs +++ b/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/Buyer.cs @@ -32,8 +32,8 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.B int cardTypeId, string alias, string cardNumber, string securityNumber, string cardHolderName, DateTime expiration, int orderId) { - var existingPayment = _paymentMethods.Where(p => p.IsEqualTo(cardTypeId, cardNumber, expiration)) - .SingleOrDefault(); + var existingPayment = _paymentMethods + .SingleOrDefault(p => p.IsEqualTo(cardTypeId, cardNumber, expiration)); if (existingPayment != null) { @@ -41,16 +41,14 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.B return existingPayment; } - else - { - var payment = new PaymentMethod(cardTypeId, alias, cardNumber, securityNumber, cardHolderName, expiration); - _paymentMethods.Add(payment); + var payment = new PaymentMethod(cardTypeId, alias, cardNumber, securityNumber, cardHolderName, expiration); - AddDomainEvent(new BuyerAndPaymentMethodVerifiedDomainEvent(this, payment, orderId)); + _paymentMethods.Add(payment); - return payment; - } + AddDomainEvent(new BuyerAndPaymentMethodVerifiedDomainEvent(this, payment, orderId)); + + return payment; } } } diff --git a/src/Services/Ordering/Ordering.FunctionalTests/Ordering.FunctionalTests.csproj b/src/Services/Ordering/Ordering.FunctionalTests/Ordering.FunctionalTests.csproj index 394e7a1a7..05ede2017 100644 --- a/src/Services/Ordering/Ordering.FunctionalTests/Ordering.FunctionalTests.csproj +++ b/src/Services/Ordering/Ordering.FunctionalTests/Ordering.FunctionalTests.csproj @@ -17,7 +17,7 @@ - + diff --git a/src/Services/Ordering/Ordering.FunctionalTests/OrderingScenarios.cs b/src/Services/Ordering/Ordering.FunctionalTests/OrderingScenarios.cs index b544ea221..9b46e1841 100644 --- a/src/Services/Ordering/Ordering.FunctionalTests/OrderingScenarios.cs +++ b/src/Services/Ordering/Ordering.FunctionalTests/OrderingScenarios.cs @@ -3,7 +3,7 @@ using System.Net; using System.Net.Http; using System.Text; using System.Threading.Tasks; -using WebMVC.Models; +using WebMVC.Services.ModelDTOs; using Xunit; namespace Ordering.FunctionalTests diff --git a/src/Services/Ordering/Ordering.Infrastructure/EntityConfigurations/BuyerEntityTYpeConfiguration.cs b/src/Services/Ordering/Ordering.Infrastructure/EntityConfigurations/BuyerEntityTypeConfiguration.cs similarity index 100% rename from src/Services/Ordering/Ordering.Infrastructure/EntityConfigurations/BuyerEntityTYpeConfiguration.cs rename to src/Services/Ordering/Ordering.Infrastructure/EntityConfigurations/BuyerEntityTypeConfiguration.cs diff --git a/src/Services/Ordering/Ordering.Infrastructure/OrderingContext.cs b/src/Services/Ordering/Ordering.Infrastructure/OrderingContext.cs index c0e37fb14..f668d5cc8 100644 --- a/src/Services/Ordering/Ordering.Infrastructure/OrderingContext.cs +++ b/src/Services/Ordering/Ordering.Infrastructure/OrderingContext.cs @@ -27,10 +27,12 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure private readonly IMediator _mediator; private IDbContextTransaction _currentTransaction; - private OrderingContext(DbContextOptions options) : base (options) { } + private OrderingContext(DbContextOptions options) : base(options) { } public IDbContextTransaction GetCurrentTransaction => _currentTransaction; + public bool HasActiveTransaction => _currentTransaction != null; + public OrderingContext(DbContextOptions options, IMediator mediator) : base(options) { _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); @@ -47,7 +49,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure modelBuilder.ApplyConfiguration(new OrderItemEntityTypeConfiguration()); modelBuilder.ApplyConfiguration(new CardTypeEntityTypeConfiguration()); modelBuilder.ApplyConfiguration(new OrderStatusEntityTypeConfiguration()); - modelBuilder.ApplyConfiguration(new BuyerEntityTypeConfiguration()); + modelBuilder.ApplyConfiguration(new BuyerEntityTypeConfiguration()); } public async Task SaveEntitiesAsync(CancellationToken cancellationToken = default(CancellationToken)) @@ -67,17 +69,24 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure return true; } - public async Task BeginTransactionAsync() + public async Task BeginTransactionAsync() { - _currentTransaction = _currentTransaction ?? await Database.BeginTransactionAsync(IsolationLevel.ReadCommitted); + if (_currentTransaction != null) return null; + + _currentTransaction = await Database.BeginTransactionAsync(IsolationLevel.ReadCommitted); + + return _currentTransaction; } - public async Task CommitTransactionAsync() + public async Task CommitTransactionAsync(IDbContextTransaction transaction) { + if (transaction == null) throw new ArgumentNullException(nameof(transaction)); + if (transaction != _currentTransaction) throw new InvalidOperationException($"Transaction {transaction.TransactionId} is not current"); + try { await SaveChangesAsync(); - _currentTransaction?.Commit(); + transaction.Commit(); } catch { @@ -118,7 +127,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure var optionsBuilder = new DbContextOptionsBuilder() .UseSqlServer("Server=.;Initial Catalog=Microsoft.eShopOnContainers.Services.OrderingDb;Integrated Security=true"); - return new OrderingContext(optionsBuilder.Options,new NoMediator()); + return new OrderingContext(optionsBuilder.Options, new NoMediator()); } class NoMediator : IMediator diff --git a/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToAwaitingValidationIntegrationEventHandler.cs b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToAwaitingValidationIntegrationEventHandler.cs new file mode 100644 index 000000000..0e2665232 --- /dev/null +++ b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToAwaitingValidationIntegrationEventHandler.cs @@ -0,0 +1,38 @@ +using Microsoft.AspNetCore.SignalR; +using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; +using Microsoft.Extensions.Logging; +using Serilog.Context; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Ordering.SignalrHub.IntegrationEvents +{ + public class OrderStatusChangedToAwaitingValidationIntegrationEventHandler : IIntegrationEventHandler + { + private readonly IHubContext _hubContext; + private readonly ILogger _logger; + + public OrderStatusChangedToAwaitingValidationIntegrationEventHandler( + IHubContext hubContext, + ILogger logger) + { + _hubContext = hubContext ?? throw new ArgumentNullException(nameof(hubContext)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + + public async Task Handle(OrderStatusChangedToAwaitingValidationIntegrationEvent @event) + { + using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) + { + _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); + + await _hubContext.Clients + .Group(@event.BuyerName) + .SendAsync("UpdatedOrderState", new { OrderId = @event.OrderId, Status = @event.OrderStatus }); + } + } + } +} diff --git a/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToCancelledIntegrationEventHandler.cs b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToCancelledIntegrationEventHandler.cs index 171eb81f7..6257bb237 100644 --- a/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToCancelledIntegrationEventHandler.cs +++ b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToCancelledIntegrationEventHandler.cs @@ -1,6 +1,8 @@ using Microsoft.AspNetCore.SignalR; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; +using Microsoft.Extensions.Logging; using Ordering.SignalrHub.IntegrationEvents.Events; +using Serilog.Context; using System; using System.Collections.Generic; using System.Linq; @@ -11,18 +13,27 @@ namespace Ordering.SignalrHub.IntegrationEvents.EventHandling public class OrderStatusChangedToCancelledIntegrationEventHandler : IIntegrationEventHandler { private readonly IHubContext _hubContext; + private readonly ILogger _logger; - public OrderStatusChangedToCancelledIntegrationEventHandler(IHubContext hubContext) + public OrderStatusChangedToCancelledIntegrationEventHandler( + IHubContext hubContext, + ILogger logger) { _hubContext = hubContext ?? throw new ArgumentNullException(nameof(hubContext)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public async Task Handle(OrderStatusChangedToCancelledIntegrationEvent @event) { - await _hubContext.Clients - .Group(@event.BuyerName) - .SendAsync("UpdatedOrderState", new { OrderId = @event.OrderId, Status = @event.OrderStatus }); + using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) + { + _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); + + await _hubContext.Clients + .Group(@event.BuyerName) + .SendAsync("UpdatedOrderState", new { OrderId = @event.OrderId, Status = @event.OrderStatus }); + } } } } \ No newline at end of file diff --git a/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToPaidIntegrationEventHandler.cs b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToPaidIntegrationEventHandler.cs index 4b0eb780c..836e02d3c 100644 --- a/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToPaidIntegrationEventHandler.cs +++ b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToPaidIntegrationEventHandler.cs @@ -1,6 +1,8 @@ using Microsoft.AspNetCore.SignalR; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; +using Microsoft.Extensions.Logging; using Ordering.SignalrHub.IntegrationEvents.Events; +using Serilog.Context; using System; using System.Threading.Tasks; @@ -9,18 +11,27 @@ namespace Ordering.SignalrHub.IntegrationEvents.EventHandling public class OrderStatusChangedToPaidIntegrationEventHandler : IIntegrationEventHandler { private readonly IHubContext _hubContext; + private readonly ILogger _logger; - public OrderStatusChangedToPaidIntegrationEventHandler(IHubContext hubContext) + public OrderStatusChangedToPaidIntegrationEventHandler( + IHubContext hubContext, + ILogger logger) { _hubContext = hubContext ?? throw new ArgumentNullException(nameof(hubContext)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public async Task Handle(OrderStatusChangedToPaidIntegrationEvent @event) { - await _hubContext.Clients - .Group(@event.BuyerName) - .SendAsync("UpdatedOrderState", new { OrderId = @event.OrderId, Status = @event.OrderStatus }); + using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) + { + _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); + + await _hubContext.Clients + .Group(@event.BuyerName) + .SendAsync("UpdatedOrderState", new { OrderId = @event.OrderId, Status = @event.OrderStatus }); + } } } } diff --git a/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToShippedIntegrationEventHandler.cs b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToShippedIntegrationEventHandler.cs index 7a19a0659..5b08d08b4 100644 --- a/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToShippedIntegrationEventHandler.cs +++ b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToShippedIntegrationEventHandler.cs @@ -1,6 +1,8 @@ using Microsoft.AspNetCore.SignalR; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; +using Microsoft.Extensions.Logging; using Ordering.SignalrHub.IntegrationEvents.Events; +using Serilog.Context; using System; using System.Collections.Generic; using System.Linq; @@ -11,18 +13,27 @@ namespace Ordering.SignalrHub.IntegrationEvents.EventHandling public class OrderStatusChangedToShippedIntegrationEventHandler : IIntegrationEventHandler { private readonly IHubContext _hubContext; + private readonly ILogger _logger; - public OrderStatusChangedToShippedIntegrationEventHandler(IHubContext hubContext) + public OrderStatusChangedToShippedIntegrationEventHandler( + IHubContext hubContext, + ILogger logger) { _hubContext = hubContext ?? throw new ArgumentNullException(nameof(hubContext)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public async Task Handle(OrderStatusChangedToShippedIntegrationEvent @event) { - await _hubContext.Clients - .Group(@event.BuyerName) - .SendAsync("UpdatedOrderState", new { OrderId = @event.OrderId, Status = @event.OrderStatus }); + using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) + { + _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); + + await _hubContext.Clients + .Group(@event.BuyerName) + .SendAsync("UpdatedOrderState", new { OrderId = @event.OrderId, Status = @event.OrderStatus }); + } } } } diff --git a/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToStockConfirmedIntegrationEventHandler.cs b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToStockConfirmedIntegrationEventHandler.cs index 324ea6259..348627716 100644 --- a/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToStockConfirmedIntegrationEventHandler.cs +++ b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToStockConfirmedIntegrationEventHandler.cs @@ -1,6 +1,8 @@ using Microsoft.AspNetCore.SignalR; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; +using Microsoft.Extensions.Logging; using Ordering.SignalrHub.IntegrationEvents.Events; +using Serilog.Context; using System; using System.Collections.Generic; using System.Linq; @@ -12,18 +14,27 @@ namespace Ordering.SignalrHub.IntegrationEvents.EventHandling IIntegrationEventHandler { private readonly IHubContext _hubContext; + private readonly ILogger _logger; - public OrderStatusChangedToStockConfirmedIntegrationEventHandler(IHubContext hubContext) + public OrderStatusChangedToStockConfirmedIntegrationEventHandler( + IHubContext hubContext, + ILogger logger) { _hubContext = hubContext ?? throw new ArgumentNullException(nameof(hubContext)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public async Task Handle(OrderStatusChangedToStockConfirmedIntegrationEvent @event) { - await _hubContext.Clients - .Group(@event.BuyerName) - .SendAsync("UpdatedOrderState", new { OrderId = @event.OrderId, Status = @event.OrderStatus }); + using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) + { + _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); + + await _hubContext.Clients + .Group(@event.BuyerName) + .SendAsync("UpdatedOrderState", new { OrderId = @event.OrderId, Status = @event.OrderStatus }); + } } } } diff --git a/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToSubmittedIntegrationEventHandler.cs b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToSubmittedIntegrationEventHandler.cs index 5fac4c1da..422bc4a7a 100644 --- a/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToSubmittedIntegrationEventHandler.cs +++ b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToSubmittedIntegrationEventHandler.cs @@ -1,6 +1,8 @@ using Microsoft.AspNetCore.SignalR; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; +using Microsoft.Extensions.Logging; using Ordering.SignalrHub.IntegrationEvents.Events; +using Serilog.Context; using System; using System.Collections.Generic; using System.Linq; @@ -12,18 +14,27 @@ namespace Ordering.SignalrHub.IntegrationEvents.EventHandling IIntegrationEventHandler { private readonly IHubContext _hubContext; + private readonly ILogger _logger; - public OrderStatusChangedToSubmittedIntegrationEventHandler(IHubContext hubContext) + public OrderStatusChangedToSubmittedIntegrationEventHandler( + IHubContext hubContext, + ILogger logger) { _hubContext = hubContext ?? throw new ArgumentNullException(nameof(hubContext)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public async Task Handle(OrderStatusChangedToSubmittedIntegrationEvent @event) { - await _hubContext.Clients - .Group(@event.BuyerName) - .SendAsync("UpdatedOrderState", new { OrderId = @event.OrderId, Status = @event.OrderStatus }); + using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) + { + _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); + + await _hubContext.Clients + .Group(@event.BuyerName) + .SendAsync("UpdatedOrderState", new { OrderId = @event.OrderId, Status = @event.OrderStatus }); + } } } } \ No newline at end of file diff --git a/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/orderStatusChangedToAwaitingValidationIntegrationEventHandler.cs b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/orderStatusChangedToAwaitingValidationIntegrationEventHandler.cs deleted file mode 100644 index 6c2733b77..000000000 --- a/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/orderStatusChangedToAwaitingValidationIntegrationEventHandler.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Microsoft.AspNetCore.SignalR; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Ordering.SignalrHub.IntegrationEvents -{ - public class OrderStatusChangedToAwaitingValidationIntegrationEventHandler : IIntegrationEventHandler - { - private readonly IHubContext _hubContext; - - public OrderStatusChangedToAwaitingValidationIntegrationEventHandler(IHubContext hubContext) - { - _hubContext = hubContext ?? throw new ArgumentNullException(nameof(hubContext)); - } - - - public async Task Handle(OrderStatusChangedToAwaitingValidationIntegrationEvent @event) - { - await _hubContext.Clients - .Group(@event.BuyerName) - .SendAsync("UpdatedOrderState", new { OrderId = @event.OrderId, Status = @event.OrderStatus }); - } - } -} diff --git a/src/Services/Ordering/Ordering.SignalrHub/NotificationHub.cs b/src/Services/Ordering/Ordering.SignalrHub/NotificationHub.cs index 5fd6f3eb9..943198980 100644 --- a/src/Services/Ordering/Ordering.SignalrHub/NotificationHub.cs +++ b/src/Services/Ordering/Ordering.SignalrHub/NotificationHub.cs @@ -19,7 +19,7 @@ namespace Ordering.SignalrHub public override async Task OnDisconnectedAsync(Exception ex) { - await Groups.AddToGroupAsync(Context.ConnectionId, Context.User.Identity.Name); + await Groups.RemoveFromGroupAsync(Context.ConnectionId, Context.User.Identity.Name); await base.OnDisconnectedAsync(ex); } } diff --git a/src/Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj b/src/Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj index 1bd2334b4..08df863ce 100644 --- a/src/Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj +++ b/src/Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj @@ -1,4 +1,4 @@ - + netcoreapp2.2 @@ -10,9 +10,9 @@ - + - + @@ -20,14 +20,16 @@ - - + + - + + + diff --git a/src/Services/Ordering/Ordering.SignalrHub/Program.cs b/src/Services/Ordering/Ordering.SignalrHub/Program.cs index 3fe2e8c24..fdb3f5f3a 100644 --- a/src/Services/Ordering/Ordering.SignalrHub/Program.cs +++ b/src/Services/Ordering/Ordering.SignalrHub/Program.cs @@ -13,28 +13,67 @@ namespace Ordering.SignalrHub { public class Program { - public static void Main(string[] args) + public static readonly string Namespace = typeof(Program).Namespace; + public static readonly string AppName = Namespace; + + public static int Main(string[] args) { - BuildWebHost(args).Run(); + var configuration = GetConfiguration(); + + Log.Logger = CreateSerilogLogger(configuration); + + try + { + Log.Information("Configuring web host ({ApplicationContext})...", AppName); + var host = BuildWebHost(configuration, args); + + Log.Information("Starting web host ({ApplicationContext})...", AppName); + host.Run(); + + return 0; + } + catch (Exception ex) + { + Log.Fatal(ex, "Program terminated unexpectedly ({ApplicationContext})!", AppName); + return 1; + } + finally + { + Log.CloseAndFlush(); + } } - public static IWebHost BuildWebHost(string[] args) => + private static IWebHost BuildWebHost(IConfiguration configuration, string[] args) => WebHost.CreateDefaultBuilder(args) + .CaptureStartupErrors(false) .UseStartup() - .ConfigureLogging((hostingContext, builder) => - { - builder.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); - builder.AddConsole(); - builder.AddDebug(); - builder.AddAzureWebAppDiagnostics(); - }) - .UseSerilog((builderContext, config) => - { - config - .MinimumLevel.Information() - .Enrich.FromLogContext() - .WriteTo.Console(); - }) + .UseConfiguration(configuration) + .UseSerilog() .Build(); + + private static Serilog.ILogger CreateSerilogLogger(IConfiguration configuration) + { + var seqServerUrl = configuration["Serilog:SeqServerUrl"]; + + return new LoggerConfiguration() + .MinimumLevel.Verbose() + .Enrich.WithProperty("ApplicationContext", AppName) + .Enrich.FromLogContext() + .WriteTo.Console() + .WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl) + .ReadFrom.Configuration(configuration) + .CreateLogger(); + } + + private static IConfiguration GetConfiguration() + { + var builder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .AddEnvironmentVariables(); + + return builder.Build(); + } + } } diff --git a/src/Services/Ordering/Ordering.SignalrHub/Startup.cs b/src/Services/Ordering/Ordering.SignalrHub/Startup.cs index 20f8b739b..e8bb7ecd1 100644 --- a/src/Services/Ordering/Ordering.SignalrHub/Startup.cs +++ b/src/Services/Ordering/Ordering.SignalrHub/Startup.cs @@ -30,6 +30,8 @@ namespace Ordering.SignalrHub Configuration = configuration; } + public IConfiguration Configuration { get; } + // This method gets called by the runtime. Use this method to add services to the container. // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 public IServiceProvider ConfigureServices(IServiceCollection services) @@ -115,17 +117,18 @@ namespace Ordering.SignalrHub return new AutofacServiceProvider(container.Build()); } - public IConfiguration Configuration { get; } - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) { - loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); + //loggerFactory.AddConsole(Configuration.GetSection("Logging")); + //loggerFactory.AddDebug(); + //loggerFactory.AddAzureWebAppDiagnostics(); + //loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); var pathBase = Configuration["PATH_BASE"]; if (!string.IsNullOrEmpty(pathBase)) { - loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'"); + loggerFactory.CreateLogger().LogDebug("Using PATH BASE '{pathBase}'", pathBase); app.UsePathBase(pathBase); } @@ -156,13 +159,13 @@ namespace Ordering.SignalrHub private void ConfigureEventBus(IApplicationBuilder app) { var eventBus = app.ApplicationServices.GetRequiredService(); - + eventBus.Subscribe(); eventBus.Subscribe(); eventBus.Subscribe(); eventBus.Subscribe(); eventBus.Subscribe(); - eventBus.Subscribe(); + eventBus.Subscribe(); } private void ConfigureAuthService(IServiceCollection services) diff --git a/src/Services/Ordering/Ordering.SignalrHub/appsettings.json b/src/Services/Ordering/Ordering.SignalrHub/appsettings.json index ab02fda0f..bd752de48 100644 --- a/src/Services/Ordering/Ordering.SignalrHub/appsettings.json +++ b/src/Services/Ordering/Ordering.SignalrHub/appsettings.json @@ -1,11 +1,14 @@ { "IdentityUrl": "http://localhost:5105", - "Logging": { - "IncludeScopes": false, - "LogLevel": { - "Default": "Trace", - "System": "Information", - "Microsoft": "Information" + "Serilog": { + "SeqServerUrl": null, + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Warning", + "Microsoft.eShopOnContainers": "Information", + "System": "Warning" + } } }, "AzureServiceBusEnabled": false, diff --git a/src/Services/Ordering/Ordering.UnitTests/Application/IdentifiedCommandHandlerTest.cs b/src/Services/Ordering/Ordering.UnitTests/Application/IdentifiedCommandHandlerTest.cs index e0f861017..95bc4cc81 100644 --- a/src/Services/Ordering/Ordering.UnitTests/Application/IdentifiedCommandHandlerTest.cs +++ b/src/Services/Ordering/Ordering.UnitTests/Application/IdentifiedCommandHandlerTest.cs @@ -8,6 +8,7 @@ namespace UnitTest.Ordering.Application using MediatR; using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; + using Microsoft.Extensions.Logging; using Moq; using System.Collections; using System.Collections.Generic; @@ -17,11 +18,13 @@ namespace UnitTest.Ordering.Application { private readonly Mock _requestManager; private readonly Mock _mediator; + private readonly Mock>> _loggerMock; public IdentifiedCommandHandlerTest() { _requestManager = new Mock(); _mediator = new Mock(); + _loggerMock = new Mock>>(); } [Fact] @@ -38,7 +41,7 @@ namespace UnitTest.Ordering.Application .Returns(Task.FromResult(true)); //Act - var handler = new IdentifiedCommandHandler(_mediator.Object, _requestManager.Object); + var handler = new IdentifiedCommandHandler(_mediator.Object, _requestManager.Object, _loggerMock.Object); var cltToken = new System.Threading.CancellationToken(); var result = await handler.Handle(fakeOrderCmd, cltToken); @@ -61,7 +64,7 @@ namespace UnitTest.Ordering.Application .Returns(Task.FromResult(true)); //Act - var handler = new IdentifiedCommandHandler(_mediator.Object, _requestManager.Object); + var handler = new IdentifiedCommandHandler(_mediator.Object, _requestManager.Object, _loggerMock.Object); var cltToken = new System.Threading.CancellationToken(); var result = await handler.Handle(fakeOrderCmd, cltToken); diff --git a/src/Services/Ordering/Ordering.UnitTests/Application/NewOrderCommandHandlerTest.cs b/src/Services/Ordering/Ordering.UnitTests/Application/NewOrderCommandHandlerTest.cs index 80a0deb25..77fede857 100644 --- a/src/Services/Ordering/Ordering.UnitTests/Application/NewOrderCommandHandlerTest.cs +++ b/src/Services/Ordering/Ordering.UnitTests/Application/NewOrderCommandHandlerTest.cs @@ -13,6 +13,7 @@ namespace UnitTest.Ordering.Application using global::Ordering.API.Application.IntegrationEvents; using global::Ordering.API.Application.Models; using MediatR; + using Microsoft.Extensions.Logging; using System.Collections; using System.Collections.Generic; using Xunit; @@ -50,8 +51,9 @@ namespace UnitTest.Ordering.Application _identityServiceMock.Setup(svc => svc.GetUserIdentity()).Returns(buyerId); + var LoggerMock = new Mock>(); //Act - var handler = new CreateOrderCommandHandler(_mediator.Object, _orderingIntegrationEventService.Object, _orderRepositoryMock.Object, _identityServiceMock.Object); + var handler = new CreateOrderCommandHandler(_mediator.Object, _orderingIntegrationEventService.Object, _orderRepositoryMock.Object, _identityServiceMock.Object, LoggerMock.Object); var cltToken = new System.Threading.CancellationToken(); var result = await handler.Handle(fakeOrderCmd, cltToken); diff --git a/src/Services/Ordering/Ordering.UnitTests/Application/OrdersWebApiTest.cs b/src/Services/Ordering/Ordering.UnitTests/Application/OrdersWebApiTest.cs index c5b8a1764..b19a764e9 100644 --- a/src/Services/Ordering/Ordering.UnitTests/Application/OrdersWebApiTest.cs +++ b/src/Services/Ordering/Ordering.UnitTests/Application/OrdersWebApiTest.cs @@ -4,6 +4,7 @@ using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries; using Microsoft.eShopOnContainers.Services.Ordering.API.Controllers; using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services; +using Microsoft.Extensions.Logging; using Moq; using Ordering.API.Application.Commands; using System; @@ -19,12 +20,14 @@ namespace UnitTest.Ordering.Application private readonly Mock _mediatorMock; private readonly Mock _orderQueriesMock; private readonly Mock _identityServiceMock; + private readonly Mock> _loggerMock; public OrdersWebApiTest() { _mediatorMock = new Mock(); _orderQueriesMock = new Mock(); _identityServiceMock = new Mock(); + _loggerMock = new Mock>(); } [Fact] @@ -35,7 +38,7 @@ namespace UnitTest.Ordering.Application .Returns(Task.FromResult(true)); //Act - var orderController = new OrdersController(_mediatorMock.Object, _orderQueriesMock.Object, _identityServiceMock.Object); + var orderController = new OrdersController(_mediatorMock.Object, _orderQueriesMock.Object, _identityServiceMock.Object, _loggerMock.Object); var actionResult = await orderController.CancelOrderAsync(new CancelOrderCommand(1), Guid.NewGuid().ToString()) as OkResult; //Assert @@ -51,7 +54,7 @@ namespace UnitTest.Ordering.Application .Returns(Task.FromResult(true)); //Act - var orderController = new OrdersController(_mediatorMock.Object, _orderQueriesMock.Object, _identityServiceMock.Object); + var orderController = new OrdersController(_mediatorMock.Object, _orderQueriesMock.Object, _identityServiceMock.Object, _loggerMock.Object); var actionResult = await orderController.CancelOrderAsync(new CancelOrderCommand(1), String.Empty) as BadRequestResult; //Assert @@ -66,7 +69,7 @@ namespace UnitTest.Ordering.Application .Returns(Task.FromResult(true)); //Act - var orderController = new OrdersController(_mediatorMock.Object, _orderQueriesMock.Object, _identityServiceMock.Object); + var orderController = new OrdersController(_mediatorMock.Object, _orderQueriesMock.Object, _identityServiceMock.Object, _loggerMock.Object); var actionResult = await orderController.ShipOrderAsync(new ShipOrderCommand(1), Guid.NewGuid().ToString()) as OkResult; //Assert @@ -82,7 +85,7 @@ namespace UnitTest.Ordering.Application .Returns(Task.FromResult(true)); //Act - var orderController = new OrdersController(_mediatorMock.Object, _orderQueriesMock.Object, _identityServiceMock.Object); + var orderController = new OrdersController(_mediatorMock.Object, _orderQueriesMock.Object, _identityServiceMock.Object, _loggerMock.Object); var actionResult = await orderController.ShipOrderAsync(new ShipOrderCommand(1), String.Empty) as BadRequestResult; //Assert @@ -102,7 +105,7 @@ namespace UnitTest.Ordering.Application .Returns(Task.FromResult(fakeDynamicResult)); //Act - var orderController = new OrdersController(_mediatorMock.Object, _orderQueriesMock.Object, _identityServiceMock.Object); + var orderController = new OrdersController(_mediatorMock.Object, _orderQueriesMock.Object, _identityServiceMock.Object, _loggerMock.Object); var actionResult = await orderController.GetOrdersAsync(); //Assert @@ -119,7 +122,7 @@ namespace UnitTest.Ordering.Application .Returns(Task.FromResult(fakeDynamicResult)); //Act - var orderController = new OrdersController(_mediatorMock.Object, _orderQueriesMock.Object, _identityServiceMock.Object); + var orderController = new OrdersController(_mediatorMock.Object, _orderQueriesMock.Object, _identityServiceMock.Object, _loggerMock.Object); var actionResult = await orderController.GetOrderAsync(fakeOrderId) as OkObjectResult; //Assert @@ -135,7 +138,7 @@ namespace UnitTest.Ordering.Application .Returns(Task.FromResult(fakeDynamicResult)); //Act - var orderController = new OrdersController(_mediatorMock.Object, _orderQueriesMock.Object, _identityServiceMock.Object); + var orderController = new OrdersController(_mediatorMock.Object, _orderQueriesMock.Object, _identityServiceMock.Object, _loggerMock.Object); var actionResult = await orderController.GetCardTypesAsync(); //Assert diff --git a/src/Services/Ordering/Ordering.UnitTests/Ordering.UnitTests.csproj b/src/Services/Ordering/Ordering.UnitTests/Ordering.UnitTests.csproj index ac61a4ec3..0a35885a8 100644 --- a/src/Services/Ordering/Ordering.UnitTests/Ordering.UnitTests.csproj +++ b/src/Services/Ordering/Ordering.UnitTests/Ordering.UnitTests.csproj @@ -7,7 +7,7 @@ - + all diff --git a/src/Services/Payment/Payment.API/IntegrationEvents/EventHandling/OrderStatusChangedToStockConfirmedIntegrationEventHandler.cs b/src/Services/Payment/Payment.API/IntegrationEvents/EventHandling/OrderStatusChangedToStockConfirmedIntegrationEventHandler.cs index c70a32093..b26d64d40 100644 --- a/src/Services/Payment/Payment.API/IntegrationEvents/EventHandling/OrderStatusChangedToStockConfirmedIntegrationEventHandler.cs +++ b/src/Services/Payment/Payment.API/IntegrationEvents/EventHandling/OrderStatusChangedToStockConfirmedIntegrationEventHandler.cs @@ -2,45 +2,58 @@ { using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; + using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Payment.API.IntegrationEvents.Events; + using Serilog.Context; using System.Threading.Tasks; - public class OrderStatusChangedToStockConfirmedIntegrationEventHandler : + public class OrderStatusChangedToStockConfirmedIntegrationEventHandler : IIntegrationEventHandler { private readonly IEventBus _eventBus; private readonly PaymentSettings _settings; + private readonly ILogger _logger; - public OrderStatusChangedToStockConfirmedIntegrationEventHandler(IEventBus eventBus, - IOptionsSnapshot settings) + public OrderStatusChangedToStockConfirmedIntegrationEventHandler( + IEventBus eventBus, + IOptionsSnapshot settings, + ILogger logger) { _eventBus = eventBus; _settings = settings.Value; - } + _logger = logger ?? throw new System.ArgumentNullException(nameof(logger)); + } public async Task Handle(OrderStatusChangedToStockConfirmedIntegrationEvent @event) { - IntegrationEvent orderPaymentIntegrationEvent; + using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) + { + _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); - //Business feature comment: - // When OrderStatusChangedToStockConfirmed Integration Event is handled. - // Here we're simulating that we'd be performing the payment against any payment gateway - // Instead of a real payment we just take the env. var to simulate the payment - // The payment can be successful or it can fail + IntegrationEvent orderPaymentIntegrationEvent; - if (_settings.PaymentSucceded) - { - orderPaymentIntegrationEvent = new OrderPaymentSuccededIntegrationEvent(@event.OrderId); - } - else - { - orderPaymentIntegrationEvent = new OrderPaymentFailedIntegrationEvent(@event.OrderId); - } + //Business feature comment: + // When OrderStatusChangedToStockConfirmed Integration Event is handled. + // Here we're simulating that we'd be performing the payment against any payment gateway + // Instead of a real payment we just take the env. var to simulate the payment + // The payment can be successful or it can fail + + if (_settings.PaymentSucceded) + { + orderPaymentIntegrationEvent = new OrderPaymentSuccededIntegrationEvent(@event.OrderId); + } + else + { + orderPaymentIntegrationEvent = new OrderPaymentFailedIntegrationEvent(@event.OrderId); + } - _eventBus.Publish(orderPaymentIntegrationEvent); + _logger.LogInformation("----- Publishing integration event: {IntegrationEventId} from {AppName} - ({@IntegrationEvent})", orderPaymentIntegrationEvent.Id, Program.AppName, orderPaymentIntegrationEvent); - await Task.CompletedTask; + _eventBus.Publish(orderPaymentIntegrationEvent); + + await Task.CompletedTask; + } } } } \ No newline at end of file diff --git a/src/Services/Payment/Payment.API/Payment.API.csproj b/src/Services/Payment/Payment.API/Payment.API.csproj index a91fc6797..0ff842f74 100644 --- a/src/Services/Payment/Payment.API/Payment.API.csproj +++ b/src/Services/Payment/Payment.API/Payment.API.csproj @@ -7,20 +7,23 @@ + + - - - - + + + - - + + + + diff --git a/src/Services/Payment/Payment.API/Program.cs b/src/Services/Payment/Payment.API/Program.cs index 7a589e8a4..9b0aded5c 100644 --- a/src/Services/Payment/Payment.API/Program.cs +++ b/src/Services/Payment/Payment.API/Program.cs @@ -4,40 +4,85 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Serilog; +using System; using System.IO; namespace Payment.API { public class Program { - public static void Main(string[] args) + public static readonly string Namespace = typeof(Program).Namespace; + public static readonly string AppName = Namespace; + + public static int Main(string[] args) { - BuildWebHost(args).Run(); + var configuration = GetConfiguration(); + + Log.Logger = CreateSerilogLogger(configuration); + + try + { + Log.Information("Configuring web host ({ApplicationContext})...", AppName); + var host = BuildWebHost(configuration, args); + + Log.Information("Starting web host ({ApplicationContext})...", AppName); + host.Run(); + + return 0; + } + catch (Exception ex) + { + Log.Fatal(ex, "Program terminated unexpectedly ({ApplicationContext})!", AppName); + return 1; + } + finally + { + Log.CloseAndFlush(); + } } - public static IWebHost BuildWebHost(string[] args) => + private static IWebHost BuildWebHost(IConfiguration configuration, string[] args) => WebHost.CreateDefaultBuilder(args) - .UseContentRoot(Directory.GetCurrentDirectory()) + .CaptureStartupErrors(false) .UseStartup() - .ConfigureAppConfiguration((builderContext, config) => - { - config.AddEnvironmentVariables(); - }) - .ConfigureLogging((hostingContext, builder) => - { - builder.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); - builder.AddConsole(); - builder.AddDebug(); - builder.AddAzureWebAppDiagnostics(); - }) .UseApplicationInsights() - .UseSerilog((builderContext, config) => - { - config - .MinimumLevel.Information() - .Enrich.FromLogContext() - .WriteTo.Console(); - }) + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseConfiguration(configuration) + .UseSerilog() .Build(); + + private static Serilog.ILogger CreateSerilogLogger(IConfiguration configuration) + { + var seqServerUrl = configuration["Serilog:SeqServerUrl"]; + + return new LoggerConfiguration() + .MinimumLevel.Verbose() + .Enrich.WithProperty("ApplicationContext", AppName) + .Enrich.FromLogContext() + .WriteTo.Console() + .WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl) + .ReadFrom.Configuration(configuration) + .CreateLogger(); + } + + private static IConfiguration GetConfiguration() + { + var builder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .AddEnvironmentVariables(); + + var config = builder.Build(); + + if (config.GetValue("UseVault", false)) + { + builder.AddAzureKeyVault( + $"https://{config["Vault:Name"]}.vault.azure.net/", + config["Vault:ClientId"], + config["Vault:ClientSecret"]); + } + + return builder.Build(); + } } -} +} \ No newline at end of file diff --git a/src/Services/Payment/Payment.API/Startup.cs b/src/Services/Payment/Payment.API/Startup.cs index 62a3b2321..4d5010868 100644 --- a/src/Services/Payment/Payment.API/Startup.cs +++ b/src/Services/Payment/Payment.API/Startup.cs @@ -91,7 +91,8 @@ namespace Payment.API // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { - loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); + //loggerFactory.AddAzureWebAppDiagnostics(); + //loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); var pathBase = Configuration["PATH_BASE"]; if (!string.IsNullOrEmpty(pathBase)) diff --git a/src/Services/Payment/Payment.API/appsettings.json b/src/Services/Payment/Payment.API/appsettings.json index acc9a2be0..bf6157648 100644 --- a/src/Services/Payment/Payment.API/appsettings.json +++ b/src/Services/Payment/Payment.API/appsettings.json @@ -1,8 +1,13 @@ { - "Logging": { - "IncludeScopes": false, - "LogLevel": { - "Default": "Warning" + "Serilog": { + "SeqServerUrl": null, + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Warning", + "Microsoft.eShopOnContainers": "Information", + "System": "Warning" + } } }, "PaymentSucceded": true, diff --git a/src/Services/Webhooks/Webhooks.API/Webhooks.API.csproj b/src/Services/Webhooks/Webhooks.API/Webhooks.API.csproj index f65e0c441..4329edaec 100644 --- a/src/Services/Webhooks/Webhooks.API/Webhooks.API.csproj +++ b/src/Services/Webhooks/Webhooks.API/Webhooks.API.csproj @@ -9,9 +9,8 @@ - - - + + diff --git a/src/Web/WebMVC/Controllers/AccountController.cs b/src/Web/WebMVC/Controllers/AccountController.cs index cf22de133..0f214b8ea 100644 --- a/src/Web/WebMVC/Controllers/AccountController.cs +++ b/src/Web/WebMVC/Controllers/AccountController.cs @@ -16,7 +16,7 @@ namespace Microsoft.eShopOnContainers.WebMVC.Controllers public async Task SignIn(string returnUrl) { var user = User as ClaimsPrincipal; - + var token = await HttpContext.GetTokenAsync("access_token"); if (token != null) @@ -33,11 +33,11 @@ namespace Microsoft.eShopOnContainers.WebMVC.Controllers { await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); await HttpContext.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme); - + // "Catalog" because UrlHelper doesn't support nameof() for controllers // https://github.com/aspnet/Mvc/issues/5853 var homeUrl = Url.Action(nameof(CatalogController.Index), "Catalog"); - return new SignOutResult(OpenIdConnectDefaults.AuthenticationScheme, + return new SignOutResult(OpenIdConnectDefaults.AuthenticationScheme, new AspNetCore.Authentication.AuthenticationProperties { RedirectUri = homeUrl }); } } diff --git a/src/Web/WebMVC/Controllers/CampaignsController.cs b/src/Web/WebMVC/Controllers/CampaignsController.cs index 058012d26..d26e60f94 100644 --- a/src/Web/WebMVC/Controllers/CampaignsController.cs +++ b/src/Web/WebMVC/Controllers/CampaignsController.cs @@ -2,7 +2,7 @@ namespace Microsoft.eShopOnContainers.WebMVC.Controllers { using AspNetCore.Authorization; using AspNetCore.Mvc; - using global::WebMVC.Models; + using global::WebMVC.Services.ModelDTOs; using global::WebMVC.Services; using global::WebMVC.ViewModels; using Microsoft.Extensions.Options; diff --git a/src/Web/WebMVC/Controllers/OrderManagementController.cs b/src/Web/WebMVC/Controllers/OrderManagementController.cs index abd0b21ab..7d61b0221 100644 --- a/src/Web/WebMVC/Controllers/OrderManagementController.cs +++ b/src/Web/WebMVC/Controllers/OrderManagementController.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; -using WebMVC.Models; +using WebMVC.Services.ModelDTOs; using Microsoft.eShopOnContainers.WebMVC.Services; using Microsoft.eShopOnContainers.WebMVC.ViewModels; using Microsoft.AspNetCore.Authorization; diff --git a/src/Web/WebMVC/Infrastructure/HttpClientRequestIdDelegatingHandler.cs b/src/Web/WebMVC/Infrastructure/HttpClientRequestIdDelegatingHandler.cs index 17e4591a1..37bea4d30 100644 --- a/src/Web/WebMVC/Infrastructure/HttpClientRequestIdDelegatingHandler.cs +++ b/src/Web/WebMVC/Infrastructure/HttpClientRequestIdDelegatingHandler.cs @@ -17,7 +17,10 @@ namespace WebMVC.Infrastructure { if (request.Method == HttpMethod.Post || request.Method == HttpMethod.Put) { - request.Headers.Add("x-requestid", Guid.NewGuid().ToString()); + if (!request.Headers.Contains("x-requestid")) + { + request.Headers.Add("x-requestid", Guid.NewGuid().ToString()); + } } return await base.SendAsync(request, cancellationToken); diff --git a/src/Web/WebMVC/Infrastructure/WebContextSeed.cs b/src/Web/WebMVC/Infrastructure/WebContextSeed.cs index 65063e34f..c34730ac1 100644 --- a/src/Web/WebMVC/Infrastructure/WebContextSeed.cs +++ b/src/Web/WebMVC/Infrastructure/WebContextSeed.cs @@ -15,7 +15,7 @@ namespace WebMVC.Infrastructure { public static void Seed(IApplicationBuilder applicationBuilder, IHostingEnvironment env, ILoggerFactory loggerFactory) { - var log = loggerFactory.CreateLogger("WebMVC seed"); + var log = loggerFactory.CreateLogger(); var settings = (AppSettings)applicationBuilder .ApplicationServices.GetRequiredService>().Value; @@ -39,7 +39,7 @@ namespace WebMVC.Infrastructure string overrideCssFile = Path.Combine(contentRootPath, "Setup", "override.css"); if (!File.Exists(overrideCssFile)) { - log.LogError($" override css file '{overrideCssFile}' does not exists."); + log.LogError("Override css file '{FileName}' does not exists.", overrideCssFile); return; } @@ -48,7 +48,7 @@ namespace WebMVC.Infrastructure } catch (Exception ex) { - log.LogError($"Exception in method GetPreconfiguredCSS WebMVC. Exception Message={ex.Message}"); + log.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); } } @@ -59,7 +59,7 @@ namespace WebMVC.Infrastructure string imagesZipFile = Path.Combine(contentRootPath, "Setup", "images.zip"); if (!File.Exists(imagesZipFile)) { - log.LogError($" zip file '{imagesZipFile}' does not exists."); + log.LogError("Zip file '{ZipFileName}' does not exists.", imagesZipFile); return; } @@ -81,14 +81,14 @@ namespace WebMVC.Infrastructure } else { - log.LogWarning($"Skip file '{entry.Name}' in zipfile '{imagesZipFile}'"); + log.LogWarning("Skipped file '{FileName}' in zipfile '{ZipFileName}'", entry.Name, imagesZipFile); } } } } catch ( Exception ex ) { - log.LogError($"Exception in method GetPreconfiguredImages WebMVC. Exception Message={ex.Message}"); + log.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); } } diff --git a/src/Web/WebMVC/Program.cs b/src/Web/WebMVC/Program.cs index 5000e0422..c329237ce 100644 --- a/src/Web/WebMVC/Program.cs +++ b/src/Web/WebMVC/Program.cs @@ -3,40 +3,74 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Serilog; +using System; using System.IO; namespace Microsoft.eShopOnContainers.WebMVC { public class Program { - public static void Main(string[] args) + public static readonly string Namespace = typeof(Program).Namespace; + public static readonly string AppName = Namespace.Substring(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1); + + public static int Main(string[] args) { - BuildWebHost(args).Run(); + var configuration = GetConfiguration(); + + Log.Logger = CreateSerilogLogger(configuration); + + try + { + Log.Information("Configuring web host ({ApplicationContext})...", AppName); + var host = BuildWebHost(configuration, args); + + Log.Information("Starting web host ({ApplicationContext})...", AppName); + host.Run(); + + return 0; + } + catch (Exception ex) + { + Log.Fatal(ex, "Program terminated unexpectedly ({ApplicationContext})!", AppName); + return 1; + } + finally + { + Log.CloseAndFlush(); + } } - public static IWebHost BuildWebHost(string[] args) => + private static IWebHost BuildWebHost(IConfiguration configuration, string[] args) => WebHost.CreateDefaultBuilder(args) - .UseContentRoot(Directory.GetCurrentDirectory()) + .CaptureStartupErrors(false) .UseStartup() - .ConfigureAppConfiguration((builderContext, config) => - { - config.AddEnvironmentVariables(); - }) - .ConfigureLogging((hostingContext, builder) => - { - builder.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); - builder.AddConsole(); - builder.AddDebug(); - builder.AddAzureWebAppDiagnostics(); - }) .UseApplicationInsights() - .UseSerilog((builderContext, config) => - { - config - .MinimumLevel.Information() - .Enrich.FromLogContext() - .WriteTo.Console(); - }) + .UseConfiguration(configuration) + .UseSerilog() .Build(); + + private static Serilog.ILogger CreateSerilogLogger(IConfiguration configuration) + { + var seqServerUrl = configuration["Serilog:SeqServerUrl"]; + + return new LoggerConfiguration() + .MinimumLevel.Verbose() + .Enrich.WithProperty("ApplicationContext", AppName) + .Enrich.FromLogContext() + .WriteTo.Console() + .WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl) + .ReadFrom.Configuration(configuration) + .CreateLogger(); + } + + private static IConfiguration GetConfiguration() + { + var builder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .AddEnvironmentVariables(); + + return builder.Build(); + } } -} +} \ No newline at end of file diff --git a/src/Web/WebMVC/Services/BasketService.cs b/src/Web/WebMVC/Services/BasketService.cs index 41a0fd207..2ee26eff6 100644 --- a/src/Web/WebMVC/Services/BasketService.cs +++ b/src/Web/WebMVC/Services/BasketService.cs @@ -6,7 +6,7 @@ using System.Linq; using System.Net.Http; using System.Threading.Tasks; using WebMVC.Infrastructure; -using WebMVC.Models; +using WebMVC.Services.ModelDTOs; namespace Microsoft.eShopOnContainers.WebMVC.Services { diff --git a/src/Web/WebMVC/Services/IBasketService.cs b/src/Web/WebMVC/Services/IBasketService.cs index cfbea5ff0..8c096bfaf 100644 --- a/src/Web/WebMVC/Services/IBasketService.cs +++ b/src/Web/WebMVC/Services/IBasketService.cs @@ -3,7 +3,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using WebMVC.Models; +using WebMVC.Services.ModelDTOs; namespace Microsoft.eShopOnContainers.WebMVC.Services { diff --git a/src/Web/WebMVC/Services/ILocationService.cs b/src/Web/WebMVC/Services/ILocationService.cs index ac2295e10..d5f7b7224 100644 --- a/src/Web/WebMVC/Services/ILocationService.cs +++ b/src/Web/WebMVC/Services/ILocationService.cs @@ -1,5 +1,5 @@ using System.Threading.Tasks; -using WebMVC.Models; +using WebMVC.Services.ModelDTOs; namespace WebMVC.Services { diff --git a/src/Web/WebMVC/Services/IOrderingService.cs b/src/Web/WebMVC/Services/IOrderingService.cs index 1de2c631c..480ea4119 100644 --- a/src/Web/WebMVC/Services/IOrderingService.cs +++ b/src/Web/WebMVC/Services/IOrderingService.cs @@ -3,7 +3,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using WebMVC.Models; +using WebMVC.Services.ModelDTOs; namespace Microsoft.eShopOnContainers.WebMVC.Services { diff --git a/src/Web/WebMVC/Services/LocationService.cs b/src/Web/WebMVC/Services/LocationService.cs index 3e58ef125..8d81cbd22 100644 --- a/src/Web/WebMVC/Services/LocationService.cs +++ b/src/Web/WebMVC/Services/LocationService.cs @@ -6,7 +6,7 @@ using Newtonsoft.Json; using System.Net.Http; using System.Threading.Tasks; using WebMVC.Infrastructure; -using WebMVC.Models; +using WebMVC.Services.ModelDTOs; namespace WebMVC.Services { diff --git a/src/Web/WebMVC/Models/BasketDTO.cs b/src/Web/WebMVC/Services/ModelDTOs/BasketDTO.cs similarity index 95% rename from src/Web/WebMVC/Models/BasketDTO.cs rename to src/Web/WebMVC/Services/ModelDTOs/BasketDTO.cs index 4609c8533..130a729f5 100644 --- a/src/Web/WebMVC/Models/BasketDTO.cs +++ b/src/Web/WebMVC/Services/ModelDTOs/BasketDTO.cs @@ -1,7 +1,7 @@ using System; using System.ComponentModel.DataAnnotations; -namespace WebMVC.Models +namespace WebMVC.Services.ModelDTOs { public class BasketDTO { diff --git a/src/Web/WebMVC/Models/LocationDTO.cs b/src/Web/WebMVC/Services/ModelDTOs/LocationDTO.cs similarity index 86% rename from src/Web/WebMVC/Models/LocationDTO.cs rename to src/Web/WebMVC/Services/ModelDTOs/LocationDTO.cs index 88169c421..40c9f07a8 100644 --- a/src/Web/WebMVC/Models/LocationDTO.cs +++ b/src/Web/WebMVC/Services/ModelDTOs/LocationDTO.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -namespace WebMVC.Models +namespace WebMVC.Services.ModelDTOs { public class LocationDTO { diff --git a/src/Web/WebMVC/Models/OrderDTO.cs b/src/Web/WebMVC/Services/ModelDTOs/OrderDTO.cs similarity index 82% rename from src/Web/WebMVC/Models/OrderDTO.cs rename to src/Web/WebMVC/Services/ModelDTOs/OrderDTO.cs index 13646ea38..5f925a8ee 100644 --- a/src/Web/WebMVC/Models/OrderDTO.cs +++ b/src/Web/WebMVC/Services/ModelDTOs/OrderDTO.cs @@ -1,7 +1,7 @@ using System; using System.ComponentModel.DataAnnotations; -namespace WebMVC.Models +namespace WebMVC.Services.ModelDTOs { public class OrderDTO { diff --git a/src/Web/WebMVC/Models/OrderProcessAction.cs b/src/Web/WebMVC/Services/ModelDTOs/OrderProcessAction.cs similarity index 93% rename from src/Web/WebMVC/Models/OrderProcessAction.cs rename to src/Web/WebMVC/Services/ModelDTOs/OrderProcessAction.cs index bd746bb36..c1309d59b 100644 --- a/src/Web/WebMVC/Models/OrderProcessAction.cs +++ b/src/Web/WebMVC/Services/ModelDTOs/OrderProcessAction.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -namespace WebMVC.Models +namespace WebMVC.Services.ModelDTOs { public class OrderProcessAction { diff --git a/src/Web/WebMVC/Services/OrderingService.cs b/src/Web/WebMVC/Services/OrderingService.cs index e3d24422f..489946db5 100644 --- a/src/Web/WebMVC/Services/OrderingService.cs +++ b/src/Web/WebMVC/Services/OrderingService.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using System.Net.Http; using System.Threading.Tasks; using WebMVC.Infrastructure; -using WebMVC.Models; +using WebMVC.Services.ModelDTOs; namespace Microsoft.eShopOnContainers.WebMVC.Services { diff --git a/src/Web/WebMVC/Startup.cs b/src/Web/WebMVC/Startup.cs index 0ec33bc8c..6156bdf09 100644 --- a/src/Web/WebMVC/Startup.cs +++ b/src/Web/WebMVC/Startup.cs @@ -52,7 +52,8 @@ namespace Microsoft.eShopOnContainers.WebMVC { JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); - loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); + //loggerFactory.AddAzureWebAppDiagnostics(); + //loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); app.UseHealthChecks("/hc", new HealthCheckOptions() { @@ -73,7 +74,7 @@ namespace Microsoft.eShopOnContainers.WebMVC var pathBase = Configuration["PATH_BASE"]; if (!string.IsNullOrEmpty(pathBase)) { - loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'"); + loggerFactory.CreateLogger().LogDebug("Using PATH BASE '{PathBase}'", pathBase); app.UsePathBase(pathBase); } @@ -92,8 +93,6 @@ namespace Microsoft.eShopOnContainers.WebMVC app.UseAuthentication(); - var log = loggerFactory.CreateLogger("identity"); - WebContextSeed.Seed(app, env, loggerFactory); app.UseHttpsRedirection(); diff --git a/src/Web/WebMVC/ViewModels/Order.cs b/src/Web/WebMVC/ViewModels/Order.cs index 28dbe9968..2beaf43b1 100644 --- a/src/Web/WebMVC/ViewModels/Order.cs +++ b/src/Web/WebMVC/ViewModels/Order.cs @@ -7,7 +7,7 @@ using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Threading.Tasks; -using WebMVC.Models; +using WebMVC.Services.ModelDTOs; namespace Microsoft.eShopOnContainers.WebMVC.ViewModels { diff --git a/src/Web/WebMVC/WebMVC.csproj b/src/Web/WebMVC/WebMVC.csproj index 0dc03e164..317a0140a 100644 --- a/src/Web/WebMVC/WebMVC.csproj +++ b/src/Web/WebMVC/WebMVC.csproj @@ -20,10 +20,10 @@ - + - - + + @@ -37,7 +37,10 @@ + + + diff --git a/src/Web/WebMVC/appsettings.json b/src/Web/WebMVC/appsettings.json index 161e247a6..591964725 100644 --- a/src/Web/WebMVC/appsettings.json +++ b/src/Web/WebMVC/appsettings.json @@ -11,12 +11,15 @@ "UseLoadTest": false, "ActivateCampaignDetailFunction": "False", "UseCustomizationData": false, - "Logging": { - "IncludeScopes": false, - "LogLevel": { - "Default": "Trace", - "System": "Information", - "Microsoft": "Information" + "Serilog": { + "SeqServerUrl": null, + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Warning", + "Microsoft.eShopOnContainers": "Information", + "System": "Warning" + } } }, "ApplicationInsights": { diff --git a/src/Web/WebMonolithic/eShopWeb/Infrastructure/CatalogContextSeed.cs b/src/Web/WebMonolithic/eShopWeb/Infrastructure/CatalogContextSeed.cs index c67baebc3..e95fdd8f4 100644 --- a/src/Web/WebMonolithic/eShopWeb/Infrastructure/CatalogContextSeed.cs +++ b/src/Web/WebMonolithic/eShopWeb/Infrastructure/CatalogContextSeed.cs @@ -51,7 +51,7 @@ { retryForAvaiability++; var log = loggerFactory.CreateLogger("catalog seed"); - log.LogError(ex.Message); + log.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); await SeedAsync(applicationBuilder, loggerFactory, retryForAvaiability); } } diff --git a/src/Web/WebSPA/Server/Infrastructure/WebContextSeed.cs b/src/Web/WebSPA/Server/Infrastructure/WebContextSeed.cs index a2fde45b3..74de66b5c 100644 --- a/src/Web/WebSPA/Server/Infrastructure/WebContextSeed.cs +++ b/src/Web/WebSPA/Server/Infrastructure/WebContextSeed.cs @@ -15,7 +15,7 @@ namespace WebSPA.Infrastructure { public static void Seed(IApplicationBuilder applicationBuilder, IHostingEnvironment env, ILoggerFactory loggerFactory) { - var log = loggerFactory.CreateLogger("WebSPA seed"); + var log = loggerFactory.CreateLogger(); var settings = (AppSettings)applicationBuilder .ApplicationServices.GetRequiredService>().Value; @@ -37,7 +37,7 @@ namespace WebSPA.Infrastructure string imagesZipFile = Path.Combine(contentRootPath, "Setup", "images.zip"); if (!File.Exists(imagesZipFile)) { - log.LogError($" zip file '{imagesZipFile}' does not exists."); + log.LogError("Zip file '{ZipFileName}' does not exists.", imagesZipFile); return; } @@ -59,14 +59,14 @@ namespace WebSPA.Infrastructure } else { - log.LogWarning($"Skip file '{entry.Name}' in zipfile '{imagesZipFile}'"); + log.LogWarning("Skipped file '{FileName}' in zipfile '{ZipFileName}'", entry.Name, imagesZipFile); } } } } catch (Exception ex) { - log.LogError($"Exception in method GetPreconfiguredImages WebSPA. Exception Message={ex.Message}"); + log.LogError(ex, "ERROR in GetPreconfiguredImages: {Message}", ex.Message); } } } diff --git a/src/Web/WebSPA/Startup.cs b/src/Web/WebSPA/Startup.cs index 5cfeb98fc..7a418c432 100644 --- a/src/Web/WebSPA/Startup.cs +++ b/src/Web/WebSPA/Startup.cs @@ -116,7 +116,7 @@ namespace eShopConContainers.WebSPA var pathBase = Configuration["PATH_BASE"]; if (!string.IsNullOrEmpty(pathBase)) { - loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'"); + loggerFactory.CreateLogger().LogDebug("Using PATH BASE '{pathBase}'", pathBase); app.UsePathBase(pathBase); } diff --git a/src/Web/WebSPA/WebSPA.csproj b/src/Web/WebSPA/WebSPA.csproj index 2d554810b..3afb1a033 100644 --- a/src/Web/WebSPA/WebSPA.csproj +++ b/src/Web/WebSPA/WebSPA.csproj @@ -86,16 +86,16 @@ - - - + + + - + diff --git a/src/Web/WebStatus/Program.cs b/src/Web/WebStatus/Program.cs index 11a4f02b9..d2de3ceae 100644 --- a/src/Web/WebStatus/Program.cs +++ b/src/Web/WebStatus/Program.cs @@ -3,36 +3,85 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Serilog; +using System; using System.IO; namespace WebStatus { public class Program { - public static void Main(string[] args) + public static readonly string Namespace = typeof(Program).Namespace; + public static readonly string AppName = Namespace; + + public static int Main(string[] args) { - BuildWebHost(args).Run(); + var configuration = GetConfiguration(); + + Log.Logger = CreateSerilogLogger(configuration); + + try + { + Log.Information("Configuring web host ({ApplicationContext})...", AppName); + var host = BuildWebHost(configuration, args); + + Log.Information("Starting web host ({ApplicationContext})...", AppName); + host.Run(); + + return 0; + } + catch (Exception ex) + { + Log.Fatal(ex, "Program terminated unexpectedly ({ApplicationContext})!", AppName); + return 1; + } + finally + { + Log.CloseAndFlush(); + } } - public static IWebHost BuildWebHost(string[] args) => + private static IWebHost BuildWebHost(IConfiguration configuration, string[] args) => WebHost.CreateDefaultBuilder(args) - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseStartup() - .ConfigureLogging((hostingContext, builder) => - { - builder.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); - builder.AddConsole(); - builder.AddDebug(); - builder.AddAzureWebAppDiagnostics(); - }) + .CaptureStartupErrors(false) + .UseStartup() .UseApplicationInsights() - .UseSerilog((builderContext, config) => - { - config - .MinimumLevel.Information() - .Enrich.FromLogContext() - .WriteTo.Console(); - }) + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseConfiguration(configuration) + .UseSerilog() .Build(); + + private static Serilog.ILogger CreateSerilogLogger(IConfiguration configuration) + { + var seqServerUrl = configuration["Serilog:SeqServerUrl"]; + + return new LoggerConfiguration() + .MinimumLevel.Verbose() + .Enrich.WithProperty("ApplicationContext", AppName) + .Enrich.FromLogContext() + .WriteTo.Console() + .WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl) + .ReadFrom.Configuration(configuration) + .CreateLogger(); + } + + private static IConfiguration GetConfiguration() + { + var builder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .AddEnvironmentVariables(); + + var config = builder.Build(); + + if (config.GetValue("UseVault", false)) + { + builder.AddAzureKeyVault( + $"https://{config["Vault:Name"]}.vault.azure.net/", + config["Vault:ClientId"], + config["Vault:ClientSecret"]); + } + + return builder.Build(); + } } -} +} \ No newline at end of file diff --git a/src/Web/WebStatus/Startup.cs b/src/Web/WebStatus/Startup.cs index fbfa5e74c..5b35ee84f 100644 --- a/src/Web/WebStatus/Startup.cs +++ b/src/Web/WebStatus/Startup.cs @@ -39,7 +39,8 @@ namespace WebStatus // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { - loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); + //loggerFactory.AddAzureWebAppDiagnostics(); + //loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); if (env.IsDevelopment()) { diff --git a/src/Web/WebStatus/WebStatus.csproj b/src/Web/WebStatus/WebStatus.csproj index f2b912dbb..60c6f00e2 100644 --- a/src/Web/WebStatus/WebStatus.csproj +++ b/src/Web/WebStatus/WebStatus.csproj @@ -11,20 +11,23 @@ - + - - - + + + + - - + + + + diff --git a/src/Web/WebStatus/appsettings.json b/src/Web/WebStatus/appsettings.json index 6f486424c..9ff359423 100644 --- a/src/Web/WebStatus/appsettings.json +++ b/src/Web/WebStatus/appsettings.json @@ -80,5 +80,16 @@ ], "EvaluationTimeOnSeconds": 10, "MinimumSecondsBetweenFailureNotifications": 60 + }, + "Serilog": { + "SeqServerUrl": null, + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Warning", + "Microsoft.eShopOnContainers": "Information", + "System": "Warning" + } + } } } diff --git a/src/Web/WebhookClient/WebhookClient.csproj b/src/Web/WebhookClient/WebhookClient.csproj index 895cbac54..989606184 100644 --- a/src/Web/WebhookClient/WebhookClient.csproj +++ b/src/Web/WebhookClient/WebhookClient.csproj @@ -11,7 +11,6 @@ - diff --git a/test/ServicesTests/Application.FunctionalTests/Application.FunctionalTests.csproj b/test/ServicesTests/Application.FunctionalTests/Application.FunctionalTests.csproj index 47a467300..c7ae7fd95 100644 --- a/test/ServicesTests/Application.FunctionalTests/Application.FunctionalTests.csproj +++ b/test/ServicesTests/Application.FunctionalTests/Application.FunctionalTests.csproj @@ -67,7 +67,7 @@ - + diff --git a/test/ServicesTests/Application.FunctionalTests/Services/Ordering/OrderingScenarios.cs b/test/ServicesTests/Application.FunctionalTests/Services/Ordering/OrderingScenarios.cs index b09526bf3..179717583 100644 --- a/test/ServicesTests/Application.FunctionalTests/Services/Ordering/OrderingScenarios.cs +++ b/test/ServicesTests/Application.FunctionalTests/Services/Ordering/OrderingScenarios.cs @@ -9,7 +9,7 @@ using System.Linq; using System.Net.Http; using System.Text; using System.Threading.Tasks; -using WebMVC.Models; +using WebMVC.Services.ModelDTOs; using Xunit; namespace FunctionalTests.Services.Ordering