Микроинструменты: централизованное логирование и просмотрщик иконсетов Fontello

В процессе разработки чего-либо веб-разработчик сталкивается с рядом рутинных задач. В сегодняшнем посте я хотел бы поделиться двумя своими микроинструментами, решающими две задачи: централизованное логирование проектов и просмотр собственноручно созданных в сервисе Fontello иконочных шрифтов. Итак, доброго всем здравия и добро пожаловать под кат.Централизованное логирование проектов Количество проектов, размещённых на моей площадке исчисляется десятками. Проекты разные — от мала до велика. И большую часть я периодически меняю, отлаживаю и т.д… Дело каждого, конечно, но лично я считаю вывод ошибок отладки на страницы самого проекта моветоном. Тем более, что существует возможность определить в файле htaccess перенаправление вывода ошибок в отдельный файл. Разумеется, держать лог проекта в его же папке не всегда удобно, да и вообще логично собирать все логи с одной площадки в одно и то же место. Но мало их собрать. Нужен ещё и удобный инструмент, дабы управлять логами из браузера, ибо удобно.Собственно сам скрипт довольно прост и содержит в себе всё необходимое, не имея внешних файлов:

\1-\2-\3 \5:\6:\7'); /* DATA */ $_ = << LOG viewer

DATA; define («APP_PAGE»,$_); define («APP_EMPTY»,'
Log is empty!'.»
\n\n»); $_ = <<').addClass ('logitem error').append ( \$('').addClass ('fa fa-'+((I == 401 || I == 403)?'lock':'times-circle')) ).append ( \$('').text (' '+E.statusText) )); } \$.fn.logClick = function (T, E, F){ E = E || false; if (E) E.removeClass ('active error').addClass ('loading').siblings ().removeClass ('active error loading'); \$.get (\$(this).attr ('href')).success (function (D){ \$('#el-root > div.'+T+': first > div: first').html (D); if (E) E.removeClass ('loading').addClass ('active'); if (typeof F == 'function') F (); }).error (function®{ \$('#el-root > div.'+T+': first > div: first').HTTPError®; if (E) E.removeClass ('loading').addClass ('error'); }); return false; } })(jQuery);

