Дебаг-сервер

Итак, опираясь на материалы о работе с XMLSocket и возможностях отладки haXe-кода делаем собственный дебаг-сервер.

Серверная часть у нас будет на Ruby, и она будет небольшая и простая:

#!/usr/bin/ruby -w
puts 'Debug Server v 0.01'
policyInfo = '<?xml version="1.0"?>'
policyInfo += '<cross-domain-policy>'
policyInfo += '<allow-access-from domain="*" to-ports="4444,80" />'
policyInfo += "</cross-domain-policy>\0"
require 'socket'
server = TCPServer.new('localhost', 4444);
while(session = server.accept)
        Thread.new(session) do |s|
                s.print policyInfo
                while(msg = s.gets)
                        msg.strip!
                        if msg == '<policy-file-request/>'
                                puts "\n =========== \n\n"
                        else
                                puts msg
                        end
                end
        end
end

Сервер просто принимает соединение, отсылает клиенту cross-domain-policy и пишит в консоль все, что присылает клиент.

Клиентская часть будет состоять из двух классов. Основной класс приложения:

/**
 * @author Yzh
 */

package com.yzh44yzh.site;

import com.yzh44yzh.core.Application;
import com.yzh44yzh.core.Debug;

class ThreeManApp extends Application
{      
        // static
        static public function main():Void { new ThreeManApp(); }

        // constructor
        public function new()
        {
                super();
                this.version = '0.1';

                Debug.Init(this.Start);
        }

        // methods
        private override function Start():Void
        {
                trace(this + ' version: ' + version);
                super.Start();
                trace('Hello world!');
                trace('Error: some error');
                trace('And some more trace');
        }

        public override function toString():String { return 'ThreeManApp'; }            
}

В конструкторе класса инициализируется дебаг. Ему отдается метод, который нужно вызывать, когда инициализация закончится (в данном случае это метод Start). Дебагу нужно некоторое время, чтобы установить соединение с сервером.

А теперь сам класс дебага:

/**
 * @author Yzh
 */

package com.yzh44yzh.core;

import flash.events.DataEvent;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.SecurityErrorEvent;
import flash.net.XMLSocket;
import flash.system.Security;
import flash.text.TextField;
import flash.text.TextFormat;

class Debug
{
        // constants
        static inline var DEFAULT:      String = '\033[00m';
        static inline var GREY:         String = '\033[01;30m';
        static inline var RED:          String = '\033[01;31m';
        static inline var GREEN:        String = '\033[00;32m';
        static inline var YELLOW:       String = '\033[01;33m';
        static inline var BLUE:         String = '\033[00;34m';
        static inline var MAGENTA:      String = '\033[00;35m';
        static inline var CYAN:         String = '\033[00;36m';

        // static vars
        static private var instance:    Debug;

        // static methods
        static public function Init(ConnectHandler:Void->Void):Void
        {
                Debug.instance = new Debug();
                Debug.instance.ConnectHandler = ConnectHandler;
                Debug.instance.Connect();

                haxe.Log.trace = Debug.Trace;
        }

        static public function Trace(data:Dynamic, ?pos:haxe.PosInfos):Void
        {
                var msg = data.toString();
                if(Debug.instance.connected)
                {
                        var data = BLUE;
                        data += pos.className + '.' + pos.methodName + ':' + pos.lineNumber + ':\n';
                        data += if(msg.toLowerCase().indexOf('error') != -1) RED else YELLOW;
                        data += msg.toString() + '\n' + DEFAULT;
                        Debug.instance.socket.send(data);
                }
                else
                {
                        Debug.instance.Display('no connection to debug-server\n' + msg);
                }
        }
       
        // properties
        private var connected:          Bool;
        private var socket:             XMLSocket;
        private var tfDisplay:          TextField;
        private var ConnectHandler:     Void->Void;

        // constructor
        public function new() { }

        // methods
        private function Display(msg:String):Void
        {
                if(this.tfDisplay == null)
                {
                        this.tfDisplay = new TextField();
                        this.tfDisplay.defaultTextFormat = new TextFormat(null, 24, 0xff0000);
                        this.tfDisplay.autoSize = 'left';
                        this.tfDisplay.border = true;
                        this.tfDisplay.text = '';
                        flash.Lib.current.addChild(this.tfDisplay);
                }
                this.tfDisplay.text += msg + '\n';
        }

