Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(Server): Add server telemetry (#1687)
(cherry picked from commit d57afd1) Some changes related to the usage telemetry: - Passing system data as event context (will simplify later integrations) - Show warning message on server start to clear preventing users - Include the Telemetry section inside the Reference doc.
- Loading branch information
1 parent
9613742
commit 106a8bd
Showing
21 changed files
with
338 additions
and
45 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
# Telemetry | ||
Rubrix uses telemetry to report anonymous usage and error information. As an open-source software, this type of information is important to improve and understand how the product is used. | ||
|
||
## How to opt-out | ||
You can opt-out of telemetry reporting using the `ENV` variable `RUBRIX_ENABLE_TELEMETRY` before launching the server. Setting this variable to `0` will completely disable telemetry reporting. | ||
|
||
If you are a Linux/MacOs users you should run: | ||
|
||
```bash | ||
export RUBRIX_ENABLE_TELEMETRY=0 | ||
``` | ||
|
||
If you are Windows users you should run: | ||
|
||
```bash | ||
set RUBRIX_ENABLE_TELEMETRY=0 | ||
``` | ||
|
||
To opt-in again, you can set the variable to `1`. | ||
|
||
## Why reporting telemetry | ||
Anonymous telemetry information enable us to continously improve the product and detect recurring problems to better serve all users. We collect aggregated information about general usage and errors. We do NOT collect any information of users' data records, datasets, or metadata information. | ||
|
||
## Sensitive data | ||
We do not collect any piece of information related to the source data you store in Rubrix. We don't identify individual users. Your data does not leave your server at any time: | ||
|
||
* No dataset record is collected. | ||
* No dataset names or metadata are collected. | ||
|
||
## Information reported | ||
The following usage and error information is reported: | ||
|
||
* The code of the raised error | ||
* The `user-agent` and `accept-language` http headers | ||
* Task name and number of records for bulk operations | ||
* An anonymous generated user uuid | ||
* The rubrix version running the server | ||
* The python version, e.g. `3.8.13` | ||
* The system/OS name, such as `Linux`, `Darwin`, `Windows` | ||
* The system’s release version, e.g. `Darwin Kernel Version 21.5.0: Tue Apr 26 21:08:22 PDT 2022; root:xnu-8020` | ||
* The machine type, e.g. `AMD64` | ||
* The underlying platform spec with as much useful information as possible. (ej. `macOS-10.16-x86_64-i386-64bit`) | ||
|
||
|
||
This is performed by registering information from the following API methods: | ||
|
||
* `/api/me` | ||
* `/api/dataset/{name}/{task}:bulk` | ||
* Raised server API errors | ||
|
||
|
||
For transparency, you can inspect the source code where this is performed here (add link to the source). | ||
|
||
If you have any doubts, don't hesitate to join our [Slack channel](https://join.slack.com/t/rubrixworkspace/shared_invite/zt-whigkyjn-a3IUJLD7gDbTZ0rKlvcJ5g) or open a GitHub issue. We'd be very happy to discuss about how we can improve this. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
import dataclasses | ||
import logging | ||
import platform | ||
import uuid | ||
from typing import Any, Dict, Optional | ||
|
||
import httpx | ||
from fastapi import Request | ||
|
||
from rubrix.server.commons.models import TaskType | ||
from rubrix.server.errors.base_errors import RubrixServerError | ||
from rubrix.server.settings import settings | ||
|
||
try: | ||
from analytics import Client | ||
except ModuleNotFoundError: | ||
# TODO: show some warning info | ||
settings.enable_telemetry = False | ||
Client = None | ||
|
||
|
||
def _configure_analytics(disable_send: bool = False) -> Client: | ||
API_KEY = settings.telemetry_key or "C6FkcaoCbt78rACAgvyBxGBcMB3dM3nn" | ||
TELEMETRY_HOST = "https://api.segment.io" | ||
|
||
# Check host connection | ||
httpx.options(TELEMETRY_HOST, timeout=1, verify=False) | ||
|
||
return Client( | ||
write_key=API_KEY, | ||
gzip=True, | ||
host=TELEMETRY_HOST, | ||
send=not disable_send, | ||
max_retries=5, | ||
) | ||
|
||
|
||
@dataclasses.dataclass | ||
class _TelemetryClient: | ||
|
||
client: Client | ||
|
||
__INSTANCE__: "_TelemetryClient" = None | ||
__server_id__: Optional[uuid.UUID] = dataclasses.field(init=False, default=None) | ||
|
||
@property | ||
def server_id(self) -> uuid.UUID: | ||
return self.__server_id__ | ||
|
||
@classmethod | ||
def get(cls): | ||
if settings.enable_telemetry: | ||
if cls.__INSTANCE__ is None: | ||
try: | ||
cls.__INSTANCE__ = cls(client=_configure_analytics()) | ||
except Exception as err: | ||
logging.getLogger(__name__).warning( | ||
f"Cannot initialize telemetry. Error: {err}. Disabling..." | ||
) | ||
settings.enable_telemetry = False | ||
return None | ||
return cls.__INSTANCE__ | ||
|
||
def __post_init__(self): | ||
|
||
from rubrix import __version__ | ||
|
||
self.__server_id__ = uuid.UUID(int=uuid.getnode()) | ||
self.__server_id_str__ = str(self.__server_id__) | ||
self.__system_info__ = { | ||
"system": platform.system(), | ||
"machine": platform.machine(), | ||
"platform": platform.platform(), | ||
"python_version": platform.python_version(), | ||
"sys_version": platform.version(), | ||
"version": __version__, | ||
} | ||
|
||
def track_data( | ||
self, action: str, data: Dict[str, Any], include_system_info: bool = True | ||
): | ||
event_data = data.copy() | ||
self.client.track( | ||
user_id=self.__server_id_str__, | ||
event=action, | ||
properties=event_data, | ||
context=self.__system_info__ if include_system_info else {}, | ||
) | ||
|
||
|
||
def _process_request_info(request: Request): | ||
return { | ||
header: request.headers.get(header) | ||
for header in ["user-agent", "accept-language"] | ||
} | ||
|
||
|
||
async def track_error(error: RubrixServerError, request: Request): | ||
client = _TelemetryClient.get() | ||
if client: | ||
client.track_data( | ||
"ServerErrorFound", {"code": error.code, **_process_request_info(request)} | ||
) | ||
|
||
|
||
async def track_bulk(task: TaskType, records: int): | ||
client = _TelemetryClient.get() | ||
if client: | ||
client.track_data("LogRecordsRequested", {"task": task, "records": records}) | ||
|
||
|
||
async def track_login(request: Request, username: str): | ||
client = _TelemetryClient.get() | ||
if client: | ||
client.track_data( | ||
"UserInfoRequested", | ||
{ | ||
"is_default_user": username == "rubrix", | ||
"user_hash": str(uuid.uuid5(namespace=client.server_id, name=username)), | ||
**_process_request_info(request), | ||
}, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.