Skip to content

Commit 8f7594f

Browse files
committed
more telemetry
1 parent cf5a003 commit 8f7594f

3 files changed

Lines changed: 122 additions & 39 deletions

File tree

src/enochecker3/chaindb.py

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from typing import Any, Optional
22

3+
from opentelemetry import trace
4+
from opentelemetry.trace import get_current_span
35
from pymongo.asynchronous.collection import AsyncCollection
46

57

@@ -9,26 +11,38 @@ def __init__(self, collection: AsyncCollection, task_chain_id: str):
911
self.task_chain_id: str = task_chain_id
1012

1113
async def get(self, key: str) -> Any:
12-
val: Optional[Any] = await self.collection.find_one(
13-
{
14-
"task_chain_id": self.task_chain_id,
15-
"key": key,
16-
}
17-
)
18-
if val is None:
19-
raise KeyError(f"Key {key} not found")
20-
return val["value"]
14+
with (
15+
trace.get_tracer_provider()
16+
.get_tracer(__name__)
17+
.start_as_current_span(f"ChainDB.get({key})")
18+
):
19+
val: Optional[Any] = await self.collection.find_one(
20+
{
21+
"task_chain_id": self.task_chain_id,
22+
"key": key,
23+
}
24+
)
25+
if val is None:
26+
raise KeyError(f"Key {key} not found")
27+
get_current_span().set_attribute(f"chaindb.value.{key}", val["value"])
28+
return val["value"]
2129

2230
async def set(self, key: str, val: Any) -> None:
23-
await self.collection.replace_one(
24-
{
25-
"task_chain_id": self.task_chain_id,
26-
"key": key,
27-
},
28-
{
29-
"task_chain_id": self.task_chain_id,
30-
"key": key,
31-
"value": val,
32-
},
33-
upsert=True,
34-
)
31+
with (
32+
trace.get_tracer_provider()
33+
.get_tracer(__name__)
34+
.start_as_current_span(f"ChainDB.set({key})")
35+
):
36+
await self.collection.replace_one(
37+
{
38+
"task_chain_id": self.task_chain_id,
39+
"key": key,
40+
},
41+
{
42+
"task_chain_id": self.task_chain_id,
43+
"key": key,
44+
"value": val,
45+
},
46+
upsert=True,
47+
)
48+
get_current_span().set_attribute(f"chaindb.value.{key}", val)

src/enochecker3/enochecker.py

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,19 @@
2929
from fastapi import FastAPI
3030
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
3131
from opentelemetry.instrumentation.pymongo import PymongoInstrumentor
32-
from opentelemetry.trace import get_current_span
32+
from opentelemetry.trace import StatusCode, get_current_span
3333
from pymongo import AsyncMongoClient
3434
from pymongo.asynchronous.collection import AsyncCollection
3535
from pymongo.asynchronous.database import AsyncDatabase
3636

37-
from enochecker3.utils import FlagSearcher
3837
from enochecker3.telemetry import (
38+
CommonAttributesLogFilter,
3939
SaarctfTracer,
4040
instrument_httpx_without_propagation,
41-
CommonAttributesLogFilter,
4241
setup_telemetry,
4342
)
4443
from enochecker3.telemetry_attributes import telemetry_attributes
44+
from enochecker3.utils import FlagSearcher
4545