\$(function (){ \$('#el-root > div.side: first').on ('click','li > a.item', function (){ return \$(this).logClick ('content',\$(this).parent ()); }); \$('div.toolbox').on ('click','a.btn', function (){ return \$(this).logClick (\$(this).attr ('data-target')); }); \$('#el-root > div.side: first').on ('click','li > a.fa-times', function (){ return \$(this).logClick ('content',\$(this).parent (), function (){ \$('#el-menu').click (); \$('#el-content').click (); }); }); \$('#el-menu').click (); \$('#el-content').click (); }); JS; define («APP_JS»,$_); $_ = << div.side, #el-root > div.content { height: 100%; border: 1 px solid silver; border-radius: 5 px; } #el-root > div.side > div, #el-root > div.content > div { height: 100%; overflow-x: hidden; overflow-y: auto; } #el-root > div.side ul { list-style-type: none; margin: 0; padding: 0; } #el-root > div.side ul li { display: block; padding: 4 px; color: #358; font: 16 px Cuprum, Arial, sans-serif; border-radius: 4 px; position: relative; } #el-root > div.side ul li.loading { background: #707788; color: #d0dddf; } #el-root > div.side ul li.error { background: #510; color: #d0dddf; } #el-root > div.side ul li.active { background: #358; color: white; } #el-root > div.side ul li > a.fa-times { display: none; position: absolute; right: 4 px; text-decoration: none; color: red; } #el-root > div.side ul li: hover > a.fa-times { display: inline; } #el-root > div.side ul li.active > a.fa-times, #el-root > div.side ul li.error > a.fa-times, #el-root > div.side ul li.active > a.item, #el-root > div.side ul li.error > a.item { color: white; text-decoration: none; } #el-root > div.side ul li.loading > a.fa-times { display: none; } #el-root > div.side ul li.loading > a.item { color: #d0dddf; text-decoration: none; } #el-root > div.side ul li: before { margin-right: 4 px; font: 16 px FontAwesome; } div.logitem { padding: 4 px; border-radius: 4 px; } div.logitem.info, div.logitem.error { font: 20 px Cuprum, Arial, sans-serif; padding: 24 px; } div.logitem.info { background: #e0eeff; } div.logitem.info span.fa { color: royalblue; } div.logitem.error { background: #ffe0cc; } div.logitem.error span.fa { color: red; } CSS; define («APP_CSS»,$_); $_= 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2Fy' . 'ZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAfJJREFUeNqMUj1rG0EQnbtboYhESFfY' . 'Jp8ESYFgo8qNazltuvyI/InYYFKkDoG4NqjLHwiGgCGFK3N9jCVIYYyRzrY+zljS' . 'Ku+NtdIanJCFudnd2ffmzcwF77a2hCsIgrdwj+Xf64e19thOJjKdTvXCuA38k73t' . '7d0bBEc0a2WCmIsnSSJ7+/vvmQv2yzGGZJtZYAkajdRurq8lGwyk3+9Lr9dT/21n' . 'ZxfvNoF75QjMBODZCkgd4puLooXo8VjGIO52u/Kl2ZTG6urXz83mB0Q+KoFdEIQE' . '/zw6UtnlclmGw6G8rtWUpNFo6H2xWJTvSZLOFYx9BSCoAcBS+DiOY3mQy91RoluU' . 'uygBAb+EVqul2TudjpKczBpZKpUkyzKp1+sCzL0EWkK1Wp1nd0qc8RzhDTDhogSP' . 'gLTtdnsOdEroeWZPnq6soJr7CbQHToHfB3+fC0MSeCVg5r4C1wOOjUA/ez6fl5dQ' . 'AMxfSoCCSqWiB2Yj8BmaRyJLVbBAB+KVcHl6Kg+Xlu5Mwcll1hdQgPaKzEbIRrsS' . 'BufnYo4PDuT5xoYU4tjw8s36+m0P2Hmc6cPbGSkJkfjFTZam8vvwUGMFbJYv0zTv' . 'dRPvQ80WwdMMfm9jjETwfEsMscxagC1fnZ1dPFpb+yT/sWyWXRDDdvwRYAB4+UxU' . 'y0hc0wAAAABJRU5ErkJggg=='; define («APP_ICON»,$_);

