Add two metrics to track

This commit is contained in:
Alek Petuskey 2024-07-31 14:04:51 -07:00
parent 0845d2ee76
commit 05442a4aca
5 changed files with 50 additions and 22 deletions

View File

@ -4,25 +4,34 @@ Only the app attribute is explicitly exposed.
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
import time
from reflex import constants from reflex import constants
from reflex.utils import telemetry
from reflex.utils.exec import is_prod_mode from reflex.utils.exec import is_prod_mode
from reflex.utils.prerequisites import get_app from reflex.utils.prerequisites import get_app
if constants.CompileVars.APP != "app": if constants.CompileVars.APP != "app":
raise AssertionError("unexpected variable name for 'app'") raise AssertionError("unexpected variable name for 'app'")
telemetry.send("compile")
app_module = get_app(reload=False) app_module = get_app(reload=False)
app = getattr(app_module, constants.CompileVars.APP) app = getattr(app_module, constants.CompileVars.APP)
# For py3.8 and py3.9 compatibility when redis is used, we MUST add any decorator pages # For py3.8 and py3.9 compatibility when redis is used, we MUST add any decorator pages
# before compiling the app in a thread to avoid event loop error (REF-2172). # before compiling the app in a thread to avoid event loop error (REF-2172).
app._apply_decorated_pages() app._apply_decorated_pages()
start_time = time.perf_counter()
def compile_callback(f):
from reflex.utils import telemetry
try:
# Force background compile errors to print eagerly
f.result()
finally:
telemetry.send("test-compile", duration=time.perf_counter()- start_time)
del telemetry
compile_future = ThreadPoolExecutor(max_workers=1).submit(app._compile) compile_future = ThreadPoolExecutor(max_workers=1).submit(app._compile)
compile_future.add_done_callback( compile_future.add_done_callback(compile_callback)
# Force background compile errors to print eagerly
lambda f: f.result()
)
# Wait for the compile to finish in prod mode to ensure all optional endpoints are mounted. # Wait for the compile to finish in prod mode to ensure all optional endpoints are mounted.
if is_prod_mode(): if is_prod_mode():
compile_future.result() compile_future.result()
@ -32,6 +41,6 @@ del app_module
del compile_future del compile_future
del get_app del get_app
del is_prod_mode del is_prod_mode
del telemetry
del constants del constants
del ThreadPoolExecutor del ThreadPoolExecutor

View File

@ -44,6 +44,7 @@ class Meta(BaseHTML): # Inherits common attributes from BaseHTML
"""Display the meta element.""" """Display the meta element."""
tag = "meta" tag = "meta"
char_set: Var[Union[str, int, bool]] char_set: Var[Union[str, int, bool]]
content: Var[Union[str, int, bool]] content: Var[Union[str, int, bool]]
http_equiv: Var[Union[str, int, bool]] http_equiv: Var[Union[str, int, bool]]

View File

