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

instrument NADE commands using metric spans #1844

Merged
merged 30 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
a2a833e
draft PR for feedback on instrumentation
sfc-gh-mchok Nov 8, 2024
46a3028
remove changes on loggers
sfc-gh-mchok Nov 8, 2024
cf3a339
Merge branch 'main' into mchok-metric-spans-instrument-nade-cmds
sfc-gh-mchok Nov 8, 2024
d26af10
extract enum value for proper key to message dict
sfc-gh-mchok Nov 8, 2024
6b3d12e
write custom decorator for spans, add tests
sfc-gh-mchok Nov 11, 2024
ca990d4
Merge branch 'main' into mchok-metric-spans-instrument-nade-cmds
sfc-gh-mchok Nov 11, 2024
4edd8b2
rename spans as logical steps, remove extra param from console phase,…
sfc-gh-mchok Nov 12, 2024
f99a275
adjust tests for new spans
sfc-gh-mchok Nov 12, 2024
db00d94
time.monotonic() -> time.perf_counter() for proper ordering on windows
sfc-gh-mchok Nov 12, 2024
84209f0
add span for getting snowsight url since it is a blind spot
sfc-gh-mchok Nov 12, 2024
506bae2
Merge branch 'main' into mchok-metric-spans-instrument-nade-cmds
sfc-gh-mchok Nov 13, 2024
7846c43
Merge branch 'main' into mchok-metric-spans-instrument-nade-cmds
sfc-gh-mchok Nov 13, 2024
ec44e0c
Merge remote-tracking branch 'origin' into mchok-metric-spans-instrum…
sfc-gh-mchok Nov 13, 2024
d5e2376
add event sharing counters from merge conflicts to new file
sfc-gh-mchok Nov 13, 2024
15955f7
add span for create_or_upgrade_app to address blind spot
sfc-gh-mchok Nov 13, 2024
1b1d99a
Merge branch 'main' into mchok-metric-spans-instrument-nade-cmds
sfc-gh-mchok Nov 14, 2024
a32a21b
Merge branch 'main' into mchok-metric-spans-instrument-nade-cmds
sfc-gh-mchok Nov 14, 2024
b77d135
adjust span names for consistency
sfc-gh-mchok Nov 14, 2024
cef9f19
add docstring for entity action span decorator
sfc-gh-mchok Nov 14, 2024
8a45192
fix spans bundle test
sfc-gh-mchok Nov 14, 2024
bf8cf5b
Merge branch 'main' into mchok-metric-spans-instrument-nade-cmds
sfc-gh-mchok Nov 18, 2024
ae43f25
sort spans deterministically by creation order
sfc-gh-mchok Nov 18, 2024
93e5dc9
Merge branch 'main' into mchok-metric-spans-instrument-nade-cmds
sfc-gh-mchok Nov 18, 2024
bfc4f21
Merge branch 'main' into mchok-metric-spans-instrument-nade-cmds
sfc-gh-mchok Nov 18, 2024
5e1e38c
Merge branch 'main' into mchok-metric-spans-instrument-nade-cmds
sfc-gh-mchok Nov 18, 2024
348aac2
Merge branch 'main' into mchok-metric-spans-instrument-nade-cmds
sfc-gh-mchok Nov 19, 2024
c618da6
only one sort, remove counter
sfc-gh-mchok Nov 19, 2024
2fbdcb6
Merge branch 'main' into mchok-metric-spans-instrument-nade-cmds
sfc-gh-mchok Nov 20, 2024
05d81a9
Merge branch 'main' into mchok-metric-spans-instrument-nade-cmds
sfc-gh-mchok Nov 20, 2024
e96a0ee
update comment for span decorator to reflect new method name
sfc-gh-mchok Nov 21, 2024
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
13 changes: 10 additions & 3 deletions src/snowflake/cli/_app/telemetry.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ class CLITelemetryField(Enum):
CONFIG_FEATURE_FLAGS = "config_feature_flags"
# Metrics
COUNTERS = "counters"
SPANS = "spans"
COMPLETED_SPANS = "completed_spans"
NUM_SPANS_PAST_DEPTH_LIMIT = "num_spans_past_depth_limit"
NUM_SPANS_PAST_TOTAL_LIMIT = "num_spans_past_total_limit"
# Information
EVENT = "event"
ERROR_MSG = "error_msg"
Expand Down Expand Up @@ -129,9 +133,12 @@ def _get_command_metrics() -> TelemetryDict:
cli_context = get_cli_context()

