Browse Source

Improvements/logging (#452)

* Improvements to logging system.
* Log level.
* Log level adjusted.
pull/453/head
Sebastian Stehle 6 years ago
committed by GitHub
parent
commit
dc6d69bc47
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      backend/src/Squidex.Infrastructure/Commands/LogCommandMiddleware.cs
  2. 8
      backend/src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerGrain.cs
  3. 12
      backend/src/Squidex.Infrastructure/Log/SemanticLog.cs
  4. 14
      backend/src/Squidex.Infrastructure/Log/SemanticLogOptions.cs
  5. 16
      backend/src/Squidex.Web/Pipeline/RequestLogOptions.cs
  6. 22
      backend/src/Squidex.Web/Pipeline/RequestLogPerformanceMiddleware.cs
  7. 6
      backend/src/Squidex/Config/Domain/LoggingServices.cs
  8. 16
      backend/src/Squidex/appsettings.json
  9. 28
      backend/tests/Squidex.Infrastructure.Tests/Commands/LogCommandMiddlewareTests.cs
  10. 6
      backend/tests/Squidex.Infrastructure.Tests/Log/SemanticLogAdapterTests.cs
  11. 18
      backend/tests/Squidex.Infrastructure.Tests/Log/SemanticLogTests.cs

2
backend/src/Squidex.Infrastructure/Commands/LogCommandMiddleware.cs

@ -28,7 +28,7 @@ namespace Squidex.Infrastructure.Commands
try try
{ {
log.LogInformation(logContext, (ctx, w) => w log.LogDebug(logContext, (ctx, w) => w
.WriteProperty("action", "HandleCommand.") .WriteProperty("action", "HandleCommand.")
.WriteProperty("actionId", ctx.id) .WriteProperty("actionId", ctx.id)
.WriteProperty("status", "Started") .WriteProperty("status", "Started")

8
backend/src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerGrain.cs

@ -203,13 +203,13 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
{ {
var logContext = (actionId: Guid.NewGuid().ToString(), consumer: eventConsumer.Name); var logContext = (actionId: Guid.NewGuid().ToString(), consumer: eventConsumer.Name);
log.LogInformation(logContext, (ctx, w) => w log.LogDebug(logContext, (ctx, w) => w
.WriteProperty("action", "EventConsumerReset") .WriteProperty("action", "EventConsumerReset")
.WriteProperty("actionId", ctx.actionId) .WriteProperty("actionId", ctx.actionId)
.WriteProperty("status", "Started") .WriteProperty("status", "Started")
.WriteProperty("eventConsumer", ctx.consumer)); .WriteProperty("eventConsumer", ctx.consumer));
using (log.MeasureTrace(logContext, (ctx, w) => w using (log.MeasureInformation(logContext, (ctx, w) => w
.WriteProperty("action", "EventConsumerReset") .WriteProperty("action", "EventConsumerReset")
.WriteProperty("actionId", ctx.actionId) .WriteProperty("actionId", ctx.actionId)
.WriteProperty("status", "Completed") .WriteProperty("status", "Completed")
@ -226,7 +226,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
var logContext = (eventId, eventType, consumer: eventConsumer.Name); var logContext = (eventId, eventType, consumer: eventConsumer.Name);
log.LogInformation(logContext, (ctx, w) => w log.LogDebug(logContext, (ctx, w) => w
.WriteProperty("action", "HandleEvent") .WriteProperty("action", "HandleEvent")
.WriteProperty("actionId", ctx.eventId) .WriteProperty("actionId", ctx.eventId)
.WriteProperty("status", "Started") .WriteProperty("status", "Started")
@ -234,7 +234,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
.WriteProperty("eventType", ctx.eventType) .WriteProperty("eventType", ctx.eventType)
.WriteProperty("eventConsumer", ctx.consumer)); .WriteProperty("eventConsumer", ctx.consumer));
using (log.MeasureTrace(logContext, (ctx, w) => w using (log.MeasureInformation(logContext, (ctx, w) => w
.WriteProperty("action", "HandleEvent") .WriteProperty("action", "HandleEvent")
.WriteProperty("actionId", ctx.eventId) .WriteProperty("actionId", ctx.eventId)
.WriteProperty("status", "Completed") .WriteProperty("status", "Completed")

12
backend/src/Squidex.Infrastructure/Log/SemanticLog.cs

@ -8,6 +8,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Microsoft.Extensions.Options;
namespace Squidex.Infrastructure.Log namespace Squidex.Infrastructure.Log
{ {
@ -15,19 +16,23 @@ namespace Squidex.Infrastructure.Log
{ {
private readonly ILogChannel[] channels; private readonly ILogChannel[] channels;
private readonly ILogAppender[] appenders; private readonly ILogAppender[] appenders;
private readonly IOptions<SemanticLogOptions> options;
private readonly IObjectWriterFactory writerFactory; private readonly IObjectWriterFactory writerFactory;
public SemanticLog( public SemanticLog(
IOptions<SemanticLogOptions> options,
IEnumerable<ILogChannel> channels, IEnumerable<ILogChannel> channels,
IEnumerable<ILogAppender> appenders, IEnumerable<ILogAppender> appenders,
IObjectWriterFactory writerFactory) IObjectWriterFactory writerFactory)
{ {
Guard.NotNull(options);
Guard.NotNull(channels); Guard.NotNull(channels);
Guard.NotNull(appenders); Guard.NotNull(appenders);
Guard.NotNull(writerFactory); Guard.NotNull(writerFactory);
this.channels = channels.ToArray(); this.channels = channels.ToArray();
this.appenders = appenders.ToArray(); this.appenders = appenders.ToArray();
this.options = options;
this.writerFactory = writerFactory; this.writerFactory = writerFactory;
} }
@ -35,6 +40,11 @@ namespace Squidex.Infrastructure.Log
{ {
Guard.NotNull(action); Guard.NotNull(action);
if (logLevel < options.Value.Level)
{
return;
}
var formattedText = FormatText(logLevel, context, action); var formattedText = FormatText(logLevel, context, action);
LogFormattedText(logLevel, formattedText); LogFormattedText(logLevel, formattedText);
@ -92,7 +102,7 @@ namespace Squidex.Infrastructure.Log
public ISemanticLog CreateScope(Action<IObjectWriter> objectWriter) public ISemanticLog CreateScope(Action<IObjectWriter> objectWriter)
{ {
return new SemanticLog(channels, appenders.Union(new ILogAppender[] { new ConstantsLogWriter(objectWriter) }).ToArray(), writerFactory); return new SemanticLog(options, channels, appenders.Union(new ILogAppender[] { new ConstantsLogWriter(objectWriter) }).ToArray(), writerFactory);
} }
} }
} }

14
backend/src/Squidex.Infrastructure/Log/SemanticLogOptions.cs

@ -0,0 +1,14 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
namespace Squidex.Infrastructure.Log
{
public sealed class SemanticLogOptions
{
public SemanticLogLevel Level { get; set; } = SemanticLogLevel.Information;
}
}

16
backend/src/Squidex.Web/Pipeline/RequestLogOptions.cs

@ -0,0 +1,16 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
namespace Squidex.Web.Pipeline
{
public sealed class RequestLogOptions
{
public bool LogRequest { get; set; }
public bool LogProfiler { get; set; }
}
}

22
backend/src/Squidex.Web/Pipeline/RequestLogPerformanceMiddleware.cs

@ -7,6 +7,7 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Log; using Squidex.Infrastructure.Log;
using Squidex.Infrastructure.Security; using Squidex.Infrastructure.Security;
@ -15,10 +16,13 @@ namespace Squidex.Web.Pipeline
{ {
public sealed class RequestLogPerformanceMiddleware : IMiddleware public sealed class RequestLogPerformanceMiddleware : IMiddleware
{ {
private readonly RequestLogOptions options;
private readonly ISemanticLog log; private readonly ISemanticLog log;
public RequestLogPerformanceMiddleware(ISemanticLog log) public RequestLogPerformanceMiddleware(IOptions<RequestLogOptions> options, ISemanticLog log)
{ {
this.options = options.Value;
this.log = log; this.log = log;
} }
@ -36,13 +40,19 @@ namespace Squidex.Web.Pipeline
{ {
var elapsedMs = watch.Stop(); var elapsedMs = watch.Stop();
log.LogInformation((elapsedMs, context), (ctx, w) => if (options.LogRequest)
{ {
Profiler.Session?.Write(w); log.LogInformation((elapsedMs, context), (ctx, w) =>
{
if (options.LogProfiler)
{
Profiler.Session?.Write(w);
}
w.WriteObject("filters", ctx.context, LogFilters); w.WriteObject("filters", ctx.context, LogFilters);
w.WriteProperty("elapsedRequestMs", ctx.elapsedMs); w.WriteProperty("elapsedRequestMs", ctx.elapsedMs);
}); });
}
} }
} }
} }

6
backend/src/Squidex/Config/Domain/LoggingServices.cs

@ -32,6 +32,12 @@ namespace Squidex.Config.Domain
private static void AddServices(this IServiceCollection services, IConfiguration config) private static void AddServices(this IServiceCollection services, IConfiguration config)
{ {
services.Configure<RequestLogOptions>(
config.GetSection("logging"));
services.Configure<SemanticLogOptions>(
config.GetSection("logging"));
if (config.GetValue<bool>("logging:human")) if (config.GetValue<bool>("logging:human"))
{ {
services.AddSingletonAs(_ => JsonLogWriterFactory.Readable()) services.AddSingletonAs(_ => JsonLogWriterFactory.Readable())

16
backend/src/Squidex/appsettings.json

@ -225,6 +225,12 @@
}, },
"logging": { "logging": {
/*
* The log level.
*
* Trace, Debug, Information, Warning, Error, Fatal
*/
"level": "Information",
/* /*
* Setting the flag to true, enables well formatteds json logs. * Setting the flag to true, enables well formatteds json logs.
*/ */
@ -232,7 +238,15 @@
/* /*
* Set to true, to use colors. * Set to true, to use colors.
*/ */
"colors": true "colors": true,
/*
* Set to false to disable logging of http requests.
*/
"logRequests": true,
/*
* Set to true to enable logging of profiler information.
*/
"logProfiler": false
}, },
"assetStore": { "assetStore": {

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

@ -24,13 +24,10 @@ namespace Squidex.Infrastructure.Commands
private sealed class MyLog : ISemanticLog private sealed class MyLog : ISemanticLog
{ {
public int LogCount { get; private set; }
public Dictionary<SemanticLogLevel, int> LogLevels { get; } = new Dictionary<SemanticLogLevel, int>(); 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, Action<T, IObjectWriter> action)
{ {
LogCount++;
LogLevels[logLevel] = LogLevels.GetOrDefault(logLevel) + 1; LogLevels[logLevel] = LogLevels.GetOrDefault(logLevel) + 1;
} }
@ -57,8 +54,11 @@ namespace Squidex.Infrastructure.Commands
return TaskHelper.Done; return TaskHelper.Done;
}); });
Assert.Equal(3, log.LogCount); Assert.Equal(log.LogLevels, new Dictionary<SemanticLogLevel, int>
Assert.Equal(3, log.LogLevels[SemanticLogLevel.Information]); {
[SemanticLogLevel.Debug] = 1,
[SemanticLogLevel.Information] = 2
});
} }
[Fact] [Fact]
@ -71,9 +71,12 @@ namespace Squidex.Infrastructure.Commands
await sut.HandleAsync(context, () => throw new InvalidOperationException()); await sut.HandleAsync(context, () => throw new InvalidOperationException());
}); });
Assert.Equal(3, log.LogCount); Assert.Equal(log.LogLevels, new Dictionary<SemanticLogLevel, int>
Assert.Equal(2, log.LogLevels[SemanticLogLevel.Information]); {
Assert.Equal(1, log.LogLevels[SemanticLogLevel.Error]); [SemanticLogLevel.Debug] = 1,
[SemanticLogLevel.Information] = 1,
[SemanticLogLevel.Error] = 1,
});
} }
[Fact] [Fact]
@ -83,9 +86,12 @@ namespace Squidex.Infrastructure.Commands
await sut.HandleAsync(context, () => TaskHelper.Done); await sut.HandleAsync(context, () => TaskHelper.Done);
Assert.Equal(4, log.LogCount); Assert.Equal(log.LogLevels, new Dictionary<SemanticLogLevel, int>
Assert.Equal(3, log.LogLevels[SemanticLogLevel.Information]); {
Assert.Equal(1, log.LogLevels[SemanticLogLevel.Fatal]); [SemanticLogLevel.Debug] = 1,
[SemanticLogLevel.Information] = 2,
[SemanticLogLevel.Fatal] = 1,
});
} }
} }
} }

6
backend/tests/Squidex.Infrastructure.Tests/Log/SemanticLogAdapterTests.cs

@ -10,6 +10,7 @@ using System.Collections.Generic;
using FakeItEasy; using FakeItEasy;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Squidex.Infrastructure.Log.Adapter; using Squidex.Infrastructure.Log.Adapter;
using Xunit; using Xunit;
@ -17,6 +18,7 @@ namespace Squidex.Infrastructure.Log
{ {
public class SemanticLogAdapterTests public class SemanticLogAdapterTests
{ {
private readonly IOptions<SemanticLogOptions> options = Options.Create(new SemanticLogOptions());
private readonly List<ILogChannel> channels = new List<ILogChannel>(); private readonly List<ILogChannel> channels = new List<ILogChannel>();
private readonly Lazy<SemanticLog> log; private readonly Lazy<SemanticLog> log;
private readonly ILogChannel channel = A.Fake<ILogChannel>(); private readonly ILogChannel channel = A.Fake<ILogChannel>();
@ -30,6 +32,8 @@ namespace Squidex.Infrastructure.Log
public SemanticLogAdapterTests() public SemanticLogAdapterTests()
{ {
options.Value.Level = SemanticLogLevel.Trace;
channels.Add(channel); channels.Add(channel);
A.CallTo(() => channel.Log(A<SemanticLogLevel>.Ignored, A<string>.Ignored)) A.CallTo(() => channel.Log(A<SemanticLogLevel>.Ignored, A<string>.Ignored))
@ -38,7 +42,7 @@ namespace Squidex.Infrastructure.Log
output = message; output = message;
}); });
log = new Lazy<SemanticLog>(() => new SemanticLog(channels, new List<ILogAppender>(), JsonLogWriterFactory.Default())); log = new Lazy<SemanticLog>(() => new SemanticLog(options, channels, new List<ILogAppender>(), JsonLogWriterFactory.Default()));
sut = SemanticLogLoggerProvider.ForTesting(log.Value); sut = SemanticLogLoggerProvider.ForTesting(log.Value);
} }

18
backend/tests/Squidex.Infrastructure.Tests/Log/SemanticLogTests.cs

@ -10,6 +10,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using FakeItEasy; using FakeItEasy;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NodaTime; using NodaTime;
using Squidex.Infrastructure.Log.Adapter; using Squidex.Infrastructure.Log.Adapter;
using Xunit; using Xunit;
@ -20,6 +21,7 @@ namespace Squidex.Infrastructure.Log
{ {
private readonly List<ILogAppender> appenders = new List<ILogAppender>(); private readonly List<ILogAppender> appenders = new List<ILogAppender>();
private readonly List<ILogChannel> channels = new List<ILogChannel>(); private readonly List<ILogChannel> channels = new List<ILogChannel>();
private readonly IOptions<SemanticLogOptions> options = Options.Create(new SemanticLogOptions());
private readonly Lazy<SemanticLog> log; private readonly Lazy<SemanticLog> log;
private readonly ILogChannel channel = A.Fake<ILogChannel>(); private readonly ILogChannel channel = A.Fake<ILogChannel>();
private string output = string.Empty; private string output = string.Empty;
@ -31,6 +33,8 @@ namespace Squidex.Infrastructure.Log
public SemanticLogTests() public SemanticLogTests()
{ {
options.Value.Level = SemanticLogLevel.Trace;
channels.Add(channel); channels.Add(channel);
A.CallTo(() => channel.Log(A<SemanticLogLevel>.Ignored, A<string>.Ignored)) A.CallTo(() => channel.Log(A<SemanticLogLevel>.Ignored, A<string>.Ignored))
@ -39,7 +43,7 @@ namespace Squidex.Infrastructure.Log
output += message; output += message;
}); });
log = new Lazy<SemanticLog>(() => new SemanticLog(channels, appenders, JsonLogWriterFactory.Default())); log = new Lazy<SemanticLog>(() => new SemanticLog(options, channels, appenders, JsonLogWriterFactory.Default()));
} }
[Fact] [Fact]
@ -498,7 +502,7 @@ namespace Squidex.Infrastructure.Log
A.CallTo(() => channel1.Log(A<SemanticLogLevel>.Ignored, A<string>.Ignored)).Throws(exception1); A.CallTo(() => channel1.Log(A<SemanticLogLevel>.Ignored, A<string>.Ignored)).Throws(exception1);
A.CallTo(() => channel2.Log(A<SemanticLogLevel>.Ignored, A<string>.Ignored)).Throws(exception2); A.CallTo(() => channel2.Log(A<SemanticLogLevel>.Ignored, A<string>.Ignored)).Throws(exception2);
var sut = new SemanticLog(new[] { channel1, channel2 }, Enumerable.Empty<ILogAppender>(), JsonLogWriterFactory.Default()); var sut = new SemanticLog(options, new[] { channel1, channel2 }, Enumerable.Empty<ILogAppender>(), JsonLogWriterFactory.Default());
try try
{ {
@ -513,6 +517,16 @@ namespace Squidex.Infrastructure.Log
} }
} }
[Fact]
public void Should_not_log_if_level_is_too_low()
{
options.Value.Level = SemanticLogLevel.Error;
Log.LogWarning(w => w.WriteProperty("Property", "Value"));
Assert.Equal(string.Empty, output);
}
private static string LogTest(Action<IObjectWriter> writer) private static string LogTest(Action<IObjectWriter> writer)
{ {
var sut = JsonLogWriterFactory.Default().Create(); var sut = JsonLogWriterFactory.Default().Create();

Loading…
Cancel
Save