« Доступ к базам данных из php. Часть 8 | Сжатие css. Небольшая самописная утилитка » |
Маленький дебаггер для flex
Утилита “smalldebugger” служит для отладки flash/flex приложений. Состоит из двух частей: сервер и клиент. Коммуникация между этими двумя частями идет с помощью LocalConnection.Клиент представляет собой небольшой файл blzdebugger.as, подключаемый к вашему проекту. В файле blzdebugger.as находится одноименный класс blzdebugger реализующий логику сбора информации и отправку ее на сервер. Пример использования приведен ниже:
protected function foo1 ():void{
foo2 ();
}
protected function foo2 ():void{
foo3 ();
}
protected function foo3 ():void{
var blz2 : blzdebugger = new blzdebugger ('def connect 1');
blz2.warn ("X привет вася");
blz2.error ("Y пока петя");
blz2.fatal ('Z дети пришли в лес');
blz2.info("W bug or not to bug");
// вызов следующей функции в иерархии
foo4 ();
}
protected function foo4 ():void{
var blz : blzdebugger = new blzdebugger ('def connect 0');
blz.warn ("привет вася");
blz.error ("пока петя");
blz.fatal ('дети пришли в лес');
blz.info("bug or not to bug");
}
// конструктор некоторого класса, нуждающийся в отладке,
// выполняет запуск цепочки вызовов функций foo1 – foo2 – foo3 – foo4
public function ScrollerTest (){
// возможно включать и отключать подсистему отправки сообщений
// В составе класса blzdebugger есть статическая булева переменная
// IF_DEBUGGER_ACTIVE, изначально данная переменная равна true
foo1 ();
//Для отключения подсистемы присвойте этой переменной значение false
blzdebugger.IF_DEBUGGER_ACTIVE = false;
foo1 ();
}
Предупреждение: Автор целенаправленно отказался от поддержки в клиентской части рекомендуемых adobe интерфейсов ILogger, ILoggingTarget из пакета mx.logging. Дело в том, что данный пакет доступен только для flex-основанных проектов, а для flash8/9 такой поддержки нет, что сузило бы сферу применения.
Серверная часть утилиты “smalldebugger” представляет собой flex-приложение. После своего запуска она создает подключение LocalConnection и слушает сообщения от клиентов. Если создать подключение не возможно, например, вы по ошибке запустили два экземпляра данной утилиты, то будет выведено сообщение об ошибке.

После успешного запуска вы видите следующее окно утилиты:

Окно разделено на три части:
- Вверху расположена панель управления. Она состоит из падающего списка, в котором перечислены имена полученных сессий.
Выбор сессии определяется при создании клиенсткой части logger-а:
// имя первой сессии будет def connect 0
var blz : blzdebugger = new blzdebugger ('def connect 0');
// имя второй сессии будет def connect 1
var blz2 : blzdebugger = new blzdebugger ('def connect 1');