@ -8,6 +8,7 @@ import webbrowser
from pathlib import Path from pathlib import Path
from typing import List, Optional from typing import List, Optional
import time
import typer import typer
import typer.core import typer.core
from reflex_cli.deployments import deployments_cli from reflex_cli.deployments import deployments_cli
@ -108,8 +109,13 @@ def _init(
raise typer.Exit(2) raise typer.Exit(2)
template = constants.Templates.DEFAULT template = constants.Templates.DEFAULT
start_time = time.perf_counter()
# Check if the app is already initialized.
reinit = os.path.exists(constants.Config.FILE)
# Initialize the app. # Initialize the app.
prerequisites.initialize_app(app_name, template) prerequisites.initialize_app(app_name, template, reinit=reinit)
# If a reflex.build generation hash is available, download the code and apply it to the main module. # If a reflex.build generation hash is available, download the code and apply it to the main module.
if generation_hash: if generation_hash:
@ -129,7 +135,10 @@ def _init(
# Finish initializing the app. # Finish initializing the app.
console.success(f"Initialized {app_name}") console.success(f"Initialized {app_name}")
# Post telemetry event
event_type = "reinit" if reinit else "init"
telemetry.send(event_type, duration=time.perf_counter() - start_time)
@cli.command() @cli.command()
def init( def init(
name: str = typer.Option( name: str = typer.Option(

View File

@ -1419,7 +1419,7 @@ def create_config_init_app_from_remote_template(
shutil.rmtree(unzip_dir) shutil.rmtree(unzip_dir)
def initialize_app(app_name: str, template: str | None = None): def initialize_app(app_name: str, template: str | None = None, reinit: bool = False):
"""Initialize the app either from a remote template or a blank app. If the config file exists, it is considered as reinit. """Initialize the app either from a remote template or a blank app. If the config file exists, it is considered as reinit.
Args: Args:
@ -1429,12 +1429,8 @@ def initialize_app(app_name: str, template: str | None = None):
Raises: Raises:
Exit: If template is directly provided in the command flag and is invalid. Exit: If template is directly provided in the command flag and is invalid.
""" """
# Local imports to avoid circular imports. # Check if the app is already initialized. If so, we don't need to init.
from reflex.utils import telemetry if reinit:
# Check if the app is already initialized.
if os.path.exists(constants.Config.FILE):
telemetry.send("reinit")
return return
# Get the available templates # Get the available templates
@ -1473,9 +1469,6 @@ def initialize_app(app_name: str, template: str | None = None):
template_url=template_url, template_url=template_url,
) )
telemetry.send("init", template=template)
def initialize_main_module_index_from_generation(app_name: str, generation_hash: str): def initialize_main_module_index_from_generation(app_name: str, generation_hash: str):
"""Overwrite the `index` function in the main module with reflex.build generated code. """Overwrite the `index` function in the main module with reflex.build generated code.

View File

@ -6,7 +6,7 @@ import asyncio
import multiprocessing import multiprocessing
import platform import platform
import warnings import warnings
import os
try: try:
from datetime import UTC, datetime from datetime import UTC, datetime
except ImportError: except ImportError:
@ -69,7 +69,7 @@ def get_cpu_count() -> int:
""" """
return multiprocessing.cpu_count() return multiprocessing.cpu_count()
def get_memory() -> int: def get_memory() -> int:
"""Get the total memory in MB. """Get the total memory in MB.
@ -80,6 +80,21 @@ def get_memory() -> int:
return psutil.virtual_memory().total >> 20 return psutil.virtual_memory().total >> 20
except ValueError: # needed to pass ubuntu test except ValueError: # needed to pass ubuntu test
return 0 return 0
def get_folder_size(folder: str) -> int:
"""Get the total size of a folder in bytes, ignoring 'node_modules' folder.
Args:
folder: The path to the folder.
Returns:
The total size of the folder in bytes.
"""
total_files = 0
for dirpath, dirnames, filenames in os.walk(folder):
total_files += len(filenames)
return total_files
def _raise_on_missing_project_hash() -> bool: def _raise_on_missing_project_hash() -> bool:
@ -128,7 +143,7 @@ def _prepare_event(event: str, **kwargs) -> dict:
cpuinfo = get_cpu_info() cpuinfo = get_cpu_info()
additional_keys = ["template", "context", "detail"] additional_keys = ["template", "context", "detail", "duration"]
additional_fields = { additional_fields = {
key: value for key in additional_keys if (value := kwargs.get(key)) is not None key: value for key in additional_keys if (value := kwargs.get(key)) is not None
} }
@ -145,6 +160,7 @@ def _prepare_event(event: str, **kwargs) -> dict:
"cpu_count": get_cpu_count(), "cpu_count": get_cpu_count(),
"memory": get_memory(), "memory": get_memory(),
"cpu_info": dict(cpuinfo) if cpuinfo else {}, "cpu_info": dict(cpuinfo) if cpuinfo else {},
"pages_count": get_folder_size(".web/pages") if event == "test-compile" or event == "run-dev" else None,
**additional_fields, **additional_fields,
}, },
"timestamp": stamp, "timestamp": stamp,