Browse Source

Merge branch 'master' into feature/measure-traffic

# Conflicts:
#	backend/src/Squidex.Web/Pipeline/RequestExceptionMiddleware.cs
pull/491/head
Sebastian 6 years ago
parent
commit
2b6ea96a30
  1. 3
      backend/extensions/Squidex.Extensions/Actions/Kafka/KafkaProducer.cs
  2. 26
      backend/src/Squidex.Infrastructure/Log/Adapter/SemanticLogLogger.cs
  3. 2
      backend/src/Squidex.Infrastructure/Log/ApplicationInfoLogAppender.cs
  4. 2
      backend/src/Squidex.Infrastructure/Log/ConstantsLogWriter.cs
  5. 4
      backend/src/Squidex.Infrastructure/Log/ILogAppender.cs
  6. 8
      backend/src/Squidex.Infrastructure/Log/ISemanticLog.cs
  7. 57
      backend/src/Squidex.Infrastructure/Log/SemanticLog.cs
  8. 122
      backend/src/Squidex.Infrastructure/Log/SemanticLogExtensions.cs
  9. 3
      backend/src/Squidex.Infrastructure/Log/TimestampLogAppender.cs
  10. 6
      backend/src/Squidex.Web/ApiExceptionFilterAttribute.cs
  11. 2
      backend/src/Squidex.Web/Pipeline/ActionContextLogAppender.cs
  12. 31
      backend/src/Squidex.Web/Pipeline/DefaultExceptionHandler.cs
  13. 17
      backend/src/Squidex.Web/Pipeline/IExceptionHandler.cs
  14. 11
      backend/src/Squidex.Web/Pipeline/RequestExceptionMiddleware.cs
  15. 3
      backend/src/Squidex/Config/Web/WebServices.cs
  16. 2
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Invitation/Notifications/NotificationEmailEventConsumerTests.cs
  17. 3
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Invitation/Notifications/NotificationEmailSenderTests.cs
  18. 2
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/RecursiveDeleterTests.cs
  19. 4
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleDequeuerTests.cs
  20. 2
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Search/SearchManagerTests.cs
  21. 7
      backend/tests/Squidex.Infrastructure.Tests/Commands/LogCommandMiddlewareTests.cs
  22. 25
      backend/tests/Squidex.Infrastructure.Tests/Log/SemanticLogAdapterTests.cs
  23. 28
      backend/tests/Squidex.Infrastructure.Tests/Log/SemanticLogTests.cs
  24. 2
      backend/tests/Squidex.Infrastructure.Tests/Migrations/MigratorTests.cs
  25. 6
      backend/tests/Squidex.Infrastructure.Tests/Orleans/LoggingFilterTests.cs
  26. 26
      backend/tests/Squidex.Web.Tests/ApiExceptionFilterAttributeTests.cs

3
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,7 +64,7 @@ namespace Squidex.Extensions.Actions.Kafka
break;
}
log.Log<None>(level, default, (_, w) => w
log.Log(level, null, w => w
.WriteProperty("action", "KafkaAction")
.WriteProperty("name", message.Name)
.WriteProperty("message", message.Message));

26
backend/src/Squidex.Infrastructure/Log/Adapter/SemanticLogLogger.cs

@ -49,9 +49,20 @@ namespace Squidex.Infrastructure.Log.Adapter
break;
}
if (state is IReadOnlyList<KeyValuePair<string, object>> 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<KeyValuePair<string, object>> parameters)
if (ctx.state is IReadOnlyList<KeyValuePair<string, object>> 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);
}
});
}

2
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)

2
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);
}

4
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);
}
}

8
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>(T context, IObjectWriter writer);
public interface ISemanticLog
{
void Log<T>(SemanticLogLevel logLevel, T context, Action<T, IObjectWriter> action);
void Log<T>(SemanticLogLevel logLevel, T context, Exception? exception, LogFormatter<T> action);
void Log(SemanticLogLevel logLevel, Exception? exception, LogFormatter action);
ISemanticLog CreateScope(Action<IObjectWriter> objectWriter);
}

57
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<T>(SemanticLogLevel logLevel, T context, Action<T, IObjectWriter> action)
public void Log<T>(SemanticLogLevel logLevel, T context, Exception? exception, LogFormatter<T> 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<T>(SemanticLogLevel logLevel, T context, Action<T, IObjectWriter> 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<T>(SemanticLogLevel logLevel, T context, Exception? exception, LogFormatter<T> 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<IObjectWriter> 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);
}
}
}

