diff --git a/backend/extensions/Squidex.Extensions/Actions/Kafka/KafkaProducer.cs b/backend/extensions/Squidex.Extensions/Actions/Kafka/KafkaProducer.cs index 053c53abb..cc74e2b90 100644 --- a/backend/extensions/Squidex.Extensions/Actions/Kafka/KafkaProducer.cs +++ b/backend/extensions/Squidex.Extensions/Actions/Kafka/KafkaProducer.cs @@ -8,7 +8,6 @@ using System.Threading.Tasks; using Confluent.Kafka; using Microsoft.Extensions.Options; -using Squidex.Infrastructure; using Squidex.Infrastructure.Log; namespace Squidex.Extensions.Actions.Kafka @@ -65,10 +64,10 @@ namespace Squidex.Extensions.Actions.Kafka break; } - log.Log(level, default, (_, w) => w - .WriteProperty("action", "KafkaAction") - .WriteProperty("name", message.Name) - .WriteProperty("message", message.Message)); + log.Log(level, null, w => w + .WriteProperty("action", "KafkaAction") + .WriteProperty("name", message.Name) + .WriteProperty("message", message.Message)); } private static void LogError(ISemanticLog log, Error error) diff --git a/backend/src/Squidex.Infrastructure/Log/Adapter/SemanticLogLogger.cs b/backend/src/Squidex.Infrastructure/Log/Adapter/SemanticLogLogger.cs index 6534a870b..8bf1ee07f 100644 --- a/backend/src/Squidex.Infrastructure/Log/Adapter/SemanticLogLogger.cs +++ b/backend/src/Squidex.Infrastructure/Log/Adapter/SemanticLogLogger.cs @@ -49,9 +49,20 @@ namespace Squidex.Infrastructure.Log.Adapter break; } + if (state is IReadOnlyList> parameters) + { + foreach (var (key, value) in parameters) + { + if (value is Exception ex && exception == null) + { + exception = ex; + } + } + } + var context = (eventId, state, exception, formatter); - semanticLog.Log(semanticLogLevel, context, (ctx, writer) => + semanticLog.Log(semanticLogLevel, context, exception, (ctx, writer) => { var message = ctx.formatter(ctx.state, ctx.exception); @@ -73,26 +84,23 @@ namespace Squidex.Infrastructure.Log.Adapter }); } - if (ctx.state is IReadOnlyList> parameters) + if (ctx.state is IReadOnlyList> parameters2) { - foreach (var (key, value) in parameters) + foreach (var (key, value) in parameters2) { if (value != null) { var trimmedName = key.Trim('{', '}', ' '); - if (trimmedName.Length > 2 && !string.Equals(trimmedName, "originalFormat", StringComparison.OrdinalIgnoreCase)) + if (trimmedName.Length > 2 && + !string.Equals(trimmedName, "exception", StringComparison.OrdinalIgnoreCase) && + !string.Equals(trimmedName, "originalFormat", StringComparison.OrdinalIgnoreCase)) { writer.WriteProperty(trimmedName.ToCamelCase(), value.ToString()); } } } } - - if (ctx.exception != null) - { - writer.WriteException(ctx.exception); - } }); } diff --git a/backend/src/Squidex.Infrastructure/Log/ApplicationInfoLogAppender.cs b/backend/src/Squidex.Infrastructure/Log/ApplicationInfoLogAppender.cs index 55af0fa92..487000916 100644 --- a/backend/src/Squidex.Infrastructure/Log/ApplicationInfoLogAppender.cs +++ b/backend/src/Squidex.Infrastructure/Log/ApplicationInfoLogAppender.cs @@ -30,7 +30,7 @@ namespace Squidex.Infrastructure.Log applicationSessionId = applicationSession.ToString(); } - public void Append(IObjectWriter writer, SemanticLogLevel logLevel) + public void Append(IObjectWriter writer, SemanticLogLevel logLevel, Exception? exception) { writer.WriteObject("app", w => w .WriteProperty("name", applicationName) diff --git a/backend/src/Squidex.Infrastructure/Log/ConstantsLogWriter.cs b/backend/src/Squidex.Infrastructure/Log/ConstantsLogWriter.cs index 4fcb839ab..9af8ebe8c 100644 --- a/backend/src/Squidex.Infrastructure/Log/ConstantsLogWriter.cs +++ b/backend/src/Squidex.Infrastructure/Log/ConstantsLogWriter.cs @@ -20,7 +20,7 @@ namespace Squidex.Infrastructure.Log this.objectWriter = objectWriter; } - public void Append(IObjectWriter writer, SemanticLogLevel logLevel) + public void Append(IObjectWriter writer, SemanticLogLevel logLevel, Exception? exception) { objectWriter(writer); } diff --git a/backend/src/Squidex.Infrastructure/Log/ILogAppender.cs b/backend/src/Squidex.Infrastructure/Log/ILogAppender.cs index d96b23fde..ca59ca47c 100644 --- a/backend/src/Squidex.Infrastructure/Log/ILogAppender.cs +++ b/backend/src/Squidex.Infrastructure/Log/ILogAppender.cs @@ -5,10 +5,12 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System; + namespace Squidex.Infrastructure.Log { public interface ILogAppender { - void Append(IObjectWriter writer, SemanticLogLevel logLevel); + void Append(IObjectWriter writer, SemanticLogLevel logLevel, Exception? exception); } } diff --git a/backend/src/Squidex.Infrastructure/Log/ISemanticLog.cs b/backend/src/Squidex.Infrastructure/Log/ISemanticLog.cs index 2dc24197f..77123f866 100644 --- a/backend/src/Squidex.Infrastructure/Log/ISemanticLog.cs +++ b/backend/src/Squidex.Infrastructure/Log/ISemanticLog.cs @@ -9,9 +9,15 @@ using System; namespace Squidex.Infrastructure.Log { + public delegate void LogFormatter(IObjectWriter writer); + + public delegate void LogFormatter(T context, IObjectWriter writer); + public interface ISemanticLog { - void Log(SemanticLogLevel logLevel, T context, Action action); + void Log(SemanticLogLevel logLevel, T context, Exception? exception, LogFormatter action); + + void Log(SemanticLogLevel logLevel, Exception? exception, LogFormatter action); ISemanticLog CreateScope(Action objectWriter); } diff --git a/backend/src/Squidex.Infrastructure/Log/SemanticLog.cs b/backend/src/Squidex.Infrastructure/Log/SemanticLog.cs index 3b0ed9f97..b592a1452 100644 --- a/backend/src/Squidex.Infrastructure/Log/SemanticLog.cs +++ b/backend/src/Squidex.Infrastructure/Log/SemanticLog.cs @@ -30,13 +30,13 @@ namespace Squidex.Infrastructure.Log Guard.NotNull(appenders); Guard.NotNull(writerFactory); + this.options = options; this.channels = channels.ToArray(); this.appenders = appenders.ToArray(); - this.options = options; this.writerFactory = writerFactory; } - public void Log(SemanticLogLevel logLevel, T context, Action action) + public void Log(SemanticLogLevel logLevel, T context, Exception? exception, LogFormatter action) { Guard.NotNull(action); @@ -45,7 +45,21 @@ namespace Squidex.Infrastructure.Log return; } - var formattedText = FormatText(logLevel, context, action); + var formattedText = FormatText(logLevel, context, exception, action); + + LogFormattedText(logLevel, formattedText); + } + + public void Log(SemanticLogLevel logLevel, Exception? exception, LogFormatter action) + { + Guard.NotNull(action); + + if (logLevel < options.Value.Level) + { + return; + } + + var formattedText = FormatText(logLevel, exception, action); LogFormattedText(logLevel, formattedText); } @@ -77,7 +91,7 @@ namespace Squidex.Infrastructure.Log } } - private string FormatText(SemanticLogLevel logLevel, T context, Action objectWriter) + private string FormatText(SemanticLogLevel logLevel, Exception? exception, LogFormatter action) { var writer = writerFactory.Create(); @@ -85,13 +99,40 @@ namespace Squidex.Infrastructure.Log { writer.WriteProperty(nameof(logLevel), logLevel.ToString()); - objectWriter(context, writer); + action(writer); for (var i = 0; i < appenders.Length; i++) { - appenders[i].Append(writer, logLevel); + appenders[i].Append(writer, logLevel, exception); } + writer.WriteException(exception); + + return writer.ToString(); + } + finally + { + writerFactory.Release(writer); + } + } + + private string FormatText(SemanticLogLevel logLevel, T context, Exception? exception, LogFormatter action) + { + var writer = writerFactory.Create(); + + try + { + writer.WriteProperty(nameof(logLevel), logLevel.ToString()); + + action(context, writer); + + for (var i = 0; i < appenders.Length; i++) + { + appenders[i].Append(writer, logLevel, exception); + } + + writer.WriteException(exception); + return writer.ToString(); } finally @@ -102,7 +143,9 @@ namespace Squidex.Infrastructure.Log public ISemanticLog CreateScope(Action objectWriter) { - return new SemanticLog(options, channels, appenders.Union(new ILogAppender[] { new ConstantsLogWriter(objectWriter) }).ToArray(), writerFactory); + var newAppenders = appenders.Union(Enumerable.Repeat(new ConstantsLogWriter(objectWriter), 1)); + + return new SemanticLog(options, channels, newAppenders, writerFactory); } } } diff --git a/backend/src/Squidex.Infrastructure/Log/SemanticLogExtensions.cs b/backend/src/Squidex.Infrastructure/Log/SemanticLogExtensions.cs index 606097fe6..205476539 100644 --- a/backend/src/Squidex.Infrastructure/Log/SemanticLogExtensions.cs +++ b/backend/src/Squidex.Infrastructure/Log/SemanticLogExtensions.cs @@ -11,114 +11,94 @@ namespace Squidex.Infrastructure.Log { public static class SemanticLogExtensions { - public static void LogTrace(this ISemanticLog log, Action objectWriter) + public static void LogTrace(this ISemanticLog log, LogFormatter action) { - log.Log(SemanticLogLevel.Trace, None.Value, (_, w) => objectWriter(w)); + log.Log(SemanticLogLevel.Trace, null, action); } - public static void LogTrace(this ISemanticLog log, T context, Action objectWriter) + public static void LogTrace(this ISemanticLog log, T context, LogFormatter action) { - log.Log(SemanticLogLevel.Trace, context, objectWriter); + log.Log(SemanticLogLevel.Trace, context, null, action); } - public static void LogDebug(this ISemanticLog log, Action objectWriter) + public static void LogDebug(this ISemanticLog log, LogFormatter action) { - log.Log(SemanticLogLevel.Debug, None.Value, (_, w) => objectWriter(w)); + log.Log(SemanticLogLevel.Debug, null, action); } - public static void LogDebug(this ISemanticLog log, T context, Action objectWriter) + public static void LogDebug(this ISemanticLog log, T context, LogFormatter action) { - log.Log(SemanticLogLevel.Debug, context, objectWriter); + log.Log(SemanticLogLevel.Debug, context, null, action); } - public static void LogInformation(this ISemanticLog log, Action objectWriter) + public static void LogInformation(this ISemanticLog log, LogFormatter action) { - log.Log(SemanticLogLevel.Information, None.Value, (_, w) => objectWriter(w)); + log.Log(SemanticLogLevel.Information, null, action); } - public static void LogInformation(this ISemanticLog log, T context, Action objectWriter) + public static void LogInformation(this ISemanticLog log, T context, LogFormatter action) { - log.Log(SemanticLogLevel.Information, context, objectWriter); + log.Log(SemanticLogLevel.Information, context, null, action); } - public static void LogWarning(this ISemanticLog log, Action objectWriter) + public static void LogWarning(this ISemanticLog log, LogFormatter action) { - log.Log(SemanticLogLevel.Warning, None.Value, (_, w) => objectWriter(w)); + log.Log(SemanticLogLevel.Warning, null, action); } - public static void LogWarning(this ISemanticLog log, T context, Action objectWriter) + public static void LogWarning(this ISemanticLog log, T context, LogFormatter action) { - log.Log(SemanticLogLevel.Warning, context, objectWriter); + log.Log(SemanticLogLevel.Warning, context, null, action); } - public static void LogWarning(this ISemanticLog log, Exception exception, Action? objectWriter = null) + public static void LogWarning(this ISemanticLog log, Exception exception, LogFormatter action) { - log.Log(SemanticLogLevel.Warning, None.Value, (_, w) => w.WriteException(exception, objectWriter)); + log.Log(SemanticLogLevel.Warning, exception, action); } - public static void LogWarning(this ISemanticLog log, Exception exception, T context, Action? objectWriter = null) + public static void LogWarning(this ISemanticLog log, Exception exception, T context, LogFormatter action) { - log.Log(SemanticLogLevel.Warning, context, (ctx, w) => w.WriteException(exception, ctx, objectWriter)); + log.Log(SemanticLogLevel.Warning, context, exception, action); } - public static void LogError(this ISemanticLog log, Action objectWriter) + public static void LogError(this ISemanticLog log, LogFormatter action) { - log.Log(SemanticLogLevel.Error, None.Value, (_, w) => objectWriter(w)); + log.Log(SemanticLogLevel.Error, null, action); } - public static void LogError(this ISemanticLog log, T context, Action objectWriter) + public static void LogError(this ISemanticLog log, T context, LogFormatter action) { - log.Log(SemanticLogLevel.Error, context, objectWriter); + log.Log(SemanticLogLevel.Error, context, null, action); } - public static void LogError(this ISemanticLog log, Exception exception, Action? objectWriter = null) + public static void LogError(this ISemanticLog log, Exception exception, LogFormatter action) { - log.Log(SemanticLogLevel.Error, None.Value, (_, w) => w.WriteException(exception, objectWriter)); + log.Log(SemanticLogLevel.Error, exception, action); } - public static void LogError(this ISemanticLog log, Exception exception, T context, Action? objectWriter = null) + public static void LogError(this ISemanticLog log, Exception exception, T context, LogFormatter action) { - log.Log(SemanticLogLevel.Error, context, (ctx, w) => w.WriteException(exception, ctx, objectWriter)); + log.Log(SemanticLogLevel.Error, context, exception, action); } - public static void LogFatal(this ISemanticLog log, Action objectWriter) + public static void LogFatal(this ISemanticLog log, LogFormatter action) { - log.Log(SemanticLogLevel.Fatal, None.Value, (_, w) => objectWriter(w)); + log.Log(SemanticLogLevel.Fatal, null, action); } - public static void LogFatal(this ISemanticLog log, T context, Action objectWriter) + public static void LogFatal(this ISemanticLog log, T context, LogFormatter action) { - log.Log(SemanticLogLevel.Fatal, context, objectWriter); + log.Log(SemanticLogLevel.Fatal, context, null, action); } - public static void LogFatal(this ISemanticLog log, Exception? exception, Action? objectWriter = null) + public static void LogFatal(this ISemanticLog log, Exception? exception, LogFormatter action) { - log.Log(SemanticLogLevel.Fatal, None.Value, (_, w) => w.WriteException(exception, objectWriter)); + log.Log(SemanticLogLevel.Fatal, exception, action); } - public static void LogFatal(this ISemanticLog log, Exception? exception, T context, Action? objectWriter = null) + public static void LogFatal(this ISemanticLog log, Exception? exception, T context, LogFormatter action) { - log.Log(SemanticLogLevel.Fatal, context, (ctx, w) => w.WriteException(exception, ctx, objectWriter)); - } - - private static void WriteException(this IObjectWriter writer, Exception? exception, Action? objectWriter) - { - objectWriter?.Invoke(writer); - - if (exception != null) - { - writer.WriteException(exception); - } - } - - private static void WriteException(this IObjectWriter writer, Exception? exception, T context, Action? objectWriter) - { - objectWriter?.Invoke(context, writer); - - if (exception != null) - { - writer.WriteException(exception); - } + log.Log(SemanticLogLevel.Fatal, context, exception, action); } public static IObjectWriter WriteException(this IObjectWriter writer, Exception? exception) @@ -144,37 +124,37 @@ namespace Squidex.Infrastructure.Log }); } - public static IDisposable MeasureTrace(this ISemanticLog log, Action objectWriter) + public static IDisposable MeasureTrace(this ISemanticLog log, LogFormatter action) { - return log.Measure(SemanticLogLevel.Trace, None.Value, (_, w) => objectWriter(w)); + return log.Measure(SemanticLogLevel.Trace, None.Value, (_, w) => action(w)); } - public static IDisposable MeasureTrace(this ISemanticLog log, T context, Action objectWriter) + public static IDisposable MeasureTrace(this ISemanticLog log, T context, LogFormatter action) { - return log.Measure(SemanticLogLevel.Trace, context, objectWriter); + return log.Measure(SemanticLogLevel.Trace, context, action); } - public static IDisposable MeasureDebug(this ISemanticLog log, Action objectWriter) + public static IDisposable MeasureDebug(this ISemanticLog log, LogFormatter action) { - return log.Measure(SemanticLogLevel.Debug, None.Value, (_, w) => objectWriter(w)); + return log.Measure(SemanticLogLevel.Debug, None.Value, (_, w) => action(w)); } - public static IDisposable MeasureDebug(this ISemanticLog log, T context, Action objectWriter) + public static IDisposable MeasureDebug(this ISemanticLog log, T context, LogFormatter action) { - return log.Measure(SemanticLogLevel.Debug, context, objectWriter); + return log.Measure(SemanticLogLevel.Debug, context, action); } - public static IDisposable MeasureInformation(this ISemanticLog log, Action objectWriter) + public static IDisposable MeasureInformation(this ISemanticLog log, LogFormatter action) { - return log.Measure(SemanticLogLevel.Information, None.Value, (_, w) => objectWriter(w)); + return log.Measure(SemanticLogLevel.Information, None.Value, (_, w) => action(w)); } - public static IDisposable MeasureInformation(this ISemanticLog log, T context, Action objectWriter) + public static IDisposable MeasureInformation(this ISemanticLog log, T context, LogFormatter action) { - return log.Measure(SemanticLogLevel.Information, context, objectWriter); + return log.Measure(SemanticLogLevel.Information, context, action); } - private static IDisposable Measure(this ISemanticLog log, SemanticLogLevel logLevel, T context, Action objectWriter) + private static IDisposable Measure(this ISemanticLog log, SemanticLogLevel logLevel, T context, LogFormatter action) { var watch = ValueStopwatch.StartNew(); @@ -182,9 +162,9 @@ namespace Squidex.Infrastructure.Log { var elapsedMs = watch.Stop(); - log.Log(logLevel, (Context: context, elapsedMs), (ctx, w) => + log.Log(logLevel, (Context: context, elapsedMs), null, (ctx, w) => { - objectWriter?.Invoke(ctx.Context, w); + action?.Invoke(ctx.Context, w); w.WriteProperty("elapsedMs", elapsedMs); }); diff --git a/backend/src/Squidex.Infrastructure/Log/TimestampLogAppender.cs b/backend/src/Squidex.Infrastructure/Log/TimestampLogAppender.cs index a971e529e..a16c82e7b 100644 --- a/backend/src/Squidex.Infrastructure/Log/TimestampLogAppender.cs +++ b/backend/src/Squidex.Infrastructure/Log/TimestampLogAppender.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System; using NodaTime; namespace Squidex.Infrastructure.Log @@ -18,7 +19,7 @@ namespace Squidex.Infrastructure.Log this.clock = clock ?? SystemClock.Instance; } - public void Append(IObjectWriter writer, SemanticLogLevel logLevel) + public void Append(IObjectWriter writer, SemanticLogLevel logLevel, Exception? exception) { writer.WriteProperty("timestamp", clock.GetCurrentInstant()); } diff --git a/backend/src/Squidex.Web/ApiExceptionFilterAttribute.cs b/backend/src/Squidex.Web/ApiExceptionFilterAttribute.cs index 042031a84..63813ed55 100644 --- a/backend/src/Squidex.Web/ApiExceptionFilterAttribute.cs +++ b/backend/src/Squidex.Web/ApiExceptionFilterAttribute.cs @@ -9,7 +9,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.DependencyInjection; -using Squidex.Web.Pipeline; +using Squidex.Infrastructure.Log; namespace Squidex.Web { @@ -33,9 +33,9 @@ namespace Squidex.Web if (!wellKnown) { - var exceptionHandler = context.HttpContext.RequestServices.GetService(); + var log = context.HttpContext.RequestServices.GetService(); - exceptionHandler.Handle(context.Exception, context.HttpContext); + log.LogError(context.Exception, w => w.WriteProperty("messag", "An unexpected exception has occurred.")); } context.Result = GetResult(error); diff --git a/backend/src/Squidex.Web/Pipeline/ActionContextLogAppender.cs b/backend/src/Squidex.Web/Pipeline/ActionContextLogAppender.cs index 7e03bc0b9..e72d8eb22 100644 --- a/backend/src/Squidex.Web/Pipeline/ActionContextLogAppender.cs +++ b/backend/src/Squidex.Web/Pipeline/ActionContextLogAppender.cs @@ -29,7 +29,7 @@ namespace Squidex.Web.Pipeline this.httpContextAccessor = httpContextAccessor; } - public void Append(IObjectWriter writer, SemanticLogLevel logLevel) + public void Append(IObjectWriter writer, SemanticLogLevel logLevel, Exception? exception) { var httpContext = httpContextAccessor.HttpContext; diff --git a/backend/src/Squidex.Web/Pipeline/DefaultExceptionHandler.cs b/backend/src/Squidex.Web/Pipeline/DefaultExceptionHandler.cs deleted file mode 100644 index a179950bf..000000000 --- a/backend/src/Squidex.Web/Pipeline/DefaultExceptionHandler.cs +++ /dev/null @@ -1,31 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System; -using Microsoft.AspNetCore.Http; -using Squidex.Infrastructure; -using Squidex.Infrastructure.Log; - -namespace Squidex.Web.Pipeline -{ - public class DefaultExceptionHandler : IExceptionHandler - { - private readonly ISemanticLog log; - - public DefaultExceptionHandler(ISemanticLog log) - { - Guard.NotNull(log); - - this.log = log; - } - - public virtual void Handle(Exception exception, HttpContext? httpContext = null) - { - log.LogError(exception, w => w.WriteProperty("status", "UnhandledException")); - } - } -} diff --git a/backend/src/Squidex.Web/Pipeline/IExceptionHandler.cs b/backend/src/Squidex.Web/Pipeline/IExceptionHandler.cs deleted file mode 100644 index 5f67eda13..000000000 --- a/backend/src/Squidex.Web/Pipeline/IExceptionHandler.cs +++ /dev/null @@ -1,17 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System; -using Microsoft.AspNetCore.Http; - -namespace Squidex.Web.Pipeline -{ - public interface IExceptionHandler - { - void Handle(Exception exception, HttpContext? httpContext = null); - } -} diff --git a/backend/src/Squidex.Web/Pipeline/RequestExceptionMiddleware.cs b/backend/src/Squidex.Web/Pipeline/RequestExceptionMiddleware.cs index c3d4a86bc..c11e0da4b 100644 --- a/backend/src/Squidex.Web/Pipeline/RequestExceptionMiddleware.cs +++ b/backend/src/Squidex.Web/Pipeline/RequestExceptionMiddleware.cs @@ -9,18 +9,19 @@ using System; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Squidex.Infrastructure; +using Squidex.Infrastructure.Log; namespace Squidex.Web.Pipeline { public sealed class RequestExceptionMiddleware : IMiddleware { - private readonly IExceptionHandler exceptionHandler; + private readonly ISemanticLog log; - public RequestExceptionMiddleware(IExceptionHandler exceptionHandler) + public RequestExceptionMiddleware(ISemanticLog log) { - Guard.NotNull(exceptionHandler); + Guard.NotNull(log); - this.exceptionHandler = exceptionHandler; + this.log = log; } public async Task InvokeAsync(HttpContext context, RequestDelegate next) @@ -31,7 +32,7 @@ namespace Squidex.Web.Pipeline } catch (Exception ex) { - exceptionHandler.Handle(ex, context); + log.LogError(ex, w => w.WriteProperty("messag", "An unexpected exception has occurred.")); context.Response.StatusCode = 500; } diff --git a/backend/src/Squidex/Config/Web/WebServices.cs b/backend/src/Squidex/Config/Web/WebServices.cs index 76e024145..bd064d2c5 100644 --- a/backend/src/Squidex/Config/Web/WebServices.cs +++ b/backend/src/Squidex/Config/Web/WebServices.cs @@ -67,9 +67,6 @@ namespace Squidex.Config.Web services.AddSingletonAs() .As(); - services.AddSingletonAs() - .AsOptional(); - services.Configure(options => { options.SuppressModelStateInvalidFilter = true; diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Invitation/Notifications/NotificationEmailEventConsumerTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Invitation/Notifications/NotificationEmailEventConsumerTests.cs index 137cab05c..4dc1a59d1 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Invitation/Notifications/NotificationEmailEventConsumerTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Invitation/Notifications/NotificationEmailEventConsumerTests.cs @@ -153,7 +153,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Invitation.Notifications private void MustLogWarning() { - A.CallTo(() => log.Log(SemanticLogLevel.Warning, A._, A>._)) + A.CallTo(() => log.Log(A._, A._, A._!)) .MustHaveHappened(); } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Invitation/Notifications/NotificationEmailSenderTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Invitation/Notifications/NotificationEmailSenderTests.cs index 0ef451ae6..439f36f38 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Invitation/Notifications/NotificationEmailSenderTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Invitation/Notifications/NotificationEmailSenderTests.cs @@ -11,7 +11,6 @@ using System.Security.Claims; using System.Threading.Tasks; using FakeItEasy; using Microsoft.Extensions.Options; -using Squidex.Infrastructure; using Squidex.Infrastructure.Email; using Squidex.Infrastructure.Log; using Squidex.Shared.Identity; @@ -137,7 +136,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Invitation.Notifications private void MustLogWarning() { - A.CallTo(() => log.Log(SemanticLogLevel.Warning, A._, A>._)) + A.CallTo(() => log.Log(A._, A._, A._!)) .MustHaveHappened(); } } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/RecursiveDeleterTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/RecursiveDeleterTests.cs index b8e85caa9..6b55daf0a 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/RecursiveDeleterTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/RecursiveDeleterTests.cs @@ -103,7 +103,7 @@ namespace Squidex.Domain.Apps.Entities.Assets A.CallTo(() => commandBus.PublishAsync(A.That.Matches(x => x.AssetId == childId2))) .MustHaveHappened(); - A.CallTo(() => log.Log(SemanticLogLevel.Error, None.Value, A>.Ignored)) + A.CallTo(() => log.Log(A._, A._, A._!)) .MustHaveHappened(); } } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleDequeuerTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleDequeuerTests.cs index 8297b0ec9..e24e35476 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleDequeuerTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleDequeuerTests.cs @@ -53,7 +53,7 @@ namespace Squidex.Domain.Apps.Entities.Rules await sut.QueryAsync(); - A.CallTo(() => log.Log(SemanticLogLevel.Error, None.Value, A>._)) + A.CallTo(() => log.Log(A._, A._, A._!)) .MustHaveHappened(); } @@ -67,7 +67,7 @@ namespace Squidex.Domain.Apps.Entities.Rules await sut.HandleAsync(@event); - A.CallTo(() => log.Log(SemanticLogLevel.Error, None.Value, A>._)) + A.CallTo(() => log.Log(A._, A._, A._!)) .MustHaveHappened(); } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Search/SearchManagerTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Search/SearchManagerTests.cs index 9124c9eca..0f04ede63 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Search/SearchManagerTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Search/SearchManagerTests.cs @@ -94,7 +94,7 @@ namespace Squidex.Domain.Apps.Entities.Search result.Should().BeEquivalentTo(result2); - A.CallTo(() => log.Log(SemanticLogLevel.Error, query, A>._)) + A.CallTo(() => log.Log(A._, A._, A._, A>._!)) .MustHaveHappened(); } } diff --git a/backend/tests/Squidex.Infrastructure.Tests/Commands/LogCommandMiddlewareTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Commands/LogCommandMiddlewareTests.cs index 403256c19..d9dfe5bef 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/Commands/LogCommandMiddlewareTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/Commands/LogCommandMiddlewareTests.cs @@ -25,7 +25,12 @@ namespace Squidex.Infrastructure.Commands { public Dictionary LogLevels { get; } = new Dictionary(); - public void Log(SemanticLogLevel logLevel, T context, Action action) + public void Log(SemanticLogLevel logLevel, T context, Exception? exception, LogFormatter action) + { + LogLevels[logLevel] = LogLevels.GetOrDefault(logLevel) + 1; + } + + public void Log(SemanticLogLevel logLevel, Exception? exception, LogFormatter action) { LogLevels[logLevel] = LogLevels.GetOrDefault(logLevel) + 1; } diff --git a/backend/tests/Squidex.Infrastructure.Tests/Log/SemanticLogAdapterTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Log/SemanticLogAdapterTests.cs index ed4dbee2e..2fc09d7d2 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/Log/SemanticLogAdapterTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/Log/SemanticLogAdapterTests.cs @@ -126,14 +126,33 @@ namespace Squidex.Infrastructure.Log var logger = sut.CreateLogger("my-category"); - logger.Log(LogLevel.Debug, new EventId(0), 1, exception, (x, e) => "my-message"); + logger.Log(LogLevel.Debug, new EventId(0), exception, "my-message"); var expected = MakeTestCall(w => w .WriteProperty("logLevel", "Debug") .WriteProperty("message", "my-message") - .WriteException(exception) - .WriteProperty("category", "my-category")); + .WriteProperty("category", "my-category") + .WriteException(exception)); + + Assert.Equal(expected, output); + } + + [Fact] + public void Should_log_message_with_integrated_exception() + { + var exception = new InvalidOperationException(); + + var logger = sut.CreateLogger("my-category"); + + logger.Log(LogLevel.Debug, new EventId(0), "exception: {exception}", exception); + + var expected = + MakeTestCall(w => w + .WriteProperty("logLevel", "Debug") + .WriteProperty("message", $"exception: {exception}") + .WriteProperty("category", "my-category") + .WriteException(exception)); Assert.Equal(expected, output); } diff --git a/backend/tests/Squidex.Infrastructure.Tests/Log/SemanticLogTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Log/SemanticLogTests.cs index 556128cea..04ac1aeeb 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/Log/SemanticLogTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/Log/SemanticLogTests.cs @@ -49,8 +49,8 @@ namespace Squidex.Infrastructure.Log [Fact] public void Should_log_multiple_lines() { - Log.Log(SemanticLogLevel.Error, None.Value, (_, w) => w.WriteProperty("logMessage", "Msg1")); - Log.Log(SemanticLogLevel.Error, None.Value, (_, w) => w.WriteProperty("logMessage", "Msg2")); + Log.Log(SemanticLogLevel.Error, null, w => w.WriteProperty("logMessage", "Msg1")); + Log.Log(SemanticLogLevel.Error, null, w => w.WriteProperty("logMessage", "Msg2")); var expected1 = LogTest(w => w @@ -229,7 +229,7 @@ namespace Squidex.Infrastructure.Log { var exception = new InvalidOperationException(); - Log.LogWarning(exception); + Log.LogWarning(exception, w => { }); var expected = LogTest(w => w @@ -286,7 +286,7 @@ namespace Squidex.Infrastructure.Log { var exception = new InvalidOperationException(); - Log.LogError(exception); + Log.LogError(exception, w => { }); var expected = LogTest(w => w @@ -343,7 +343,7 @@ namespace Squidex.Infrastructure.Log { var exception = new InvalidOperationException(); - Log.LogFatal(exception); + Log.LogFatal(exception, w => { }); var expected = LogTest(w => w @@ -369,18 +369,6 @@ namespace Squidex.Infrastructure.Log Assert.Equal(expected, output); } - [Fact] - public void Should_log_nothing_when_exception_is_null() - { - Log.LogFatal((Exception?)null); - - var expected = - LogTest(w => w - .WriteProperty("logLevel", "Fatal")); - - Assert.Equal(expected, output); - } - [Fact] public void Should_measure_trace() { @@ -484,8 +472,8 @@ namespace Squidex.Infrastructure.Log .WriteObject("eventId", e => e .WriteProperty("id", 123) .WriteProperty("name", "EventName")) - .WriteException(exception) - .WriteProperty("category", "Squidex.Infrastructure.Log.SemanticLogTests")); + .WriteProperty("category", "Squidex.Infrastructure.Log.SemanticLogTests") + .WriteException(exception)); Assert.Equal(expected, output); } @@ -506,7 +494,7 @@ namespace Squidex.Infrastructure.Log try { - sut.Log(SemanticLogLevel.Debug, None.Value, (_, w) => w.WriteProperty("should", "throw")); + sut.Log(SemanticLogLevel.Debug, null, w => w.WriteProperty("should", "throw")); Assert.False(true); } diff --git a/backend/tests/Squidex.Infrastructure.Tests/Migrations/MigratorTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Migrations/MigratorTests.cs index 391577144..80ac9034d 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/Migrations/MigratorTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/Migrations/MigratorTests.cs @@ -131,7 +131,7 @@ namespace Squidex.Infrastructure.Migrations await Assert.ThrowsAsync(() => sut.MigrateAsync()); - A.CallTo(() => log.Log(SemanticLogLevel.Fatal, None.Value, A>._)) + A.CallTo(() => log.Log(SemanticLogLevel.Fatal, ex, A._!)) .MustHaveHappened(); A.CallTo(() => migrator_1_2.UpdateAsync()) diff --git a/backend/tests/Squidex.Infrastructure.Tests/Orleans/LoggingFilterTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Orleans/LoggingFilterTests.cs index 63b952bbe..201760937 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/Orleans/LoggingFilterTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/Orleans/LoggingFilterTests.cs @@ -31,7 +31,7 @@ namespace Squidex.Infrastructure.Orleans { await sut.Invoke(context); - A.CallTo(() => log.Log(A._, A._, A>._)) + A.CallTo(() => log.Log(A._, A._, A._!)) .MustNotHaveHappened(); } @@ -43,7 +43,7 @@ namespace Squidex.Infrastructure.Orleans await Assert.ThrowsAsync(() => sut.Invoke(context)); - A.CallTo(() => log.Log(A._, A._, A>._)) + A.CallTo(() => log.Log(A._, A._, A._!)) .MustNotHaveHappened(); } @@ -55,7 +55,7 @@ namespace Squidex.Infrastructure.Orleans await Assert.ThrowsAsync(() => sut.Invoke(context)); - A.CallTo(() => log.Log(A._, A._, A>._)) + A.CallTo(() => log.Log(A._, A._, A._!)) .MustHaveHappened(); } } diff --git a/backend/tests/Squidex.Web.Tests/ApiExceptionFilterAttributeTests.cs b/backend/tests/Squidex.Web.Tests/ApiExceptionFilterAttributeTests.cs index f734f073a..9dd10f97b 100644 --- a/backend/tests/Squidex.Web.Tests/ApiExceptionFilterAttributeTests.cs +++ b/backend/tests/Squidex.Web.Tests/ApiExceptionFilterAttributeTests.cs @@ -17,15 +17,15 @@ using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Routing; using Squidex.Infrastructure; +using Squidex.Infrastructure.Log; using Squidex.Infrastructure.Validation; -using Squidex.Web.Pipeline; using Xunit; namespace Squidex.Web { public class ApiExceptionFilterAttributeTests { - private readonly IExceptionHandler exceptionHandler = A.Fake(); + private readonly ISemanticLog log = A.Fake(); private readonly ApiExceptionFilterAttribute sut = new ApiExceptionFilterAttribute(); [Fact] @@ -49,7 +49,7 @@ namespace Squidex.Web Assert.Equal(new[] { "Error1", "P: Error2", "P1, P2: Error3" }, ((ErrorDto)result.Value).Details); - A.CallTo(() => exceptionHandler.Handle(A._, A._)) + A.CallTo(() => log.Log(A._, A._, A._!)) .MustNotHaveHappened(); } @@ -62,7 +62,7 @@ namespace Squidex.Web Assert.IsType(context.Result); - A.CallTo(() => exceptionHandler.Handle(A._, A._)) + A.CallTo(() => log.Log(A._, A._, A._!)) .MustNotHaveHappened(); } @@ -75,7 +75,7 @@ namespace Squidex.Web Validate(500, context.Result, null); - A.CallTo(() => exceptionHandler.Handle(context.Exception, A._)) + A.CallTo(() => log.Log(A._, context.Exception, A._!)) .MustHaveHappened(); } @@ -88,7 +88,7 @@ namespace Squidex.Web Validate(400, context.Result, context.Exception); - A.CallTo(() => exceptionHandler.Handle(A._, A._)) + A.CallTo(() => log.Log(A._, A._, A._!)) .MustNotHaveHappened(); } @@ -101,7 +101,7 @@ namespace Squidex.Web Validate(400, context.Result, context.Exception); - A.CallTo(() => exceptionHandler.Handle(A._, A._)) + A.CallTo(() => log.Log(A._, A._, A._!)) .MustNotHaveHappened(); } @@ -114,7 +114,7 @@ namespace Squidex.Web Validate(412, context.Result, context.Exception); - A.CallTo(() => exceptionHandler.Handle(A._, A._)) + A.CallTo(() => log.Log(A._, A._, A._!)) .MustNotHaveHappened(); } @@ -127,7 +127,7 @@ namespace Squidex.Web Validate(403, context.Result, context.Exception); - A.CallTo(() => exceptionHandler.Handle(A._, A._)) + A.CallTo(() => log.Log(A._, A._, A._!)) .MustNotHaveHappened(); } @@ -140,7 +140,7 @@ namespace Squidex.Web Validate(403, context.Result, null); - A.CallTo(() => exceptionHandler.Handle(context.Exception, A._)) + A.CallTo(() => log.Log(A._, context.Exception, A._!)) .MustHaveHappened(); } @@ -153,7 +153,7 @@ namespace Squidex.Web Validate(403, context.Result, null); - A.CallTo(() => exceptionHandler.Handle(A._, A._)) + A.CallTo(() => log.Log(A._, A._, A._!)) .MustNotHaveHappened(); } @@ -189,8 +189,8 @@ namespace Squidex.Web { var services = A.Fake(); - A.CallTo(() => services.GetService(typeof(IExceptionHandler))) - .Returns(exceptionHandler); + A.CallTo(() => services.GetService(typeof(ISemanticLog))) + .Returns(log); var httpContext = new DefaultHttpContext {