v 1.0.2 - Added ability to manage log result by specific platform

This commit is contained in:
Claudio Boggian
2025-02-26 09:35:04 +01:00
parent 8e097e90c1
commit 4f4669d234
11 changed files with 315 additions and 86 deletions
@@ -19,6 +19,12 @@
"OwnerKey" = "8:_UNDEFINED" "OwnerKey" = "8:_UNDEFINED"
"MsmSig" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED"
} }
"Entry"
{
"MsmKey" = "8:_B7C9512C21954D9088AFF74B0D0F682B"
"OwnerKey" = "8:_UNDEFINED"
"MsmSig" = "8:_UNDEFINED"
}
} }
"Configurations" "Configurations"
{ {
@@ -44,6 +50,14 @@
"PrerequisitesLocation" = "2:1" "PrerequisitesLocation" = "2:1"
"Url" = "8:" "Url" = "8:"
"ComponentsUrl" = "8:" "ComponentsUrl" = "8:"
"Items"
{
"{EDC2488A-8267-493A-A98E-7D9C3B36CDF3}:.NETFramework,Version=v4.7.2"
{
"Name" = "8:Microsoft .NET Framework 4.7.2 (x86 and x64)"
"ProductCode" = "8:.NETFramework,Version=v4.7.2"
}
}
} }
} }
"Release" "Release"
@@ -108,6 +122,26 @@
} }
"File" "File"
{ {
"{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B7C9512C21954D9088AFF74B0D0F682B"
{
"SourcePath" = "8:E:\\GFX\\Loghi e Icone\\008 - Log 4 Graylog\\log-4-graylog.ico"
"TargetName" = "8:log-4-graylog.ico"
"Tag" = "8:"
"Folder" = "8:_9B600ABCFF494174A956D068E6CF1046"
"Condition" = "8:"
"Transitive" = "11:FALSE"
"Vital" = "11:TRUE"
"ReadOnly" = "11:FALSE"
"Hidden" = "11:FALSE"
"System" = "11:FALSE"
"Permanent" = "11:FALSE"
"SharedLegacy" = "11:FALSE"
"PackageAs" = "3:1"
"Register" = "3:1"
"Exclude" = "11:FALSE"
"IsDependency" = "11:FALSE"
"IsolateTo" = "8:"
}
} }
"FileType" "FileType"
{ {
@@ -164,15 +198,15 @@
{ {
"Name" = "8:Microsoft Visual Studio" "Name" = "8:Microsoft Visual Studio"
"ProductName" = "8:Log 4 Graylog" "ProductName" = "8:Log 4 Graylog"
"ProductCode" = "8:{20B40DF7-DE7D-4525-8A5A-70A7553AD142}" "ProductCode" = "8:{E73F35BF-F750-4609-A786-958CE5C83DE7}"
"PackageCode" = "8:{8B5226CE-438C-4CA5-AA45-6B913E396AD8}" "PackageCode" = "8:{ACC078E3-F12A-4087-A8AE-D7B8DF393845}"
"UpgradeCode" = "8:{7CE09C91-3C26-4C8E-BC66-003536E12481}" "UpgradeCode" = "8:{7CE09C91-3C26-4C8E-BC66-003536E12481}"
"AspNetVersion" = "8:4.0.30319.0" "AspNetVersion" = "8:4.0.30319.0"
"RestartWWWService" = "11:FALSE" "RestartWWWService" = "11:FALSE"
"RemovePreviousVersions" = "11:FALSE" "RemovePreviousVersions" = "11:FALSE"
"DetectNewerInstalledVersion" = "11:TRUE" "DetectNewerInstalledVersion" = "11:TRUE"
"InstallAllUsers" = "11:TRUE" "InstallAllUsers" = "11:TRUE"
"ProductVersion" = "8:1.0.1" "ProductVersion" = "8:1.0.2"
"Manufacturer" = "8:PAL s.r.l." "Manufacturer" = "8:PAL s.r.l."
"ARPHELPTELEPHONE" = "8:" "ARPHELPTELEPHONE" = "8:"
"ARPHELPLINK" = "8:" "ARPHELPLINK" = "8:"
@@ -285,32 +319,32 @@
} }
"Shortcut" "Shortcut"
{ {
"{970C0BB2-C7D0-45D7-ABFA-7EC378858BC0}:_7FC43709505E4AD69D729799E1A9FD55" "{970C0BB2-C7D0-45D7-ABFA-7EC378858BC0}:_0AC95449D7BB49C294086916871E955F"
{ {
"Name" = "8:Log 4 Graylog" "Name" = "8:Log 4 Graylog"
"Arguments" = "8:" "Arguments" = "8:"
"Description" = "8:" "Description" = "8:"
"ShowCmd" = "3:1" "ShowCmd" = "3:1"
"IconIndex" = "3:32512" "IconIndex" = "3:0"
"Transitive" = "11:FALSE" "Transitive" = "11:FALSE"
"Target" = "8:_485C7AAA71924496B4CCA8F2B60EB371" "Target" = "8:_485C7AAA71924496B4CCA8F2B60EB371"
"Folder" = "8:_5CBF406C9E44415E87BBF1E35E297CB9" "Folder" = "8:_5CBF406C9E44415E87BBF1E35E297CB9"
"WorkingFolder" = "8:_9B600ABCFF494174A956D068E6CF1046" "WorkingFolder" = "8:_9B600ABCFF494174A956D068E6CF1046"
"Icon" = "8:_485C7AAA71924496B4CCA8F2B60EB371" "Icon" = "8:_B7C9512C21954D9088AFF74B0D0F682B"
"Feature" = "8:" "Feature" = "8:"
} }
"{970C0BB2-C7D0-45D7-ABFA-7EC378858BC0}:_80AF6F01831346B88EC66109D1FF16DE" "{970C0BB2-C7D0-45D7-ABFA-7EC378858BC0}:_7D574C8D097B44558FBEBD9AF12F9D46"
{ {
"Name" = "8:Log 4 Graylog" "Name" = "8:Log 4 Graylog"
"Arguments" = "8:" "Arguments" = "8:"
"Description" = "8:" "Description" = "8:"
"ShowCmd" = "3:1" "ShowCmd" = "3:1"
"IconIndex" = "3:32512" "IconIndex" = "3:0"
"Transitive" = "11:FALSE" "Transitive" = "11:FALSE"
"Target" = "8:_485C7AAA71924496B4CCA8F2B60EB371" "Target" = "8:_485C7AAA71924496B4CCA8F2B60EB371"
"Folder" = "8:_1D6B671D343A4228A354CA138B3705BD" "Folder" = "8:_1D6B671D343A4228A354CA138B3705BD"
"WorkingFolder" = "8:_9B600ABCFF494174A956D068E6CF1046" "WorkingFolder" = "8:_9B600ABCFF494174A956D068E6CF1046"
"Icon" = "8:_485C7AAA71924496B4CCA8F2B60EB371" "Icon" = "8:_B7C9512C21954D9088AFF74B0D0F682B"
"Feature" = "8:" "Feature" = "8:"
} }
} }
@@ -0,0 +1,8 @@
namespace console_log4graylog.Enums
{
public enum LogTypeEnums
{
None,
CyberPlan
}
}
@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace console_log4graylog.Models
{
public class CyberPlanModel
{
public DateTime DateTime { get; set; }
public string Type { get; set; }
public string Instance { get; set; }
public string Message { get; set; }
public string Content { get; set; }
}
}
@@ -3,8 +3,11 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using console_log4graylog.Enums;
using Microsoft.Extensions.Logging;
using Serilog.Events;
namespace console_log4graylog.Model namespace console_log4graylog.Models
{ {
public class SettingsModel public class SettingsModel
{ {
@@ -15,8 +18,9 @@ namespace console_log4graylog.Model
public class GraylogSettings public class GraylogSettings
{ {
public string FQDN { get; set; } public string HostnameOrAddress { get; set; }
public int Port { get; set; } public int Port { get; set; }
public LogEventLevel MinimumLogEventLevel { get; set; }
public string Facility { get; set; } public string Facility { get; set; }
} }
@@ -25,5 +29,6 @@ namespace console_log4graylog.Model
public string DirPath { get; set; } public string DirPath { get; set; }
public string FileNamePattern { get; set; } public string FileNamePattern { get; set; }
public string LogPath { get; set; } public string LogPath { get; set; }
public LogTypeEnums LogType { get; set; }
} }
} }
+9 -14
View File
@@ -1,11 +1,12 @@
using console_log4graylog.Model; using console_log4graylog.Workers;
using console_log4graylog.Workers;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Serilog; using Serilog;
using Serilog.Core; using Serilog.Core;
using Serilog.Events; using Serilog.Events;
using Serilog.Sinks.Graylog; using Serilog.Sinks.Graylog;
using System.ServiceProcess; using System.ServiceProcess;
using console_log4graylog.Models;
using System.Net;
namespace console_log4graylog namespace console_log4graylog
{ {
@@ -23,25 +24,19 @@ namespace console_log4graylog
var graylogSettings = config.GetSection("GraylogSettings").Get<GraylogSettings>(); var graylogSettings = config.GetSection("GraylogSettings").Get<GraylogSettings>();
Log.Logger = new LoggerConfiguration() Log.Logger = new LoggerConfiguration()
.Enrich.With(new PalLogger()) .Enrich.FromLogContext()
//.Enrich.WithProperty("HostName", Dns.GetHostName())
.WriteTo.Graylog(new GraylogSinkOptions .WriteTo.Graylog(new GraylogSinkOptions
{ {
HostnameOrAddress = graylogSettings.FQDN, HostnameOrAddress = graylogSettings.HostnameOrAddress,
Port = graylogSettings.Port, Port = graylogSettings.Port,
MinimumLogEventLevel = LogEventLevel.Warning, MinimumLogEventLevel = graylogSettings.MinimumLogEventLevel,
Facility = graylogSettings.Facility Facility = graylogSettings.Facility
}).CreateLogger(); })
.CreateLogger();
ServiceBase.Run(new MainWorker(config)); ServiceBase.Run(new MainWorker(config));
} }
} }
public class PalLogger : ILogEventEnricher
{
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
{
logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("Username", Environment.UserName, true));
}
}
} }
+92
View File
@@ -0,0 +1,92 @@
# <img src="Log-4-Graylog.ico" width="50" height="50"> Log4Graylog
![DotNet](https://img.shields.io/badge/Code-.NET%209.0-purple?style=flat-square&logo=dotnet)
![Graylog](https://img.shields.io/badge/Device-Graylog-red?style=flat-square&logo=graylog)
![Release](https://img.shields.io/badge/Release-v1.0.2-green?style=flat-square)
Service for monitoring and sending log files to Graylog
## AppSettings
<details>
<summary>GraylogSettings</summary>
<br>
>HostnameOrAddress
logs.pal.it [Default]
>Port
12204 [Default]
>MinimumLogEventLevel
0. Verbose
1. Debug
2. Information [Default]
3. Warning
4. Error
5. Fatal
>Facility
ConsoleApp [Default]
---
</details>
<details>
<summary>GeneralSettings</summary>
<br>
>LogType
0. None
1. Cyperplan
>DirPath
Path to the folder where the file to be monitored is located
>FileNamePattern
"\*.\*" Regex for file search
---
</details>
### Already run example and backup
<details>
<summary>PALCYBERPAN01</summary>
<br>
```
{
"GraylogSettings" : {
"HostnameOrAddress" : "logs.pal.local",
"Port" : 12204,
"MinimumLogEventLevel": 2,
"Facility" : "CyberPlan Logs"
},
"GeneralSettings" : {
"LogType": 1,
"DirPath" : "E:\\CyberPlanWeb_Data\\cybinstance\\PLANNING\\logs",
"FileNamePattern": "debug.*.log*"
}
}
```
</details>
## Run as Services
**Powershell**
```
New-Service -Name "Log4Graylog" `
-BinaryPathName "C:\Program Files\PAL s.r.l\Log 4 Graylog\console_log4graylog.exe" `
-DisplayName "Log 4 Graylog" `
-Description "Servizio per il monitoring ed invio dei file di log a Graylog" `
-StartupType Automatic
```
+48
View File
@@ -0,0 +1,48 @@
using console_log4graylog.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Serilog;
using Serilog.Events;
namespace console_log4graylog.Utils
{
public class Log4Platform
{
public static void CyberPlanLogger(string logString, CancellationToken cancellationToken)
{
try
{
cancellationToken.ThrowIfCancellationRequested();
var log = logString.Split('\t');
var logModel = new CyberPlanModel
{
DateTime = DateTime.Parse(log[0].Trim(new[] { '[', ']' })),
Type = log[1].Trim(':'),
Instance = log[2].Trim(new[] { '[', ']' }),
Message = log[3],
Content = log[4]
};
Log.Logger
.ForContext("logts", logModel.DateTime)
.ForContext("instance", logModel.Instance)
.ForContext("event", logModel.Type.FirstCharToUpper())
.ForContext("content", logModel.Content)
.Write(
SharedUtils.GetLogEventLevel(logModel.Type),
logModel.Message
);
}
catch (TaskCanceledException) { return; }
catch (Exception ex)
{
SharedUtils.LogError(ex);
}
}
}
}
+50
View File
@@ -0,0 +1,50 @@
using Serilog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using console_log4graylog.Models;
using Serilog.Events;
namespace console_log4graylog.Utils
{
public static class SharedUtils
{
public static void LogError(Exception ex)
{
Log.Logger.Fatal(ex, "⚠️ " + ex.Message);
throw new Exception(ex.Message);
}
public static FileInfo GetLatestLogFile(GeneralSettings gs)
{
DirectoryInfo dir = new DirectoryInfo(gs.DirPath);
return dir.GetFiles(gs.FileNamePattern)
.OrderByDescending(f => f.LastWriteTime)
.FirstOrDefault();
}
public static LogEventLevel GetLogEventLevel(string logType)
{
switch (logType)
{
case "error":
return LogEventLevel.Error;
case "info":
case "debug":
return LogEventLevel.Information;
default:
return LogEventLevel.Warning;
}
}
public static string FirstCharToUpper(this string input) =>
input switch
{
null => throw new ArgumentNullException(nameof(input)),
"" => throw new ArgumentException($"{nameof(input)} cannot be empty", nameof(input)),
_ => string.Concat(input[0].ToString().ToUpper(), input.AsSpan(1))
};
}
}
+31 -53
View File
@@ -1,14 +1,16 @@
using console_log4graylog.Model; using console_log4graylog.Models;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Serilog; using Serilog;
using System.Net; using System.Net;
using System.ServiceProcess; using System.ServiceProcess;
using console_log4graylog.Enums;
using console_log4graylog.Utils;
namespace console_log4graylog.Workers namespace console_log4graylog.Workers
{ {
internal class MainWorker : ServiceBase internal class MainWorker : ServiceBase
{ {
private SettingsModel Settings { get; set; } private SettingsModel Settings { get; }
private FileSystemWatcher watcher; private FileSystemWatcher watcher;
private Task monitorTask; private Task monitorTask;
private CancellationTokenSource cancellationTokenSource; private CancellationTokenSource cancellationTokenSource;
@@ -17,14 +19,14 @@ namespace console_log4graylog.Workers
{ {
Settings = new () Settings = new ()
{ {
GeneralSettings = _configuration.GetSection("GeneralSettings").Get<GeneralSettings>() GeneralSettings = _configuration.GetSection("GeneralSettings").Get<GeneralSettings>(),
GraylogSettings = _configuration.GetSection("GraylogSettings").Get<GraylogSettings>()
}; };
} }
protected override void OnStart(string[] args) protected override void OnStart(string[] args)
{ {
Log2File("Start Log 4 Graylog"); Log.Logger.Information("▶️ Start Log 4 Graylog on " + Dns.GetHostName());
Log.Logger.Warning("▶️ Start Log 4 Graylog on " + Dns.GetHostName());
cancellationTokenSource = new CancellationTokenSource(); cancellationTokenSource = new CancellationTokenSource();
monitorTask = Task.Run(() => MonitorFolder(cancellationTokenSource.Token), cancellationTokenSource.Token); monitorTask = Task.Run(() => MonitorFolder(cancellationTokenSource.Token), cancellationTokenSource.Token);
@@ -33,8 +35,7 @@ namespace console_log4graylog.Workers
protected override void OnStop() protected override void OnStop()
{ {
Log2File("End Log 4 Graylog"); Log.Logger.Fatal("⏹️ End Log 4 Graylog on " + Dns.GetHostName());
Log.Logger.Error("⏹️ End Log 4 Graylog on " + Dns.GetHostName());
cancellationTokenSource.Cancel(); cancellationTokenSource.Cancel();
watcher?.Dispose(); watcher?.Dispose();
@@ -47,7 +48,7 @@ namespace console_log4graylog.Workers
{ {
try try
{ {
var latestFile = GetLatestLogFile(); var latestFile = SharedUtils.GetLatestLogFile(Settings.GeneralSettings);
if (latestFile != null) if (latestFile != null)
{ {
await MonitorLogFile(latestFile, cancellationToken); await MonitorLogFile(latestFile, cancellationToken);
@@ -60,25 +61,16 @@ namespace console_log4graylog.Workers
catch (TaskCanceledException) { return; } catch (TaskCanceledException) { return; }
catch (Exception ex) catch (Exception ex)
{ {
LogError("Service encountered an error: " + ex.Message); SharedUtils.LogError(ex);
} }
} }
} }
private FileInfo GetLatestLogFile()
{
DirectoryInfo dir = new DirectoryInfo(Settings.GeneralSettings.DirPath);
return dir.GetFiles(Settings.GeneralSettings.FileNamePattern)
.OrderByDescending(f => f.LastWriteTime)
.FirstOrDefault();
}
private async Task MonitorLogFile(FileInfo file, CancellationToken cancellationToken) private async Task MonitorLogFile(FileInfo file, CancellationToken cancellationToken)
{ {
try try
{ {
Log2File("Attach to file: " + file.FullName); Log.Logger.Information("🗂️ Attach to file: " + file.FullName);
Log.Logger.Warning("🗂️ Attach to file: " + file.FullName);
using (var fs = new FileStream(file.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) using (var fs = new FileStream(file.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (var reader = new StreamReader(fs)) using (var reader = new StreamReader(fs))
{ {
@@ -90,13 +82,27 @@ namespace console_log4graylog.Workers
string line = reader.ReadLine(); string line = reader.ReadLine();
while (line != null) while (line != null)
{ {
Log.Logger.Warning(line); switch (Settings.GeneralSettings.LogType)
line = reader.ReadLine();
}
if (GetLatestLogFile()?.FullName != file.FullName)
{ {
case LogTypeEnums.CyberPlan:
Log4Platform.CyberPlanLogger(line, cancellationToken);
break;
default:
Log.Logger.Write(
Settings.GraylogSettings.MinimumLogEventLevel,
line
);
break; break;
} }
line = reader.ReadLine();
}
if (SharedUtils.GetLatestLogFile(Settings.GeneralSettings)?.FullName != file.FullName)
break;
if (line == null)
await Task.Delay(1, cancellationToken); await Task.Delay(1, cancellationToken);
} }
} }
@@ -104,36 +110,8 @@ namespace console_log4graylog.Workers
catch (TaskCanceledException) { return; } catch (TaskCanceledException) { return; }
catch (Exception ex) catch (Exception ex)
{ {
LogError("Error monitoring file: " + ex.Message); SharedUtils.LogError(ex);
} }
}
private void LogError(string errorMessage)
{
Log2File(errorMessage);
Log.Logger.Error("⚠️ " + errorMessage);
throw new Exception(errorMessage);
}
private void Log2File(string message)
{
if (!Directory.Exists(Path.GetDirectoryName(Settings.GeneralSettings.LogPath)))
{
Directory.CreateDirectory(Path.GetDirectoryName(Settings.GeneralSettings.LogPath));
}
if (!File.Exists(Settings.GeneralSettings.LogPath))
{
using (FileStream fs = File.Create(Settings.GeneralSettings.LogPath))
{
byte[] info = new System.Text.UTF8Encoding(true).GetBytes("");
fs.Write(info, 0, info.Length);
}
}
File.AppendAllText(Settings.GeneralSettings.LogPath, DateTime.Now + " - " + message + "\n");
} }
} }
} }
+5 -4
View File
@@ -1,12 +1,13 @@
{ {
"GraylogSettings": { "GraylogSettings": {
"FQDN" : "graylog.local", "HostnameOrAddress": "logs.pal.it",
"Port" : 12202, "Port": 12204,
"MinimumLogEventLevel": 2,
"Facility": "ConsoleApp" "Facility": "ConsoleApp"
}, },
"GeneralSettings" : { "GeneralSettings" : {
"LogType": 1,
"DirPath" : "C:\\Logs", "DirPath" : "C:\\Logs",
"FileNamePattern": "*.log", "FileNamePattern": "*.log"
"LogPath": "C:\\Logs\\debug.l4g"
} }
} }
@@ -6,14 +6,15 @@
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ApplicationIcon>Log-4-Graylog.ico</ApplicationIcon> <ApplicationIcon>Log-4-Graylog.ico</ApplicationIcon>
<NuGetAuditLevel>critical</NuGetAuditLevel>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>none</DebugType> <DebugType>full</DebugType>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DebugType>none</DebugType> <DebugType>full</DebugType>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>