122
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<IObjectWriter> 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<T>(this ISemanticLog log, T context, Action<T, IObjectWriter> objectWriter)
public static void LogTrace<T>(this ISemanticLog log, T context, LogFormatter<T> action)
{
log.Log(SemanticLogLevel.Trace, context, objectWriter);
log.Log(SemanticLogLevel.Trace, context, null, action);
}
public static void LogDebug(this ISemanticLog log, Action<IObjectWriter> 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<T>(this ISemanticLog log, T context, Action<T, IObjectWriter> objectWriter)
public static void LogDebug<T>(this ISemanticLog log, T context, LogFormatter<T> action)
{
log.Log(SemanticLogLevel.Debug, context, objectWriter);
log.Log(SemanticLogLevel.Debug, context, null, action);
}
public static void LogInformation(this ISemanticLog log, Action<IObjectWriter> 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<T>(this ISemanticLog log, T context, Action<T, IObjectWriter> objectWriter)
public static void LogInformation<T>(this ISemanticLog log, T context, LogFormatter<T> action)
{
log.Log(SemanticLogLevel.Information, context, objectWriter);
log.Log(SemanticLogLevel.Information, context, null, action);
}
public static void LogWarning(this ISemanticLog log, Action<IObjectWriter> 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<T>(this ISemanticLog log, T context, Action<T, IObjectWriter> objectWriter)
public static void LogWarning<T>(this ISemanticLog log, T context, LogFormatter<T> action)
{
log.Log(SemanticLogLevel.Warning, context, objectWriter);
log.Log(SemanticLogLevel.Warning, context, null, action);
}
public static void LogWarning(this ISemanticLog log, Exception exception, Action<IObjectWriter>? 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<T>(this ISemanticLog log, Exception exception, T context, Action<T, IObjectWriter>? objectWriter = null)
public static void LogWarning<T>(this ISemanticLog log, Exception exception, T context, LogFormatter<T> 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<IObjectWriter> 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<T>(this ISemanticLog log, T context, Action<T, IObjectWriter> objectWriter)
public static void LogError<T>(this ISemanticLog log, T context, LogFormatter<T> action)
{
log.Log(SemanticLogLevel.Error, context, objectWriter);
log.Log(SemanticLogLevel.Error, context, null, action);
}
public static void LogError(this ISemanticLog log, Exception exception, Action<IObjectWriter>? 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<T>(this ISemanticLog log, Exception exception, T context, Action<T, IObjectWriter>? objectWriter = null)
public static void LogError<T>(this ISemanticLog log, Exception exception, T context, LogFormatter<T> 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<IObjectWriter> 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<T>(this ISemanticLog log, T context, Action<T, IObjectWriter> objectWriter)
public static void LogFatal<T>(this ISemanticLog log, T context, LogFormatter<T> action)
{
log.Log(SemanticLogLevel.Fatal, context, objectWriter);
log.Log(SemanticLogLevel.Fatal, context, null, action);
}
public static void LogFatal(this ISemanticLog log, Exception? exception, Action<IObjectWriter>? 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<T>(this ISemanticLog log, Exception? exception, T context, Action<T, IObjectWriter>? objectWriter = null)
public static void LogFatal<T>(this ISemanticLog log, Exception? exception, T context, LogFormatter<T> action)
{
log.Log(SemanticLogLevel.Fatal, context, (ctx, w) => w.WriteException(exception, ctx, objectWriter));
}
private static void WriteException(this IObjectWriter writer, Exception? exception, Action<IObjectWriter>? objectWriter)
{
objectWriter?.Invoke(writer);
if (exception != null)
{
writer.WriteException(exception);
}
}
private static void WriteException<T>(this IObjectWriter writer, Exception? exception, T context, Action<T, IObjectWriter>? 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<IObjectWriter> 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<T>(this ISemanticLog log, T context, Action<T, IObjectWriter> objectWriter)
public static IDisposable MeasureTrace<T>(this ISemanticLog log, T context, LogFormatter<T> action)
{
return log.Measure(SemanticLogLevel.Trace, context, objectWriter);
return log.Measure(SemanticLogLevel.Trace, context, action);
}
public static IDisposable MeasureDebug(this ISemanticLog log, Action<IObjectWriter> 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<T>(this ISemanticLog log, T context, Action<T, IObjectWriter> objectWriter)
public static IDisposable MeasureDebug<T>(this ISemanticLog log, T context, LogFormatter<T> action)
{
return log.Measure(SemanticLogLevel.Debug, context, objectWriter);
return log.Measure(SemanticLogLevel.Debug, context, action);
}
public static IDisposable MeasureInformation(this ISemanticLog log, Action<IObjectWriter> 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<T>(this ISemanticLog log, T context, Action<T, IObjectWriter> objectWriter)
public static IDisposable MeasureInformation<T>(this ISemanticLog log, T context, LogFormatter<T> action)
{
return log.Measure(SemanticLogLevel.Information, context, objectWriter);
return log.Measure(SemanticLogLevel.Information, context, action);
}
private static IDisposable Measure<T>(this ISemanticLog log, SemanticLogLevel logLevel, T context, Action<T, IObjectWriter> objectWriter)
private static IDisposable Measure<T>(this ISemanticLog log, SemanticLogLevel logLevel, T context, LogFormatter<T> 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);
});

3
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());
}

6
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<IExceptionHandler>();
var log = context.HttpContext.RequestServices.GetService<ISemanticLog>();
exceptionHandler.Handle(context.Exception, context.HttpContext);
log.LogError(context.Exception, w => w.WriteProperty("messag", "An unexpected exception has occurred."));
}
context.Result = GetResult(error);

2
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;

31
backend/src/Squidex.Web/Pipeline/DefaultExceptionHandler.cs

@ -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"));
}
}
}

17
backend/src/Squidex.Web/Pipeline/IExceptionHandler.cs

@ -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);
}
}

