Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable exporting spans to snowflake stage if a TruLensSnowflakeSpanExporter is provided #1708

Merged
merged 74 commits into from
Jan 17, 2025
Merged
Show file tree
Hide file tree
Changes from 63 commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
929187f
move things out of app.py
sfc-gh-gtokernliang Jan 3, 2025
b4d2c84
add tests
sfc-gh-gtokernliang Jan 3, 2025
f112f38
update golden
sfc-gh-gtokernliang Jan 3, 2025
197cd0a
update app.py
sfc-gh-gtokernliang Jan 3, 2025
062d197
update golden
sfc-gh-gtokernliang Jan 3, 2025
1cc2a95
update golden
sfc-gh-gtokernliang Jan 3, 2025
dfc3fa5
move main output
sfc-gh-gtokernliang Jan 4, 2025
95f250c
PR feedback
sfc-gh-gtokernliang Jan 8, 2025
788f945
not sure why this is needed
sfc-gh-gtokernliang Jan 9, 2025
ef17076
add tests
sfc-gh-gtokernliang Jan 3, 2025
2c9d9bb
update golden
sfc-gh-gtokernliang Jan 3, 2025
85cbdc6
update test
sfc-gh-gtokernliang Jan 9, 2025
65544c9
update golden
sfc-gh-gtokernliang Jan 9, 2025
9b9644b
save
sfc-gh-gtokernliang Dec 16, 2024
02855f9
draft
sfc-gh-gtokernliang Dec 19, 2024
d9a4694
save
sfc-gh-gtokernliang Dec 20, 2024
5c939ac
add back debugger
sfc-gh-gtokernliang Dec 20, 2024
d025a07
update notebook
sfc-gh-gtokernliang Dec 20, 2024
bcd677f
fix
sfc-gh-gtokernliang Dec 21, 2024
8456019
update semcov
sfc-gh-gtokernliang Dec 21, 2024
d2d2361
remove artifacts
sfc-gh-gtokernliang Dec 24, 2024
b3ff929
remove span_types from SpanAttributes
sfc-gh-gtokernliang Dec 24, 2024
d4603b1
modified it to accept multiple tokens
sfc-gh-gtokernliang Dec 24, 2024
cbeb5a4
fix bug with multiple func calls
sfc-gh-gtokernliang Dec 24, 2024
1846dba
PR feedback
sfc-gh-gtokernliang Dec 24, 2024
eab220e
add tests
sfc-gh-gtokernliang Jan 2, 2025
093f8ad
add tests
sfc-gh-gtokernliang Jan 2, 2025
0236798
nits
sfc-gh-gtokernliang Jan 2, 2025
7d0e800
pr feedback
sfc-gh-gtokernliang Jan 3, 2025
a8e9084
update golden
sfc-gh-gtokernliang Jan 3, 2025
5d35d11
update test file
sfc-gh-gtokernliang Jan 3, 2025
af18dde
update golden
sfc-gh-gtokernliang Jan 6, 2025
f3c9e22
PR feedback
sfc-gh-gtokernliang Jan 9, 2025
e94147f
draft
sfc-gh-gtokernliang Jan 4, 2025
2a74796
remove file
sfc-gh-gtokernliang Jan 4, 2025
fd645d1
don't compress
sfc-gh-gtokernliang Jan 4, 2025
258abd2
remove print
sfc-gh-gtokernliang Jan 4, 2025
28cee08
update golden
sfc-gh-gtokernliang Jan 6, 2025
7dc4a1c
added comments
sfc-gh-gtokernliang Jan 6, 2025
d8422ad
gzip
sfc-gh-gtokernliang Jan 6, 2025
09f69ca
update
sfc-gh-gtokernliang Jan 9, 2025
ab0bb6a
update protobuf
sfc-gh-gtokernliang Jan 9, 2025
9e50f2c
Merge branch 'main' of github.com:truera/trulens into garett/SNOW-187…
sfc-gh-gtokernliang Jan 9, 2025
21f9b72
pr feedback
sfc-gh-gtokernliang Jan 9, 2025
37b6dcc
is this where I add it?
sfc-gh-gtokernliang Jan 9, 2025
e23b2fc
maybe here?
sfc-gh-gtokernliang Jan 10, 2025
af90428
undo migration
sfc-gh-gtokernliang Jan 10, 2025
0fce605
update
sfc-gh-gtokernliang Jan 10, 2025
0422373
save
sfc-gh-gtokernliang Jan 11, 2025
3083dd1
improvements
sfc-gh-gtokernliang Jan 11, 2025
18d1094
feedback
sfc-gh-gtokernliang Jan 11, 2025
910265b
remove app.py
sfc-gh-gtokernliang Jan 11, 2025
9280281
remove instruments.py
sfc-gh-gtokernliang Jan 11, 2025
6ca1f3c
remove unused
sfc-gh-gtokernliang Jan 11, 2025
a91b0eb
update
sfc-gh-gtokernliang Jan 11, 2025
b3c5eca
Merge branch 'main' of github.com:truera/trulens into garett/SNOW-187…
sfc-gh-gtokernliang Jan 11, 2025
bd3fb50
update
sfc-gh-gtokernliang Jan 11, 2025
881c68d
update
sfc-gh-gtokernliang Jan 11, 2025
5fff6fc
extract
sfc-gh-gtokernliang Jan 11, 2025
2d7b6a0
Merge branch 'garett/add-utils' of github.com:truera/trulens into gar…
sfc-gh-gtokernliang Jan 11, 2025
8bae5fb
nits
sfc-gh-gtokernliang Jan 11, 2025
c11b1c9
update golden
sfc-gh-gtokernliang Jan 12, 2025
5a55584
Merge branch 'garett/add-utils' of github.com:truera/trulens into gar…
sfc-gh-gtokernliang Jan 12, 2025
61d87a8
pr feedback
sfc-gh-gtokernliang Jan 14, 2025
8824794
Merge branch 'garett/add-utils' of github.com:truera/trulens into gar…
sfc-gh-gtokernliang Jan 14, 2025
a9f0537
pr feedback
sfc-gh-gtokernliang Jan 14, 2025
9f4c8a3
Merge branch 'main' of github.com:truera/trulens into garett/SNOW-187…
sfc-gh-gtokernliang Jan 16, 2025
f46af3c
save
sfc-gh-gtokernliang Jan 16, 2025
2903b52
draft
sfc-gh-gtokernliang Jan 17, 2025
1a5d455
comments
sfc-gh-gtokernliang Jan 17, 2025
0353258
PR feedback
sfc-gh-gtokernliang Jan 17, 2025
c28e10d
pr feedback
sfc-gh-gtokernliang Jan 17, 2025
1c1d718
update golden
sfc-gh-gtokernliang Jan 17, 2025
4e27038
PR feedback
sfc-gh-gtokernliang Jan 17, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,13 @@ env-tests:
nbconvert \
nbformat \
opentelemetry-sdk \
opentelemetry-proto \
pre-commit \
pytest \
pytest-azurepipelines \
pytest-cov \
pytest-subtests \
ruff \
ruff

