Flexにもログを #2

2011.10.21

前回はログAPIの基本的な使い方を確認して、実際に標準出力にログを出力してみました。今回はログターゲット(出力処理コンポーネント)を拡張して、ブラウザのコンソールにログを出力してみたいと思います。

ログターゲットの実装を確認

前回利用したTraceTargetはLineFormattedTargetを継承したクラスです。LineFormattedTargetはAbstractTargetを継承しています。実際にログターゲットを実装する前に、TraceTargetとその2つのスーパークラスの実装を確認しておきましょう。

AbstractTarget

AbstractTargetは名前の通り、ログターゲット実装の抽象クラスです。このクラスでは、ILoggingTargetインターフェースの実装として以下の機能が実装がされています。

  • 出力命令コンポーネントとの連携部分
  • 出力ログレベルと出力カテゴリのフィルタリング処理とその設定のためのプロパティ

このクラスにはログターゲットの基本的な機能の大部分が実装されているので、ログターゲットを新規に実装する際は最低でもこのクラスを継承することになると思います。

LineFormattedTarget

LineFormattedTargetには以下の機能が実装されています。

  • 出力するログのフォーマット処理
  • 出力する項目を設定するプロパティ

フォーマットされたログは、前回TraceTargetで出力されたような形になります。出力するログのフォーマットをカスタマイズしたい場合は、LineFormattedTargetまたはその拡張クラスのlogEventメソッドをオーバーライドするか、AbstractTargetを継承したクラスであらためて実装することになります。

TraceTarget

TraceTargetのソースコードの抜粋を見てみましょう。

override mx_internal function internalLog(message:String):void
{
    trace(message);
}

TraceTargetの実装は上のコードのみで、ほとんど実装がありません。internalLogメソッドをオーバーライドしてtraceを呼び出しているだけです。internalLogメソッドの引数には、LineFormattedTargetでフォーマット処理されたログメッセージが渡されています。TraceTargetはフォーマットされたメッセージをtrace関数に渡すことによって、標準出力にログを出力しています。このことから、出力先を変更したい場合はinternalLogメソッドをオーバーライドすればよいことが分かります。

ログターゲットを拡張

さて、ここまで確認してきたことを踏まえた上で、LineFormattedTargetを拡張してブラウザのコンソールにログを出力するログターゲットを作ってみましょう。以下ソースコードです。

package jp.classmethod.sample.logtarget
{
    import flash.external.ExternalInterface;
    
    import mx.core.mx_internal;
    import mx.logging.targets.LineFormattedTarget;
    
    use namespace mx_internal;
    
    public class ConsoleTarget extends LineFormattedTarget
    {
        public function ConsoleTarget()
        {
            super();
        }
        
        override mx_internal function internalLog(message:String):void
        {
            if (ExternalInterface.available)
            {
                try 
                {
                    ExternalInterface.call("console.log", message);
                } 
                catch (error:Error) 
                {
                }
            }
        }
    }
}

オーバーライドしたinternalLogメソッド内で、ExternalInterfaceを利用してブラウザのconsoleオブジェクトのlogメソッドを実行してログを出力しています。たったこれだけの実装で、ログの出力先がブラウザのコンソールであるログターゲットを作成することができました。

ブラウザのコンソールに出力

