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

Add opt-out exception logging telemetry #1535

Merged
merged 5 commits into from
May 19, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
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
4 changes: 2 additions & 2 deletions docs/feast-on-kubernetes/advanced-1/telemetry.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

## How telemetry is used

The Feast maintainers use anonymous usage statistics to help shape the Feast roadmap. Several client methods are tracked, beginning in Feast 0.9. Users are assigned a UUID which is sent along with the name of the method, the Feast version, the OS \(using `sys.platform`\), and the current time. For more detailed information see [the source code](https://github.com/feast-dev/feast/blob/master/sdk/python/feast/telemetry.py).
The Feast maintainers use anonymous usage statistics and error tracking to help shape the Feast roadmap. Several client methods are tracked, beginning in Feast 0.9. Users are assigned a UUID which is sent along with the name of the method, the Feast version, the OS \(using `sys.platform`\), and the current time. For more detailed information see [the source code](https://github.com/feast-dev/feast/blob/master/sdk/python/feast/telemetry.py).
Copy link
Member

Choose a reason for hiding this comment

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

@jklegar can we pull the Telemetry docs out of feast-on-kubernetes? What about creating a Telemetry page under Reference?


## How to disable telemetry

To opt out of telemetry, simply set the environment variable `FEAST_TELEMETRY` to `False` in the environment in which the Feast client is run.
To opt out of all telemetry, simply set the environment variable `FEAST_TELEMETRY` to `False` in the environment in which the Feast client is run.

9 changes: 0 additions & 9 deletions sdk/python/feast/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
registry_dump,
teardown,
)
from feast.telemetry import Telemetry

_logger = logging.getLogger(__name__)
DATETIME_ISO = "%Y-%m-%dT%H:%M:%s"
Expand Down Expand Up @@ -175,8 +174,6 @@ def apply_total_command(ctx: click.Context):
repo = ctx.obj["CHDIR"]
cli_check_repo(repo)
repo_config = load_repo_config(repo)
tele = Telemetry()
tele.log("apply")
try:
apply_total(repo_config, repo)
except FeastProviderLoginError as e:
Expand All @@ -192,8 +189,6 @@ def teardown_command(ctx: click.Context):
repo = ctx.obj["CHDIR"]
cli_check_repo(repo)
repo_config = load_repo_config(repo)
tele = Telemetry()
tele.log("teardown")

teardown(repo_config, repo)

Expand All @@ -207,8 +202,6 @@ def registry_dump_command(ctx: click.Context):
repo = ctx.obj["CHDIR"]
cli_check_repo(repo)
repo_config = load_repo_config(repo)
tele = Telemetry()
tele.log("registry-dump")

registry_dump(repo_config, repo_path=repo)

Expand Down Expand Up @@ -285,8 +278,6 @@ def init_command(project_directory, minimal: bool, template: str):
if minimal:
template = "minimal"

tele = Telemetry()
tele.log("init")
init_repo(project_directory, template)


Expand Down
2 changes: 2 additions & 0 deletions sdk/python/feast/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from feast.protos.feast.core.Entity_pb2 import Entity as EntityV2Proto
from feast.protos.feast.core.Entity_pb2 import EntityMeta as EntityMetaProto
from feast.protos.feast.core.Entity_pb2 import EntitySpecV2 as EntitySpecProto
from feast.telemetry import log_exceptions
from feast.value_type import ValueType


Expand All @@ -31,6 +32,7 @@ class Entity:
Represents a collection of entities and associated metadata.
"""

@log_exceptions
def __init__(
self,
name: str,
Expand Down
30 changes: 16 additions & 14 deletions sdk/python/feast/feature_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
from feast.protos.feast.types.EntityKey_pb2 import EntityKey as EntityKeyProto
from feast.registry import Registry
from feast.repo_config import RepoConfig, load_repo_config
from feast.telemetry import Telemetry
from feast.telemetry import log_exceptions, log_exceptions_and_usage
from feast.version import get_version


Expand All @@ -52,6 +52,7 @@ class FeatureStore:
repo_path: Path
_registry: Registry

@log_exceptions
def __init__(
self, repo_path: Optional[str] = None, config: Optional[RepoConfig] = None,
):
Expand All @@ -72,8 +73,8 @@ def __init__(
repo_path=self.repo_path,
cache_ttl=timedelta(seconds=registry_config.cache_ttl_seconds),
)
self._tele = Telemetry()

@log_exceptions
def version(self) -> str:
"""Returns the version of the current Feast SDK/CLI"""

Expand All @@ -87,6 +88,7 @@ def _get_provider(self) -> Provider:
# TODO: Bake self.repo_path into self.config so that we dont only have one interface to paths
return get_provider(self.config, self.repo_path)

@log_exceptions_and_usage
def refresh_registry(self):
"""Fetches and caches a copy of the feature registry in memory.

Expand All @@ -101,7 +103,6 @@ def refresh_registry(self):
greater than 0, then once the cache becomes stale (more time than the TTL has passed), a new cache will be
downloaded synchronously, which may increase latencies if the triggering method is get_online_features()
"""
self._tele.log("refresh_registry")

registry_config = self.config.get_registry_config()
self._registry = Registry(
Expand All @@ -111,6 +112,7 @@ def refresh_registry(self):
)
self._registry.refresh()

@log_exceptions_and_usage
def list_entities(self, allow_cache: bool = False) -> List[Entity]:
"""
Retrieve a list of entities from the registry
Expand All @@ -121,21 +123,21 @@ def list_entities(self, allow_cache: bool = False) -> List[Entity]:
Returns:
List of entities
"""
self._tele.log("list_entities")

return self._registry.list_entities(self.project, allow_cache=allow_cache)

@log_exceptions_and_usage
def list_feature_views(self) -> List[FeatureView]:
"""
Retrieve a list of feature views from the registry

Returns:
List of feature views
"""
self._tele.log("list_feature_views")

return self._registry.list_feature_views(self.project)

@log_exceptions_and_usage
def get_entity(self, name: str) -> Entity:
"""
Retrieves an entity.
Expand All @@ -147,10 +149,10 @@ def get_entity(self, name: str) -> Entity:
Returns either the specified entity, or raises an exception if
none is found
"""
self._tele.log("get_entity")

return self._registry.get_entity(name, self.project)

@log_exceptions_and_usage
def get_feature_view(self, name: str) -> FeatureView:
"""
Retrieves a feature view.
Expand All @@ -162,21 +164,21 @@ def get_feature_view(self, name: str) -> FeatureView:
Returns either the specified feature view, or raises an exception if
none is found
"""
self._tele.log("get_feature_view")

return self._registry.get_feature_view(name, self.project)

@log_exceptions_and_usage
def delete_feature_view(self, name: str):
"""
Deletes a feature view or raises an exception if not found.

Args:
name: Name of feature view
"""
self._tele.log("delete_feature_view")

return self._registry.delete_feature_view(name, self.project)

@log_exceptions_and_usage
def apply(
self, objects: Union[Entity, FeatureView, List[Union[FeatureView, Entity]]]
):
Expand Down Expand Up @@ -210,13 +212,13 @@ def apply(
>>> fs.apply([customer_entity, customer_feature_view])
"""

self._tele.log("apply")

# TODO: Add locking
# TODO: Optimize by only making a single call (read/write)

if isinstance(objects, Entity) or isinstance(objects, FeatureView):
objects = [objects]
assert isinstance(objects, list)

views_to_update = []
entities_to_update = []
for ob in objects:
Expand All @@ -239,6 +241,7 @@ def apply(
partial=True,
)

@log_exceptions_and_usage
def get_historical_features(
self, entity_df: Union[pd.DataFrame, str], feature_refs: List[str],
) -> RetrievalJob:
Expand Down Expand Up @@ -279,7 +282,6 @@ def get_historical_features(
>>> feature_data = job.to_df()
>>> model.fit(feature_data) # insert your modeling framework here.
"""
self._tele.log("get_historical_features")

all_feature_views = self._registry.list_feature_views(project=self.project)
try:
Expand All @@ -304,6 +306,7 @@ def get_historical_features(

return job

@log_exceptions_and_usage
def materialize_incremental(
self, end_date: datetime, feature_views: Optional[List[str]] = None,
) -> None:
Expand All @@ -330,7 +333,6 @@ def materialize_incremental(
>>> fs = FeatureStore(config=RepoConfig(provider="gcp", registry="gs://my-fs/", project="my_fs_proj"))
>>> fs.materialize_incremental(end_date=datetime.utcnow() - timedelta(minutes=5))
"""
self._tele.log("materialize_incremental")

feature_views_to_materialize = []
if feature_views is None:
Expand Down Expand Up @@ -377,6 +379,7 @@ def tqdm_builder(length):
tqdm_builder,
)

@log_exceptions_and_usage
def materialize(
self,
start_date: datetime,
Expand Down Expand Up @@ -408,7 +411,6 @@ def materialize(
>>> start_date=datetime.utcnow() - timedelta(hours=3), end_date=datetime.utcnow() - timedelta(minutes=10)
>>> )
"""
self._tele.log("materialize")

if utils.make_tzaware(start_date) > utils.make_tzaware(end_date):
raise ValueError(
Expand Down Expand Up @@ -448,6 +450,7 @@ def tqdm_builder(length):
tqdm_builder,
)

@log_exceptions_and_usage
def get_online_features(
self, feature_refs: List[str], entity_rows: List[Dict[str, Any]],
) -> OnlineResponse:
Expand Down Expand Up @@ -484,7 +487,6 @@ def get_online_features(
>>> print(online_response_dict)
{'sales:daily_transactions': [1.1,1.2], 'sales:customer_id': [0,1]}
"""
self._tele.log("get_online_features")

provider = self._get_provider()
entities = self.list_entities(allow_cache=True)
Expand Down
2 changes: 2 additions & 0 deletions sdk/python/feast/feature_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
from feast.protos.feast.core.FeatureView_pb2 import (
MaterializationInterval as MaterializationIntervalProto,
)
from feast.telemetry import log_exceptions
from feast.value_type import ValueType


Expand All @@ -52,6 +53,7 @@ class FeatureView:
last_updated_timestamp: Optional[Timestamp] = None
materialization_intervals: List[Tuple[datetime, datetime]]

@log_exceptions
def __init__(
self,
name: str,
Expand Down
3 changes: 3 additions & 0 deletions sdk/python/feast/repo_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
from pydantic.error_wrappers import ErrorWrapper
from pydantic.typing import Dict, Literal, Optional, Union

from feast.telemetry import log_exceptions


class FeastBaseModel(BaseModel):
""" Feast Pydantic Configuration Class """
Expand Down Expand Up @@ -75,6 +77,7 @@ def get_registry_config(self):
return self.registry

@root_validator(pre=True)
@log_exceptions
def _validate_online_store_config(cls, values):
# This method will validate whether the online store configurations are set correctly. This explicit validation
# is necessary because Pydantic Unions throw very verbose and cryptic exceptions. We also use this method to
Expand Down
5 changes: 5 additions & 0 deletions sdk/python/feast/repo_operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from feast.names import adjectives, animals
from feast.registry import Registry
from feast.repo_config import RepoConfig
from feast.telemetry import log_exceptions_and_usage


def py_path_to_module(path: Path, repo_root: Path) -> str:
Expand Down Expand Up @@ -103,6 +104,7 @@ def parse_repo(repo_root: Path) -> ParsedRepo:
return res


@log_exceptions_and_usage
def apply_total(repo_config: RepoConfig, repo_path: Path):
from colorama import Fore, Style

Expand Down Expand Up @@ -209,6 +211,7 @@ def apply_total(repo_config: RepoConfig, repo_path: Path):
)


@log_exceptions_and_usage
def teardown(repo_config: RepoConfig, repo_path: Path):
registry_config = repo_config.get_registry_config()
registry = Registry(
Expand All @@ -229,6 +232,7 @@ def teardown(repo_config: RepoConfig, repo_path: Path):
)


@log_exceptions_and_usage
def registry_dump(repo_config: RepoConfig, repo_path: Path):
""" For debugging only: output contents of the metadata registry """
registry_config = repo_config.get_registry_config()
Expand All @@ -255,6 +259,7 @@ def cli_check_repo(repo_path: Path):
sys.exit(1)


@log_exceptions_and_usage
def init_repo(repo_name: str, template: str):
import os
from distutils.dir_util import copy_tree
Expand Down
Loading