c# парсинг логов

136
24 января 2020, 12:30

необходимо отследить доставку сообщения по логам. Как можно это сделать с помощью 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

Answer 1

Набросал парсер логов с самописными классами машины состояний.

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();
        }
    }
}

Остальное по ссылке, слишком много кода нужно сюда скопировать.

READ ALSO
InvalidOperationException в Unity при вызове GetProcessesByName

InvalidOperationException в Unity при вызове GetProcessesByName

Проверяю запущен ли стим

129
Ошибка с xml при создании уведомления

Ошибка с xml при создании уведомления

Создаю простое консольное приложение, которое создаёт уведомление в винде 10Но уже какой час, проблема с методом GetXml

116
Как исправить ajax фильтр?

Как исправить ajax фильтр?

Пишу фильтр на с использованием ajax, который выдает пост по содержанию кастомного поля

134