fix(otel): identify Hermes resources safely

This commit is contained in:
Victor Kyriazakos 2026-07-22 16:03:24 +00:00
parent 16e774f223
commit 98a0260b0a
4 changed files with 66 additions and 11 deletions

View file

@ -72,6 +72,20 @@ def _safe_resource_attributes(raw: Any) -> Dict[str, str]:
return attrs
def _runtime_resource_attributes(
config: Dict[str, Any], *, telemetry_scope: str
) -> Dict[str, str]:
"""Build the safe OTLP resource shared by metrics and diagnostic logs."""
gh = _gateway_health_config(config)
attrs = _safe_resource_attributes(gh.get("resource_attributes"))
from agent.monitoring.gateway_health import _safe_instance_id
attrs["service.name"] = "hermes-gateway"
attrs["service.instance.id"] = _safe_instance_id(_install_id(config))
attrs["telemetry.scope"] = telemetry_scope
return attrs
def _diagnostic_log_attributes(event: Dict[str, Any]) -> Dict[str, Any]:
attrs: Dict[str, Any] = {}
for key in _DIAGNOSTIC_ATTRIBUTE_KEYS:
@ -299,9 +313,9 @@ def _start_metric_provider(config: Dict[str, Any], sdk: Dict[str, Any]) -> Any:
exporter = sdk["OTLPMetricExporter"](endpoint=endpoint, headers=headers or None)
interval_ms = max(5, int(gh.get("export_interval_seconds", 60))) * 1000
reader = sdk["PeriodicExportingMetricReader"](exporter, export_interval_millis=interval_ms)
resource_attrs = _safe_resource_attributes(gh.get("resource_attributes"))
resource_attrs["service.name"] = "hermes-gateway"
resource_attrs["telemetry.scope"] = "gateway_health"
resource_attrs = _runtime_resource_attributes(
config, telemetry_scope="gateway_health"
)
provider = sdk["MeterProvider"](
metric_readers=[reader],
resource=sdk["Resource"].create(resource_attrs),
@ -354,12 +368,11 @@ class GatewayDiagnosticLogStreamer:
def __init__(self, config: Dict[str, Any], sdk: Dict[str, Any]):
otlp = _otlp_config(config)
gh = _gateway_health_config(config)
headers = _resolve_headers(otlp.get("headers_env"))
endpoint = _logs_endpoint(str(otlp.get("endpoint")))
resource_attrs = _safe_resource_attributes(gh.get("resource_attributes"))
resource_attrs["service.name"] = "hermes-gateway"
resource_attrs["telemetry.scope"] = "gateway_diagnostics"
resource_attrs = _runtime_resource_attributes(
config, telemetry_scope="gateway_diagnostics"
)
self._provider = sdk["LoggerProvider"](resource=sdk["Resource"].create(resource_attrs))
self._processor = sdk["BatchLogRecordProcessor"](
sdk["OTLPLogExporter"](endpoint=endpoint, headers=headers or None)

View file

@ -115,12 +115,20 @@ def build_exporter(config: Dict[str, Any]):
return sdk["OTLPSpanExporter"](endpoint=endpoint, headers=headers or None)
def _resource_attributes(config: Dict[str, Any]) -> Dict[str, str]:
from agent.monitoring.gateway_health import _safe_instance_id
from agent.monitoring.policy import ensure_install_id
return {
"service.name": "hermes-gateway",
"service.instance.id": _safe_instance_id(ensure_install_id(config)),
"telemetry.scope": "gateway_monitoring",
}
def _make_provider(config: Dict[str, Any]):
sdk = _require_sdk()
resource = sdk["Resource"].create({
"service.name": "hermes-gateway",
"telemetry.scope": "gateway_monitoring",
})
resource = sdk["Resource"].create(_resource_attributes(config))
provider = sdk["TracerProvider"](resource=resource)
processor = sdk["BatchSpanProcessor"](build_exporter(config))
provider.add_span_processor(processor)

View file

@ -299,6 +299,28 @@ def test_instance_id_hash_is_stable_and_distinguishes_instances():
assert "install-1" not in first
def test_runtime_resource_attributes_include_stable_hashed_instance():
from agent.monitoring.gateway_health_export import _runtime_resource_attributes
config = {
"monitoring": {
"install_id": "private-install-id",
"gateway_health_export": {
"resource_attributes": {"deployment.environment.name": "staging"}
},
}
}
attrs = _runtime_resource_attributes(config, telemetry_scope="gateway_health")
assert attrs["service.name"] == "hermes-gateway"
assert attrs["service.instance.id"].startswith("sha256:")
assert len(attrs["service.instance.id"]) == len("sha256:") + 24
assert "private-install-id" not in str(attrs)
assert attrs["deployment.environment.name"] == "staging"
assert attrs["telemetry.scope"] == "gateway_health"
def test_diagnostic_log_attributes_are_allowlisted_redacted_and_profile_free():
from agent.monitoring.gateway_health_export import _diagnostic_log_attributes

View file

@ -76,6 +76,18 @@ def test_is_enabled_requires_endpoint_and_flag():
assert not OE.is_enabled({})
def test_trace_resource_includes_stable_hashed_instance():
attrs = OE._resource_attributes(
{"monitoring": {"install_id": "private-install-id"}}
)
assert attrs["service.name"] == "hermes-gateway"
assert attrs["service.instance.id"].startswith("sha256:")
assert len(attrs["service.instance.id"]) == len("sha256:") + 24
assert "private-install-id" not in str(attrs)
assert attrs["telemetry.scope"] == "gateway_monitoring"
def test_export_otlp_feature_specs_match_pyproject():
from tools.lazy_deps import LAZY_DEPS
import re