env-tests-required:
poetry install --only required \
Expand Down
59 changes: 41 additions & 18 deletions examples/experimental/otel_exporter.ipynb
Original file line number Diff line number Diff line change
@@ -1,15 +1,5 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# !pip install opentelemetry-api\n",
"# !pip install opentelemetry-sdk"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand Down Expand Up @@ -41,6 +31,7 @@
"formatter = logging.Formatter(\n",
" \"%(asctime)s - %(name)s - %(levelname)s - %(message)s\"\n",
")\n",
"handler.addFilter(logging.Filter(\"trulens\"))\n",
"handler.setFormatter(formatter)\n",
"root.addHandler(handler)"
]
Expand Down Expand Up @@ -100,17 +91,35 @@
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"\n",
"import dotenv\n",
"from trulens.connectors.snowflake import SnowflakeConnector\n",
"from trulens.core.session import TruSession\n",
"from trulens.experimental.otel_tracing.core.init import init\n",
"from trulens.experimental.otel_tracing.core.exporter import (\n",
" TruLensSnowflakeSpanExporter,\n",
")\n",
"\n",
"dotenv.load_dotenv()\n",
"\n",
"session = TruSession()\n",
"session.experimental_enable_feature(\"otel_tracing\")\n",
"session.reset_database()\n",
"connection_params = {\n",
" \"account\": os.environ[\"SNOWFLAKE_ACCOUNT\"],\n",
" \"user\": os.environ[\"SNOWFLAKE_USER\"],\n",
" \"password\": os.environ[\"SNOWFLAKE_USER_PASSWORD\"],\n",
" \"role\": os.environ.get(\"SNOWFLAKE_ROLE\", \"ENGINEER\"),\n",
" \"database\": os.environ.get(\"SNOWFLAKE_DATABASE\"),\n",
" \"schema\": os.environ.get(\"SNOWFLAKE_SCHEMA\"),\n",
" \"warehouse\": os.environ.get(\"SNOWFLAKE_WAREHOUSE\"),\n",
"}\n",
"\n",
"connector = SnowflakeConnector(\n",
" **connection_params,\n",
")\n",
"\n",
"exporter = TruLensSnowflakeSpanExporter(connector=connector)\n",
"\n",
"init(session, debug=True)"
"# Note that the connector for the session can be different from the connector for the exporter.\n",
"session = TruSession(_experimental_otel_exporter=exporter)"
]
},
{
Expand All @@ -122,14 +131,28 @@
"from trulens.apps.custom import TruCustomApp\n",
"\n",
"test_app = TestApp()\n",
"custom_app = TruCustomApp(test_app)\n",
"custom_app = TruCustomApp(\n",
" test_app,\n",
" app_name=\"database.schema.app\",\n",
" app_version=\"1.0.0\",\n",
" session=session,\n",
")\n",
"\n",
"with custom_app as recording:\n",
"with custom_app(run_name=\"test run\", input_id=\"123\") as recording:\n",
" test_app.respond_to_query(\"test\")\n",
"\n",
"with custom_app as recording:\n",
"with custom_app(run_name=\"test run\", input_id=\"456\") as recording:\n",
" test_app.respond_to_query(\"throw\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"session.experimental_force_flush()"
]
}
],
"metadata": {
Expand Down
1 change: 1 addition & 0 deletions src/core/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ importlib-resources = "^6.0"
trulens-otel-semconv = { version = "^1.0.0", optional = true }
opentelemetry-api = { version = "^1.0.0", optional = true }
opentelemetry-sdk = { version = "^1.0.0", optional = true }
opentelemetry-proto = { version = "^1.0.0", optional = true }

[tool.poetry.group.tqdm]
optional = true
Expand Down
26 changes: 20 additions & 6 deletions src/core/trulens/core/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -898,7 +898,7 @@ def __enter__(self):
core_experimental.Feature.OTEL_TRACING
):
from trulens.experimental.otel_tracing.core.instrument import (
App as OTELApp,
OTELRecordingContext as OTELApp,
)

