Last active
June 25, 2021 07:37
-
-
Save teyc/d331084afec76038602a1aad1713f1d2 to your computer and use it in GitHub Desktop.
Ships log files to Seq
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<Project Sdk="Microsoft.NET.Sdk.Worker"> | |
<PropertyGroup> | |
<OutputType>Exe</OutputType> | |
<TargetFramework>net5.0</TargetFramework> | |
</PropertyGroup> | |
<ItemGroup> | |
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="5.0.0" /> | |
<PackageReference Include="Microsoft.Extensions.Hosting" Version="5.0.0" /> | |
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="5.0.1" /> | |
<PackageReference Include="Serilog" Version="2.10.0" /> | |
<PackageReference Include="Serilog.Sinks.Seq" Version="5.0.1" /> | |
</ItemGroup> | |
<ItemGroup> | |
<None Update="appsettings.json"> | |
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | |
</None> | |
</ItemGroup> | |
</Project> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"Watch": { | |
"EML.Services.K2.ProductGroups": "C:\\logs\\Application2\\*.txt" | |
}, | |
"Logging": { | |
"LogLevel": { | |
"Default": "Debug", | |
"System": "Information", | |
"Microsoft": "Information" | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.IO; | |
using System.Linq; | |
using System.Threading; | |
using System.Threading.Tasks; | |
using Microsoft.Extensions.Hosting; | |
using Microsoft.Extensions.Logging; | |
using Serilog; | |
using Serilog.Events; | |
using Serilog.Parsing; | |
using static System.Threading.Tasks.Task; | |
using ILogger = Serilog.ILogger; | |
namespace Tools.LogShipper | |
{ | |
public class FileTail : BackgroundService | |
{ | |
private readonly string _applicationName; | |
private readonly string _pathPattern; | |
private readonly ILogger<FileTail> _appLog; | |
private readonly string _directory; | |
private readonly string _path; | |
private readonly ILogger _logger; | |
public FileTail(string applicationName, string pathPattern, ILogger<FileTail> appLog) | |
{ | |
if (applicationName == null) throw new ArgumentNullException(nameof(applicationName)); | |
if (pathPattern == null) throw new ArgumentNullException(nameof(pathPattern)); | |
var directory = Path.GetDirectoryName(pathPattern); | |
var path = pathPattern.Substring(directory.Length + 1); | |
_applicationName = applicationName; | |
_pathPattern = pathPattern; | |
_appLog = appLog; | |
_directory = directory; | |
_path = path; | |
_logger = Log.ForContext("Application", applicationName); | |
} | |
protected override async Task ExecuteAsync(CancellationToken stoppingToken) | |
{ | |
if (!Directory.Exists(_directory)) | |
{ | |
_logger.Warning("{Directory} not found. No logs.", _directory); | |
return; | |
} | |
var fileToTail = await GetFileToTail(stoppingToken); | |
_appLog.LogInformation("Tailing {File}", fileToTail); | |
await foreach (var line in ReadLines(fileToTail, stoppingToken)) | |
{ | |
_logger.Write(ConvertToLogEvent(line)); | |
} | |
} | |
private LogEvent ConvertToLogEvent(string line) | |
{ | |
// parses log4net files | |
// line: | |
// 2021-06-25 16:07:52,353 [61] INFO MyModuleName - Attempting to dispatch Request: GetProductGroups. | |
var (level, message) = | |
line.Contains(" INFO ") ? (LogEventLevel.Information, line.Split(" INFO ", 2)[1]) : | |
line.Contains(" DEBUG ") ? (LogEventLevel.Debug, line.Split(" DEBUG ", 2)[1]) : | |
line.Contains(" WARN ") ? (LogEventLevel.Warning, line.Split(" WARN ", 2)[1]) : | |
line.Contains(" ERROR ") ? (LogEventLevel.Error, line.Split(" ERROR ", 2)[1]) : | |
(LogEventLevel.Verbose, line); | |
return new LogEvent(DateTimeOffset.Now, level, null, | |
new MessageTemplate(message, Array.Empty<MessageTemplateToken>()), Array.Empty<LogEventProperty>()); | |
} | |
private async IAsyncEnumerable<string> ReadLines(string fileToTail, CancellationToken stoppingToken) | |
{ | |
using var stream = File.Open(fileToTail, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); | |
using var reader = new StreamReader(stream); | |
reader.BaseStream.Seek(0, SeekOrigin.End); | |
while (!stoppingToken.IsCancellationRequested) | |
{ | |
var line = await reader.ReadLineAsync(); | |
if (reader.BaseStream.Length < reader.BaseStream.Position) | |
reader.BaseStream.Seek(0, SeekOrigin.Begin); | |
if (line != null) | |
{ | |
yield return line; | |
} | |
else await Delay(500, stoppingToken); | |
} | |
} | |
private async Task<string?> GetFileToTail(CancellationToken stoppingToken) | |
{ | |
again: | |
var files = Directory.GetFiles(_directory, _path, SearchOption.TopDirectoryOnly); | |
var fileToShip = files.OrderByDescending(file => new FileInfo(file).LastWriteTime).FirstOrDefault(); | |
if (fileToShip == null) | |
{ | |
await Delay(TimeSpan.FromSeconds(10), stoppingToken); | |
goto again; | |
} | |
return fileToShip; | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.Extensions.Configuration; | |
using Microsoft.Extensions.DependencyInjection; | |
using Microsoft.Extensions.Hosting; | |
using Microsoft.Extensions.Logging; | |
using Serilog; | |
namespace Tools.LogShipper | |
{ | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
Log.Logger = new LoggerConfiguration().WriteTo.Seq("http://localhost:5341/").CreateLogger(); | |
var hostStatic = CreateHostBuilder(args).Build(); | |
hostStatic.Run(); | |
} | |
private static IHostBuilder CreateHostBuilder(string[] args) | |
{ | |
return Host.CreateDefaultBuilder(args) | |
.UseWindowsService() | |
.ConfigureAppConfiguration((hostContext, configurationBuilder) => | |
{ | |
configurationBuilder.AddJsonFile("appsettings.json"); | |
}) | |
.ConfigureServices((hostContext, services) => | |
{ | |
foreach (var section in hostContext.Configuration.GetSection("Watch").GetChildren()) | |
{ | |
services.AddSingleton<IHostedService>(_ => new FileTail(section.Key, section.Value, _.GetService<ILogger<FileTail>>())); | |
} | |
}); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment