Skip to content

Commit d45b30d

Browse files
committed
feat: add trace_semantics check
1 parent 2f88046 commit d45b30d

File tree

4 files changed

+55
-10
lines changed

4 files changed

+55
-10
lines changed

ddapm_test_agent/agent.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
from .trace_checks import CheckTraceDDService
5252
from .trace_checks import CheckTracePeerService
5353
from .trace_checks import CheckTraceStallAsync
54+
from .trace_checks import CheckTraceSemantics
5455
from .tracerflare import TracerFlareEvent
5556
from .tracerflare import v1_decode as v1_tracerflare_decode
5657
from .tracestats import decode_v06 as tracestats_decode_v06
@@ -646,6 +647,9 @@ async def _handle_traces(self, request: Request, version: Literal["v0.4", "v0.5"
646647
await checks.check(
647648
"trace_peer_service", span=span, dd_config_env=request.get("_dd_trace_env_variables", {})
648649
)
650+
await checks.check(
651+
"trace_semantics", span=span
652+
)
649653

650654
await checks.check(
651655
"trace_dd_service", trace=trace, dd_config_env=request.get("_dd_trace_env_variables", {})
@@ -1051,12 +1055,13 @@ def make_app(
10511055
)
10521056
checks = Checks(
10531057
checks=[
1054-
CheckMetaTracerVersionHeader,
1055-
CheckTraceCountHeader,
1056-
CheckTraceContentLength,
1057-
CheckTraceStallAsync,
1058-
CheckTracePeerService,
1059-
CheckTraceDDService,
1058+
CheckMetaTracerVersionHeader(),
1059+
CheckTraceCountHeader(),
1060+
CheckTraceContentLength(),
1061+
CheckTraceStallAsync(),
1062+
CheckTracePeerService(),
1063+
CheckTraceDDService(),
1064+
CheckTraceSemantics(),
10601065
],
10611066
enabled=enabled_checks,
10621067
)

ddapm_test_agent/checks.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
from typing import Generator
99
from typing import List
1010
from typing import Tuple
11-
from typing import Type
1211

1312

1413
CHECK_TRACE: contextvars.ContextVar["CheckTrace"] = contextvars.ContextVar("check_trace")
@@ -181,10 +180,10 @@ def check(self, *args, **kwargs):
181180

182181
@dataclasses.dataclass()
183182
class Checks:
184-
checks: List[Type[Check]] = dataclasses.field(init=True)
183+
checks: List[Check] = dataclasses.field(init=True)
185184
enabled: List[str] = dataclasses.field(init=True)
186185

187-
def _get_check(self, name: str) -> Type[Check]:
186+
def _get_check(self, name: str) -> Check:
188187
for c in self.checks:
189188
if c.name == name:
190189
return c
@@ -199,7 +198,7 @@ def is_enabled(self, name: str) -> bool:
199198

200199
async def check(self, name: str, *args: Any, **kwargs: Any) -> None:
201200
"""Find and run the check with the given ``name`` if it is enabled."""
202-
check = self._get_check(name)()
201+
check = self._get_check(name)
203202

204203
if self.is_enabled(name):
205204
# Register the check with the current trace

ddapm_test_agent/trace_checks.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import asyncio
22
import json
33
import logging
4+
import requests
45
from typing import Dict
56
from typing import List
67

78
from aiohttp.web import Request
89
from multidict import CIMultiDictProxy
10+
from jsonschema import validate, ValidationError
911

1012
from .checks import Check
1113
from .trace import Span
@@ -221,3 +223,41 @@ def check(self, trace: List[Span], dd_config_env: Dict[str, str]) -> None:
221223
else:
222224
log.debug(f"Successfully completed ``service`` name Span Check for Span: {span['name']}")
223225
return
226+
227+
228+
SCHEMA_URL = "https://raw.githubusercontent.com/DataDog/schema/main/semantic-core/v1/schema.json"
229+
230+
231+
class CheckTraceSemantics(Check):
232+
name = "trace_semantics"
233+
description = """
234+
The trace should follow semantic conventions defined by semantic-core.
235+
More info here: https://github.com/datadog/semantic-core.
236+
""".strip()
237+
schema = None
238+
239+
def __init__(self):
240+
log.debug("Initializing CheckTraceSemantics")
241+
242+
resp = requests.get(SCHEMA_URL)
243+
if resp.status_code != 200:
244+
log.fatal(f"Failed to download trace semantics schema: {resp}")
245+
246+
log.debug("Successfully downloaded semantic-core JSON Schema")
247+
248+
self.schema = resp.json()
249+
super().__init__()
250+
251+
def check(self, span: Span) -> None:
252+
log.info("Performing ``Trace Semantics`` Span Check")
253+
254+
try:
255+
validate(instance=span, schema=self.schema)
256+
log.debug(
257+
f"Span Check ``trace_semantics`` succeeded for Span {span['name']}"
258+
)
259+
except ValidationError as err:
260+
self.fail(
261+
json.dumps(span, indent=4)
262+
+ f"\nSpan '{span['name']}' failed semantic validation: {err}."
263+
)

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"requests",
3434
"typing_extensions",
3535
"yarl",
36+
"jsonschema",
3637
],
3738
tests_require=testing_deps,
3839
setup_requires=["setuptools_scm"],

0 commit comments

Comments
 (0)