return OTELApp.__enter__(self)
Expand All @@ -916,16 +916,14 @@ def __exit__(self, exc_type, exc_value, exc_tb):
core_experimental.Feature.OTEL_TRACING
):
from trulens.experimental.otel_tracing.core.instrument import (
App as OTELApp,
OTELRecordingContext as OTELApp,
)

return OTELApp.__exit__(self, exc_type, exc_value, exc_tb)

ctx = self.recording_contexts.get()
self.recording_contexts.reset(ctx.token)

# self._reset_context_vars()

if exc_type is not None:
raise exc_value

Expand All @@ -937,7 +935,7 @@ async def __aenter__(self):
core_experimental.Feature.OTEL_TRACING
):
from trulens.experimental.otel_tracing.core.instrument import (
App as OTELApp,
OTELRecordingContext as OTELApp,
)

return OTELApp.__enter__(self)
Expand All @@ -957,7 +955,7 @@ async def __aexit__(self, exc_type, exc_value, exc_tb):
core_experimental.Feature.OTEL_TRACING
):
from trulens.experimental.otel_tracing.core.instrument import (
App as OTELApp,
OTELRecordingContext as OTELApp,
)

return OTELApp.__exit__(self, exc_type, exc_value, exc_tb)
Expand All @@ -972,6 +970,22 @@ async def __aexit__(self, exc_type, exc_value, exc_tb):

return

