mirror of https://github.com/Squidex/squidex.git
9 changed files with 172 additions and 18 deletions
@ -0,0 +1,34 @@ |
|||
// ==========================================================================
|
|||
// FileChannel.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using Squidex.Infrastructure.Log.Internal; |
|||
|
|||
namespace Squidex.Infrastructure.Log |
|||
{ |
|||
public sealed class FileChannel : ILogChannel, IExternalSystem |
|||
{ |
|||
private readonly FileLogProcessor processor; |
|||
|
|||
public FileChannel(string path) |
|||
{ |
|||
Guard.NotNullOrEmpty(path, nameof(path)); |
|||
|
|||
processor = new FileLogProcessor(path); |
|||
} |
|||
|
|||
public void Log(SemanticLogLevel logLevel, string message) |
|||
{ |
|||
processor.EnqueueMessage(new LogMessageEntry { Message = message, Level = logLevel }); |
|||
} |
|||
|
|||
public void Connect() |
|||
{ |
|||
processor.Connect(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,95 @@ |
|||
// ==========================================================================
|
|||
// FileLogChannel.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Concurrent; |
|||
using System.IO; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Squidex.Infrastructure.Log.Internal |
|||
{ |
|||
public class FileLogProcessor : IDisposable |
|||
{ |
|||
private const int MaxQueuedMessages = 1024; |
|||
private const int Retries = 10; |
|||
private readonly BlockingCollection<LogMessageEntry> messageQueue = new BlockingCollection<LogMessageEntry>(MaxQueuedMessages); |
|||
private readonly Task outputTask; |
|||
private readonly string path; |
|||
|
|||
public FileLogProcessor(string path) |
|||
{ |
|||
this.path = path; |
|||
|
|||
outputTask = Task.Factory.StartNew(ProcessLogQueue, this, TaskCreationOptions.LongRunning); |
|||
} |
|||
|
|||
public void Connect() |
|||
{ |
|||
var fileInfo = new FileInfo(path); |
|||
|
|||
if (!fileInfo.Directory.Exists) |
|||
{ |
|||
throw new ConfigurationException($"Log directory '{fileInfo.Directory.FullName}' does not exist."); |
|||
} |
|||
} |
|||
|
|||
public void EnqueueMessage(LogMessageEntry message) |
|||
{ |
|||
messageQueue.Add(message); |
|||
} |
|||
|
|||
private async Task ProcessLogQueue() |
|||
{ |
|||
foreach (var entry in messageQueue.GetConsumingEnumerable()) |
|||
{ |
|||
for (var i = 1; i <= Retries; i++) |
|||
{ |
|||
try |
|||
{ |
|||
File.AppendAllText(path, entry.Message + Environment.NewLine, Encoding.UTF8); |
|||
|
|||
break; |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
await Task.Delay(i * 10); |
|||
|
|||
if (i == Retries) |
|||
{ |
|||
Console.WriteLine("Failed to write to log file '{0}': {1}", path, ex); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
private static Task ProcessLogQueue(object state) |
|||
{ |
|||
var processor = (FileLogProcessor)state; |
|||
|
|||
return 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) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue