Продолжаем о UCSVLOG. Начало читайте тут – Часть 1. Проблема и Идея, потом тут мы продолжили – Часть 2. Решение , ну а сейчас о плюхах
Плюшки это типо сладости, которые тебе дают, если ты прилежно все делаешь. Так и с логами. Если ты правильно и аккуратно их пишешь – то в результате получаешь правильную и четкую аналитику любой сложности и не только.
django-ucsvlog
Проект лежит на bitbucket.org
Первым для себя применением ucsvlog я нашел в как апой к Django. Он был создан за долго до того, как у Django появились свои логи. И пока миграцию на них я не планирую, а думаю, как объеденить и взять что-то хорошее с джанговских и сделать процесс миграции с джанговских на UCSVLOG более простым.
Блоком в данном случае у нас будет запрос пользователя. Подключается с помощью одной или двух мидлварь, каждая из которых открывает блок.
Первая ‘djucsvlog.middleware.LogRequestInfo‘ идет как самой первой в Вашем списке мидлварь,а значит должна запускаться еще до того, как какая-либо из них начнет работать.
Она открывает блок, в который кладется информация о запросе, а в settings задается список полей которые мы хотим записываться, например имя домена, путь, гет, пост параметры, файлы или BROWSER_UUID_COOKIE.
BROWSER_UUID_COOKIE – это простой механизм, который по средствам кук следит за действиями пользователя. Когда приходит пользователь и у нег нет нашей куки – мы ему создаем ее и при каждом его запросе кладет ее в лог. Далее это позволит сводить свою аналитику.
Как дополнительная возможность – это вести лог файлов, которые аплоадит пользователей. Т.е. мы в отдельное место сохраняем все аплоады пользователей, а в логах отмечаем – что и куда мы сохранено.
Вторая ‘djucsvlog.middleware.LogViewInfo‘ записывается последней мидлварей, она опциональная, т.е. работать будет и без нее, сюда кладется инфа накопленная с других мидлварь, например информация о залогиненом пользователе или любая другая информация, которая может быть собранная уже с ваших мидлварь.
Последнее – это указать у себя в сетингсах шаблон имени файла UCSVLOG_FILE, и можно начинать играться.
Во время работы сам объект логера лежит в глобальной области видимости. Т.е. воспользоваться им можно в любой момент.
За время работы с django-ucsvlog у нас накопилось много настроек для кастомизации этих логов. Все они лежат в djucsvlog.settings.py немного документированные, в лучших традициях опенсорса, с указанием дефолтных значений. Такие как – правило формирования отчет об ексепшене, буферизация, правило определение IP пользователя, возможность сохранять отдельно загружаемые файлы, а в лог класть инфу о том, куда мы сохранили текущий загруженый файл.
+ UCSVLOG_CHANGE_MODEL в этом дикте можно указать – за изменением каких моделей вы хотите следить ( предварительно указав ‘djucsvlog.components.change_model’ в списке компонентов UCSVLOG_COMPONENTS ), и какие поля этих моделей вы хотите класть в лог файл в момент их изменения. Т.е. теперь благодаря логам вы можете связывать изменения любой модели с определенным пользователем – ваще мечта.
Пример settings.py вашего приложения:
-
MIDDLEWARE_CLASSES = (
-
'djucsvlog.middleware.LogRequestInfo', ##первый
-
'django.middleware.common.CommonMiddleware',
-
'django.contrib.sessions.middleware.SessionMiddleware',
-
'django.middleware.csrf.CsrfViewMiddleware',
-
'django.middleware.locale.LocaleMiddleware',
-
'django.contrib.auth.middleware.AuthenticationMiddleware',
-
'django.contrib.messages.middleware.MessageMiddleware',
-
'django.middleware.transaction.TransactionMiddleware',
-
'djucsvlog.middleware.LogViewInfo', ## Второй
-
)
-
UCSVLOG_COMPONENTS = (
-
'djucsvlog.components.change_model',
-
) # указываем список компонентов ( расширений )
-
-
UCSVLOG_FILE_VERSION = 'v3' # о смысле такого хука мы расскажем позже
-
UCSVLOG_FILE = '/var/log/django/console-%(year)s-%(month)s-%(day)s-'+UCSVLOG_FILE_VERSION+'.ucsv'
-
-
# По умолчанию False тоже, но если мы хотим, чтоб логи не велись, а выводились в консоль то делаем тру
-
UCSVLOG_PRINT = False
-
-
# Эти поля будут класться в лог в момент закрытия блока запроса, т.е. в самом конце
-
UCSVLOG_RESPONSE_FIELDS = ['status','ctype','content']
-
-
# кастомные пользовательские функции для логирования
-
def server_ip(request,*args,**kwargs):
-
return request.META.get('REMOTE_ADDR', '0.0.0.0')
-
-
def ucsvlog_last_login(request,*args,**kwargs):
-
return request.session.get('last_login','')
-
-
# поля, которые мы созраняем при открытии реквеста ( в первой мидлвари )
-
UCSVLOG_REQUEST_FIELDS = ['http_host','browser_uuid','remote_addr',server_ip,\
-
'path','get','post','save_files','http_user_agent',\
-
'http_referer','http_accept_language']
-
-
UCSVLOG_VIEW_OPEN_FIELDS = ['userid',ucsvlog_last_login] # во второй мидлвари
-
UCSVLOG_RESPONSE_FIELDS = ['ctype','content','status','headers'] #при закрытии
-
UCSVLOG_REQUEST_REQ_REMOTE_ADDR_REAL_IP = 'HTTP_X_REAL_IP'
-
# каталог с исходниками, относительно него будет писаться инфа о вызове функций
-
UCSVLOG_RELATED_FOLDER = PRJ_ROOT
Большая часть этих настроек имеют значения по умолчанию, поэтому заведется и с такими вот:
-
MIDDLEWARE_CLASSES = (
-
'djucsvlog.middleware.LogRequestInfo', ##первый
-
'django.middleware.common.CommonMiddleware',
-
'django.contrib.sessions.middleware.SessionMiddleware',
-
'django.middleware.csrf.CsrfViewMiddleware',
-
'django.middleware.locale.LocaleMiddleware',
-
'django.contrib.auth.middleware.AuthenticationMiddleware',
-
'django.contrib.messages.middleware.MessageMiddleware',
-
'django.middleware.transaction.TransactionMiddleware',
-
'djucsvlog.middleware.LogViewInfo', ## Второй
-
)
-
UCSVLOG_FILE = '/var/log/django/console-%(year)s-%(month)s-%(day)s.ucsv'
django-ucsvlog-analytics
Проект лежит на bitbucket.org
Но все самое вкусное для этих логов, благодаря их хорошей структуризации – должно лежать в аналитике.
Аналитике представленна набором базовых коммнад, которые расширяются исходя из нужд конкретной задачи.
Приятной особенностью в написании логов является то, что на вход функций анализа приходят не массивы, а объекты класса Row , у которого есть множество свойтсв, заполеныне исходя из настроек django-ucsvlog. Т.к. к примеру, когда приходит row из первого индекса реквеста то у него уже есть к примеру аттрибуты row.data_path или row.data_ip
Но этот бонус также накладывает и свои ограничения, о которых я расскажу позже.
BaseSimpleAnalyticCommand – простой анализатор
Это самый простой метод анализа. Подходит в случае если вы своим логам хотите задать все один и довольно конктерный вопрос. “Дай мне количество хитов за период?” или “Дай мне топ стран”. В этом случае команде кормится набор логфайлов, а в вашу функцию collect_row передаются объекты строк, где вы можете проводить анализ и выдавать пользователю. Тут в принципе ничего сверхествественного не происходит.
Вот пример команды, которая собирает из Ваших логов топ accepted languages
-
from djucsvlog_analytics.analytic_commands import BaseSimpleAnalyticCommand
-
-
class Command(BaseSimpleAnalyticCommand):
-
data = {}
-
# переопределяя эту функцию – вы указываете,
-
# какие записи вы ходите видеть в анализе
-
# в нашем случае мы хотим видеть только
-
# первые записи блока реквеста,
-
# потому что только в них есть инфа о
-
# браузере пользователя
-
def filter_row(self,row):
-
return row.is_a_req
-
-
# собственно функция анализа тех записей, которые
-
# прошли через фильтер
-
def collect_row(self,row):
-
al = row.data_http_accept_language.\
-
split(';')[0].split(',')[0].split('-')[0]
-
if al in self.data:
-
self.data[al]+=1
-
else:
-
self.data[al] = 1
-
-
# и вывод результатов
-
def output_results(self):
-
for item in sorted(self.data.items(),\
-
key=lambda a:a[1]):
-
print item[0], item[1]
Если это сохранить как команду djucsvlog_test_simple_analytics то запуск будет выглядить следующим образом:
-
$ python manage.py djucsvlog_test_simple_analytics /var/log/django/stats-2012-3-21-v3.ucsv –settings=settings.analytics
djucsvlog_user_path_convertor
Это уже команда на основе базового класса BaseAnalyticCommand ( наследник от BaseSimpleAnalyticCommand ).
Очень полезна, когда для одних логов вы хотите провести более детальный анализ. Когда Вам нужен не просто маленький отчет, а когда Вам надо разобраться дельано со сложившейся проблемой с трафиком. Это очень похоже на то, когда трафик есть а продаже нет. Почему?
Идея проста convertor собирает из логов sqlite3 БД и кладет в один файл, при этом может дополнить его информацией о браузере, оси и стране пользователя. Таблици внутри него не просто набор строк, а связные таблицы. Хосты имеют много пользователей, пользователи имеют много реквестов, а реквес имеет много лог записей. Сам этот файл уже по сути часть анализа можно просто зайти в нее и на уровне SQL получать необходимые выборки и сводить статистику.
djucsvlog_user_path_convertor – это именно наша идея конвертации в такую вот струкруту БД. Если вы заходите сделать свой конвертато в БД, то просто пишите команду, наследник от BaseAnalyticCommand.
Ниже несколько примеров запуска такой команды в наших проектах
-
$ python manage.py djucsvlog_user_path_convertor /var/log/django/* \
-
–out=/tmp/django.userpath.db
Это мы просто конвертим все файлы из папки /var/log/django/ в базу
-
$ python manage.py djucsvlog_user_path_convertor /var/log/django/* \
-
–out=/tmp/django.userpath.db –force-new
Указываем, что если БД уже есть, то в нее не дописывать а создавать заново
-
$ python manage.py djucsvlog_user_path_convertor /var/log/django/* \
-
–out=/tmp/django.userpath.db –force-new\
-
–geoip-db=/var/geodb/2012-04-01.db
Передаем ссылку на базу GEO IP для добавления дополнительного поля со страной
djucsvlog_user_path_analytics
Но мы очень быстро поняли, что очень много задач не решаются просто SQL командой, поэтому мы к sqlite3 базе начали дописывать скриптики для ее анализа, и все это свернули в отдельную команды, на вход которой передается один или несколько sqlite файлов и параметры для анализа. Причем параметры разделаются на задачи и на условия. Т.е. мы при вызове команды можем передать ей на вход несколько задач например топ браузеров или топ стран, и условия например пользователи, которые зашли на определенную страницу, пользователи, которые сделали больше определенного количества шагов. Или как связка – дай мне следующий шаг после переданного.
Сама команда является наследником от BaseAnalyticReadCommand, а задачи являются наследниками от BaseAnalyticElement, список условий передаются при создании элемента задачи
Ниже несколько примеров использования этой команды:
-
$ python manage.py djucsvlog_user_path_analytics \
-
/tmp/django.userpath.db –get-entry-points
Получаем ТОП точек входа на сайт
-
$ python manage.py djucsvlog_user_path_analytics \
-
/tmp/django.userpath.db /tmp/django_2.userpath.db \
-
–get-entry-points
Для анализа можно указывать не один файл
-
$ python manage.py djucsvlog_user_path_analytics \
-
/tmp/django.userpath.db /tmp/django_2.userpath.db \
-
–get-entry-points –after-path=/
Какой был следующий шаг после /
-
$ python manage.py djucsvlog_user_path_analytics \
-
/tmp/django.userpath.db
-
–top-404
Невероятно полезная команда после переверстки
-
$ python manage.py djucsvlog_user_path_analytics \
-
/tmp/django.userpath.db
-
–get-country-ip
Дай топ стран
-
$ python manage.py djucsvlog_user_path_analytics \
-
/tmp/django.userpath.db
-
–get-country-ip –get-os
топ стран и топ осей
-
$ python manage.py djucsvlog_user_path_analytics \
-
/tmp/django.userpath.db
-
–get-country-ip –get-os –after-steps=3
топ стран и топ осей пользователей которые сделали на сайте больше 3х шагов
Тут мне даже сложно будет перечеслить все возможности которые есть у этой команды, а сколько всего Вы еще можете дописать!!!
Следующие 2 возможности особо мне теплы, т.к. кроме того что они добавляют аналитику они еще и существенно облегчают жизнь программисту.
BaseStreamCommand – отложеная статистика
Раньше, для сбора онлайн статистики о ваших пользователях – Вам надо было в момент захода этого пользователя раскидывать записи по таблицам и возможно совершать какие-то дополнительные расчеты. Причем это надо было делать максимально быстро, чтоб пользователь не видел никаких задержек. С четко структурированными логами, такими как ucsvlog, нет необходимости делать это в момент захода пользователя. Достаточно просто дочитывать периодически логи, которые пишутся, и уже в момент чтений, уже раскидывать статистику. При этом ваши расчеты уже никого не задерживают. Я назвал это “отложенная статистика”, т.к. мы откладывает всю работу по ее расчет в отдельную команду.
Вы пишите команду на вход которой подаются новые записи из каталога с логами. Причем подаются эти записи именно в хронологии возникновения их в логах. Т.е. если ваш сервис пишет 10 различных лог файлов одновременно и команда в момент запуска определила, что все они выросли с момента последнего запуска, то она будет выдавать к вам на вход записи с разных файлов в разброс в зависимости от того, где записи появляются раньше. Чтоб аналитика в этом случае была максимально четкой.
Особенности работы такой команды является индексный файл ( sqlite3 ), в котором она держит данные которые используются между ее вызовами, например такие как – последние размеры лог файлов
Помимо того плюса ускоренной работы пользователя является еще и то, что в случае, когда клиент захочет расширить онлайн статистику, например добавить в нее дополнительный ТОП – ему не надо будет ждать пока она накопится. Мы просто дописываем команду по сбору статистики, которая в момент первого запуска просто накопит недостающие данные и пойдет считать стату дальше.
Наследника BaseStreamBlockCommand, передает анализу не одну строку,а уже собранный блок.
BaseConvertBlockCommand – конвертация логов.
Последней плюшкой анализатора, которая нами в данным момент обкатывается еще – это конвертатор.
Меня всегда расстраивал тот факт, что логи надо удалять
А при использования django-ucsvlog апетиты засунуть в них чего по больше только растут, в результате они и стают очень толстыми. Самое грустное, что единственным правилом для удаления логов всегда служил их срок, который прямо пропорционален размеру вашего винта и размеру плодящихся логов. Говоря короче большие логи живут меньше, т.к. надо удалять их быстрее, чтоб освобождать место другим логам.
Но я не хочу удалять все логи, я хочу удалять из логов только ненужное. Мне через месяц будет неважен call_info т.е. место, где были вызваны эти логи. Мне будет все также все равно на заходы пользователя на всякие информационные страници, или к примеру стуки всяких мониторингов меня тоже будут не волновать. Но я хочу держать как можно дольше процесс совершения покупки и процессинга карты. Для этого и делается конвертатор, который перегоняет логи из толстых, в которых часть инфы актуальна только несколько дней, в тонкие, в которых остается инфа, актуальность которой максимально долгая.
Ваша команда наследник от BaseConvertBlockCommand легко может с этим справляться.
Вот пример команды, которая делает это у меня:
-
class Command(BaseConvertBlockCommand):
-
def filter_convert_row(self,row):
-
if not row.is_a_req:
-
return True
-
return not(row.data_path.startswith('/info') \
-
or row.data_path.startswith('/test-exception') \
-
or row.data_path.startswith('/check-back') \
-
or row.data_path.startswith('/calculate-statistics') \
-
or row.data_path.startswith('/captcha') \
-
or row.data_path.startswith('/media') \
-
or row.data_path.startswith('/favicon'))
Тут указывается фильтр – какие строки надо оставить. Какие столбци надо оставить указывается в сетингсах. Детали смотрите в сетингсах, там под это выделен подраздел.
Самое приятное в стриминге и конверторе – это то, что абсолютно не важно – сколько сервисов ведут свой лог. У Вас может быть ucsvlog на твистеде, отдельный на Django, еще отдельные логи ведут кроновские команды. А вот стриминг с конвертором берут и объединяют их в один поток, в один файл, где вы в хронологии сможете посмотреть события каждого из них. ( Да и не только питон, сам формат логов очень простой )
Версионность логов.
Ваша структура логов будет меняться с тем, как меняется и растет Ваш проект. И разные версии логов не должны мешаться в файлах. Поэтому я в формат файла добавляю маркер версии. И для каждой версии создаю отдельный сетингс для анализатора.
Например, когда я только подключил ucsvlog к системе имена всех файлов логов оканчиваются на v1.ucsv. Потом я добавил дополнительные поля в строку открытия реквеста, и изменил формат файлов на v2.ucsv, но также создал файл analytics/v1.py в которую положил настройки для первой версии логов. Теперь когда я буду парсить логи из первой версии я буду использовать этот сетингс.
-
$ python manage.py djucsvlog_user_path_convertor \
-
/var/logs/django/*-v1.ucsv \
-
–settings=analytics.v1
Тоже самое с каждой следующей версией
Заключение
Как видите логи могут стать мощным средством для аналитики процессов системы и ее пользователей. Они могут даже стать часть коммуникации между вашими сервисами. С помощью них вы можете выделять самые главное и хранить это веками. И еще много чего они могут делать, чего я и сам еще не знаю
Вобщем подключайтесь, Вам понравится, я уверен