        private function Connect():Void
        {
                this.connected = false;
                try
                {
                        Security.loadPolicyFile('xmlsocket://localhost:4444');

                        this.socket = new XMLSocket('localhost', 4444);
                        this.socket.addEventListener(Event.CONNECT, OnConnect);
                        this.socket.addEventListener(Event.CLOSE, OnClose);
                        this.socket.addEventListener(IOErrorEvent.IO_ERROR, OnIOError);
                        this.socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, OnSecurityError);
                }
                catch(e:Dynamic) { this.Display(e.toString()); }
        }

        private function OnConnect(event:Event):Void
        {
                this.connected = true;
                this.ConnectHandler();
        }

        private function OnClose(event:Event):Void
        {
                this.Display('lost connection to debug-server');
        }

        private function OnIOError(event:IOErrorEvent):Void
        {
                this.Display(event.toString());
        }

        private function OnSecurityError(event:SecurityErrorEvent):Void
        {
                this.Display(event.toString());
        }

        public function toString():String { return 'Debug'; }          
}

Здесь все немного сложнее. Во-первых, что будет, если дебаг-сервер не доступен (не запущен)? Нужно как-то сообщить об этом и как-то показывать сообщения. Поэтому у нас будет резервная система. Эту роль выполняет метод Display
        private function Display(msg:String):Void
        {
                if(this.tfDisplay == null)
                {
                        this.tfDisplay = new TextField();
                        this.tfDisplay.defaultTextFormat = new TextFormat(null, 24, 0xff0000);
                        this.tfDisplay.autoSize = 'left';
                        this.tfDisplay.border = true;
                        this.tfDisplay.text = '';
                        flash.Lib.current.addChild(this.tfDisplay);
                }
                this.tfDisplay.text += msg + '\n';
        }

Он создает текстовое поле в корневом мувике (если оно не создано), и выводит сообщения туда.

Все начинается с метода Init

        static public function Init(ConnectHandler:Void->Void):Void
        {
                Debug.instance = new Debug();
                Debug.instance.ConnectHandler = ConnectHandler;
                Debug.instance.Connect();

                haxe.Log.trace = Debug.Trace;
        }

Здесь создается экземпляр класса Debug. Сохраняется ссылка на метод, который нужно будет вызвать, когда Debug будет готов к работе, устанавливается соединение с дебаг-сервером и переопределяется haxe.Log.trace

Установка соединения уже рассматривалась в статье про XMLSocket. После того, как соединение установлено, вызывается ConnectHandler, которым, в данном случае, является метод TreeManApp.Start.

Наконец, метод Trace, который заменяет собой haxe.Log.trace.

        static public function Trace(data:Dynamic, ?pos:haxe.PosInfos):Void
        {
                var msg = data.toString();
                if(Debug.instance.connected)
                {
                        var data = BLUE;
                        data += pos.className + '.' + pos.methodName + ':' + pos.lineNumber + ':\n';
                        data += if(msg.toLowerCase().indexOf('error') != -1) RED else YELLOW;
                        data += msg.toString() + '\n' + DEFAULT;
                        Debug.instance.socket.send(data);
                }
                else
                {
                        Debug.instance.Display('no connection to debug-server\n' + msg);
                }
        }

Если нет соединения с дебаг-сервером, сообщение передается методу Display. А в нормальном режиме формируется сообщение и отправляется дебаг-серверу. Сообщение раскрашивается цветами: PosInfo -- синим, msg -- желтым. Если сообщение содержит подскроку "error", то оно окрашивается красным. Для этого используются коды терминала. Это работает только в Linux, в Windows терминал, к сожалению, нельзя расскрасить разными цветами.

Вот и все. В одном окне открываем терминал и запускаем дебаг-сервер, в другом окне (в браузере или в stand-alone плеере) запускаем флэшку. Любуемся сообщениями в терминале. В аттаче пара скриншотов, как это выглядит у меня.

Комментарии

Раскраска терминала Windows

Терминал Windows можно легко раскрасить. Для этого достаточно установить поддержку кодов ANSI. Для этого ставим AnsiCon (http://adoxa.110mb.com/ansicon/index.html). Далее распаковываем куда угодно, после чего запускаем ansicon.exe -i (ansicon.exe с параметром -i, т.е. install).
После этого текст прекрасно раскрашивается и под Windows. Проверено под Win XP, но авторы AnsiCon вроде обещают и поддержку Win 7.

С уважением, Алексей.

Настройки просмотра комментариев

Выберите нужный метод показа комментариев и нажмите "Сохранить установки".

Отправить комментарий

Содержание этого поля является приватным и не предназначено к показу.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <blockcode> <dd>
  • Строки и параграфы переносятся автоматически.
  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • You can enable syntax highlighting of source code with the following tags: <code>, <blockcode>.

Подробнее о форматировании

CAPTCHA
Этот вопрос помогает предотвратить автоматический спам
Image CAPTCHA
Enter the characters shown in the image without spaces, also respect upper and lower case.
To prevent automated spam submissions leave this field empty.