4646
from .chaindb import ChainDB
4747
from .types import (
@@ -171,7 +171,7 @@ def __init__(self, service_name: str, service_port: int):
171171
# handler.setFormatter(ELKFormatter("%(message)s"))
172172

173173
self._logger.addHandler(handler)
174-
self._logger.setLevel(logging.DEBUG)
174+
self._logger.setLevel(logging.INFO)
175175

176176
if __name__ == "uvicorn":
177177
self._logger.setLevel(
@@ -417,6 +417,8 @@ async def _inject_dependencies(
417417
async def _call_method_raw(self, task: CheckerTaskMessage) -> Optional[str | bytes]:
418418
variant_id = task.variant_id
419419
method = task.method
420+
print(task)
421+
print(self._method_variants)
420422
try:
421423
f = self._method_variants[method][variant_id]
422424
except KeyError:
@@ -437,38 +439,50 @@ async def _call_method_raw(self, task: CheckerTaskMessage) -> Optional[str | byt
437439

438440
async def _call_method(self, task: CheckerTaskMessage) -> Optional[str | bytes]:
439441
try:
440-
return await asyncio.wait_for(
442+
timeout = (task.timeout / 1000) - TIMEOUT_BUFFER
443+
get_current_span().set_attribute(
444+
"enochecker.internal_timeout_ms", timeout * 1000
445+
)
446+
res = await asyncio.wait_for(
441447
self._call_method_raw(task),
442-
timeout=(task.timeout / 1000) - TIMEOUT_BUFFER,
448+
timeout=timeout,
443449
)
450+
get_current_span().set_attribute("enochecker.status", "OK")
451+
return res
444452
except (MumbleException, OfflineException, InternalErrorException):
445453
raise
446-
except (httpx.ConnectError, httpx.ConnectTimeout):
454+
except (httpx.ConnectError, httpx.ConnectTimeout) as e:
455+
get_current_span().record_exception(e)
447456
trace = traceback.format_exc()
448457
logger = self._get_logger_adapter(task)
449458
logger.info(f"Connection to service failed\n{trace}")
450459
raise OfflineException("Connection to service failed")
451-
except (EOFError, httpx.ReadError, httpx.WriteError):
460+
except (EOFError, httpx.ReadError, httpx.WriteError) as e:
461+
get_current_span().record_exception(e)
452462
trace = traceback.format_exc()
453463
logger = self._get_logger_adapter(task)
454464
logger.error(f"Connection to service closed abruptly\n{trace}")
455465
raise MumbleException("Closed to service closed abruptly")
456-
except (TimeoutError, httpx.TimeoutException):
466+
except (TimeoutError, httpx.TimeoutException) as e:
467+
get_current_span().record_exception(e)
457468
trace = traceback.format_exc()
458469
logger = self._get_logger_adapter(task)
459470
logger.error(f"Service responding too slow\n{trace}")
460471
raise MumbleException("Service responding too slow")
461-
except (ConnectionResetError, httpx.CloseError):
472+
except (ConnectionResetError, httpx.CloseError) as e:
473+
get_current_span().record_exception(e)
462474
trace = traceback.format_exc()
463475
logger = self._get_logger_adapter(task)
464476
logger.error(f"Connection reset by service\n{trace}")
465477
raise MumbleException("Connection reset by services")
466-
except (httpx.RemoteProtocolError, httpx.DecodingError):
478+
except (httpx.RemoteProtocolError, httpx.DecodingError) as e:
479+
get_current_span().record_exception(e)
467480
trace = traceback.format_exc()
468481
logger = self._get_logger_adapter(task)
469482
logger.info(f"HTTP connection to service failed\n{trace}")
470483
raise OfflineException("HTTP connection to service failed")
471484
except Exception as e:
485+
get_current_span().record_exception(e)
472486
trace = traceback.format_exc()
473487
logger = self._get_logger_adapter(task)
474488
logger.info(f"Checker internal error\n{trace}")
@@ -480,6 +494,7 @@ async def _call_putflag(
480494
attack_info: Optional[str | bytes] = await self._call_method(task)
481495
if isinstance(attack_info, bytes):
482496
attack_info = attack_info.decode()
497+
get_current_span().set_attribute("enochecker.result.attack_info", attack_info)
483498
return CheckerResultMessage(
484499
result=CheckerTaskResult.OK, attack_info=attack_info
485500
)
@@ -851,14 +866,28 @@ def service() -> CheckerInfoMessage:
851866

852867
@app.post("/", response_model=CheckerResultMessage)
853868
async def checker(task: CheckerTaskMessage) -> CheckerResultMessage:
869+
print(task.model_dump_json())
854870
attributes = {
855-
"enochecker.method": str(task.method),
856-
"enochecker.team_id": task.team_id,
857-
"enochecker.variant_id": task.variant_id,
858-
"enochecker.related_round_id": task.related_round_id,
871+
"enochecker.task.task_id": task.task_id,
872+
"enochecker.task.method": str(task.method),
873+
"enochecker.task.address": task.address,
874+
"enochecker.task.team_id": task.team_id,
875+
"enochecker.task.team_name": task.team_name,
876+
"enochecker.task.current_round_id": task.current_round_id,
877+
"enochecker.task.related_round_id": task.related_round_id,
878+
"enochecker.task.flag": task.flag,
879+
"enochecker.task.variant_id": task.variant_id,
880+
"enochecker.task.timeout": task.timeout,
881+
"enochecker.task.round_length": task.round_length,
882+
"enochecker.task.task_chain_id": task.task_chain_id,
883+
"enochecker.task.flag_regex": task.flag_regex,
884+
"enochecker.task.flag_hash": task.flag_hash,
885+
"enochecker.task.attack_info": task.attack_info,
859886
}
860887
with telemetry_attributes(attributes):
861-
SaarctfTracer.add_span_attributes(get_current_span())
888+
span = get_current_span()
889+
span.update_name(str(task.method))
890+
SaarctfTracer.add_span_attributes(span)
862891
cls = METHOD_TO_TASK_MESSAGE_MAPPING[task.method]
863892
_task = cls(**task.model_dump())
864893
logger = self._get_logger_adapter(_task)
@@ -898,6 +927,9 @@ async def checker(task: CheckerTaskMessage) -> CheckerResultMessage:
898927
message=f"Unsupported method: {task.method}",
899928
)
900929
except MumbleException as e:
930+
span.record_exception(e)
931+
span.set_attribute("enochecker.result", "MUMBLE")
932+
span.set_attribute("enochecker.result.message", e.message)
901933
trace = traceback.format_exc()
902934
if e.log_message:
903935
logger.info(e.log_message)
@@ -906,6 +938,9 @@ async def checker(task: CheckerTaskMessage) -> CheckerResultMessage:
906938
result=CheckerTaskResult.MUMBLE, message=e.message
907939
)
908940
except OfflineException as e:
941+
span.record_exception(e)
942+
span.set_attribute("enochecker.result", "OFFLINE")
943+
span.set_attribute("enochecker.result.message", e.message)
909944
trace = traceback.format_exc()
910945
if e.log_message:
911946
logger.info(e.log_message)
@@ -914,6 +949,10 @@ async def checker(task: CheckerTaskMessage) -> CheckerResultMessage:
914949
result=CheckerTaskResult.OFFLINE, message=e.message
915950
)
916951
except InternalErrorException as e:
952+
span.record_exception(e)
953+
span.set_status(StatusCode.ERROR)
954+
span.set_attribute("enochecker.result", "INTERNAL_ERROR")
955+
span.set_attribute("enochecker.result.message", e.message)
917956
trace = traceback.format_exc()
918957
if e.log_message:
919958
logger.info(e.log_message)

src/enochecker3/telemetry.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from opentelemetry.context import Context
99
from opentelemetry.exporter.otlp.proto.grpc._log_exporter import OTLPLogExporter
1010
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
11+
from opentelemetry.instrumentation.httpx import RequestInfo, ResponseInfo
1112
from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
1213
from opentelemetry.sdk._logs._internal.export import (
1314
BatchLogRecordProcessor,
@@ -178,6 +179,29 @@ def _nop(*args: Any, **kwargs: Any) -> None:
178179
pass
179180

180181

182+
async def async_request_hook(span: Span, request: RequestInfo):
183+
print(type(span))
184+
print(type(request))
185+
span.update_name(f"{request.method.decode()} {request.url}")
186+
for k, v in request.headers.items():
187+
span.set_attribute(f"http.headers.{k}", v)
188+
print(request.method)
189+
print(request.url)
190+
print(request.headers)
191+
print(request.stream)
192+
print(request.extensions)
193+
194+
195+
async def async_response_hook(
196+
span: Span,
197+
request: RequestInfo,
198+
response: ResponseInfo,
199+
):
200+
print(type(span))
201+
print(type(request))
202+
print(type(response))
203+
204+
181205
def instrument_httpx_without_propagation(client: httpx.AsyncClient) -> None:
182206
"""
183207
We do not want to transport traceparent headers towards services (fingerprinting etc).
@@ -186,5 +210,11 @@ def instrument_httpx_without_propagation(client: httpx.AsyncClient) -> None:
186210
from opentelemetry.instrumentation import httpx as httpx_instrumentation
187211

188212
httpx_instrumentation._inject_propagation_headers = _nop
189-
httpx_instrumentation.inject = _nop
190-
httpx_instrumentation.HTTPXClientInstrumentor.instrument_client(client)
213+
# httpx_instrumentation.inject = _nop
214+
httpx_instrumentation.HTTPXClientInstrumentor.instrument_client(
215+
client,
216+
request_hook=async_request_hook,
217+
response_hook=async_response_hook,
218+
# async_request_hook=async_request_hook,
219+
# async_response_hook=async_response_hook,
220+
)

0 commit comments

Comments
 (0)