Edited at

Power Query のトレースログ を Power Query でパースしてみた

Power Query のトレースログをパースしてみたなど。そのまま眺めても何となく見えてくるのだけど整理された状態で見えたらよいよね。コミュニティイベントでチラ見せしたものを後生大事に塩漬けしててもしょうがないので公開しておこう。修行のお供にでも。


パーサー本体

トレースログ出力先は [Power Query エディター] ⇒ [オプションと設定] ⇒ [診断]

トレース開始は [トレースを有効にする] を ON


解析、リファクタリングなどお好きにどうぞ

let

Source = Folder.Contents(" << >> "),
FilteredRows = Table.SelectRows(Source, each Text.Lower([Extension]) = ".log"),
Content = List.Combine(List.Transform(FilteredRows[Content], Lines.FromBinary)),
TableFromContent = Table.FromColumns({Content}, {"Value"}),
SplitColumnByDelimiter1 = Table.SplitColumn(
TableFromContent,
"Value",
Splitter.SplitTextByEachDelimiter(
{":"},
QuoteStyle.None,
false
),
{"Value.1", "Value.2"}
),
SplitColumnByDelimiter2 = Table.SplitColumn(
SplitColumnByDelimiter1,
"Value.2",
Splitter.SplitTextByEachDelimiter(
{":"},
QuoteStyle.None,
false
),
{"Value.2", "Value.3"}
),
TryParseJSON = Table.TransformColumns(
SplitColumnByDelimiter2,
{
"Value.3",
each List.Last(
List.Generate(
()=>[SourceText = _, Condition = true, Limit = 0],
each [Condition] and [Limit] <= 5,
each [
Limit = [Limit] + 1,
_try = try Json.Document([SourceText]),
Condition = _try[HasError],
_field = Text.BetweenDelimiters(_try[Error][Message], "'", "'"),
_SourceText = Text.Split([SourceText], """" & _field & """"),
SourceText = Text.Combine(
List.Combine(
List.Zip(
{
_SourceText,
List.Transform(
{1 .. List.Count(_SourceText) - 1},
each """" & _field & Text.From(_) & """"
)
}
)
)
)
],
each [SourceText]
)
)
}
),
ParsedJSON = Table.TransformColumns(
TryParseJSON,
{"Value.3", Json.Document}
),
RemovedErrors = Table.RemoveRowsWithErrors(ParsedJSON),
// ここで展開するフィールドを調整
ExpandedValue3 = Table.ExpandRecordColumn(
RemovedErrors,
"Value.3",
{"Start", "Action", "Duration", "Message", "RequestUri"},
{"Start", "Action", "Duration", "Message", "RequestUri"}
),
ChangedType = Table.TransformColumnTypes(
ExpandedValue3,
{
{"Start", type text},
{"Action", type text},
{"Duration", type text},
{"Message", type text}
}
),
SortedRows = Table.Sort(ChangedType,{{"Start", Order.Ascending}}),
AddedIndex = Table.AddIndexColumn(SortedRows, "Index", 1, 1)
in
AddedIndex


ざっくり注釈


  • Folder.Contents の引数には、トレースログが出力されるパス。Folder.Files ではないので サブフォルダーのファイルは見えません。

  • ファイル拡張子 ".log" でフィルタしパースしないファイルを除外。

  • ログファイル (binary)ごとに Lines.FromBinary でテキスト (text)へ変換しつつリスト (list)にして、それらを List.Combine でひとつのリスト (list) におまとめ

  • このあと列 (column)の分解などするので テーブル (table) に変換

  • おまとめ済みリストのアイテムごとに見たとき、デリミタ ":" で 3つに展開できそうなのがわかるので、さきにふたつ抽出。このとき、QuoteStyle.None にしておかないと困ることが発生する。

  • 残る最後の列 (column)をみたとき、もしかして JSON ? で、Json.Document でパースすると Expression.Error が頻発。調査したら フィールド名が重複することがわかったので、フィールド名をリネームしながら Expression.Error が発生しなくなるまで List.Generate で繰り返し

  • おおむね JSONとしてパースしても問題ない状態なったのでテーブル(table)になるよう展開

  • 時系列でソートしインデックスをつけておいた


思ったこと🙄

Power BI Desktop のログは Excel で、Excel のログは Power BI Desktop で 眺めた方が楽かも。

どこまで細かに把握できるかはわからない。ログのフォーマットはいつか変わるかもしれないし。でも、どのように処理がされるのかヒントを得ることは可能じゃないかな。Diagnostics.Trace については別にポストしようか。


その他