Proj smallflexdebugger

Материал из Dom.

Перейти к: навигация, поиск

[править] Отладь flex/flash код вместе с Smalldebugger

Утилита “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 ();
}

Методы класса warn, error, fatal, info, debug служат для отправки сообщений заданного типа серверу.

Предупреждение: Автор целенаправленно отказался от поддержки в клиентской части рекомендуемых adobe интерфейсов ILogger, ILoggingTarget из пакета mx.logging. Дело в том, что данный пакет доступен только для flex-основанных проектов, а для flash8/9 такой поддержки нет, что сузило бы сферу применения.


Серверная часть утилиты “smalldebugger” представляет собой flex-приложение. После своего запуска она создает подключение LocalConnection и слушает сообщения от клиентов. Если создать подключение не возможно, например, вы по ошибке запустили два экземпляра данной утилиты, то будет выведено сообщение об ошибке.

Изображение:small_debugger_1.png

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

Изображение:small_debugger_2.png

Окно разделено на три части:

- Вверху расположена панель управления. Она состоит из падающего списка, в котором перечислены имена полученных сессий. Выбор сессии определяется при создании клиенсткой части logger-а:

// имя первой сессии будет def connect 0
   var blz : blzdebugger = new blzdebugger ('def connect 0'); 
   // имя второй сессии будет def connect 1
   var blz2 : blzdebugger = new blzdebugger ('def connect 1');

Все полученные сесии выводятся в падающем списке:

Изображение:small_debugger_3.png

Кнопка “clear log” приводит к очистке журнала сообщений. Выводится текстовая надпись со сведениями: количество сессий, количество записей в текущей сессии и количество записей во всех сессиях. Для удобства работы добавлена на панель картинка с изображением буквы B, всякий раз, когда приходит сообщение, данная картинка начинает вращение.

Также на панели управления расположен набор checkbox-ов, управляющих параметрами фильтрации сообщений. Для каждого из типов сообщений указывается то, сколько сообщений заданного типа было “поймано”. Убирая отметки, вы управляете тем, какие сообщения будут отображены в расположенной в центральной части экрана сетке.

- Посередине “сетка” с перечислением полученных сообщений. Для каждого сообщения выводится его порядковый номер (#); тип (kind); время, прошедшее от момента получения первого сообщения (в текущей сессии) до момента получения данного сообщения(delta); собственно текст сообщения (message); имя функции, которая выбросила данное сообщение (function); имя класса, выбросившего данное сообщение (class). Стандартные возможности: сортировка отображаемой информации в сетке при клике по заголовку столбца.

- Внизу расположена панель с указанием подробных характеристик сообщения. В ней указываются все свойства видимые в сетке, но, кроме того, выводится в списке иерархия вызовов (stacktrace), приведших к генерации данного сообщения.

Изображение:small_debugger_4.png

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

Изображение:small_debugger_4a.png

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

Изображение:small_debugger_5.png

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

Изображение:small_debugger_6.png

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

Изображение:small_debugger_7.png

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

Изображение:small_debugger_8.png

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

Изображение:small_debugger_9.png

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

Изображение:small_debugger_10.png

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");

Также была добавлена кнопка "Clear All" очищающая журналы для всех сессий, а не только для текущей как работала ранее кнопка "Clear Log".

Изображение:addon_debugger_2.png

Основные изменения в клиентской части. Здесь добавлена возможность фильтрации сообщений которые отправляются на сервер на основании шаблонов имен классов (ранее можно было только включать и отключать отправку всех сообщений не избирательно). Теперь вы может для каждого из объектов логгеров по-отдельности либо для всех вместе запретить отправлять сообщения из некоторых классов. При этом возможно использовать шаблоны:

// запрет на отправку сообщений из классов начинающихся на слово 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::*");

Следующее крупное улучшение - возможность отправлять не только текстовое сообщение на сервер, но и произвольные переменные, для этого функции warn, info, debug, fatal и другие были расширены так чтобы могли получать кроме текста сообщения произвольный список переменных:

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);

Переменные будут преобразованы в xml-документ и отправлены на сервер. Для того чтобы отобразить эту информацию были сделаны небольшие правки интерфейса: панель свойств экспорта сообщений в mysql-сервер была перемещена, а вместо ее отображается дерево с содержимым переменных:

Изображение:addon_debugger_1.png

Изображение:addon_debugger_4.png

[править] И ссылка на исходники проекта (серверная часть):

http://black-zorro.com/sources/flash/smalldebugger/index.html

Загрузить скомпилированный swf-файл с серверной частью

http://black-zorro.com/sources/flash/smalldebugger/debugserver.swf

(все исходники откорректированы по состоянию на 22.12.2007)

Subscribe Now!

...

ObMachine projects & articles (java, flash, flex, php, ...)  -- black-zorro.com


Личные инструменты
Навигация