[observability] add ServerArgs.stat_loggers for pluggable metrics backend (#24610)
Signed-off-by: Dongjun Na <kmu5544616@gmail.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import os
|
||||
import unittest
|
||||
from typing import Dict, List
|
||||
|
||||
@@ -8,6 +9,8 @@ from prometheus_client.samples import Sample
|
||||
from sglang.srt.environ import envs
|
||||
from sglang.srt.observability.metrics_collector import (
|
||||
ROUTING_KEY_REQ_COUNT_BUCKET_BOUNDS,
|
||||
STAT_LOGGER_ROLE_SCHEDULER,
|
||||
SchedulerMetricsCollector,
|
||||
compute_routing_key_stats,
|
||||
)
|
||||
from sglang.srt.utils import kill_process_tree
|
||||
@@ -274,6 +277,66 @@ def _check_metrics_positive(test_case, metrics, metrics_to_check):
|
||||
test_case.assertGreater(value, 0, f"{metric_name} {labels}")
|
||||
|
||||
|
||||
_DI_MARKER_PATH = "/tmp/sglang_di_test_marker"
|
||||
|
||||
|
||||
class _MarkingSchedulerCollector(SchedulerMetricsCollector):
|
||||
"""Records its own instantiation to a file so the test can verify the
|
||||
custom subclass was used in the scheduler subprocess.
|
||||
|
||||
Defined at module level so it is picklable into the scheduler process.
|
||||
Cross-process signalling uses a filesystem marker because the scheduler
|
||||
runs in its own subprocess and cannot share in-memory state with the
|
||||
test runner.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
with open(_DI_MARKER_PATH, "w") as f:
|
||||
f.write("scheduler_collector_initialized\n")
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class TestStatLoggersDI(CustomTestCase):
|
||||
"""Verify that a custom MetricsCollector subclass passed through
|
||||
``ServerArgs.stat_loggers`` is the one instantiated inside the
|
||||
scheduler subprocess."""
|
||||
|
||||
def setUp(self) -> None:
|
||||
try:
|
||||
os.unlink(_DI_MARKER_PATH)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
def tearDown(self) -> None:
|
||||
try:
|
||||
os.unlink(_DI_MARKER_PATH)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
def test_engine_custom_scheduler_collector(self):
|
||||
import sglang as sgl
|
||||
|
||||
engine = sgl.Engine(
|
||||
model_path=_MODEL_NAME,
|
||||
enable_metrics=True,
|
||||
stat_loggers={
|
||||
STAT_LOGGER_ROLE_SCHEDULER: _MarkingSchedulerCollector,
|
||||
},
|
||||
)
|
||||
try:
|
||||
# One small generation triggers scheduler init, which is where
|
||||
# resolve_collector_class() picks the injected subclass.
|
||||
engine.generate("Hello", {"max_new_tokens": 4})
|
||||
finally:
|
||||
engine.shutdown()
|
||||
|
||||
self.assertTrue(
|
||||
os.path.exists(_DI_MARKER_PATH),
|
||||
"Custom SchedulerMetricsCollector was not instantiated; "
|
||||
"stat_loggers DI did not take effect.",
|
||||
)
|
||||
|
||||
|
||||
class TestComputeRoutingKeyStats(unittest.TestCase):
|
||||
def test_empty(self):
|
||||
num_unique, req_counts = compute_routing_key_stats([])
|
||||
|
||||
Reference in New Issue
Block a user