Кнопка “clear log” приводит к очистке журнала сообщений. Выводится текстовая надпись со сведениями: количество сессий, количество записей в текущей сессии и количество записей во всех сессиях. Для удобства работы добавлена на панель картинка с изображением буквы B, всякий раз, когда приходит сообщение, данная картинка начинает вращение.
Также на панели управления расположен набор checkbox-ов, управляющих параметрами фильтрации сообщений. Для каждого из типов сообщений указывается то, сколько сообщений заданного типа было “поймано”. Убирая отметки, вы управляете тем, какие сообщения будут отображены в расположенной в центральной части экрана сетке.
- Посередине “сетка” с перечислением полученных сообщений. Для каждого сообщения выводится его порядковый номер (#); тип (kind); время, прошедшее от момента получения первого сообщения (в текущей сессии) до момента получения данного сообщения(delta); собственно текст сообщения (message); имя функции, которая выбросила данное сообщение (function); имя класса, выбросившего данное сообщение (class). Стандартные возможности: сортировка отображаемой информации в сетке при клике по заголовку столбца.
- Внизу расположена панель с указанием подробных характеристик сообщения. В ней указываются все свойства видимые в сетке, но, кроме того, выводится в списке иерархия вызовов (stacktrace), приведших к генерации данного сообщения.

Также на нижней панели можно найти кнопки для запуска следующих функций:

1-ая кнопка приводит к тому, что область, занимаемая панелью свойств, становится меньше, чтобы уместить на экране большее количество сообщений в “сетке”.

2-ая кнопка служит для того, чтобы скопировать в буфер обмена код в виде html-таблицы с перечислением всего того, что вы видите на экране. Затем вы можете вставить этот текст внутрь excel, например, так:

3-я кнопка служит для того, чтобы выполнить копирование содержимого сетки (списка сообщений) внутрь сервера mysql. Здесь надо сказать сначала о настройках подключения к mysql-серверу. Здесь же на панели свойств располагается форма для ввода параметров подключения, вы должны указать имя сервера, если сервер mysql работает на не стандартном порту (не на 3306), то имя сервера должно включать в себя имя:порт, например, localhost:3307. Также вам нужно указать имя пользователя и пароль для подключения. Следующий шаг выбрать базу данных, в которой находится таблица специального вида. Именно в эту таблицу будут помещаться полученные сервером отладки записи. Если вы точно не помните имя базы, то, используя кнопку “list dbs”, вы заставите программу подключится к серверу и получить список всех баз данных, имена этих баз будут помещены в падающий список, хотя вы можете ввести в данное поле название базы и без помощи мастера.

В выбранной базе вы должны указать имя таблицы. В том случае если вы опять забыли имя таблицы, то используйте кнопку подсказки “list tables”, что приведет к тому, что в падающем списке появятся имена всех таблиц в текущей базе данных, опять таки, вы можете явно ввести имя таблицы в текстовое поле без подсказки.
Для создания таблицы журнала используется 4-ая кнопка. Если ее нажать то, будет автоматически создана таблица специальной структуры.

Будьте внимательны, если вы выбрали в списке “table” имя некоторой существующей таблицы, то она будет автоматически удалена.
Указав все параметры подключения к серверу, вы можете включить режим автоматического перенаправления всех полученных сообщений в таблицу-журнал. Используйте для этого checkbox “auto redirect”. Или же вы можете выполнять копирование записей по требованию с помощью 3-ей кнопки.
Для удобства работы вы можете сохранить введенные параметры подключения к серверу с помощью SharedObject, либо прочитать из SharedObject эти свойства. Используйте соответственно кнопки номер 5 или 6.

Последняя 6-ая кнопка просто выводит окно сообщения об авторе данной программке. Ведь страна должна знать своих героев.

package {
import flash.net.LocalConnection;
import flash.utils.describeType;
/**
Класс клиент системы отладки, служит для отправки сообщений серверу
*/
public class blzdebugger {
/**
Переменная управляющая тем активен или нет дебаг
*/
public static var IF_DEBUGGER_ACTIVE : Boolean = true;
/**
* Избирательный запрет на debug из определенных классов
* Есть два варианта запретов на уровне конкретного экземпляра объекта отладчика
* или на глобальном уровне для всех отладчиков
*/
protected var obj_disallowedClasses : Array = new Array();
protected static var stat_disallowedClasses : Array = new Array();
/**
Переменная в которой хранится строка с именем сессии
*/
private var sessionname : String = 'default';
/**
Объект LocalConnection служащий для обмена сообщениями с сервером
*/
private var sessionpipe : LocalConnection = new LocalConnection ();
/**
* Все объекты логгеры регистрируются внутри специального массива
*/
private static var logger_registry : Array = new Array ();
/**
* Функция по заданному имени логгера ищет его в списке зарегистрированных объектов
* в случае если такого логгера нет,то возвращается null
* @param loggerName Имя логгера который надо найти
*/
public static function getExistsLogger (loggerName : String):blzdebugger{
for (var i : int = 0; i < logger_registry.length; i++){
var blz : blzdebugger = logger_registry[i] as blzdebugger;
if (blz.sessionname == loggerName)
return blz;
}
return null;
}
/**
* Функция по заданному имени логгера ищет его в списке зарегистрированных объектов
* в случае если такого логгера нет, то он создается и возвращается
* @param loggerName Имя логгера который надо найти
*/
public static function getExistsOrNewLogger (loggerName : String):blzdebugger{
var blz : blzdebugger = getExistsLogger(loggerName);
if (blz != null) return blz;
blz = new blzdebugger(loggerName);
logger_registry.push(blz);
return blz;
}
/**
Конструктор класса клиента, получает в качестве параметра строку с именем сессии отладки
*/
public function blzdebugger(sessionname: String) {
this.sessionname = sessionname;
}
public function disableDebugFor (className : String):void{
if (! obj_disallowedClasses.indexOf(className))
obj_disallowedClasses.push(className);
}
public function enableDebugFor (className : String):void{
var pos : int = 0;
pos = obj_disallowedClasses.indexOf(className);
if (pos >= 0)
obj_disallowedClasses.splice(pos, 1);
}
public static function disableGlobalDebugFor (className : String):void{
if (! stat_disallowedClasses.indexOf(className))
stat_disallowedClasses.push(className);
}
public static function enableGlobalDebugFor (className : String):void{
var pos : int = 0;
pos = stat_disallowedClasses.indexOf(className);
if (pos >= 0)
stat_disallowedClasses.splice(pos, 1);
}
protected function startsWith (what : String, by : String):Boolean{
if (by == "") return true;
return what.substr(0, by.length) == by;
}
protected function isDisallowedClass (className : String):Boolean{
return _isDisallowedClass (className, obj_disallowedClasses) ||
_isDisallowedClass (className , stat_disallowedClasses)
;
}
protected function _isDisallowedClass (className : String, disallowedClasses : Array):Boolean{
//pack1.pack2::class1
//class2
//*
//class*
//pack1.pack2.*
for (var i : int = 0; i < disallowedClasses.length; i++){
var cla : String = disallowedClasses[i] as String;
var pos_star : int = cla.indexOf("*");
if (pos_star != -1)
cla = cla.substring(0, pos_star);
if (startsWith (className, cla) ) return true;
}
return false;
}
/**
Родовая функция служит для сбора информации об "происшествии" и отправляет ее серверу
*/
protected function unified_send(kind: String, msg : String, rest : Array):void {
try {
throw new Error("nullexception");
} catch (e:Error) {
var stacktraces_clean: Array = [];
var stacktraces : String = e.getStackTrace ();
var arrS : Array = stacktraces.split ("\n");
arrS.splice(0,3);
var fst_class : String, fst_function: String;
for (var i:int = 0; i < arrS.length; i++) {
var tmps : String = arrS[i];
var regexpi : Array = tmps.match(/^\s*at\s*([^ ].*)$/);
if (regexpi != null) {
tmps = regexpi [1];
if (i == 0) {
var pos_slash:int = tmps.indexOf ('/');
var pos_cash:int = tmps.indexOf ('$');
if (pos_slash != -1) {
fst_function = tmps.substr(pos_slash+1);
fst_class = tmps.substr(0, pos_slash);
}
if (pos_cash != -1) {
fst_function = tmps.substr(pos_cash+1);
fst_class = tmps.substr(0, pos_cash);
}
}
stacktraces_clean.push(tmps);
}
}
if (! isDisallowedClass (fst_class)){
var rest2 : XML = <param name="variables" />;
for (var j : int = 0; j < rest.length; j++){
var x : XML = recBuild(rest[j]);
rest2.appendChild(x);
}
sessionpipe.send(
"blz-debugserver", "debug", sessionname, kind, msg, fst_function, fst_class, stacktraces_clean, rest2);
}
}
}
/**
* Функция выполняющая форматирование объекта (все его свойства) в виде xml-документа
* @param obj Объект подлежащий форматированию
*/
protected function recBuild (obj : Object):XML{
if (obj == null) return <param name="null" />;
var x : XML = _recBuild (obj, []);
if (x.localName() == '__tostring__'){
var x2 : XML = <param name="__scalar__" />;
x2.appendChild(x.children().toString());
x = x2;
}
return x;
}
/**
* Вспомогательнаф функция выполняюща форматирование объекта в виде xml
* @param obj Объект подлежащий форматированию
* @param history История объектов. Нужна для того чтобы избежать рекурсии в ходе обработки
*/
protected function _recBuild (obj : Object, history : Array):XML{
if (obj == null) return null;
if (history.indexOf(obj) >= 0) return <prop name="#was#" />;
var x : XML = <prop />;
var cnt_props:int = 0;
for (var okey : Object in obj){
var ovalue : Object = obj[okey];
var xprop : XML = <prop />;
xprop.@name = ""+okey;
history.push(obj);
var tmp : XML = _recBuild(ovalue, history);
if (tmp == null){
xprop.appendChild("null");
}
else{
if (tmp.localName() == "__tostring__")
xprop.appendChild(tmp.children().toString());
else
xprop.appendChild(tmp);
}
history.pop();
x.appendChild(xprop);
cnt_props++;
}
if (cnt_props == 0){
x = <__tostring__ />;
x.appendChild(""+obj);
return x;
}
return x;
}
/**
Функция отправляющая сообщение типа warn, для своей работы перевызывает функцию unified_send
*/
public function warn(msg:String, ...args: Array):void {
if (IF_DEBUGGER_ACTIVE) unified_send('warn', msg, args);
}
/**
Функция отправляющая сообщение типа fatal, для своей работы перевызывает функцию unified_send
*/
public function fatal(msg:String, ...args: Array):void {
if (IF_DEBUGGER_ACTIVE) unified_send('fatal', msg, args);
}
/**
Функция отправляющая сообщение типа info, для своей работы перевызывает функцию unified_send
*/
public function info(msg:String, ...args: Array):void {
if (IF_DEBUGGER_ACTIVE) unified_send('info', msg, args);
}
/**
Функция отправляющая сообщение типа debug, для своей работы перевызывает функцию unified_send
*/
public function debug(msg:String, ...args: Array):void {
if (IF_DEBUGGER_ACTIVE) unified_send('debug', msg, args);
}
/**
Функция отправляющая сообщение типа error, для своей работы перевызывает функцию unified_send
*/
public function error(msg:String, ...args: Array):void {
if (IF_DEBUGGER_ACTIVE) unified_send('error', msg, args);
}
//----------------------------------- набор статических функций --------------------
/**
Функция отправляющая сообщение типа warn, для своей работы перевызывает функцию unified_send
*/
public static function warn(sessionName : String, msg:String, ...args : Array):void {
if (IF_DEBUGGER_ACTIVE) getExistsOrNewLogger(sessionName).unified_send('warn', msg, args);
}
/**
Функция отправляющая сообщение типа fatal, для своей работы перевызывает функцию unified_send
*/
public static function fatal(sessionName : String,msg:String, ...args: Array):void {
if (IF_DEBUGGER_ACTIVE) getExistsOrNewLogger(sessionName).unified_send('fatal', msg, args);
}
/**
Функция отправляющая сообщение типа info, для своей работы перевызывает функцию unified_send
*/
public static function info(sessionName : String,msg:String, ...args: Array):void {
if (IF_DEBUGGER_ACTIVE) getExistsOrNewLogger(sessionName).unified_send('info', msg, args);
}
/**
Функция отправляющая сообщение типа debug, для своей работы перевызывает функцию unified_send
*/
public static function debug(sessionName : String,msg:String, ...args: Array):void {
if (IF_DEBUGGER_ACTIVE) getExistsOrNewLogger(sessionName).unified_send('debug', msg, args);
}
/**
Функция отправляющая сообщение типа error, для своей работы перевызывает функцию unified_send
*/
public static function error(sessionName : String,msg:String, ...args: Array):void {
if (IF_DEBUGGER_ACTIVE) getExistsOrNewLogger(sessionName).unified_send('error', msg, args);
}
}// end of CLASS
}// end of PACKAGE
Улучшения и дополнения от 22.12.2007
Незначительно изменен интерфейс серверной части. В верхней панели выводится также текущий Security.sandboxType. Включена поддержка следующих доменов для соединения с сервером:
lc.allowDomain("*", "localhost");
lc.allowInsecureDomain("*", "localhost");

Основные изменения в клиентской части. Здесь добавлена возможность фильтрации сообщений которые отправляются на сервер на основании шаблонов имен классов (ранее можно было только включать и отключать отправку всех сообщений не избирательно). Теперь вы может для каждого из объектов логгеров по-отдельности либо для всех вместе запретить отправлять сообщения из некоторых классов. При этом возможно использовать шаблоны:
// запрет на отправку сообщений из классов начинающихся на слово Hessian и находящихся в пакете logic
// этот запрет будет действовать для всех объектов-логгеров
blzdebugger.disableGlobalDebugFor("logic::Hessian*");
blzdebugger.disableGlobalDebugFor("user_components.logic::*");
// запреты можно давать и для конкретных объектов-логгеров
var loc : blzdebugger = new blzdebugger("session_1");
loc.disableDebugFor("logic.datasets*");
loc.disableDebugFor("foo*");
// запреты можно и отменить для этого используются функции
loc.enableDebugFor("foo*");
blzdebugger.enableGlobalDebugFor("bar.tar::*");
public function info(msg:String, ...args: Array):void {
....
}
protected var logger : blzdebugger = new blzdebugger ("Analytics");
....
var chartdata : Array = data['result']['chartdata'];
var kind : String = data['result']['kind'];
// обратите на передачу двух переменных внутрь функции info
logger.info("got server response", kind, chartdata);


И ссылка на исходники проекта (там же находится скомпилированная версия файла):
https://github.com/study-and-dev-site-attachments/all-in-one/tree/master/flash/smalldebugger
(все исходники откорректированы по состоянию на 22.12.2007)
« Доступ к базам данных из php. Часть 8 | Сжатие css. Небольшая самописная утилитка » |