11
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;
}

3
backend/src/Squidex/Config/Web/WebServices.cs

@ -67,9 +67,6 @@ namespace Squidex.Config.Web
services.AddSingletonAs<ActionContextAccessor>()
.As<IActionContextAccessor>();
services.AddSingletonAs<DefaultExceptionHandler>()
.AsOptional<IExceptionHandler>();
services.Configure<ApiBehaviorOptions>(options =>
{
options.SuppressModelStateInvalidFilter = true;

2
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<None>._, A<Action<None, IObjectWriter>>._))
A.CallTo(() => log.Log(A<SemanticLogLevel>._, A<Exception?>._, A<LogFormatter>._!))
.MustHaveHappened();
}

3
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<None>._, A<Action<None, IObjectWriter>>._))
A.CallTo(() => log.Log(A<SemanticLogLevel>._, A<Exception?>._, A<LogFormatter>._!))
.MustHaveHappened();
}
}

2
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<DeleteAsset>.That.Matches(x => x.AssetId == childId2)))
.MustHaveHappened();
A.CallTo(() => log.Log(SemanticLogLevel.Error, None.Value, A<Action<None, IObjectWriter>>.Ignored))
A.CallTo(() => log.Log(A<SemanticLogLevel>._, A<Exception?>._, A<LogFormatter>._!))
.MustHaveHappened();
}
}

4
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<Action<None, IObjectWriter>>._))
A.CallTo(() => log.Log(A<SemanticLogLevel>._, A<Exception?>._, A<LogFormatter>._!))
.MustHaveHappened();
}
@ -67,7 +67,7 @@ namespace Squidex.Domain.Apps.Entities.Rules
await sut.HandleAsync(@event);
A.CallTo(() => log.Log(SemanticLogLevel.Error, None.Value, A<Action<None, IObjectWriter>>._))
A.CallTo(() => log.Log(A<SemanticLogLevel>._, A<Exception?>._, A<LogFormatter>._!))
.MustHaveHappened();
}

2
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<Action<string, IObjectWriter>>._))
A.CallTo(() => log.Log<string>(A<SemanticLogLevel>._, A<string>._, A<Exception?>._, A<LogFormatter<string>>._!))
.MustHaveHappened();
}
}

7
backend/tests/Squidex.Infrastructure.Tests/Commands/LogCommandMiddlewareTests.cs

@ -25,7 +25,12 @@ namespace Squidex.Infrastructure.Commands
{
public Dictionary<SemanticLogLevel, int> LogLevels { get; } = new Dictionary<SemanticLogLevel, int>();
public void Log<T>(SemanticLogLevel logLevel, T context, Action<T, IObjectWriter> action)
public void Log<T>(SemanticLogLevel logLevel, T context, Exception? exception, LogFormatter<T> action)
{
LogLevels[logLevel] = LogLevels.GetOrDefault(logLevel) + 1;
}
public void Log(SemanticLogLevel logLevel, Exception? exception, LogFormatter action)
{
LogLevels[logLevel] = LogLevels.GetOrDefault(logLevel) + 1;
}

25
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);
}

28
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);
}

2
backend/tests/Squidex.Infrastructure.Tests/Migrations/MigratorTests.cs

@ -131,7 +131,7 @@ namespace Squidex.Infrastructure.Migrations
await Assert.ThrowsAsync<MigrationFailedException>(() => sut.MigrateAsync());
A.CallTo(() => log.Log(SemanticLogLevel.Fatal, None.Value, A<Action<None, IObjectWriter>>._))
A.CallTo(() => log.Log(SemanticLogLevel.Fatal, ex, A<LogFormatter>._!))
.MustHaveHappened();
A.CallTo(() => migrator_1_2.UpdateAsync())