return {
CLITelemetryField.COUNTERS: {
**cli_context.metrics.counters,
}
CLITelemetryField.COUNTERS: cli_context.metrics.counters,
CLITelemetryField.SPANS: {
CLITelemetryField.COMPLETED_SPANS.value: cli_context.metrics.completed_spans,
CLITelemetryField.NUM_SPANS_PAST_DEPTH_LIMIT.value: cli_context.metrics.num_spans_past_depth_limit,
CLITelemetryField.NUM_SPANS_PAST_TOTAL_LIMIT.value: cli_context.metrics.num_spans_past_total_limit,
},
}


Expand Down
2 changes: 1 addition & 1 deletion src/snowflake/cli/_plugins/nativeapp/codegen/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ def compile_artifacts(self):
if not self._should_invoke_processors():
return

with cc.phase("Invoking artifact processors"):
with cc.phase("Invoking artifact processors", span_name="compile_artifacts"):
Copy link
Contributor

Choose a reason for hiding this comment

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

there's no compilation happening. How about simply "artifact_processors"? We should probably also have individual processors as sub-spans.

if self._bundle_ctx.generated_root.exists():
raise ClickException(
f"Path {self._bundle_ctx.generated_root} already exists. Please choose a different name for your generated directory in the project definition file."
Expand Down
10 changes: 6 additions & 4 deletions src/snowflake/cli/_plugins/nativeapp/entities/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
from snowflake.cli._plugins.nativeapp.sf_facade import get_snowflake_facade
from snowflake.cli._plugins.nativeapp.utils import needs_confirmation
from snowflake.cli._plugins.workspace.context import ActionContext
from snowflake.cli.api.cli_global_context import start_cli_metrics_span
from snowflake.cli.api.entities.common import EntityBase, get_sql_executor
from snowflake.cli.api.entities.utils import (
drop_generic_object,
Expand Down Expand Up @@ -420,6 +421,7 @@ def get_objects_owned_by_application(self) -> List[ApplicationOwnedObject]:
).fetchall()
return [{"name": row[1], "type": row[2]} for row in results]

@start_cli_metrics_span("create_or_upgrade_app")
Copy link
Contributor

Choose a reason for hiding this comment

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

spans shouldn't be implementation-specific, but rather represent a logic step in the overall operation being performed.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Adjusted the names and locations of all the spans to better encapsulate this, let me know if they work for you!

def create_or_upgrade_app(
self,
package: ApplicationPackageEntity,
Expand Down Expand Up @@ -513,10 +515,10 @@ def create_or_upgrade_app(
create_cursor = sql_executor.execute_query(
dedent(
f"""\
create application {self.name}
from application package {package.name} {using_clause} {debug_mode_clause}
comment = {SPECIAL_COMMENT}
"""
create application {self.name}
from application package {package.name} {using_clause} {debug_mode_clause}
comment = {SPECIAL_COMMENT}
"""
),
)
print_messages(console, create_cursor)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
from snowflake.cli._plugins.stage.diff import DiffResult
from snowflake.cli._plugins.stage.manager import StageManager
from snowflake.cli._plugins.workspace.context import ActionContext
from snowflake.cli.api.cli_global_context import start_cli_metrics_span
from snowflake.cli.api.entities.common import EntityBase, get_sql_executor
from snowflake.cli.api.entities.utils import (
drop_generic_object,
Expand Down Expand Up @@ -915,6 +916,7 @@ def validate_setup_script(
if validation_result["status"] == "FAIL":
raise SetupScriptFailedValidation()

@start_cli_metrics_span("get_validation_result")
Copy link
Contributor

Choose a reason for hiding this comment

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

ditto, let's not name things according to the implementation. That can change over time.

def get_validation_result(
self, use_scratch_stage: bool, interactive: bool, force: bool
):
Expand Down
5 changes: 4 additions & 1 deletion src/snowflake/cli/_plugins/workspace/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,10 @@ def perform_action(self, entity_id: str, action: EntityActions, *args, **kwargs)
action_ctx = ActionContext(
get_entity=self.get_entity,
)
return entity.perform(action, action_ctx, *args, **kwargs)
with get_cli_context().metrics.start_span(
f"{type(entity).__name__}.{action.value}"
):
return entity.perform(action, action_ctx, *args, **kwargs)
else:
raise ValueError(f'This entity type does not support "{action.value}"')

Expand Down
23 changes: 22 additions & 1 deletion src/snowflake/cli/api/cli_global_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from contextlib import contextmanager
from contextvars import ContextVar
from dataclasses import dataclass, field, replace
from functools import wraps
from pathlib import Path
from typing import TYPE_CHECKING, Iterator

Expand Down Expand Up @@ -157,7 +158,7 @@ def enable_tracebacks(self) -> bool:
return self._manager.enable_tracebacks

@property
def metrics(self):
def metrics(self) -> CLIMetrics:
return self._manager.metrics

@property
Expand Down Expand Up @@ -213,6 +214,26 @@ def get_cli_context() -> _CliGlobalContextAccess:
return _CliGlobalContextAccess(get_cli_context_manager())


def start_cli_metrics_span(span_name: str):
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think "start" makes sense here, since this both starts and stops the span at the appropriate time. Let's workshop the name of this + the start_span method it calls.

Copy link
Contributor

Choose a reason for hiding this comment

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

+1, I feel @span would suffice

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Changed both the method name and the decorator name to just span, let me know if this works for you

"""
Decorator to start a command metrics span that encompasses a whole function

Must be used instead of directly calling @get_cli_context().metrics.start_span(span_name)
Copy link
Contributor

Choose a reason for hiding this comment

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

it's span now, not start_span

as a decorator to ensure that the cli context is grabbed at run time instead of at
module load time, which would not reflect forking
"""

def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
with get_cli_context().metrics.start_span(span_name):
return func(*args, **kwargs)

return wrapper

return decorator


@contextmanager
def fork_cli_context(
connection_overrides: dict | None = None,
Expand Down
1 change: 1 addition & 0 deletions src/snowflake/cli/api/console/abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ def phase(
self,
enter_message: str,
exit_message: Optional[str] = None,
span_name: Optional[str] = None,
Copy link
Contributor

Choose a reason for hiding this comment

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

not sure if this is truly needed, since we can use multiple context managers on the same line. Thoughts?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I forgot about that feature, I adjusted these instances, thanks for the reminder

) -> Iterator[Callable[[str], None]]:
"""A context manager for organising steps into logical group."""

Expand Down
15 changes: 12 additions & 3 deletions src/snowflake/cli/api/console/console.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@

from __future__ import annotations

from contextlib import contextmanager
from contextlib import contextmanager, nullcontext
from typing import Optional

from rich.style import Style
from rich.text import Text
from snowflake.cli.api.cli_global_context import get_cli_context
from snowflake.cli.api.console.abc import AbstractConsole
from snowflake.cli.api.console.enum import Output

Expand Down Expand Up @@ -68,7 +69,12 @@ def _format_message(self, message: str, output: Output) -> Text:
return text

@contextmanager
def phase(self, enter_message: str, exit_message: Optional[str] = None):
def phase(
Copy link
Contributor

Choose a reason for hiding this comment

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

you're no longer touching this, I'd revert the file entirely

self,
enter_message: str,
exit_message: Optional[str] = None,
span_name: Optional[str] = None,
):
"""A context manager for organising steps into logical group."""
if self.in_phase:
raise CliConsoleNestingProhibitedError("Only one phase allowed at a time.")
Expand All @@ -81,7 +87,10 @@ def phase(self, enter_message: str, exit_message: Optional[str] = None):
self._in_phase = True

try:
yield self.step
with get_cli_context().metrics.start_span(
span_name
) if span_name else nullcontext():
yield self.step
finally:
self._in_phase = False
if exit_message:
Expand Down
9 changes: 7 additions & 2 deletions src/snowflake/cli/api/entities/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
to_stage_path,
)
from snowflake.cli._plugins.stage.utils import print_diff_to_console
from snowflake.cli.api.cli_global_context import get_cli_context
from snowflake.cli.api.cli_global_context import get_cli_context, start_cli_metrics_span
from snowflake.cli.api.console.abc import AbstractConsole
from snowflake.cli.api.entities.common import get_sql_executor
from snowflake.cli.api.errno import (
Expand Down Expand Up @@ -76,6 +76,7 @@ def _get_stage_paths_to_sync(
return stage_paths


@start_cli_metrics_span("sync_deploy_root_with_stage")
def sync_deploy_root_with_stage(
console: AbstractConsole,
deploy_root: Path,
Expand Down Expand Up @@ -219,7 +220,10 @@ def execute_post_deploy_hooks(

get_cli_context().metrics.set_counter(CLICounterField.POST_DEPLOY_SCRIPTS, 1)

with console.phase(f"Executing {deployed_object_type} post-deploy actions"):
with console.phase(
f"Executing {deployed_object_type} post-deploy actions",
span_name="execute_post_deploy_hooks",
):
sql_scripts_paths = []
display_paths = []
for hook in post_deploy_hooks:
Expand Down Expand Up @@ -304,6 +308,7 @@ def validation_item_to_str(item: dict[str, str | int]):
return s


@start_cli_metrics_span("drop_generic_object")
Copy link
Contributor

Choose a reason for hiding this comment

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

same here

def drop_generic_object(
console: AbstractConsole,
object_type: str,
Expand Down
Loading
Loading