необходимо отследить доставку сообщения по логам. Как можно это сделать с помощью Regex? Full Log.txt
Mon 2019-02-25 01:31:57.886: <-- MAIL FROM: <Marion@frd.ru>
Mon 2019-02-25 01:31:57.887: --> 250 2.1.0 Sender OK
Mon 2019-02-25 01:31:57.890: <-- RCPT TO: <almira@frd.ru>
Mon 2019-02-25 01:31:57.895: --> 250 2.1.5 Recipient OK
Mon 2019-02-25 01:31:57.898: <-- RCPT TO: <Lyahova@frd.ru>
Mon 2019-02-25 01:31:57.903: --> 250 2.1.5 Recipient OK
Mon 2019-02-25 01:31:58.063: --> 250 2.6.0 Ok, message saved <Message-ID: <016401d4cccb$3a0b8d50$ae22a7f0$@frd.ru>>
Mon 2019-02-25 01:43:00.581: <-- QUIT
Mon 2019-02-25 01:43:00.581: --> 221 2.0.0 See ya in cyberspace
Mon 2019-02-25 01:43:00.581: SMTP session successful (Bytes in/out: 21527/449)
Нужно получить Marion@frd.ru, almira@frd.ru, Lyahova@frd.ru, SMTP session successful
Набросал парсер логов с самописными классами машины состояний.
Disclaimer
Я особо нигде не подсматривал, написал как придумал, в принципе, это был первый раз, когда я вообще попробовал использовать машину состояний в виде отдельной её реализации и классов для каждого состояния. Не пропадать же потраченному времени зря, так что выкладываю.
Я понимаю, что в консоль писать в реализации классов состояний - это не самая лучшая идея, но пора остановиться, слишком много времени потратил на эту софтину :)
Ещё, какие плюсы я вижу в использовании машины состояний - это то, что в каждом состоянии только часть своих проверок, а не проверки на каждой итерации сразу всего что можно, так же отсутствие ветвлений в виде if else или использования switch case.
В репозитории код может меняться, улучшаться и т.д.
Ссылка на репозиторий
Ссылка на пример логов, на которых я проверял
Ссылка на схему машины состояний
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using FsmLogParser.Core;
using FsmLogParser.Logs;
using FsmLogParser.Model;
using FsmLogParser.States;
using FsmLogParser.States.Enums;
namespace FsmLogParser
{
public class Program
{
#region Entry point
private static Program _program;
private static Task<int> Main(string[] args)
{
_program = new Program();
return _program.Run(args);
}
#endregion
private const int NOT_ENOUGH_POSITIONAL_CMD_ARGS_SPECIFIED_ERROR = 1;
private const int FILE_NOT_FOUND_ERROR = 2;
private const int EXIT_SUCCESS = 0;
private LogReader _logReader;
private Dictionary<int, SessionInfo> _infos;
public int IterationCounter { get; set; }
private async Task<int> Run(string[] args)
{
if (args.Length < 1)
{
Console.WriteLine("Not enough positional command-line arguments specified!");
return NOT_ENOUGH_POSITIONAL_CMD_ARGS_SPECIFIED_ERROR;
}
string pathToFile = args[0];
if (!File.Exists(pathToFile))
{
Console.WriteLine($"File \"{pathToFile}\" not found.");
return FILE_NOT_FOUND_ERROR;
}
try
{
var fsm = BuildStateMachine();
_infos = new Dictionary<int, SessionInfo>();
using (_logReader = new LogReader(pathToFile))
{
_logReader.OpenFile();
await fsm.Start(this);
}
Console.WriteLine("Results:");
foreach (var (iteration, sessionInfo) in _infos)
{
Console.WriteLine($"{iteration.ToString()}: {sessionInfo}");
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
return EXIT_SUCCESS;
}
private static StateMachine<ParserState, ParserEvent, Program> BuildStateMachine()
{
var builder = new StateMachineBuilder<ParserState, ParserEvent, Program>();
// Схема машины состояний: https://i.imgur.com/KSy6DLB.jpg
builder.SetInitialState<StartSessionSearchState>(ParserState.StartSessionSearch)
// Перейти в состояние SenderSearchState, если сейчас состояние
// ParserState.StartSessionSearch и пришло событие ParserEvent.StartSessionFound
.AddTransition<SenderSearchState>(ParserState.StartSessionSearch, ParserEvent.StartSessionFound)
.AddTransition<RecipientSearchState>(ParserState.SenderSearch, ParserEvent.SenderFound)
.AddTransition<RecipientSearchState>(ParserState.RecipientSearch, ParserEvent.RecipientFound)
.AddTransition<SessionTokenSearchState>(ParserState.RecipientSearch, ParserEvent.EndOfBlockFound)
.AddTransition<StartSessionSearchState>(ParserState.SessionTokenSearch, ParserEvent.SuccessfulSessionTokenFound)
.AddTransition<StartSessionSearchState>(ParserState.SessionTokenSearch, ParserEvent.EndOfSessionFound)
.AddTransition<DoneState>(ParserState.StartSessionSearch, ParserEvent.EndOfSearchRangeReached)
.AddTransition<DoneState>(ParserState.SenderSearch, ParserEvent.EndOfSearchRangeReached)
.AddTransition<DoneState>(ParserState.RecipientSearch, ParserEvent.EndOfSearchRangeReached)
.AddTransition<DoneState>(ParserState.SessionTokenSearch, ParserEvent.EndOfSearchRangeReached);
return builder.Build();
}
public Task<string> GetNextLine()
{
return _logReader.ReadLine();
}
public SessionInfo GetSessionInfo(int iteration)
{
if (!_infos.ContainsKey(iteration))
{
_infos.Add(iteration, new SessionInfo());
}
return _infos[iteration];
}
}
}
Пример одного из состояний и переходы:
using System;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using FsmLogParser.Core.Interfaces;
using FsmLogParser.States.Enums;
namespace FsmLogParser.States
{
/// <summary>
/// <see cref="ParserState.RecipientSearch"/>.
/// </summary>
public class RecipientSearchState : IState<Program, ParserEvent, ParserState>
{
private readonly Regex _regex;
public ParserState State { get; } = ParserState.RecipientSearch;
public RecipientSearchState()
{
var options = RegexOptions.Compiled | RegexOptions.IgnoreCase;
_regex = new Regex(@"RCPT TO:\s+<{0,1}([a-zA-Z0-9-]+@[a-zA-Z0-9-]+\.[a-zA-Z]+)>{0,1}", options);
}
public async Task DoWork(IStateMachine<ParserState, Program, ParserEvent> fsm, Program context)
{
while (true)
{
string line = await context.GetNextLine();
if (line == null)
{
Console.WriteLine($"Current state: {fsm.CurrentState}. SendEvent({nameof(ParserEvent.EndOfSearchRangeReached)})");
fsm.SendEvent(ParserEvent.EndOfSearchRangeReached);
break;
}
Match match = await Task.Run(() => _regex.Match(line));
if (match.Success)
{
Console.WriteLine($"Current state: {fsm.CurrentState}. SendEvent({nameof(ParserEvent.RecipientFound)})");
if (match.Groups.Count > 1)
{
var info = context.GetSessionInfo(context.IterationCounter);
info.Recipients.Add(match.Groups[1].Value);
}
fsm.SendEvent(ParserEvent.RecipientFound);
break;
}
if (await Task.Run(() => Regex.IsMatch(line, @"\s+QUIT$", RegexOptions.Compiled)))
{
Console.WriteLine($"Current state: {fsm.CurrentState}. SendEvent({nameof(ParserEvent.EndOfBlockFound)})");
fsm.SendEvent(ParserEvent.EndOfBlockFound);
break;
}
}
}
}
}
Чтение логов из файла
using System;
using System.IO;
using System.Threading.Tasks;
namespace FsmLogParser.Logs
{
public class LogReader : IDisposable
{
private FileStream _fileStream;
private BufferedStream _bufferedStream;
private StreamReader _reader;
public string PathToFile { get; }
public LogReader(string pathToFile)
{
PathToFile = pathToFile;
}
public void OpenFile()
{
_fileStream = File.Open(PathToFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
_bufferedStream = new BufferedStream(_fileStream);
_reader = new StreamReader(_bufferedStream);
}
public Task<string> ReadLine()
{
return _reader.ReadLineAsync();
}
public void Dispose()
{
_reader?.Dispose();
_bufferedStream?.Dispose();
_fileStream?.Dispose();
}
}
}
Остальное по ссылке, слишком много кода нужно сюда скопировать.
Виртуальный выделенный сервер (VDS) становится отличным выбором
Создаю простое консольное приложение, которое создаёт уведомление в винде 10Но уже какой час, проблема с методом GetXml
Пишу фильтр на с использованием ajax, который выдает пост по содержанию кастомного поля