6
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<SemanticLogLevel>._, A<None>._, A<Action<None, IObjectWriter>>._))
A.CallTo(() => log.Log(A<SemanticLogLevel>._, A<Exception?>._, A<LogFormatter>._!))
.MustNotHaveHappened();
}
@ -43,7 +43,7 @@ namespace Squidex.Infrastructure.Orleans
await Assert.ThrowsAsync<ValidationException>(() => sut.Invoke(context));
A.CallTo(() => log.Log(A<SemanticLogLevel>._, A<None>._, A<Action<None, IObjectWriter>>._))
A.CallTo(() => log.Log(A<SemanticLogLevel>._, A<Exception?>._, A<LogFormatter>._!))
.MustNotHaveHappened();
}
@ -55,7 +55,7 @@ namespace Squidex.Infrastructure.Orleans
await Assert.ThrowsAsync<InvalidOperationException>(() => sut.Invoke(context));
A.CallTo(() => log.Log(A<SemanticLogLevel>._, A<None>._, A<Action<None, IObjectWriter>>._))
A.CallTo(() => log.Log(A<SemanticLogLevel>._, A<Exception?>._, A<LogFormatter>._!))
.MustHaveHappened();
}
}

26
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<IExceptionHandler>();
private readonly ISemanticLog log = A.Fake<ISemanticLog>();
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<Exception>._, A<HttpContext>._))
A.CallTo(() => log.Log(A<SemanticLogLevel>._, A<Exception?>._, A<LogFormatter>._!))
.MustNotHaveHappened();
}
@ -62,7 +62,7 @@ namespace Squidex.Web
Assert.IsType<NotFoundResult>(context.Result);
A.CallTo(() => exceptionHandler.Handle(A<Exception>._, A<HttpContext>._))
A.CallTo(() => log.Log(A<SemanticLogLevel>._, A<Exception?>._, A<LogFormatter>._!))
.MustNotHaveHappened();
}
@ -75,7 +75,7 @@ namespace Squidex.Web
Validate(500, context.Result, null);
A.CallTo(() => exceptionHandler.Handle(context.Exception, A<HttpContext>._))
A.CallTo(() => log.Log(A<SemanticLogLevel>._, context.Exception, A<LogFormatter>._!))
.MustHaveHappened();
}
@ -88,7 +88,7 @@ namespace Squidex.Web
Validate(400, context.Result, context.Exception);
A.CallTo(() => exceptionHandler.Handle(A<Exception>._, A<HttpContext>._))
A.CallTo(() => log.Log(A<SemanticLogLevel>._, A<Exception?>._, A<LogFormatter>._!))
.MustNotHaveHappened();
}
@ -101,7 +101,7 @@ namespace Squidex.Web
Validate(400, context.Result, context.Exception);
A.CallTo(() => exceptionHandler.Handle(A<Exception>._, A<HttpContext>._))
A.CallTo(() => log.Log(A<SemanticLogLevel>._, A<Exception?>._, A<LogFormatter>._!))
.MustNotHaveHappened();
}
@ -114,7 +114,7 @@ namespace Squidex.Web
Validate(412, context.Result, context.Exception);
A.CallTo(() => exceptionHandler.Handle(A<Exception>._, A<HttpContext>._))
A.CallTo(() => log.Log(A<SemanticLogLevel>._, A<Exception?>._, A<LogFormatter>._!))
.MustNotHaveHappened();
}
@ -127,7 +127,7 @@ namespace Squidex.Web
Validate(403, context.Result, context.Exception);
A.CallTo(() => exceptionHandler.Handle(A<Exception>._, A<HttpContext>._))
A.CallTo(() => log.Log(A<SemanticLogLevel>._, A<Exception?>._, A<LogFormatter>._!))
.MustNotHaveHappened();
}
@ -140,7 +140,7 @@ namespace Squidex.Web
Validate(403, context.Result, null);
A.CallTo(() => exceptionHandler.Handle(context.Exception, A<HttpContext>._))
A.CallTo(() => log.Log(A<SemanticLogLevel>._, context.Exception, A<LogFormatter>._!))
.MustHaveHappened();
}
@ -153,7 +153,7 @@ namespace Squidex.Web
Validate(403, context.Result, null);
A.CallTo(() => exceptionHandler.Handle(A<Exception>._, A<HttpContext>._))
A.CallTo(() => log.Log(A<SemanticLogLevel>._, A<Exception?>._, A<LogFormatter>._!))
.MustNotHaveHappened();
}
@ -189,8 +189,8 @@ namespace Squidex.Web
{
var services = A.Fake<IServiceProvider>();
A.CallTo(() => services.GetService(typeof(IExceptionHandler)))
.Returns(exceptionHandler);
A.CallTo(() => services.GetService(typeof(ISemanticLog)))
.Returns(log);
var httpContext = new DefaultHttpContext
{

Loading…
Cancel
Save