def __call__(self, *, run_name: str, input_id: str):
if not self.session.experimental_feature(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry can you explain to me what's going on in this function?

Copy link
Contributor Author

@sfc-gh-gtokernliang sfc-gh-gtokernliang Jan 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

haha - this might not have been the best way to achieve this.

The goal is to enable syntax that looks like:

    with custom_app(run_name='run_name', input_id='input_id') as recording:
        test_app.respond_to_query(input)

to achieve this - custom_app(run_name='run_name', input_id='input_id') should return an object that is a recording context (in our case a class with the __enter__ and __exit__ methods), and that the setting of the run_name and input_id shouldn't taint the object itself.

hence - the approach here is, when custom_app is called, it:

  1. Checks that OTEL tracing is enabled
  2. creates a clone of itself using model_construct + model_dump that is classed to OTELRecordingContext (see https://github.com/truera/trulens/pull/1708/files/5a555840da38b280a0b3c3cbabb40cbd8a3e93c4#diff-25f7f4954d77bc71539bcd8d1e7b3133552596b468bf53a6962b636b176de682R96)
  3. then performs the normal recording context duties

core_experimental.Feature.OTEL_TRACING
):
raise RuntimeError("OTEL Tracing is not enabled for this session.")

from trulens.experimental.otel_tracing.core.instrument import (
OTELRecordingContext as OTELApp,
)

# Pylance shows an error here, but it is likely a false positive. due to the overriden
# model dump returning json instead of a dict.
return OTELApp.model_construct(
**self.model_dump(), run_name=run_name, input_id=input_id
)

def _set_context_vars(self):
# HACK: For debugging purposes, try setting/resetting all context vars
# used in trulens around the app context managers due to bugs in trying
Expand Down
62 changes: 47 additions & 15 deletions src/core/trulens/core/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
from trulens.experimental.otel_tracing import _feature as otel_tracing_feature

if TYPE_CHECKING:
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import SpanExporter
from trulens.core import app as base_app

tqdm = None
Expand Down Expand Up @@ -157,16 +159,18 @@ class TruSession(
"""Database Connector to use. If not provided, a default is created and
used."""

_experimental_otel_exporter: Optional[
Any
] = ( # Any = otel_export_sdk.SpanExporter
_experimental_otel_exporter: Optional[SpanExporter] = pydantic.PrivateAttr(
None
)

_experimental_tracer_provider: Optional[TracerProvider] = (
pydantic.PrivateAttr(None)
)

@property
def experimental_otel_exporter(
self,
) -> Any: # Any = Optional[otel_export_sdk.SpanExporter]
) -> Optional[SpanExporter]:
"""EXPERIMENTAL(otel_tracing): OpenTelemetry SpanExporter to send spans
to.

Expand All @@ -177,15 +181,38 @@ def experimental_otel_exporter(
return self._experimental_otel_exporter

@experimental_otel_exporter.setter
def experimental_otel_exporter(
self, value: Optional[Any]
): # Any = otel_export_sdk.SpanExporter
def experimental_otel_exporter(self, value: Optional[SpanExporter]):
otel_tracing_feature._FeatureSetup.assert_optionals_installed()

from trulens.experimental.otel_tracing.core.session import _TruSession

_TruSession._setup_otel_exporter(self, value)

def experimental_force_flush(
self, timeout_millis: Optional[int] = None
) -> bool:
"""
Force flush the OpenTelemetry exporters.

Args:
timeout_millis: The maximum amount of time to wait for spans to be
processed.

Returns:
False if the timeout is exceeded, feature is not enabled, or the provider doesn't exist, True otherwise.
"""
timeout_millis = timeout_millis or 300000
sfc-gh-gtokernliang marked this conversation as resolved.
Show resolved Hide resolved

if (
not self.experimental_feature(
core_experimental.Feature.OTEL_TRACING
)
or self._experimental_tracer_provider is None
):
return False

return self._experimental_tracer_provider.force_flush(timeout_millis)

def __str__(self) -> str:
return f"TruSession({self.connector})"

Expand All @@ -205,9 +232,7 @@ def __init__(
Iterable[core_experimental.Feature],
]
] = None,
_experimental_otel_exporter: Optional[
Any
] = None, # Any = otel_export_sdk.SpanExporter
_experimental_otel_exporter: Optional[SpanExporter] = None,
**kwargs,
):
if python_utils.safe_hasattr(self, "connector"):
Expand Down Expand Up @@ -236,24 +261,31 @@ def __init__(
f"Cannot provide both `connector` and connector argument(s) {extra_keys}."
)

connector = connector or core_connector.DefaultDBConnector(
**connector_args
)

super().__init__(
connector=connector
or core_connector.DefaultDBConnector(**connector_args),
connector=connector,
**self_args,
)

# for WithExperimentalSettings mixin
if experimental_feature_flags is not None:
self.experimental_set_features(experimental_feature_flags)

if _experimental_otel_exporter is not None:
otel_tracing_feature.assert_optionals_installed()
if _experimental_otel_exporter is not None or self.experimental_feature(
core_experimental.Feature.OTEL_TRACING
):
otel_tracing_feature._FeatureSetup.assert_optionals_installed()

from trulens.experimental.otel_tracing.core.session import (
_TruSession,
)

_TruSession._setup_otel_exporter(self, _experimental_otel_exporter)
_TruSession._setup_otel_exporter(
self, connector, _experimental_otel_exporter
)

def App(self, *args, app: Optional[Any] = None, **kwargs) -> base_app.App:
"""Create an App from the given App constructor arguments by guessing
Expand Down
1 change: 1 addition & 0 deletions src/core/trulens/core/utils/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ trulens-dashboard >= 1.0.0
# talk about these packages as required.
opentelemetry-api >= 1.0.0
opentelemetry-sdk >= 1.0.0
opentelemetry-proto >= 1.0.0
2 changes: 1 addition & 1 deletion src/core/trulens/experimental/otel_tracing/_feature.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"""Feature controlling the use of this module."""

REQUIREMENT = import_utils.format_import_errors(
["opentelemetry-api", "opentelemetry-sdk"],
["opentelemetry-api", "opentelemetry-sdk", "opentelemetry-proto"],
purpose="otel_tracing experimental feature",
)
"""Optional modules required for the otel_tracing experimental feature."""
Expand Down
Loading