1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Revitアドインのデバッグログをキャプチャする方法

Last updated at Posted at 2024-09-08

はじめに

Revitアドインの開発中にConsole.WriteLineDebug.Printでデバッグログを表示したいと思ったことはありませんか?
この記事では、Revit本体やアドインが出力するデバッグログをキャプチャする方法を紹介します。コードをコピペしたい方は デバッグログのキャプチャと表示 を確認してください。

スクリーンショット 2024-09-08 232942.png

なお、コンソールログを表示・検索するツール "ConsoleCapture" がAutodesk App Storeで無料ダウンロードできます。

デバッグログの出力

デバッグログの出力には、主にConsoleクラスを使う方法と、Debug/Traceクラスを使う方法があります。

Consoleクラス

Consoleログはプログラムとして標準出力や標準エラー出力にメッセージを表示する必要がある場合に使います。

// メッセージを標準出力に表示し改行
Console.WriteLine("ConsoleWriteLine"); // 出力: ConsoleWriteLine

// メッセージを標準出力に表示(改行なし)
Console.Write("Console"); Console.Write("Write"); // 出力: ConsoleWrite

// 標準エラー出力に表示し改行
Console.Error.WriteLine("Error message"); // 出力: Error message

Traceクラス

Traceログは製品版でも出力する可能性があるトレースログ出力に使います。標準出力には表示されずトレースリスナー(ログファイルやデバッグウィンドウなど)にメッセージを送ります。

// メッセージを出力ウィンドウに表示し改行
Trace.WriteLine("TraceWriteLine"); // 出力: TraceWriteLine

// メッセージを表示(改行なし)
Trace.Write("Trace"); Trace.Write("Write"); // 出力: TraceWrite

// メッセージをフォーマットして表示し改行
Trace.TraceInformation("Trace.{0}", "Information"); // 出力: Trace.Information

Debugクラス

Debugログはデバッグ時のみ必要なレベルのログ出力に使います。標準出力には表示されずデバッグリスナー(Visual Studioの出力ウィンドウなど)にメッセージを送ります。デバッグビルドでのみ有効になりリリースビルドでは無効になります。

// メッセージを表示し改行
Debug.WriteLine("DebugWriteLine"); // 出力: DebugWriteLine

// メッセージを表示(改行なし)
Debug.Write("Debug"); Debug.Write("Write"); // 出力: DebugWrite

// メッセージをフォーマットして表示し改行
Debug.Print("Debug.{0}", "Print"); // 出力: Debug.Print

デバッグログのキャプチャと表示

RevitはGUIアプリケーションであるため、コンソールアプリケーションのようにデバッグログを表示することはできません。そのため、標準出力やDebug/Traceリスナーをキャプチャして表示するツールを実装する必要があります。以下にWPF / MVVMパターンで実装した最小限のサンプルコードを示します。

Model

まず、TextWriterを継承してコンソールやデバッグ出力をキャプチャするクラスを用意します。このクラスでは、メッセージを受け取るとバッファに保存し、改行を検出したタイミングでまとめてキャプチャします。キャプチャしたメッセージはLogCapturedイベントを介してViewModelに通知されます。

ConsoleWriter.cs
using System;
using System.IO;
using System.Text;

namespace Addin.Model
{
    public class ConsoleWriter : TextWriter
    {
        private readonly StringBuilder _buffer = new StringBuilder();
        public event Action<string> LogCaptured;

        public ConsoleWriter() {}

        public override Encoding Encoding => Encoding.UTF8;

        // Writeメソッドのオーバーライド(改行なし)
        public override void Write(string value)
        {
            _buffer.Append(value);
            if (value.Contains(Environment.NewLine))
            {
                FlushBuffer();
            }
        }

        // WriteLineメソッドのオーバーライド(1行単位)
        public override void WriteLine(string value)
        {
            _buffer.AppendLine(value);
            FlushBuffer();
        }

        // バッファの内容をフラッシュしてイベントを発生させる
        private void FlushBuffer()
        {
            var completeMessage = _buffer.ToString().TrimEnd();
            _buffer.Clear();
            LogCaptured?.Invoke(completeMessage);
        }
    }
}

ViewModel

