mirror of https://github.com/Squidex/squidex.git
44 changed files with 1664 additions and 195 deletions
@ -1,19 +0,0 @@ |
|||
// ==========================================================================
|
|||
// RedisInfrastructureErrors.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using Microsoft.Extensions.Logging; |
|||
|
|||
namespace Squidex.Infrastructure.Redis |
|||
{ |
|||
public static class RedisInfrastructureErrors |
|||
{ |
|||
public static readonly EventId InvalidatingReceivedFailed = new EventId(50001, "InvalidingReceivedFailed"); |
|||
|
|||
public static readonly EventId InvalidatingPublishedFailed = new EventId(50002, "InvalidatingPublishedFailed"); |
|||
} |
|||
} |
|||
@ -1,25 +0,0 @@ |
|||
// ==========================================================================
|
|||
// InfrastructureErrors.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using Microsoft.Extensions.Logging; |
|||
|
|||
namespace Squidex.Infrastructure |
|||
{ |
|||
public class InfrastructureErrors |
|||
{ |
|||
public static readonly EventId CommandUnknown = new EventId(20000, "CommandUnknown"); |
|||
|
|||
public static readonly EventId CommandFailed = new EventId(20001, "CommandFailed"); |
|||
|
|||
public static readonly EventId EventResetFailed = new EventId(10000, "EventResetFailed"); |
|||
|
|||
public static readonly EventId EventHandlingFailed = new EventId(10001, "EventHandlingFailed"); |
|||
|
|||
public static readonly EventId EventDeserializationFailed = new EventId(10002, "EventDeserializationFailed"); |
|||
} |
|||
} |
|||
@ -0,0 +1,102 @@ |
|||
// ==========================================================================
|
|||
// SemanticLogLogger.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using Microsoft.Extensions.Logging; |
|||
|
|||
// ReSharper disable SwitchStatementMissingSomeCases
|
|||
|
|||
namespace Squidex.Infrastructure.Log.Adapter |
|||
{ |
|||
internal sealed class SemanticLogLogger : ILogger |
|||
{ |
|||
private readonly ISemanticLog semanticLog; |
|||
|
|||
public SemanticLogLogger(ISemanticLog semanticLog) |
|||
{ |
|||
this.semanticLog = semanticLog; |
|||
} |
|||
|
|||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) |
|||
{ |
|||
SemanticLogLevel semanticLogLevel; |
|||
|
|||
switch (logLevel) |
|||
{ |
|||
case LogLevel.Trace: |
|||
semanticLogLevel = SemanticLogLevel.Trace; |
|||
break; |
|||
case LogLevel.Debug: |
|||
semanticLogLevel = SemanticLogLevel.Debug; |
|||
break; |
|||
case LogLevel.Information: |
|||
semanticLogLevel = SemanticLogLevel.Information; |
|||
break; |
|||
case LogLevel.Warning: |
|||
semanticLogLevel = SemanticLogLevel.Warning; |
|||
break; |
|||
case LogLevel.Error: |
|||
semanticLogLevel = SemanticLogLevel.Error; |
|||
break; |
|||
case LogLevel.Critical: |
|||
semanticLogLevel = SemanticLogLevel.Fatal; |
|||
break; |
|||
default: |
|||
semanticLogLevel = SemanticLogLevel.Debug; |
|||
break; |
|||
} |
|||
|
|||
semanticLog.Log(semanticLogLevel, writer => |
|||
{ |
|||
var message = formatter(state, exception); |
|||
|
|||
if (!string.IsNullOrWhiteSpace(message)) |
|||
{ |
|||
writer.WriteProperty(nameof(message), message); |
|||
} |
|||
|
|||
if (eventId.Id > 0) |
|||
{ |
|||
writer.WriteObject(nameof(eventId), eventIdWriter => |
|||
{ |
|||
eventIdWriter.WriteProperty("id", eventId.Id); |
|||
|
|||
if (!string.IsNullOrWhiteSpace(eventId.Name)) |
|||
{ |
|||
eventIdWriter.WriteProperty("name", eventId.Name); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
if (exception != null) |
|||
{ |
|||
writer.WriteException(exception); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
public bool IsEnabled(LogLevel logLevel) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
public IDisposable BeginScope<TState>(TState state) |
|||
{ |
|||
return NoopDisposable.Instance; |
|||
} |
|||
|
|||
private class NoopDisposable : IDisposable |
|||
{ |
|||
public static readonly NoopDisposable Instance = new NoopDisposable(); |
|||
|
|||
public void Dispose() |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
// ==========================================================================
|
|||
// SemanticLogLoggerFactoryExtensions.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using Microsoft.Extensions.Logging; |
|||
|
|||
namespace Squidex.Infrastructure.Log.Adapter |
|||
{ |
|||
public static class SemanticLogLoggerFactoryExtensions |
|||
{ |
|||
public static ILoggerFactory AddSemanticLog(this ILoggerFactory factory, ISemanticLog semanticLog) |
|||
{ |
|||
factory.AddProvider(new SemanticLogLoggerProvider(semanticLog)); |
|||
|
|||
return factory; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
// ==========================================================================
|
|||
// SemanticLogLoggerProvider.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using Microsoft.Extensions.Logging; |
|||
|
|||
namespace Squidex.Infrastructure.Log.Adapter |
|||
{ |
|||
public class SemanticLogLoggerProvider : ILoggerProvider |
|||
{ |
|||
private readonly ISemanticLog semanticLog; |
|||
|
|||
public SemanticLogLoggerProvider(ISemanticLog semanticLog) |
|||
{ |
|||
this.semanticLog = semanticLog; |
|||
} |
|||
|
|||
public ILogger CreateLogger(string categoryName) |
|||
{ |
|||
return new SemanticLogLogger(semanticLog.CreateScope(writer => |
|||
{ |
|||
writer.WriteProperty("category", categoryName); |
|||
})); |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
// ==========================================================================
|
|||
// ApplicationInfoLogAppender.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Reflection; |
|||
|
|||
namespace Squidex.Infrastructure.Log |
|||
{ |
|||
public sealed class ApplicationInfoLogAppender : ILogAppender |
|||
{ |
|||
private readonly string applicationName; |
|||
private readonly string applicationVersion; |
|||
|
|||
public ApplicationInfoLogAppender(Assembly assembly) |
|||
{ |
|||
Guard.NotNull(assembly, nameof(assembly)); |
|||
|
|||
applicationName = assembly.GetName().Name; |
|||
applicationVersion = assembly.GetName().Version.ToString(); |
|||
} |
|||
|
|||
public void Append(IObjectWriter writer) |
|||
{ |
|||
writer.WriteProperty("applicationName", applicationName); |
|||
writer.WriteProperty("applicationVersion", applicationVersion); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
// ==========================================================================
|
|||
// ConsoleLogChannel.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using Squidex.Infrastructure.Log.Internal; |
|||
|
|||
namespace Squidex.Infrastructure.Log |
|||
{ |
|||
public sealed class ConsoleLogChannel : ILogChannel, IDisposable |
|||
{ |
|||
private readonly ConsoleLogProcessor processor = new ConsoleLogProcessor(); |
|||
|
|||
public void Dispose() |
|||
{ |
|||
processor.Dispose(); |
|||
} |
|||
|
|||
public void Log(SemanticLogLevel logLevel, string message) |
|||
{ |
|||
processor.EnqueueMessage(new LogMessageEntry { Message = message, Level = logLevel }); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
// ==========================================================================
|
|||
// ConstantsLogWriter.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
|
|||
namespace Squidex.Infrastructure.Log |
|||
{ |
|||
public sealed class ConstantsLogWriter : ILogAppender |
|||
{ |
|||
private readonly Action<IObjectWriter> objectWriter; |
|||
|
|||
public ConstantsLogWriter(Action<IObjectWriter> objectWriter) |
|||
{ |
|||
Guard.NotNull(objectWriter, nameof(objectWriter)); |
|||
|
|||
this.objectWriter = objectWriter; |
|||
} |
|||
|
|||
public void Append(IObjectWriter writer) |
|||
{ |
|||
objectWriter(writer); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
// ==========================================================================
|
|||
// DebugLogChannel.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Diagnostics; |
|||
|
|||
namespace Squidex.Infrastructure.Log |
|||
{ |
|||
public sealed class DebugLogChannel : ILogChannel |
|||
{ |
|||
public void Log(SemanticLogLevel logLevel, string message) |
|||
{ |
|||
Debug.WriteLine(message); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
// ==========================================================================
|
|||
// IArrayWriter.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
|
|||
namespace Squidex.Infrastructure.Log |
|||
{ |
|||
public interface IArrayWriter |
|||
{ |
|||
IArrayWriter WriteValue(string value); |
|||
IArrayWriter WriteValue(double value); |
|||
IArrayWriter WriteValue(long value); |
|||
IArrayWriter WriteValue(bool value); |
|||
|
|||
IArrayWriter WriteValue(TimeSpan value); |
|||
IArrayWriter WriteValue(DateTime value); |
|||
IArrayWriter WriteValue(DateTimeOffset value); |
|||
|
|||
IArrayWriter WriteObject(string property, Action<IObjectWriter> objectWriter); |
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
// ==========================================================================
|
|||
// ILogAppender.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
namespace Squidex.Infrastructure.Log |
|||
{ |
|||
public interface ILogAppender |
|||
{ |
|||
void Append(IObjectWriter writer); |
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
// ==========================================================================
|
|||
// ILogChannel.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
namespace Squidex.Infrastructure.Log |
|||
{ |
|||
public interface ILogChannel |
|||
{ |
|||
void Log(SemanticLogLevel logLevel, string message); |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
// ==========================================================================
|
|||
// IObjectWriter.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
|
|||
namespace Squidex.Infrastructure.Log |
|||
{ |
|||
public interface IObjectWriter |
|||
{ |
|||
IObjectWriter WriteProperty(string property, string value); |
|||
IObjectWriter WriteProperty(string property, double value); |
|||
IObjectWriter WriteProperty(string property, long value); |
|||
IObjectWriter WriteProperty(string property, bool value); |
|||
|
|||
IObjectWriter WriteProperty(string property, TimeSpan value); |
|||
IObjectWriter WriteProperty(string property, DateTime value); |
|||
IObjectWriter WriteProperty(string property, DateTimeOffset value); |
|||
|
|||
IObjectWriter WriteObject(string property, Action<IObjectWriter> objectWriter); |
|||
IObjectWriter WriteArray(string property, Action<IArrayWriter> arrayWriter); |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
// ==========================================================================
|
|||
// ISemanticLog.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
|
|||
namespace Squidex.Infrastructure.Log |
|||
{ |
|||
public interface ISemanticLog |
|||
{ |
|||
void Log(SemanticLogLevel logLevel, Action<IObjectWriter> action); |
|||
|
|||
ISemanticLog CreateScope(Action<IObjectWriter> objectWriter); |
|||
} |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
// ==========================================================================
|
|||
// AnsiLogConsole.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Text; |
|||
|
|||
// ReSharper disable SwitchStatementMissingSomeCases
|
|||
|
|||
namespace Squidex.Infrastructure.Log.Internal |
|||
{ |
|||
public class AnsiLogConsole : IConsole |
|||
{ |
|||
private readonly StringBuilder outputBuilder = new StringBuilder(); |
|||
|
|||
public void WriteLine(SemanticLogLevel level, string message) |
|||
{ |
|||
if (level >= SemanticLogLevel.Error) |
|||
{ |
|||
outputBuilder.Append("\x1B[1m\x1B[31m"); |
|||
outputBuilder.Append(message); |
|||
outputBuilder.Append("\x1B[39m\x1B[22m"); |
|||
outputBuilder.AppendLine(); |
|||
|
|||
Console.Error.Write(outputBuilder.ToString()); |
|||
} |
|||
else |
|||
{ |
|||
Console.Out.Write(outputBuilder.ToString()); |
|||
} |
|||
|
|||
outputBuilder.Clear(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,73 @@ |
|||
// ==========================================================================
|
|||
// ConsoleLogProcessor.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Concurrent; |
|||
using System.Runtime.InteropServices; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Squidex.Infrastructure.Log.Internal |
|||
{ |
|||
public class ConsoleLogProcessor : IDisposable |
|||
{ |
|||
private readonly IConsole console; |
|||
private const int MaxQueuedMessages = 1024; |
|||
private readonly BlockingCollection<LogMessageEntry> messageQueue = new BlockingCollection<LogMessageEntry>(MaxQueuedMessages); |
|||
private readonly Task outputTask; |
|||
|
|||
public ConsoleLogProcessor() |
|||
{ |
|||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) |
|||
{ |
|||
console = new WindowsLogConsole(); |
|||
} |
|||
else |
|||
{ |
|||
console = new AnsiLogConsole(); |
|||
} |
|||
|
|||
outputTask = Task.Factory.StartNew(ProcessLogQueue, this, TaskCreationOptions.LongRunning); |
|||
} |
|||
|
|||
public void EnqueueMessage(LogMessageEntry message) |
|||
{ |
|||
messageQueue.Add(message); |
|||
} |
|||
|
|||
private void ProcessLogQueue() |
|||
{ |
|||
foreach (var entry in messageQueue.GetConsumingEnumerable()) |
|||
{ |
|||
console.WriteLine(entry.Level, entry.Message); |
|||
} |
|||
} |
|||
|
|||
private static void ProcessLogQueue(object state) |
|||
{ |
|||
var processor = (ConsoleLogProcessor)state; |
|||
|
|||
processor.ProcessLogQueue(); |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
messageQueue.CompleteAdding(); |
|||
|
|||
try |
|||
{ |
|||
outputTask.Wait(1500); |
|||
} |
|||
catch (TaskCanceledException) |
|||
{ |
|||
} |
|||
catch (AggregateException ex) when (ex.InnerExceptions.Count == 1 && ex.InnerExceptions[0] is TaskCanceledException) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
// ==========================================================================
|
|||
// IConsole.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
namespace Squidex.Infrastructure.Log.Internal |
|||
{ |
|||
public interface IConsole |
|||
{ |
|||
void WriteLine(SemanticLogLevel level, string message); |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
// ==========================================================================
|
|||
// LogMessageEntry.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
namespace Squidex.Infrastructure.Log.Internal |
|||
{ |
|||
public struct LogMessageEntry |
|||
{ |
|||
public SemanticLogLevel Level; |
|||
|
|||
public string Message; |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
// ==========================================================================
|
|||
// WindowsLogConsole.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
|
|||
namespace Squidex.Infrastructure.Log.Internal |
|||
{ |
|||
public class WindowsLogConsole : IConsole |
|||
{ |
|||
public void WriteLine(SemanticLogLevel level, string message) |
|||
{ |
|||
if (level >= SemanticLogLevel.Error) |
|||
{ |
|||
Console.ForegroundColor = ConsoleColor.Red; |
|||
Console.Error.WriteLine(message); |
|||
Console.ResetColor(); |
|||
} |
|||
else |
|||
{ |
|||
Console.Out.WriteLine(message); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,185 @@ |
|||
// ==========================================================================
|
|||
// JsonLogWriter.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Globalization; |
|||
using System.IO; |
|||
using Newtonsoft.Json; |
|||
|
|||
namespace Squidex.Infrastructure.Log |
|||
{ |
|||
public sealed class JsonLogWriter : IObjectWriter, IArrayWriter |
|||
{ |
|||
private readonly bool extraLine; |
|||
private readonly StringWriter textWriter = new StringWriter(); |
|||
private readonly JsonWriter jsonWriter; |
|||
|
|||
public JsonLogWriter(Formatting formatting = Formatting.None, bool extraLine = false) |
|||
{ |
|||
this.extraLine = extraLine; |
|||
|
|||
jsonWriter = new JsonTextWriter(textWriter) { Formatting = formatting }; |
|||
jsonWriter.WriteStartObject(); |
|||
} |
|||
|
|||
IArrayWriter IArrayWriter.WriteValue(string value) |
|||
{ |
|||
jsonWriter.WriteValue(value); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
IArrayWriter IArrayWriter.WriteValue(double value) |
|||
{ |
|||
jsonWriter.WriteValue(value); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
IArrayWriter IArrayWriter.WriteValue(long value) |
|||
{ |
|||
jsonWriter.WriteValue(value); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
IArrayWriter IArrayWriter.WriteValue(bool value) |
|||
{ |
|||
jsonWriter.WriteValue(value); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
IArrayWriter IArrayWriter.WriteValue(DateTime value) |
|||
{ |
|||
jsonWriter.WriteValue(value.ToString("o", CultureInfo.InvariantCulture)); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
IArrayWriter IArrayWriter.WriteValue(DateTimeOffset value) |
|||
{ |
|||
jsonWriter.WriteValue(value.ToString("o", CultureInfo.InvariantCulture)); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
IArrayWriter IArrayWriter.WriteValue(TimeSpan value) |
|||
{ |
|||
jsonWriter.WriteValue(value); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
IObjectWriter IObjectWriter.WriteProperty(string property, string value) |
|||
{ |
|||
jsonWriter.WritePropertyName(property); |
|||
jsonWriter.WriteValue(value); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
IObjectWriter IObjectWriter.WriteProperty(string property, double value) |
|||
{ |
|||
jsonWriter.WritePropertyName(property); |
|||
jsonWriter.WriteValue(value); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
IObjectWriter IObjectWriter.WriteProperty(string property, long value) |
|||
{ |
|||
jsonWriter.WritePropertyName(property); |
|||
jsonWriter.WriteValue(value); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
IObjectWriter IObjectWriter.WriteProperty(string property, bool value) |
|||
{ |
|||
jsonWriter.WritePropertyName(property); |
|||
jsonWriter.WriteValue(value); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
IObjectWriter IObjectWriter.WriteProperty(string property, DateTime value) |
|||
{ |
|||
jsonWriter.WritePropertyName(property); |
|||
jsonWriter.WriteValue(value.ToString("o", CultureInfo.InvariantCulture)); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
IObjectWriter IObjectWriter.WriteProperty(string property, DateTimeOffset value) |
|||
{ |
|||
jsonWriter.WritePropertyName(property); |
|||
jsonWriter.WriteValue(value.ToString("o", CultureInfo.InvariantCulture)); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
IObjectWriter IObjectWriter.WriteProperty(string property, TimeSpan value) |
|||
{ |
|||
jsonWriter.WritePropertyName(property); |
|||
jsonWriter.WriteValue(value); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
IObjectWriter IObjectWriter.WriteObject(string property, Action<IObjectWriter> objectWriter) |
|||
{ |
|||
jsonWriter.WritePropertyName(property); |
|||
jsonWriter.WriteStartObject(); |
|||
|
|||
objectWriter?.Invoke(this); |
|||
|
|||
jsonWriter.WriteEndObject(); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
IObjectWriter IObjectWriter.WriteArray(string property, Action<IArrayWriter> arrayWriter) |
|||
{ |
|||
jsonWriter.WritePropertyName(property); |
|||
jsonWriter.WriteStartArray(); |
|||
|
|||
arrayWriter?.Invoke(this); |
|||
|
|||
jsonWriter.WriteEndArray(); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
IArrayWriter IArrayWriter.WriteObject(string property, Action<IObjectWriter> objectWriter) |
|||
{ |
|||
jsonWriter.WritePropertyName(property); |
|||
jsonWriter.WriteStartObject(); |
|||
|
|||
objectWriter?.Invoke(this); |
|||
|
|||
jsonWriter.WriteEndObject(); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
public override string ToString() |
|||
{ |
|||
jsonWriter.WriteEndObject(); |
|||
|
|||
var result = textWriter.ToString(); |
|||
|
|||
if (extraLine) |
|||
{ |
|||
result += Environment.NewLine; |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,86 @@ |
|||
// ==========================================================================
|
|||
// SemanticLog.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
|
|||
namespace Squidex.Infrastructure.Log |
|||
{ |
|||
public sealed class SemanticLog : ISemanticLog |
|||
{ |
|||
private readonly IEnumerable<ILogChannel> channels; |
|||
private readonly IEnumerable<ILogAppender> appenders; |
|||
private readonly Func<IObjectWriter> writerFactory; |
|||
|
|||
public SemanticLog( |
|||
IEnumerable<ILogChannel> channels, |
|||
IEnumerable<ILogAppender> appenders, |
|||
Func<IObjectWriter> writerFactory) |
|||
{ |
|||
Guard.NotNull(channels, nameof(channels)); |
|||
Guard.NotNull(appenders, nameof(appenders)); |
|||
|
|||
this.channels = channels; |
|||
this.appenders = appenders; |
|||
this.writerFactory = writerFactory; |
|||
} |
|||
|
|||
public void Log(SemanticLogLevel logLevel, Action<IObjectWriter> action) |
|||
{ |
|||
Guard.NotNull(action, nameof(action)); |
|||
|
|||
var formattedText = FormatText(logLevel, action); |
|||
|
|||
List<Exception> exceptions = null; |
|||
|
|||
foreach (var channel in channels) |
|||
{ |
|||
try |
|||
{ |
|||
channel.Log(logLevel, formattedText); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
if (exceptions == null) |
|||
{ |
|||
exceptions = new List<Exception>(); |
|||
} |
|||
|
|||
exceptions.Add(ex); |
|||
} |
|||
} |
|||
|
|||
if (exceptions != null && exceptions.Count > 0) |
|||
{ |
|||
throw new AggregateException("An error occurred while writing to logger(s).", exceptions); |
|||
} |
|||
} |
|||
|
|||
private string FormatText(SemanticLogLevel logLevel, Action<IObjectWriter> objectWriter) |
|||
{ |
|||
var writer = writerFactory(); |
|||
|
|||
writer.WriteProperty(nameof(logLevel), logLevel.ToString()); |
|||
|
|||
objectWriter(writer); |
|||
|
|||
foreach (var appender in appenders) |
|||
{ |
|||
appender.Append(writer); |
|||
} |
|||
|
|||
return writer.ToString(); |
|||
} |
|||
|
|||
public ISemanticLog CreateScope(Action<IObjectWriter> objectWriter) |
|||
{ |
|||
return new SemanticLog(channels, appenders.Union(new ILogAppender[] { new ConstantsLogWriter(objectWriter) }).ToArray(), writerFactory); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,125 @@ |
|||
// ==========================================================================
|
|||
// SemanticLogExtensions.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Diagnostics; |
|||
|
|||
// ReSharper disable InvertIf
|
|||
|
|||
namespace Squidex.Infrastructure.Log |
|||
{ |
|||
public static class SemanticLogExtensions |
|||
{ |
|||
public static void LogTrace(this ISemanticLog log, Action<IObjectWriter> objectWriter) |
|||
{ |
|||
log.Log(SemanticLogLevel.Trace, objectWriter); |
|||
} |
|||
|
|||
public static IDisposable MeasureTrace(this ISemanticLog log, Action<IObjectWriter> objectWriter) |
|||
{ |
|||
return new TimeMeasurer(log, SemanticLogLevel.Trace, objectWriter); |
|||
} |
|||
|
|||
public static void LogDebug(this ISemanticLog log, Action<IObjectWriter> objectWriter) |
|||
{ |
|||
log.Log(SemanticLogLevel.Debug, objectWriter); |
|||
} |
|||
|
|||
public static IDisposable MeasureDebug(this ISemanticLog log, Action<IObjectWriter> objectWriter) |
|||
{ |
|||
return new TimeMeasurer(log, SemanticLogLevel.Debug, objectWriter); |
|||
} |
|||
|
|||
public static void LogInformation(this ISemanticLog log, Action<IObjectWriter> objectWriter) |
|||
{ |
|||
log.Log(SemanticLogLevel.Information, objectWriter); |
|||
} |
|||
|
|||
public static IDisposable MeasureInformation(this ISemanticLog log, Action<IObjectWriter> objectWriter) |
|||
{ |
|||
return new TimeMeasurer(log, SemanticLogLevel.Information, objectWriter); |
|||
} |
|||
|
|||
public static void LogWarning(this ISemanticLog log, Action<IObjectWriter> objectWriter) |
|||
{ |
|||
log.Log(SemanticLogLevel.Warning, objectWriter); |
|||
} |
|||
|
|||
public static void LogWarning(this ISemanticLog log, Exception exception, Action<IObjectWriter> objectWriter = null) |
|||
{ |
|||
log.Log(SemanticLogLevel.Warning, writer => writer.WriteException(exception, objectWriter)); |
|||
} |
|||
|
|||
public static void LogError(this ISemanticLog log, Action<IObjectWriter> objectWriter) |
|||
{ |
|||
log.Log(SemanticLogLevel.Error, objectWriter); |
|||
} |
|||
|
|||
public static void LogError(this ISemanticLog log, Exception exception, Action<IObjectWriter> objectWriter = null) |
|||
{ |
|||
log.Log(SemanticLogLevel.Error, writer => writer.WriteException(exception, objectWriter)); |
|||
} |
|||
|
|||
public static void LogFatal(this ISemanticLog log, Action<IObjectWriter> objectWriter) |
|||
{ |
|||
log.Log(SemanticLogLevel.Fatal, objectWriter); |
|||
} |
|||
|
|||
public static void LogFatal(this ISemanticLog log, Exception exception, Action<IObjectWriter> objectWriter = null) |
|||
{ |
|||
log.Log(SemanticLogLevel.Fatal, writer => writer.WriteException(exception, objectWriter)); |
|||
} |
|||
|
|||
private static void WriteException(this IObjectWriter writer, Exception exception, Action<IObjectWriter> objectWriter) |
|||
{ |
|||
objectWriter?.Invoke(writer); |
|||
|
|||
if (exception != null) |
|||
{ |
|||
writer.WriteException(exception); |
|||
} |
|||
} |
|||
|
|||
public static IObjectWriter WriteException(this IObjectWriter writer, Exception exception) |
|||
{ |
|||
return writer.WriteObject(nameof(exception), inner => |
|||
{ |
|||
inner.WriteProperty("message", exception.Message); |
|||
inner.WriteProperty("stackTrace", exception.StackTrace); |
|||
}); |
|||
} |
|||
|
|||
private sealed class TimeMeasurer : IDisposable |
|||
{ |
|||
private readonly Stopwatch watch = Stopwatch.StartNew(); |
|||
private readonly SemanticLogLevel logLevel; |
|||
private readonly Action<IObjectWriter> objectWriter; |
|||
private readonly ISemanticLog log; |
|||
|
|||
public TimeMeasurer(ISemanticLog log, SemanticLogLevel logLevel, Action<IObjectWriter> objectWriter) |
|||
{ |
|||
this.logLevel = logLevel; |
|||
this.log = log; |
|||
|
|||
this.objectWriter = objectWriter; |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
watch.Stop(); |
|||
|
|||
log.Log(logLevel, writer => |
|||
{ |
|||
objectWriter?.Invoke(writer); |
|||
|
|||
writer.WriteProperty("elapsedMs", watch.ElapsedMilliseconds); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
// ==========================================================================
|
|||
// SemanticLogLevel.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
namespace Squidex.Infrastructure.Log |
|||
{ |
|||
public enum SemanticLogLevel |
|||
{ |
|||
Trace, |
|||
Debug, |
|||
Information, |
|||
Warning, |
|||
Error, |
|||
Fatal |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
// ==========================================================================
|
|||
// TimestampLogAppender.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
|
|||
namespace Squidex.Infrastructure.Log |
|||
{ |
|||
public sealed class TimestampLogAppender : ILogAppender |
|||
{ |
|||
private readonly Func<long> timestamp; |
|||
|
|||
public TimestampLogAppender() |
|||
: this(() => DateTimeOffset.UtcNow.ToUnixTimeSeconds()) |
|||
{ |
|||
} |
|||
|
|||
public TimestampLogAppender(Func<long> timestamp) |
|||
{ |
|||
Guard.NotNull(timestamp, nameof(timestamp)); |
|||
|
|||
this.timestamp = timestamp; |
|||
} |
|||
|
|||
public void Append(IObjectWriter writer) |
|||
{ |
|||
writer.WriteProperty("timestamp", timestamp()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,163 @@ |
|||
// ==========================================================================
|
|||
// JsonLogWriterTests.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using Newtonsoft.Json; |
|||
using System; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Infrastructure.Log |
|||
{ |
|||
public class JsonLogWriterTests |
|||
{ |
|||
private readonly IObjectWriter sut = new JsonLogWriter(); |
|||
|
|||
[Fact] |
|||
public void Should_write_boolean_property() |
|||
{ |
|||
var result = sut.WriteProperty("property", true).ToString(); |
|||
|
|||
Assert.Equal(@"{""property"":true}", result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_write_long_property() |
|||
{ |
|||
var result = sut.WriteProperty("property", 120).ToString(); |
|||
|
|||
Assert.Equal(@"{""property"":120}", result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_write_double_property() |
|||
{ |
|||
var result = sut.WriteProperty("property", 1.5).ToString(); |
|||
|
|||
Assert.Equal(@"{""property"":1.5}", result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_write_string_property() |
|||
{ |
|||
var result = sut.WriteProperty("property", "my-string").ToString(); |
|||
|
|||
Assert.Equal(@"{""property"":""my-string""}", result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_write_timespan_property() |
|||
{ |
|||
var result = sut.WriteProperty("property", new TimeSpan(1, 40, 30, 20, 100)).ToString(); |
|||
|
|||
Assert.Equal(@"{""property"":""2.16:30:20.1000000""}", result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_write_datetimeoffset_property() |
|||
{ |
|||
var value = DateTimeOffset.UtcNow; |
|||
var result = sut.WriteProperty("property", value).ToString(); |
|||
|
|||
Assert.Equal($"{{\"property\":\"{value.ToString("o")}\"}}", result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_write_date_property() |
|||
{ |
|||
var value = DateTime.UtcNow; |
|||
var result = sut.WriteProperty("property", value).ToString(); |
|||
|
|||
Assert.Equal($"{{\"property\":\"{value.ToString("o")}\"}}", result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_write_boolean_value() |
|||
{ |
|||
var result = sut.WriteArray("property", a => a.WriteValue(true)).ToString(); |
|||
|
|||
Assert.Equal(@"{""property"":[true]}", result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_write_long_value() |
|||
{ |
|||
var result = sut.WriteArray("property", a => a.WriteValue(120)).ToString(); |
|||
|
|||
Assert.Equal(@"{""property"":[120]}", result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_write_double_value() |
|||
{ |
|||
var result = sut.WriteArray("property", a => a.WriteValue(1.5)).ToString(); |
|||
|
|||
Assert.Equal(@"{""property"":[1.5]}", result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_write_string_value() |
|||
{ |
|||
var result = sut.WriteArray("property", a => a.WriteValue("my-string")).ToString(); |
|||
|
|||
Assert.Equal(@"{""property"":[""my-string""]}", result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_write_timespan_value() |
|||
{ |
|||
var result = sut.WriteArray("property", a => a.WriteValue(new TimeSpan(1, 40, 30, 20, 100))).ToString(); |
|||
|
|||
Assert.Equal(@"{""property"":[""2.16:30:20.1000000""]}", result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_write_datetimeoffset_value() |
|||
{ |
|||
var value = DateTimeOffset.UtcNow; |
|||
var result = sut.WriteArray("property", a => a.WriteValue(value)).ToString(); |
|||
|
|||
Assert.Equal($"{{\"property\":[\"{value.ToString("o")}\"]}}", result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_write_date_value() |
|||
{ |
|||
var value = DateTime.UtcNow; |
|||
var result = sut.WriteArray("property", a => a.WriteValue(value)).ToString(); |
|||
|
|||
Assert.Equal($"{{\"property\":[\"{value.ToString("o")}\"]}}", result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_write_nested_object() |
|||
{ |
|||
var result = sut.WriteObject("property", a => a.WriteProperty("nested", "my-string")).ToString(); |
|||
|
|||
Assert.Equal(@"{""property"":{""nested"":""my-string""}}", result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_write_pretty_json() |
|||
{ |
|||
IObjectWriter prettySut = new JsonLogWriter(Formatting.Indented, false); |
|||
|
|||
var result = prettySut.WriteProperty("property", 1.5).ToString(); |
|||
|
|||
Assert.Equal(@"{NL ""property"": 1.5NL}".Replace("NL", Environment.NewLine), result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_write_extra_line_after_object() |
|||
{ |
|||
IObjectWriter prettySut = new JsonLogWriter(Formatting.None, true); |
|||
|
|||
var result = prettySut.WriteProperty("property", 1.5).ToString(); |
|||
|
|||
Assert.Equal(@"{""property"":1.5}NL".Replace("NL", Environment.NewLine), result); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,300 @@ |
|||
// ==========================================================================
|
|||
// SemanticLogTests.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Reflection; |
|||
using Microsoft.Extensions.Logging; |
|||
using Moq; |
|||
using Squidex.Infrastructure.Log.Adapter; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Infrastructure.Log |
|||
{ |
|||
public class SemanticLogTests |
|||
{ |
|||
private readonly List<ILogAppender> appenders = new List<ILogAppender>(); |
|||
private readonly List<ILogChannel> channels = new List<ILogChannel>(); |
|||
private readonly Lazy<SemanticLog> log; |
|||
private readonly Mock<ILogChannel> channel = new Mock<ILogChannel>(); |
|||
private string output; |
|||
|
|||
public SemanticLog Log |
|||
{ |
|||
get { return log.Value; } |
|||
} |
|||
|
|||
public SemanticLogTests() |
|||
{ |
|||
channels.Add(channel.Object); |
|||
|
|||
channel.Setup(x => x.Log(It.IsAny<SemanticLogLevel>(), It.IsAny<string>())).Callback( |
|||
new Action<SemanticLogLevel, string>((level, message) => |
|||
{ |
|||
output = message; |
|||
})); |
|||
|
|||
log = new Lazy<SemanticLog>(() => new SemanticLog(channels, appenders, () => new JsonLogWriter())); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_log_timestamp() |
|||
{ |
|||
appenders.Add(new TimestampLogAppender(() => 1500)); |
|||
|
|||
Log.LogFatal(w => {}); |
|||
|
|||
var expected = |
|||
MakeTestCall(w => w |
|||
.WriteProperty("logLevel", "Fatal") |
|||
.WriteProperty("timestamp", 1500)); |
|||
|
|||
Assert.Equal(expected, output); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_log_values_with_appender() |
|||
{ |
|||
appenders.Add(new ConstantsLogWriter(w => w.WriteProperty("logValue", 1500))); |
|||
|
|||
Log.LogFatal(m => { }); |
|||
|
|||
var expected = |
|||
MakeTestCall(w => w |
|||
.WriteProperty("logLevel", "Fatal") |
|||
.WriteProperty("logValue", 1500)); |
|||
|
|||
Assert.Equal(expected, output); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_log_application_info() |
|||
{ |
|||
appenders.Add(new ApplicationInfoLogAppender(GetType().GetTypeInfo().Assembly)); |
|||
|
|||
Log.LogFatal(m => { }); |
|||
|
|||
var expected = |
|||
MakeTestCall(w => w |
|||
.WriteProperty("logLevel", "Fatal") |
|||
.WriteProperty("applicationName", "Squidex.Infrastructure.Tests") |
|||
.WriteProperty("applicationVersion", "1.0.0.0")); |
|||
|
|||
Assert.Equal(expected, output); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_log_with_trace() |
|||
{ |
|||
Log.LogTrace(w => w.WriteProperty("logValue", 1500)); |
|||
|
|||
var expected = |
|||
MakeTestCall(w => w |
|||
.WriteProperty("logLevel", "Trace") |
|||
.WriteProperty("logValue", 1500)); |
|||
|
|||
Assert.Equal(expected, output); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_log_with_debug() |
|||
{ |
|||
Log.LogDebug(w => w.WriteProperty("logValue", 1500)); |
|||
|
|||
var expected = |
|||
MakeTestCall(w => w |
|||
.WriteProperty("logLevel", "Debug") |
|||
.WriteProperty("logValue", 1500)); |
|||
|
|||
Assert.Equal(expected, output); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_log_with_information() |
|||
{ |
|||
Log.LogInformation(w => w.WriteProperty("logValue", 1500)); |
|||
|
|||
var expected = |
|||
MakeTestCall(w => w |
|||
.WriteProperty("logLevel", "Information") |
|||
.WriteProperty("logValue", 1500)); |
|||
|
|||
Assert.Equal(expected, output); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_log_with_warning() |
|||
{ |
|||
Log.LogWarning(w => w.WriteProperty("logValue", 1500)); |
|||
|
|||
var expected = |
|||
MakeTestCall(w => w |
|||
.WriteProperty("logLevel", "Warning") |
|||
.WriteProperty("logValue", 1500)); |
|||
|
|||
Assert.Equal(expected, output); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_log_with_warning_exception() |
|||
{ |
|||
var exception = new InvalidOperationException(); |
|||
|
|||
Log.LogWarning(exception); |
|||
|
|||
var expected = |
|||
MakeTestCall(w => w |
|||
.WriteProperty("logLevel", "Warning") |
|||
.WriteException(exception)); |
|||
|
|||
Assert.Equal(expected, output); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_log_with_error() |
|||
{ |
|||
Log.LogError(w => w.WriteProperty("logValue", 1500)); |
|||
|
|||
var expected = |
|||
MakeTestCall(w => w |
|||
.WriteProperty("logLevel", "Error") |
|||
.WriteProperty("logValue", 1500)); |
|||
|
|||
Assert.Equal(expected, output); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_log_with_error_exception() |
|||
{ |
|||
var exception = new InvalidOperationException(); |
|||
|
|||
Log.LogError(exception); |
|||
|
|||
var expected = |
|||
MakeTestCall(w => w |
|||
.WriteProperty("logLevel", "Error") |
|||
.WriteException(exception)); |
|||
|
|||
Assert.Equal(expected, output); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_log_with_fatal() |
|||
{ |
|||
Log.LogFatal(w => w.WriteProperty("logValue", 1500)); |
|||
|
|||
var expected = |
|||
MakeTestCall(w => w |
|||
.WriteProperty("logLevel", "Fatal") |
|||
.WriteProperty("logValue", 1500)); |
|||
|
|||
Assert.Equal(expected, output); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_log_with_fatal_exception() |
|||
{ |
|||
var exception = new InvalidOperationException(); |
|||
|
|||
Log.LogFatal(exception); |
|||
|
|||
var expected = |
|||
MakeTestCall(w => w |
|||
.WriteProperty("logLevel", "Fatal") |
|||
.WriteException(exception)); |
|||
|
|||
Assert.Equal(expected, output); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_log_nothing_when_exception_is_null() |
|||
{ |
|||
Log.LogFatal((Exception)null); |
|||
|
|||
var expected = |
|||
MakeTestCall(w => w |
|||
.WriteProperty("logLevel", "Fatal")); |
|||
|
|||
Assert.Equal(expected, output); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_measure_trace() |
|||
{ |
|||
Log.MeasureTrace(w => w.WriteProperty("message", "My Message")).Dispose(); |
|||
|
|||
var expected = |
|||
MakeTestCall(w => w |
|||
.WriteProperty("logLevel", "Trace") |
|||
.WriteProperty("message", "My Message") |
|||
.WriteProperty("elapsedMs", 0)); |
|||
|
|||
Assert.Equal(expected, output); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_measure_debug() |
|||
{ |
|||
Log.MeasureDebug(w => w.WriteProperty("message", "My Message")).Dispose(); |
|||
|
|||
var expected = |
|||
MakeTestCall(w => w |
|||
.WriteProperty("logLevel", "Debug") |
|||
.WriteProperty("message", "My Message") |
|||
.WriteProperty("elapsedMs", 0)); |
|||
|
|||
Assert.True(output.StartsWith(expected.Substring(0, 55))); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_measure_information() |
|||
{ |
|||
Log.MeasureInformation(w => w.WriteProperty("message", "My Message")).Dispose(); |
|||
|
|||
var expected = |
|||
MakeTestCall(w => w |
|||
.WriteProperty("logLevel", "Information") |
|||
.WriteProperty("message", "My Message") |
|||
.WriteProperty("elapsedMs", 0)); |
|||
|
|||
Assert.Equal(expected, output); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_log_with_extensions_logger() |
|||
{ |
|||
var exception = new InvalidOperationException(); |
|||
|
|||
var loggerFactory = new LoggerFactory().AddSemanticLog(Log); |
|||
var loggerInstance = loggerFactory.CreateLogger<SemanticLogTests>(); |
|||
|
|||
loggerInstance.LogCritical(new EventId(123, "EventName"), exception, "Log {0}", 123); |
|||
|
|||
var expected = |
|||
MakeTestCall(w => w |
|||
.WriteProperty("logLevel", "Fatal") |
|||
.WriteProperty("message", "Log 123") |
|||
.WriteObject("eventId", e => e |
|||
.WriteProperty("id", 123) |
|||
.WriteProperty("name", "EventName")) |
|||
.WriteException(exception) |
|||
.WriteProperty("category", "Squidex.Infrastructure.Log.SemanticLogTests")); |
|||
|
|||
Assert.Equal(expected, output); |
|||
} |
|||
|
|||
private static string MakeTestCall(Action<IObjectWriter> writer) |
|||
{ |
|||
IObjectWriter sut = new JsonLogWriter(); |
|||
|
|||
writer(sut); |
|||
|
|||
return sut.ToString(); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue