Разобрать записи многострочного журнала с помощью регулярных выражений

Я пытаюсь проанализировать записи журнала в приложении C# с помощью этого регулярного выражения: (^[0-9]{4}(-[0-9]{2}){2}([^|]+\|){3})(?!\1) для журналов в формате вроде [дата (в каком-то формате)] | [уровень] | [имя приложения] | [сообщение].

Где (я думаю):

  • ^ соответствует началу строки (включено /gm для regex101)
  • [0-9]{4}(-[0-9]{2}){2} с последующим началом даты, как 2015-03-03
  • ([^|]+\|){3}) с последующим указанием даты, уровня журнала и имени приложения
  • (?!\1) с последующим не началом новой записи в журнале (должно быть сообщение)

Например, у меня есть следующие 4 записи журнала (разделенные новой строкой для пояснения):

2015-03-03 19: 30: 47.2725 | INFO |MyApp| Это однострочный журнал.

2015-03-03 19: 31: 29.1209 | INFO |MyApp| В этом сообщении есть несколько
линии с
2015-03-03
дата в этом.

2015-03-03 19: 32: 50.1106 | INFO |MyApp| Это сообщение журнала имеет
несколько строк
но только текст.

2015-03-03 19:33:20.2683| ОШИБКА |MyApp| Это сообщение журнала имеет несколько строк, но
также какой-то запутанный текст, как
2015-03-03 19:33:20.2683| ОШИБКА | который должен
все еще быть действительным сообщением журнала.

Но регулярное выражение не захватывает сообщение, когда я тестирую его на регулярном выражении, вероятно, потому, что я не понимаю, как захватить негативную перспективу.

Если я включу .* в регулярном выражении: (^[0-9]{4}(-[0-9]{2}){2}([^|]+\|){3}).*(?!\1) это соответствует сообщению, но только одной строке (потому что . не соответствует переводу строки).

Итак, как я могу перехватить (многострочное) сообщение?

3 ответа

Решение

Вы можете использовать это регулярное выражение:

(^\d{4}(-\d{2}){2}([^|]+\|){3})([\s\S]*?)\n*(?=^\d{4}.*?(?:[^|\n]+\|){3}|\z)

RegEx Demo

Это регулярное выражение должно работать и в C#, просто убедитесь, что вы используете MULTILINE флаг.

Нечто подобное должно работать.
Смотрите комментарии в регулярном выражении.
(мод: сделать разрыв строки необязательным для EOS или однострочного сообщения)

 @"(?m)^[0-9]{4}(?:-[0-9]{2}){2}(?:[^|\r\n]+\|){3}((?:(?!^[0-9]{4}(?:-[0-9]{2}){2}(?:[^|\r\n]+\|){3}).*(?:\r?\n)?)+)"

Отформатированный ( с этим):

 (?m)                          # Modifier - multiline
 ^                             # BOL
 [0-9]{4}                      # Message header
 (?: - [0-9]{2} ){2}
 (?: [^|\r\n]+ \| ){3}
 (                             # (1 start), The Message
      (?:
           (?!                           # Assert, not a Message header
                ^                             # BOL
                [0-9]{4} 
                (?: - [0-9]{2} ){2}
                (?: [^|\r\n]+ \| ){3}
           )
           .*                            # Line is ok, its part of the message
           (?: \r? \n )?                 # Optional line break
      )+
 )                             # (1 end)

Выход:

 **  Grp 0 -  ( pos 0 , len 74 ) 
2015-03-03 19:30:47.2725|INFO|MyApp|This is a single line log message.


 **  Grp 1 -  ( pos 36 , len 38 ) 
This is a single line log message.

--------------

 **  Grp 0 -  ( pos 74 , len 108 ) 
2015-03-03 19:31:29.1209|INFO|MyApp|This log message has multiple
lines with
2015-03-03
a date in it.


 **  Grp 1 -  ( pos 110 , len 72 ) 
This log message has multiple
lines with
2015-03-03
a date in it.

--------------

 **  Grp 0 -  ( pos 182 , len 97 ) 
2015-03-03 19:32:50.1106|INFO|MyApp|This log message has
multiple lines
but just text only.


 **  Grp 1 -  ( pos 218 , len 61 ) 
This log message has
multiple lines
but just text only.

--------------

 **  Grp 0 -  ( pos 279 , len 186 ) 
2015-03-03 19:33:20.2683|ERROR|MyApp|This log message has multiple lines but
also some confusing text like
2015-03-03 19:33:20.2683|ERROR| which should
still be a valid log message.

 **  Grp 1 -  ( pos 316 , len 149 ) 
This log message has multiple lines but
also some confusing text like
2015-03-03 19:33:20.2683|ERROR| which should
still be a valid log message.

Какой движок регулярных выражений вы используете? В Java, например, есть флаг, чтобы сказать "." соответствовать символам новой строки.

Следующее регулярное выражение, кажется, делает трюк:

/(([0-9]{4})(-[0-9]{2}){2}([^|]+\|){3})((.(?!\2))*)/sg

Изменения, которые я внес в ваш запрос, были в основном некоторой очисткой (ваша группа по сбору данных была неверной) Затем я добавил. и * в этой последней группе захвата. https://regex101.com/r/fU1vV1/2

Наиболее важной частью является использование флагов sg. г заставляет его получать все совпадения. s заставляет все это рассматривать как одну строку (иначе ваш негативный взгляд никогда не сработает). Все это было бы излишним, если бы вы могли гарантировать, что комментарии были в одной строке (что они и есть в вашем примере), поскольку вы могли просто захватить их до конца строки.

Другие вопросы по тегам