O que faz cada uma das quatro flags
Com g desligada, só a primeira ocorrência é encontrada. Ligue para contar ocorrências ou substituir todas.
i ignora maiúsculas e minúsculas. m faz ^ e $ valerem por linha em vez de valerem para a string inteira, que é o necessário ao recortar começos de linha num log.
s deixa . casar também com a quebra de linha. Por padrão o ponto pula as quebras, então um bloco espalhado por várias linhas precisa dessa flag para ser pego inteiro.
Peças que vale decorar
\ddígitos,\wletras, dígitos e sublinhado,\sespaços. Em maiúscula (\D\W\S) significam o contrário.+um ou mais,*nenhum ou mais,?opcional,{2,4}de duas a quatro vezes.^começo,$fim,\bfronteira de palavra.(...)captura e aparece no resultado;(?:...)agrupa sem capturar.(?<nome>...)dá nome ao grupo para lê-lo por nome em vez de por número.
Guloso contra preguiçoso
.* avança o quanto der e depois recua. Em <b>um</b><b>dois</b>, o padrão <b>.*</b> engole tudo como uma única ocorrência.
Acrescentando ? para formar <b>.*?</b>, ele para na primeira oportunidade e saem duas ocorrências. Para o que está dentro de tags ou aspas, a versão preguiçosa costuma ser a certa.
Por que isso roda numa thread separada
Quantificadores aninhados podem explodir uma expressão. Dê ao (a+)+b uma sequência de trinta caracteres a e o motor tenta todas as maneiras de reparti-los, então o trabalho cresce em ordens de grandeza. Uma entrada minúscula o ocupa por minutos.
Esta ferramenta avalia o padrão numa thread própria e mata essa thread inteira depois de um segundo. Não há como interromper no meio, porque tudo acontece dentro de uma única chamada. É por isso que uma expressão descontrolada aqui deixa a página funcionando.
O mesmo comportamento num servidor prende uma requisição. Se a entrada do usuário chega a uma expressão regular no seu código, quantificadores aninhados são a primeira coisa a procurar.
A sintaxe muda de linguagem para linguagem
O que roda aqui é a variante do JavaScript. O lookbehind ((?<=...)) funciona nos navegadores atuais, mas recursos exclusivos do PCRE, como recursão e grupos atômicos, não existem.
Python escreve grupos nomeados como (?P<nome>...), e Java precisa escapar as barras invertidas mais uma vez dentro de uma string. Traduza a notação ao levar daqui um padrão já ajustado.