Выделяем поля из логов nginx на python (original) (raw)
January 1 2010, 18:30
Для анализа логов nginx мне понадобилось регулярное выражение, которое бы корректно разделяло запись на отдельные поля. Немного по-колдовав над логом, я понял, что наилучший способ разобраться в формате лога - это выцепить его из конфигурации сервера.
Кроме очевидного плюса - выделение полей с учетом всех разделителей и кавычек, использование формата позволяет получить именованные поля.
Вот код, который у меня получился, возможно пригодится.
import re
# формат из конфигурации nginx
FORMAT = '$remote_addr - <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi><mi>e</mi><mi>m</mi><mi>o</mi><mi>t</mi><msub><mi>e</mi><mi>u</mi></msub><mi>s</mi><mi>e</mi><mi>r</mi><mo stretchy="false">[</mo></mrow><annotation encoding="application/x-tex">remote_user [</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">re</span><span class="mord mathnormal">m</span><span class="mord mathnormal">o</span><span class="mord mathnormal">t</span><span class="mord"><span class="mord mathnormal">e</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">u</span></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord mathnormal" style="margin-right:0.02778em;">ser</span><span class="mopen">[</span></span></span></span>time_local] "$request" $status' + \
' <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>b</mi><mi>y</mi><mi>t</mi><mi>e</mi><msub><mi>s</mi><mi>s</mi></msub><mi>e</mi><mi>n</mi><mi>t</mi><mi mathvariant="normal">"</mi></mrow><annotation encoding="application/x-tex">bytes_sent "</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">b</span><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="mord mathnormal">t</span><span class="mord mathnormal">e</span><span class="mord"><span class="mord mathnormal">s</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">s</span></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord mathnormal">e</span><span class="mord mathnormal">n</span><span class="mord mathnormal">t</span><span class="mord">"</span></span></span></span>http_referer"' + \
' "$http_user_agent" "$gzip_ratio" "$cookie" $hostname'
# выражение, описывающее переменную в конфигурации логов nginx
TOKEN = '\$([a-z\_]+)'
# возможные скобки и кавычки вокруг переменных в конфигурации логов nginx
POSSIBLE_QUOTES = ('[]', '"', '\'')
def get_matcher(format):
quotes = re.escape(''.join(POSSIBLE_QUOTES))
pat = format
for token in re.findall(TOKEN, format):
res = re.search('([%s]?)(\$%s)([%s]?)' % \
(quotes, token, quotes), format).groups()
if not res:
continue
elif res[0] <> res[2]:
tq = res[0] + res[2]
tq_escaped = '\%s|\%s' % (res[0], res[2])
else:
tq = res[0]
tq_escaped = '\%s' % res[0]
tq = re.escape(tq)
tq_escaped = re.escape(tq_escaped)
if not tq:
ftoken = '\$%s' % (token)
ptoken = '(?P<' + token + '>[^\ ]*)'
else:
ftoken = '[%s]\$%s[%s]' % (tq, token, tq)
ptoken = '[%s](?P<%s>(?:%s|[^%s])*)[%s]' % (tq, token, tq_escaped, tq, tq)
pat = re.sub(ftoken, ptoken, pat)
patc = re.compile(pat)
def matcher(line):
return patc.match(line)
return matcher
def main():
f = file('access_log')
matcher = get_matcher(FORMAT)
for line in f:
print matcher(line).groupdict()
Возможно кому-нибудь будет полезным
UPD: Немного поправил код, чтобы он корректно работал с записями, в которых встречаются поля вида: “some text with escaped \” qoute”
--
Этот, а также другие мои посты по it-тематике вы можете прочитать здесь