feat: support custom OTLP trace service name (#35802)
This commit is contained in:
@@ -589,6 +589,7 @@ class ServerArgs(DisaggServerArgsMixin):
|
||||
# Tracing
|
||||
enable_trace: bool = False
|
||||
otlp_traces_endpoint: str = "localhost:4317"
|
||||
otlp_service_name: str | None = None
|
||||
|
||||
# SGLang backend for encoder stage
|
||||
srt_encoder_url: str | None = None
|
||||
@@ -2893,6 +2894,13 @@ class ServerArgs(DisaggServerArgsMixin):
|
||||
default=ServerArgs.otlp_traces_endpoint,
|
||||
help="OTLP collector endpoint when --enable-trace is set. Format: <host>:<port>",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--otlp-service-name",
|
||||
type=str,
|
||||
default=ServerArgs.otlp_service_name,
|
||||
help="Service name for OTLP traces (displayed as 'service.name' in trace backends). "
|
||||
"If unset, falls back to the OTEL_SERVICE_NAME env var, then to 'sglang-diffusion'.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--log-requests",
|
||||
action="store_true",
|
||||
|
||||
@@ -7,6 +7,7 @@ start/end bookkeeping.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from contextlib import contextmanager
|
||||
from dataclasses import dataclass
|
||||
|
||||
@@ -37,10 +38,17 @@ def init_diffusion_tracing(server_args, thread_label: str):
|
||||
trace_set_thread_info,
|
||||
)
|
||||
|
||||
# Priority: --otlp-service-name > OTEL_SERVICE_NAME > "sglang-diffusion"
|
||||
service_name = (
|
||||
server_args.otlp_service_name
|
||||
or os.getenv("OTEL_SERVICE_NAME")
|
||||
or "sglang-diffusion"
|
||||
)
|
||||
|
||||
# srt owns TraceReqContext and filters spans through its trace_modules list
|
||||
process_tracing_init(
|
||||
server_args.otlp_traces_endpoint,
|
||||
"sglang-diffusion",
|
||||
service_name,
|
||||
trace_modules=DIFFUSION_TRACE_MODULE,
|
||||
)
|
||||
trace_set_thread_info(thread_label)
|
||||
|
||||
@@ -177,6 +177,11 @@ class Observability(msgspec.Struct):
|
||||
str,
|
||||
"Config opentelemetry collector endpoint if --enable-trace is set. format: <ip>:<port>",
|
||||
] = "localhost:4317"
|
||||
otlp_service_name: A[
|
||||
Optional[str],
|
||||
"Service name for OTLP traces (displayed as 'service.name' in trace backends). "
|
||||
"If unset, falls back to the OTEL_SERVICE_NAME env var, then to 'sglang'.",
|
||||
] = None
|
||||
# RequestMetricsExporter configuration
|
||||
export_metrics_to_file: A[
|
||||
bool,
|
||||
|
||||
@@ -1775,7 +1775,7 @@ def launch_local_runtime(server_args: ServerArgs) -> EncoderRuntime:
|
||||
if get_observability().enable_trace:
|
||||
process_tracing_init(
|
||||
get_observability().otlp_traces_endpoint,
|
||||
"sglang",
|
||||
get_observability().otlp_service_name,
|
||||
trace_modules=get_observability().trace_modules,
|
||||
)
|
||||
trace_set_thread_info("Encoder")
|
||||
|
||||
@@ -329,7 +329,7 @@ class Engine(EngineScoreMixin, EngineBase):
|
||||
if get_observability().enable_trace:
|
||||
process_tracing_init(
|
||||
get_observability().otlp_traces_endpoint,
|
||||
"sglang",
|
||||
get_observability().otlp_service_name,
|
||||
trace_modules=get_observability().trace_modules,
|
||||
)
|
||||
thread_label = "Tokenizer"
|
||||
|
||||
@@ -293,7 +293,7 @@ async def lifespan(fast_api_app: FastAPI):
|
||||
if get_observability().enable_trace:
|
||||
process_tracing_init(
|
||||
get_observability().otlp_traces_endpoint,
|
||||
"sglang",
|
||||
get_observability().otlp_service_name,
|
||||
trace_modules=get_observability().trace_modules,
|
||||
)
|
||||
if get_disagg().disaggregation_mode == "prefill":
|
||||
|
||||
@@ -839,7 +839,7 @@ def run_data_parallel_controller_process(
|
||||
if get_observability().enable_trace:
|
||||
process_tracing_init(
|
||||
get_observability().otlp_traces_endpoint,
|
||||
"sglang",
|
||||
get_observability().otlp_service_name,
|
||||
trace_modules=get_observability().trace_modules,
|
||||
)
|
||||
thread_label = "DP Controller"
|
||||
|
||||
@@ -5852,7 +5852,7 @@ def run_scheduler_process(
|
||||
if get_observability().enable_trace:
|
||||
process_tracing_init(
|
||||
get_observability().otlp_traces_endpoint,
|
||||
"sglang",
|
||||
get_observability().otlp_service_name,
|
||||
trace_modules=get_observability().trace_modules,
|
||||
)
|
||||
thread_label = "Scheduler"
|
||||
|
||||
@@ -227,9 +227,12 @@ def process_tracing_init(
|
||||
)
|
||||
|
||||
try:
|
||||
# Priority: explicit server_name > OTEL_SERVICE_NAME > "sglang"
|
||||
service_name = server_name or os.getenv("OTEL_SERVICE_NAME", "sglang")
|
||||
|
||||
resource = Resource.create(
|
||||
attributes={
|
||||
SERVICE_NAME: server_name,
|
||||
SERVICE_NAME: service_name,
|
||||
}
|
||||
)
|
||||
tracer_provider = TracerProvider(
|
||||
@@ -259,7 +262,7 @@ def process_tracing_init(
|
||||
if envs.SGLANG_TRACE_ASYNC.get():
|
||||
from sglang.srt.observability.trace_async import start_trace_exporter
|
||||
|
||||
start_trace_exporter(otlp_endpoint, server_name, trace_modules=trace_modules)
|
||||
start_trace_exporter(otlp_endpoint, service_name, trace_modules=trace_modules)
|
||||
|
||||
|
||||
def get_global_tracing_enabled():
|
||||
|
||||
@@ -175,6 +175,48 @@ class TestProcessTracingInit(unittest.TestCase):
|
||||
finally:
|
||||
mod.opentelemetry_imported = orig
|
||||
|
||||
@unittest.skipIf(not _has_otel, "OpenTelemetry not installed")
|
||||
def test_service_name_priority_explicit(self):
|
||||
"""Service name priority: explicit parameter > env var > default."""
|
||||
with patch.dict(os.environ, {"OTEL_SERVICE_NAME": "from-env"}):
|
||||
with patch.object(mod, "TracerProvider"):
|
||||
with patch.object(mod, "Resource") as mock_resource:
|
||||
process_tracing_init("localhost:4317", "explicit-name")
|
||||
# Verify Resource.create was called with the explicit name
|
||||
mock_resource.create.assert_called_once()
|
||||
call_kwargs = mock_resource.create.call_args[1]
|
||||
self.assertEqual(
|
||||
call_kwargs["attributes"][mod.SERVICE_NAME], "explicit-name"
|
||||
)
|
||||
|
||||
@unittest.skipIf(not _has_otel, "OpenTelemetry not installed")
|
||||
def test_service_name_priority_env_var(self):
|
||||
"""Service name falls back to OTEL_SERVICE_NAME when explicit is None."""
|
||||
with patch.dict(os.environ, {"OTEL_SERVICE_NAME": "from-env"}):
|
||||
with patch.object(mod, "TracerProvider"):
|
||||
with patch.object(mod, "Resource") as mock_resource:
|
||||
process_tracing_init("localhost:4317", None)
|
||||
# Verify Resource.create was called with env var value
|
||||
mock_resource.create.assert_called_once()
|
||||
call_kwargs = mock_resource.create.call_args[1]
|
||||
self.assertEqual(
|
||||
call_kwargs["attributes"][mod.SERVICE_NAME], "from-env"
|
||||
)
|
||||
|
||||
@unittest.skipIf(not _has_otel, "OpenTelemetry not installed")
|
||||
def test_service_name_priority_default(self):
|
||||
"""Service name falls back to 'sglang' when both explicit and env are unset."""
|
||||
with patch.dict(os.environ, {}, clear=True):
|
||||
with patch.object(mod, "TracerProvider"):
|
||||
with patch.object(mod, "Resource") as mock_resource:
|
||||
process_tracing_init("localhost:4317", None)
|
||||
# Verify Resource.create was called with default value
|
||||
mock_resource.create.assert_called_once()
|
||||
call_kwargs = mock_resource.create.call_args[1]
|
||||
self.assertEqual(
|
||||
call_kwargs["attributes"][mod.SERVICE_NAME], "sglang"
|
||||
)
|
||||
|
||||
|
||||
class TestTraceReqContextDisabled(unittest.TestCase):
|
||||
def setUp(self):
|
||||
|
||||
Reference in New Issue
Block a user