Bug Description
event_to_span() はイベントを属性アクセスでしか読みません。dict を渡すと getattr が既定値を返すため、エラーにならず全項目が既定値の span が黙って作られます。
同じ dict を pyetwkit.export の関数に渡すと正しく処理されます。export.py には _event_to_dict() があり、オブジェクトと dict の両方を受けるためです。エクスポータ同士で挙動が食い違っています。
from types import SimpleNamespace
from pyetwkit.exporters import event_to_span
from pyetwkit.export import to_json
ev = {
"provider_name": "Microsoft-Windows-Kernel-Process",
"event_id": 1,
"process_id": 4104,
"properties": {"ImageName": "cmd.exe"},
}
event_to_span(ev)
# name = 'unknown.0'
# attrs = etw.provider='unknown', etw.event_id=0, process.pid=0
event_to_span(SimpleNamespace(**ev, thread_id=0, timestamp=1788613764.0))
# name = 'Microsoft-Windows-Kernel-Process.1'
# attrs = etw.provider='Microsoft-Windows-Kernel-Process', etw.event_id=1, process.pid=4104
to_json([ev]) # 同じ dict を正しく処理する
実際の EtwEvent を使う限り問題ありません。困るのは、記録した dict を読み込んで送る場合や、テストで dict を使う場合です。
SpanMapper.get_span_name() と extract_attributes()、event_to_log() も同じ getattr のみの実装です。
Steps to Reproduce
provider_name / event_id などを持つ dict を用意する
event_to_span(その dict) を呼ぶ
- 返ってきた span の
name と attributes を見る
Expected Behavior
dict でも属性を持つオブジェクトでも同じ span になること。export.py の _event_to_dict() と同じ扱いをするのが自然です。
dict を受け付けない設計にするなら、黙って既定値を返すのではなく TypeError を投げるべきです。現在の「静かに間違った結果を返す」だけは避けたいという趣旨の Issue です。
Actual Behavior
dict を渡すと unknown.0 という名前の、PID も provider も空の span が生成されます。例外も警告も出ません。
Environment
- OS: Windows 11 (10.0.26200)
- Python version: 3.12.10
- PyETWkit version: 3.1.0 (main,
b696112)
- Rust toolchain: 1.98.1
Error Log / Stack Trace
(エラーは出ません。それが問題です)
dict -> name='unknown.0'
attrs= [('etw.provider', {'stringValue': 'unknown'}), ('etw.event_id', {'intValue': 0}), ('process.pid', {'intValue': 0})]
object-> name='Microsoft-Windows-Kernel-Process.1'
attrs= [('etw.provider', {'stringValue': 'Microsoft-Windows-Kernel-Process'}), ('etw.event_id', {'intValue': 1}), ('process.pid', {'intValue': 4104})]
Minimal Reproduction Code
上記「Bug Description」のコードがそのまま再現コードです。
Related ETW Provider (if applicable)
- Provider name: 該当なし(エクスポータ側の問題)
- Event ID: 該当なし
Test Case Proposal (TDD)
from types import SimpleNamespace
import pytest
from pyetwkit.exporters import event_to_log, event_to_span
EVENT_FIELDS = {
"provider_name": "Microsoft-Windows-Kernel-Process",
"event_id": 1,
"process_id": 4104,
"thread_id": 512,
"timestamp": 1788613764.0,
"properties": {"ImageName": "cmd.exe"},
}
def test_event_to_span_reads_a_dict_the_same_as_an_object() -> None:
"""dict とオブジェクトで同じ span になること。現状は失敗する。"""
from_dict = event_to_span(dict(EVENT_FIELDS))
from_object = event_to_span(SimpleNamespace(**EVENT_FIELDS))
assert from_dict["name"] == from_object["name"]
def attrs(span):
return {a["key"]: a["value"] for a in span["attributes"]}
assert attrs(from_dict)["etw.provider"] == attrs(from_object)["etw.provider"]
assert attrs(from_dict)["etw.event_id"] == attrs(from_object)["etw.event_id"]
assert attrs(from_dict)["process.pid"] == attrs(from_object)["process.pid"]
def test_event_to_log_reads_a_dict_too() -> None:
from_dict = event_to_log(dict(EVENT_FIELDS))
from_object = event_to_log(SimpleNamespace(**EVENT_FIELDS))
assert from_dict["timeUnixNano"] == from_object["timeUnixNano"]
def test_span_mapper_matches_a_dict() -> None:
from pyetwkit.exporters import SpanMapper
mapper = SpanMapper()
mapper.add_rule(
provider="Microsoft-Windows-Kernel-Process",
event_id=1,
span_name="process.start",
attributes=["ImageName"],
)
assert mapper.get_span_name(dict(EVENT_FIELDS)) == "process.start"
assert mapper.extract_attributes(dict(EVENT_FIELDS)) == {"ImageName": "cmd.exe"}
def test_something_that_is_not_an_event_is_rejected() -> None:
"""既定値の span を黙って返すのではなく、はっきり失敗すること。"""
with pytest.raises(TypeError):
event_to_span("not an event")
Additional Context
Bug Description
event_to_span()はイベントを属性アクセスでしか読みません。dictを渡すとgetattrが既定値を返すため、エラーにならず全項目が既定値の span が黙って作られます。同じ
dictをpyetwkit.exportの関数に渡すと正しく処理されます。export.pyには_event_to_dict()があり、オブジェクトとdictの両方を受けるためです。エクスポータ同士で挙動が食い違っています。実際の
EtwEventを使う限り問題ありません。困るのは、記録したdictを読み込んで送る場合や、テストでdictを使う場合です。SpanMapper.get_span_name()とextract_attributes()、event_to_log()も同じgetattrのみの実装です。Steps to Reproduce
provider_name/event_idなどを持つdictを用意するevent_to_span(その dict)を呼ぶnameとattributesを見るExpected Behavior
dictでも属性を持つオブジェクトでも同じ span になること。export.pyの_event_to_dict()と同じ扱いをするのが自然です。dictを受け付けない設計にするなら、黙って既定値を返すのではなくTypeErrorを投げるべきです。現在の「静かに間違った結果を返す」だけは避けたいという趣旨の Issue です。Actual Behavior
dictを渡すとunknown.0という名前の、PID も provider も空の span が生成されます。例外も警告も出ません。Environment
b696112)Error Log / Stack Trace
Minimal Reproduction Code
上記「Bug Description」のコードがそのまま再現コードです。
Related ETW Provider (if applicable)
Test Case Proposal (TDD)
Additional Context
export.pyの_event_to_dict()が既に両方の形を扱っており、同じ方針をexporters/otlp.pyにも適用すれば揃いますdictを渡していたため実質「既定値だけの span」を検証していたことが分かりました。そのテストはSimpleNamespaceに置き換え済みですが、ライブラリ側の不整合は残っています