/* LIB */ function _is_log ($name) { return! in_array ($name, array ('.','…','cgi-bin','.htaccess','.htpasswd','index.php')); } /* MAIN */ session_start (); $out = ''; if (isset ($_GET['file'])) { if (ctype_alnum (str_replace (array ('.','_','-'),'',$_GET['file']))) { if (is_file ($_GET['file']) && _is_log ($_GET['file'])) { $_SESSION['currentlog'] = $_GET['file']; } else { header ('HTTP/1.0 404 Not found'); } } else { header ('HTTP/1.0 400 Bad request'); } } else { if (! isset ($_SESSION['currentlog'])) { $logs = scandir ('.'); $fnd = ''; foreach ($logs as $f) if (_is_log ($f) && ($fnd=='')) $fnd = $f; if ($fnd!= '') $_SESSION['currentlog'] = $fnd; } } $act = isset ($_GET['act'])?(in_array ($_GET['act'], array ('clear','del'))?$_GET['act']:'view'):'view'; $sect = 'index'; if (isset ($_GET['sect'])) $sect = in_array ($_GET['sect'], array ('menu','icon','js','css','derr'))?$_GET['sect']:'log'; if (isset ($_SESSION['currentlog']) && isset ($_SERVER['PHP_AUTH_USER'])) { switch ($sect){ case 'derr': header ('HTTP/1.0 '.intval ($_GET['e']).' debug error'); exit (); break; case 'icon': header ('Content-Type: image/png'); $img = imagecreatefromstring (base64_decode (APP_ICON)); imagesavealpha ($img, true); imagepng ($img); exit (); break; case 'css' : header ('Content-Type: text/css; charset=utf-8'); echo APP_CSS; exit (); break; case 'js' : header ('Content-Type: text/javascript; charset=utf-8'); echo APP_JS; exit (); break; case 'log' : $out = ''; switch ($act) { case 'clear': file_put_contents ($_SESSION['currentlog'],''); $out = APP_EMPTY; break; case 'del' : unlink ($_SESSION['currentlog']); unset ($_SESSION['currentlog']); break; default: $log = file ($_SESSION['currentlog']); foreach ($log as $str) $out.= '

'.preg_replace (PTPL_DATE, PRPL_DATE,$str).»
\n\n»; if ($out == '') $out = APP_EMPTY; break; } break; case 'menu': $logs = scandir ('.'); $out .= '
    '; foreach ($logs as $number => $val) if (_is_log ($val)) $out.= '
  • ' . ''.»$val» . '' .»
  • \n»; $out .= »
\n»; break; default: $out = APP_PAGE; break; } header («Content-Type: text/html; charset=utf-8»); echo $out; } ?> В папке с логами создаём скрипт index.php, копируем туда написанный выше код, защищаем папку базовой HTTP-аутенификацией (при помощи htpasswd).В .htaccess логируемого проекта добавляем строки:

php_value log_errors «on» php_value log_errors_max_len »1024» php_value error_log »/path/to/your/log/file» И, собственно, всё. При отладке проекта заходим в нашу папку с логами, авторизуемся и выбираем нужный нам лог. Логи можно очищать посредством соответствующей кнопки или удалять вовсе. Все необходимые для работы интерфейса скрипта библиотеки (bootstrap, jQuery) грузятся со своих официальных CDN. Интерфейс на английском, хотя вряд ли это проблема.Просмотрщик иконсетов Fontello В процессе разработки своего фреймворка я столкнулся с необходимостью составить свой иконочный шрифт, поскольку популярный FontAwesome не удовлетворял ряду критериев, в частности отсутствовали некоторые иконки, а часть иконок была, мягко говоря, не сильно хорошо отрисована. Шрифт был составлен, однако, я столкнулся с довольно тривиальной рутинной задачей: надо было постоянно держать в браузере вкладку с составленным шрифтом, или запоминать все коды и алиасы, или же постоянно подсматривать в CSS-файл. Все три варианта были весьма неудобны. Посему я написал простой скрипт, создающий на основе сгенерированного CSS-файла предпросмотр шрифта.Код предельно прост и самодостаточен:

$v) $fontdata[$v] = str_pad ($a[8][$k],4,'0', STR_PAD_LEFT); } ?> Font table for font »<?php echo preg_replace(fontFTitle,'\7',$fontfile); ?>» div.font-table-wrapper { padding: 4px 0; } ul.font-table li { display: block; float: left; position: relative; width: 200px; padding: 4px 40px; cursor: pointer; *cursor: hand; border-right: 1px dotted silver; } ul.font-table li.f { border-left: 1px dotted silver; } ul.font-table li .icon { position: absolute; left: 8px; top: 2px; display: block; width: 24px; height: 22px; padding-top: 2px; font-size: 14px; color: #237; border-radius: 3px; background: #cdf; text-align: center; } ul.font-table li .class { font: 15px Cuprum,Arial,sans-serif; color: #68a; } ul.font-table li .code, ul.font-table li .letter { display: block; position: absolute; top: 7px; text-align: center; font: bold 14px monospace; } ul.font-table li .code { right: 16px; color: #284; } ul.font-table li .letter { background: #c0ffc7; width: 24px; height: 20px; top: 2px; padding-top: 4px; border-radius: 3px; right: 54px; color: #697; }