では早速、作成したログターゲットを使用してログをブラウザのコンソールに出力してみましょう。以下のサンプルは、前回のサンプルに今回作成したログターゲットを追加したものです。(FlexSDK 4.5.1.21328にて動作確認)

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
               xmlns:s="library://ns.adobe.com/flex/spark"
               xmlns:mx="library://ns.adobe.com/flex/mx"
               preinitialize="application1_preinitializeHandler(event)"
               initialize="application1_initializeHandler(event)"
               creationComplete="application1_creationCompleteHandler(event)"
               applicationComplete="application1_applicationCompleteHandler(event)">
    <fx:Script>
        <![CDATA[
            import jp.classmethod.sample.logtarget.ConsoleTarget;
           
            import mx.events.FlexEvent;
            import mx.logging.ILogger;
            import mx.logging.Log;
            import mx.logging.LogEventLevel;
            import mx.logging.targets.TraceTarget;
           
            private var logger:ILogger = Log.getLogger("LoggerTest");
           
            private function initializeLogger():void
            {
                var traceTarget:TraceTarget = new TraceTarget();
                traceTarget.level = LogEventLevel.ALL;
                traceTarget.filters = ["*"];
                traceTarget.includeDate = true;
                traceTarget.includeTime = true;
                traceTarget.includeLevel = true;
                traceTarget.includeCategory = true;
                Log.addTarget(traceTarget);
                var consoleTarget:ConsoleTarget = new ConsoleTarget();
                consoleTarget.level = LogEventLevel.ALL;
                consoleTarget.filters = ["*"];
                consoleTarget.includeDate = true;
                consoleTarget.includeTime = true;
                consoleTarget.includeLevel = true;
                consoleTarget.includeCategory = true;
                Log.addTarget(consoleTarget);
            }

            protected function application1_preinitializeHandler(event:FlexEvent):void
            {
                initializeLogger();
                logger.debug("preinitialize");
            }

            protected function application1_initializeHandler(event:FlexEvent):void
            {
                logger.info("initialize");
            }

            protected function application1_creationCompleteHandler(event:FlexEvent):void
            {
                logger.warn("creationComplete");
            }

            protected function application1_applicationCompleteHandler(event:FlexEvent):void
            {
                logger.error("applicationComplete");
            }

        ]]>
    </fx:Script>
</s:Application>

initializeLoggerメソッド内で先程作成したConsoleTargetをログ出力先として登録しています。

GoogleChromeでのサンプルの実行結果です。

GoogleChromeのコンソールにログが出力されていることが確認できると思います。また、このサンプルではTraceTargetもaddTargetされているので、標準出力にもログが出力されているはずです。IEやFirefoxなどでも同じくログが出力されると思いますので、試してみて下さい。

各ブラウザでのコンソールの利用方法はこちらを参考にして下さい。
IE8 のコンソール・ログを試す
Firebugによるデバッグの基本,Console APIとその活用
Google Chrome版Firebug:デベロッパーツール取扱説明書
SafariのWebインスペクタでJavaScriptをデバッグする方法

なお、IE7以前など一部の古いブラウザではconsoleが実装されていませんので、そういったブラウザではコンソールを利用することができません。

consoleのメソッドを呼び分ける

今回のサンプルでは、どのログレベルでもブラウザ側のconsoleオブジェクトのlogメソッドを呼び出しています。しかし、ブラウザのconsoleオブジェクトにもログレベルに応じた出力メソッドが実装されており、ブラウザによっては出力の際にログレベルに応じてアイコンや文字色などをつけて分かりやすく出力してくれます。

ブラウザ側コンソールのログレベルを使い分けたい場合は、以下のようにすることで実現可能です。

private var logLevel:int;

override public function logEvent(event:LogEvent):void
{
    logLevel = event.level;
    super.logEvent(event);
}

override mx_internal function internalLog(message:String):void
{
    if (ExternalInterface.available)
    {
        switch (logLevel)
        {
            // consoleのメソッドを呼び分ける
            case LogEventLevel.FATAL:
            case LogEventLevel.ERROR:
            case LogEventLevel.WARN:
            case LogEventLevel.INFO:
            case LogEventLevel.DEBUG:
                break;
        }
    }
}
    

ログレベルの値は、logEventメソッドの引数で渡されるLogEventのlevelプロパティに入っています。このため、logEventをオーバーライドしてスーパークラスのメソッド呼び出し前にログレベルの値を保持しておき、internalLog内でその値を判断してconsoleのメソッドを呼び分けるようにします。

ただし、debugメソッドやfatalメソッドは実装されていないブラウザがありますので、logメソッドやerrorメソッドで代替する必要があります。

まとめ

今回はログの出力先を変更する例として、ブラウザのコンソールに出力するログターゲットを作成してみました。わずかな実装で出力先を変更することができることがお分かりいただけたかと思います。なお、Adobe AIRでログを出力する方法では、Adobe AIRでローカルストレージにログを出力する方法が紹介されていますので、是非参考にして下さい。