次に、メッセージの入れ物となるクラスを用意します。

LogEntry.cs
using System;

namespace Addin.Model
{
    public class LogEntry
    {
        public string Message { get; set; }
        public int Index { get; set; }
    }
}

続いて、Modelからのイベントを受け取り、LogEntryのコレクションとしてログデータを保持するクラスを用意します。DebugとTraceはイベントリスナーを共有しているため、Debug.Listenersのみにリスナーを追加すれば両方の出力をキャプチャできます。

ConsolePageVM.cs
using Addin.Model;
using System.Collections.ObjectModel;
using System.Diagnostics;

namespace Addin.ViewModel
{
    internal class ConsolePageVM : ViewModelBase
    {
        public ObservableCollection<LogEntry> Logs { get; set; }

        public ConsolePageVM()
        {
            Logs = new ObservableCollection<LogEntry>();
            SetupConsoleWriters();
        }

        // ConsoleWriterの初期設定
        private void SetupConsoleWriters()
        {
            var consoleWriter = new ConsoleWriter();
            consoleWriter.LogCaptured += OnLogCaptured;
            Console.SetOut(consoleWriter);
            Console.SetError(consoleWriter);
            var listener = new TextWriterTraceListener(consoleWriter);
            Debug.Listeners.Add(listener);
        }

        // ログキャプチャイベントハンドラー
        private void OnLogCaptured(string message)
        {
            App.Current.Dispatcher.Invoke(() =>
            {
                Logs.Add(new LogEntry
                {
                    Index = Logs.Count + 1,
                    Message = message
                });
            });
        }
    }
}

View

最後に、DataGridを使ってViewModelのLogsを表示する画面をつくります。

ConsolePage.xaml
<Page x:Class="Addin.Views.ConsolePage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:vm="clr-namespace:Addin.ViewModel"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      Title="Console Capture">
      
    <Page.DataContext>
        <vm:ConsolePageVM/>
    </Page.DataContext>
    
    <Grid>
        <DataGrid ItemsSource="{Binding Logs}" AutoGenerateColumns="False">
            <DataGrid.Columns>
                <DataGridTextColumn Header="Roq" Binding="{Binding Index}" Width="Auto"/>
                <DataGridTextColumn Header="Message" Binding="{Binding Message}" Width="*"/>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Page>

実行

試しに以下のようなコマンドを作成してデバッグログを出力してみます。

Dummy.cs
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace Addin.Commands
{
    [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
    internal class Dummy : IExternalCommand
    {
        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
        {
            try
            {
                Console.WriteLine("Console.WriteLine");
                Console.Write("Console"); Console.Write("Write"); Console.Write(Environment.NewLine);
                Console.Error.WriteLine("Console.Error.WriteLine");
                
                Debug.WriteLine("Debug.WriteLine");
                Debug.Write("Debug."); Debug.Write("Write"); Debug.Write(Environment.NewLine);
                Debug.Print("Debug.{0}", "Print");
                
                Trace.WriteLine("Trace.WriteLine");
                Trace.Write("Trace."); Trace.Write("Write"); Trace.Write(Environment.NewLine);
                
                var dict = new Dictionary<string, Dictionary<string, string>>()
                {
                    {
                        "key1",
                        new Dictionary<string, string>
                        {
                            { "key1-1", "value1-1" },
                            { "key1-2", "value1-2" }
                        }
                    },
                    {
                        "key2",
                        new Dictionary<string, string>
                        {
                            { "key2-1", "value2-1" },
                            { "key2-2", "value2-2" }
                        }
                    },
                };
                var json = System.Text.Json.JsonSerializer.Serialize(dict, new System.Text.Json.JsonSerializerOptions { WriteIndented = true });
                Console.WriteLine(json);

                return Result.Succeeded;
            }

            catch (Exception ex)
            {
                // コマンドが途中で失敗した際のエラー
                TaskDialog.Show("ERROR", ex.Message);
                return Result.Failed;
            }
        }
    }
}

以下のようにデバッグログが表示されます。System.Text.JsonでJSONをシリアル化する際にWriteIndented = trueオプションを設定することでJSONも整形された状態で表示できます。

image.png

参考リンク

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?