Осовремененный Unix Way или pipe в браузер

    Наверное, каждый, кому когда-нибудь приходилось следить одновременно за большим количеством окошек с логами, подумывал о переносе некоторых из них на экран планшета или телефона.
    А, находясь далеко от компьютера, следить за выхлопом недавно запущенного большого и страшного сервиса?
    Конечно, можно поставить ssh клиент на телефон, но это не особо удобно.
    Поэтому я решил сделать мини-сервис упрощающий «удалённый» просмотр логов.




    Краткое резюме


    Сервис cantail.me с открытым исходным кодом, выложенным на github.
    Свободный консольный клиент (github). Для простой отправки данных достаточно:
    long-runnig-app | tailme
    

    И скопировать ссылку из открывшегося окна браузера. Или можно добавить параметр -s и просто скопировать её из терминала.

    Для примера сейчас запущена публикация лога nginx'а командной:
    tail -f /var/log/nginx/cantailme.access.log | tailme -s
    


    Установка клиента


    В ubuntu 11.10+ приложение можно установить из ppa:
    add-apt-repository ppa:nvbn-rm/ppa
    apt-get update
    apt-get install tailme
    

    В других дистрибутивах можно установить через pip:
    pip install -e git+https://github.com/nvbn/cantailme-client.git#egg=tailme
    

    Либо через установочный скрипт:
    git clone https://github.com/nvbn/cantailme-client.git
    cd cantailme-client
    python setup.py install
    


    Под капотом




    Реализация клиента очень простая — цикл читающий stdin и отправляющий на сервер новые строчки.
    Для предотвращения подмены данных сервер при инициализации сессии отдаёт хеш-идентификатор и «секрет», которые в дальнейшем используются для отправки «строк».

    Серверная часть немного сложнее, её можно разделить на 3 условных сущности:
    • http «сервер» занимающийся рендеренгом 2 вьюх;
    • jsonrpc api, с помощью которого консольный клиент инициализирует сессию и отправляет данные на сервер;
    • push сервер для отправки полученных через api строк клиентам в реальном времени.


    Взаимодействие между частями системы проходит при помощи RabbitMQ.
    Push работает через tornado-sockjs.
    Для экономии ресурсов логи загружаются сразу в файл и отдаются через nginx, минуя django.
    Для желающих залить /dev/urandom или большие логи раз в 5 минут запускается «суровый» скриптик.

    Деплой




    В данном случае он нетривиален, но я уже сталкивался с похожей ситуацией. Основные причины выбора такой схемы:
    • нередка ситуация, когда нестандартные порты порезаны, поэтому tornado и django должны висеть на одном стандартном порту;
    • заставить nginx работать нормально с WebSocket'ами проблематично;
    • HAProxy не раздаёт статику;
    • эту схему я успел погонять в продакшене на неэкспериментальных проектах =)


    В случае возникновения ошибок пишите багрепорты в трекере для сервера и клиента. Либо в комментариях к этому посту =)

    PS с habrastorage произошёл какой-то косяк, перезалил картинки к себе.
    Метки:
    Поделиться публикацией
    Похожие публикации
    Реклама помогает поддерживать и развивать наши сервисы

    Подробнее
    Реклама
    Комментарии 35
    • +3
      Мне нравится идея. Давно думал, что чего-то такого не хватает. Только немного боязно публиковать логи без пароля. Ведь можно случайно или-перебором открыть что-нибудь чужое. А ставить такую связку серверной части не каждый захочет.
      • +3
        Если будет пользоваться популярностью, прикручу https и запароливание.
        • 0
          Хотелось бы какое-нибудь криптование без участия сервера: один ключ указываем в tailme, другой вбиваем в браузере.
      • –10
        Эх… Версию для Windows бы… Вот как раз для nginx нужно =)
        • 0
          Попробуйте вот это завести. Только понадобится cygwin, чтобы tail был в Windows.
          • 0
            Можно отредактировать 2 строчки в tailme и заработает в винде)
        • +3
          Дико круто!
          Надо покопаться в исходниках :) Прям сразу настроение поднял, у меня как раз такая долгоиграющая пластинка запущена =)
          • 0
            Пример (лог nginx) пустой. Chrome 20.0.1132.47
            • 0
              Пробовали подождать секунд 5?
          • 0
            Спасибо вам за человеческий подход — и под убунты пакет не поленились собрать, и для пипа, и на гитхабе все разложили по полочкам.
            Просьба — впишите argparse в пип-зависимости. А то мне пришлось отдельно ставить.
            • 0
              Спасибо. Отличный сервис. Бывало использовал следующий метод для получения итоговых логов:
              some_app | sendmail -v email@gmail.com
              Но этот способ, конечно, ни в какое сравнение с вашим сервисом не идет.
              • 0
                Для тех кому интересно, могу предложить как на скорую руку это
                можно было бы сделать с помощью xinetd:

                маленький скрипт remote-log:
                #!/usr/bin/env bash tail -f /var/log/messages

                конфиг /etc/xinetd.d/log-stream
                service remote-log { socket_type = stream protocol = tcp wait = no user = root server = /home/romones/remote-log instances = 20 }

                запись в /etc/services
                remote-log 9998/tcp

                рестарт xinetd:
                #lsof -i:9998 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME xinetd 15237 root 5u IPv6 334966 0t0 TCP *:remote-log (LISTEN)

                Работает из браузера на localhost:9998 всё добавляется построчно как надо.

                Но это решение не такое гибкое и элегантное.
                • 0
                  Ой, что-то у меня переносы в коде не получились. В первом скрипте tail с новой строки, и в конфиге {arg} = {val} тоже на разных строках, естественно :)
                  • 0
                    Этот способ пару раз использовал, но тут есть косяк. Если лог стримится минут 10, браузер обрывает соединение.
                    Ну и это менее юзерфрендли и без модных вебсокетов =)
                    • 0
                      Наверное tail -F иначе после ротейта лога tail останется на старом файле.
                    • +1
                      Я вот на днях сделал pupergrep как замену supergrep от etsy. В браузере мониторит столько логов, сколько скажете (у нас овер 100).
                      • 0
                        Прикольно, но сложнее. А почему полинг? Для ноды sockjs как-то даже роднее, чем для торнадо.
                        • 0
                          Для ноды да, но всё забирается за nginx, чтобы тот следил за доступом ко всему добру, а он не умеет иначе, насколько я понял. Сам я только за ws без костылей.

                          Насчет сложнее — возможно, просто у нас логи весьма подробные и зачастую нужно что-то подстветить, что-то убрать и т.д. В ясную погоду трейсы смотреть не приходится и они фильтруются, например.
                          • 0
                            Новый nginx умеет, но там настройка совсем не тривиальное, и вроде оно не особо стабильно.
                            И пробовали заменить на haproxy с acl'ами?
                            • 0
                              На haproxy менять не хочется, всё будет только сложнее. Те же acl проще в node.js держать, раз уж статику ей тоже придётся раздавать за haproxy.

                              Проже дождаться стабильный nginx с поддержкой ws. Вообще в планах есть раздача статики через ноду, но как-то не сейчас :)
                              • 0
                                Нее, совсем не стоит. Пробовали, нода совсем не очень держит статику. Самый рабочий вариант деплоя haproxy + nginx / lighttpd / sinatra (если ruby).
                                И даже версии nginx с поддержкой http 1.1 ведут себя совсем не очень в продакшене. Называется пробовали-проходили =)
                                • 0
                                  Логи — вещь для весьма ограниченного числа людей. Тут не будет 3k+ запросов в секунду, как на балансерах. К тому же, статика сразу укладывается в кеш, а при нормальной работе ws будут потреблять 90% запросов.

                                  Так что раздачу статики из ноды можно потерпеть для простоты деплоя и увеличения участия пользователей в разработке проекта :)
                                  • 0
                                    ws не делает запросов как таковых, а отправить данные с сервера на клиент очень «лёгкая» операция. На «мнини-продакшене» проводил тест, веб-сокеты оказываются легче для сервера, чем статика с лайти =)
                      • 0
                        В dist.py (local.py) для сервера отсутствует переменная «TORNADO_PORT», с ней заработало, но не совсем, как понимаю.

                        root@debi /home/prowl/cantailme-server # ./manage.py runpush
                        WARNING:root:Connect error on fd 10: ECONNREFUSED
                        ERROR:stormed-amqp:ERROR in on_disconnect() callback
                        Traceback (most recent call last):
                          File "/usr/local/lib/python2.6/dist-packages/stormed/connection.py", line 142, in on_closed_stream
                            self.on_disconnect()
                        TypeError: on_closed() takes exactly 2 arguments (1 given)


                        Страница создается, но логи не приходят, с cantail.me все ok.
                        • 0
                          tornado из git?
                          И от рута не стоит запускать.
                          • 0
                            Да, устанавливал по инструкции с помощью pip и requirements.txt, лишь «stormed» на «stormed-amqp» заменил, иначе не находил.
                            • 0
                              А дистрибутив какой? Я тестил только в ubuntu 10.04 и 12.04
                              • 0
                                Debian Squeeze, завтра попробую на 12.04.
                        • 0
                          Кстати говоря, при установке в virtualenv сбоит:

                          (trna)gamma:trna mktums$ pip install -e git+https://github.com/nvbn/cantailme-client.git#egg=tailme
                          Obtaining tailme from git+https://github.com/nvbn/cantailme-client.git#egg=tailme
                            Cloning https://github.com/nvbn/cantailme-client.git to /Users/mktums/.virtualenvs/trna/src/tailme
                          Unpacking objects: 100% (26/26), done.
                            Running setup.py egg_info for package tailme
                              
                          Installing collected packages: tailme
                            Running setup.py develop for tailme
                              
                              Creating /Users/mktums/.virtualenvs/trna/lib/python2.7/site-packages/cantailme-client.egg-link (link to .)
                              Adding cantailme-client 0.1dev to easy-install.pth file
                              Installing tailme script to /Users/mktums/.virtualenvs/trna/bin
                              
                              Installed /Users/mktums/.virtualenvs/trna/src/tailme
                          Successfully installed tailme
                          Cleaning up...
                            Exception:
                          Traceback (most recent call last):
                            File "/Users/mktums/.virtualenvs/trna/lib/python2.7/site-packages/pip-1.1-py2.7.egg/pip/basecommand.py", line 104, in main
                              status = self.run(options, args)
                            File "/Users/mktums/.virtualenvs/trna/lib/python2.7/site-packages/pip-1.1-py2.7.egg/pip/commands/install.py", line 265, in run
                              requirement_set.cleanup_files(bundle=self.bundle)
                            File "/Users/mktums/.virtualenvs/trna/lib/python2.7/site-packages/pip-1.1-py2.7.egg/pip/req.py", line 1081, in cleanup_files
                              rmtree(dir)
                            File "/Users/mktums/.virtualenvs/trna/lib/python2.7/site-packages/pip-1.1-py2.7.egg/pip/util.py", line 29, in rmtree
                              onerror=rmtree_errorhandler)
                            File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/shutil.py", line 249, in rmtree
                              onerror(os.remove, fullname, sys.exc_info())
                            File "/Users/mktums/.virtualenvs/trna/lib/python2.7/site-packages/pip-1.1-py2.7.egg/pip/util.py", line 46, in rmtree_errorhandler
                              os.chmod(path, stat.S_IWRITE)
                          OSError: [Errno 1] Operation not permitted: '/Users/mktums/.virtualenvs/trna/build/pip-delete-this-directory.txt'
                          
                            Storing complete log in /Users/mktums/.pip/pip.log
                          Traceback (most recent call last):
                            File "/Users/mktums/.virtualenvs/trna/bin/pip", line 8, in <module>
                              load_entry_point('pip==1.1', 'console_scripts', 'pip')()
                            File "/Users/mktums/.virtualenvs/trna/lib/python2.7/site-packages/pip-1.1-py2.7.egg/pip/__init__.py", line 116, in main
                              return command.main(args[1:], options)
                            File "/Users/mktums/.virtualenvs/trna/lib/python2.7/site-packages/pip-1.1-py2.7.egg/pip/basecommand.py", line 141, in main
                              log_fp = open_logfile(log_fn, 'w')
                            File "/Users/mktums/.virtualenvs/trna/lib/python2.7/site-packages/pip-1.1-py2.7.egg/pip/basecommand.py", line 168, in open_logfile
                              log_fp = open(filename, mode)
                          IOError: [Errno 13] Permission denied: '/Users/mktums/.pip/pip.log'
                          
                          • +1
                            Оно расчитано на linux'ы, судя по логам там что0то другое.
                            • 0
                              OS X
                              • 0
                                Сегодня проверял — должно работать
                                И тут видно по Operation not permitted, что что-то с правами у вас не то.
                                • 0
                                  Хм, и впрямь… На голом виртэнве все ок…
                          • 0
                            Есть такое решение github.com/mthenw/frontail

                            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.