Skip to content

Commit

Permalink
Fix tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
marius-baseten committed Aug 28, 2024
1 parent 95a36b1 commit 47cecfc
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 83 deletions.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "truss"
version = "0.9.30rc5"
version = "0.9.30rc667"
description = "A seamless bridge from model development to model delivery"
license = "MIT"
readme = "README.md"
Expand Down
6 changes: 4 additions & 2 deletions truss/contexts/image_builder/serving_image_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -459,11 +459,13 @@ def copy_into_build_dir(from_path: Path, path_in_build_dir: str):
# are detected and cause a build failure. If there are no
# requirements provided, we just pass an empty string,
# as there's no need to install anything.
# TODO: above reasoning leads to inconsistencies. Needs revisit.
# TODO: above reasoning leads to inconsistencies. To get consistent images
# tentatively add server requirements always. This whole point needs more
# thought and potentially a re-design.
user_provided_python_requirements = (
base_server_requirements + spec.requirements_txt
if spec.requirements
else ""
else base_server_requirements
)
if spec.requirements_file is not None:
copy_into_build_dir(
Expand Down
64 changes: 46 additions & 18 deletions truss/templates/server/common/tracing.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
import contextlib
import functools
import json
import logging
import os
import pathlib
import time
from typing import Iterator, List, Sequence
from typing import Iterator, List, Optional, Sequence

import opentelemetry.exporter.otlp.proto.grpc.trace_exporter as oltp_exporter
import opentelemetry.exporter.otlp.proto.http.trace_exporter as oltp_exporter
import opentelemetry.sdk.resources as resources
import opentelemetry.sdk.trace as sdk_trace
import opentelemetry.sdk.trace.export as trace_export
from opentelemetry import context, trace
from shared import secrets_resolver

logger = logging.getLogger(__name__)

ATTR_NAME_DURATION = "duration_sec"
OTEL_EXPORTER_OTLP_ENDPOINT = "OTEL_EXPORTER_OTLP_ENDPOINT"
OTEL_TRACING_NDJSON_FILE = "OTEL_TRACING_NDJSON_FILE"
HONEYCOMB_DATASET = "HONEYCOMB_DATASET"
HONEYCOMB_API_KEY = "HONEYCOMB_API_KEY"


class JSONFileExporter(trace_export.SpanExporter):
"""Writes spans to newline-delimited JSON file for debugging / testing."""
Expand All @@ -36,26 +42,50 @@ def shutdown(self) -> None:
self._file.close()


@functools.lru_cache(maxsize=1)
def get_truss_tracer() -> trace.Tracer:
_truss_tracer: Optional[trace.Tracer] = None


def get_truss_tracer(secrets: secrets_resolver.SecretsResolver) -> trace.Tracer:
"""Creates a cached tracer (i.e. runtime-singleton) to be used for truss
internal tracing.
The goal is to separate truss-internal tracing instrumentation
completely from potential user-defined tracing - see also `detach_context` below.
"""
global _truss_tracer
if _truss_tracer:
return _truss_tracer

span_processors: List[sdk_trace.SpanProcessor] = []
if otlp_endpoint := os.getenv("OTEL_EXPORTER_OTLP_ENDPOINT"):
if otlp_endpoint := os.getenv(OTEL_EXPORTER_OTLP_ENDPOINT):
logger.info(f"Exporting trace data to {OTEL_EXPORTER_OTLP_ENDPOINT}.")
otlp_exporter = oltp_exporter.OTLPSpanExporter(endpoint=otlp_endpoint)
otlp_processor = sdk_trace.export.BatchSpanProcessor(otlp_exporter)
span_processors.append(otlp_processor)

if tracing_log_file := os.getenv("OTEL_TRACING_NDJSON_FILE"):
if tracing_log_file := os.getenv(OTEL_TRACING_NDJSON_FILE):
logger.info("Exporting trace data to `tracing_log_file`.")
json_file_exporter = JSONFileExporter(pathlib.Path(tracing_log_file))
file_processor = sdk_trace.export.SimpleSpanProcessor(json_file_exporter)
span_processors.append(file_processor)

if honeycomb_dataset := os.getenv(HONEYCOMB_DATASET):
if HONEYCOMB_API_KEY in secrets:
honeycomb_api_key = secrets[HONEYCOMB_API_KEY]
logger.info("Exporting trace data to honeycomb.")
honeycomb_exporter = oltp_exporter.OTLPSpanExporter(
endpoint="https://api.honeycomb.io/v1/traces",
headers={
"x-honeycomb-team": honeycomb_api_key,
"x-honeycomb-dataset": honeycomb_dataset,
},
)
honeycomb_processor = sdk_trace.export.BatchSpanProcessor(
honeycomb_exporter
)
span_processors.append(honeycomb_processor)

if span_processors:
logger.info("Instantiating truss tracer.")
resource = resources.Resource.create({resources.SERVICE_NAME: "TrussServer"})
Expand All @@ -67,7 +97,8 @@ def get_truss_tracer() -> trace.Tracer:
logger.info("Using no-op tracing.")
tracer = sdk_trace.NoOpTracer()

return tracer
_truss_tracer = tracer
return _truss_tracer


@contextlib.contextmanager
Expand All @@ -82,14 +113,9 @@ def detach_context() -> Iterator[None]:
be wrapped in this context for isolation.
"""
current_context = context.get_current()
# Set the current context to an invalid span context, effectively clearing it.
# This makes sure inside the context a new root is context is created.
transient_token = context.attach(
trace.set_span_in_context(
trace.INVALID_SPAN,
trace.INVALID_SPAN_CONTEXT, # type: ignore[arg-type]
)
)
# Create an invalid tracing context. This forces that tracing code inside this
# context manager creates a new root tracing context.
transient_token = context.attach(trace.set_span_in_context(trace.INVALID_SPAN))
try:
yield
finally:
Expand All @@ -105,9 +131,11 @@ def section_as_event(span: sdk_trace.Span, section_name: str) -> Iterator[None]:
Note that events are much cheaper to create than dedicated spans.
"""
t0 = time.time()
span.add_event(f"start-{section_name}")
span.add_event(f"start: {section_name}")
try:
yield
finally:
t1 = time.time()
span.add_event(f"done-{section_name}", attributes={"duration_sec": t1 - t0})
span.add_event(
f"done: {section_name}", attributes={ATTR_NAME_DURATION: t1 - t0}
)
25 changes: 15 additions & 10 deletions truss/templates/server/common/truss_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from opentelemetry import propagate as otel_propagate
from opentelemetry.sdk import trace as sdk_trace
from shared.logging import setup_logging
from shared.secrets_resolver import SecretsResolver
from shared.serialization import (
DeepNumpyEncoder,
truss_msgpack_deserialize,
Expand Down Expand Up @@ -173,16 +174,19 @@ async def predict(

response_headers = {}
if self.is_binary(request):
response_headers["Content-Type"] = "application/octet-stream"
return Response(
content=truss_msgpack_serialize(response), headers=response_headers
)
with tracing.section_as_event(span, "binary-serialize"):
response_headers["Content-Type"] = "application/octet-stream"
return Response(
content=truss_msgpack_serialize(response),
headers=response_headers,
)
else:
response_headers["Content-Type"] = "application/json"
return Response(
content=json.dumps(response, cls=DeepNumpyEncoder),
headers=response_headers,
)
with tracing.section_as_event(span, "json-serialize"):
response_headers["Content-Type"] = "application/json"
return Response(
content=json.dumps(response, cls=DeepNumpyEncoder),
headers=response_headers,
)

async def schema(self, model_name: str) -> Dict:
model: ModelWrapper = self._safe_lookup_model(model_name)
Expand Down Expand Up @@ -223,7 +227,8 @@ def __init__(
config: Dict,
setup_json_logger: bool = True,
):
tracer = tracing.get_truss_tracer()
secrets = SecretsResolver.get_secrets(config)
tracer = tracing.get_truss_tracer(secrets)
self.http_port = http_port
self._config = config
self._model = ModelWrapper(self._config, tracer)
Expand Down
Loading

0 comments on commit 47cecfc

Please sign in to comment.