-
Notifications
You must be signed in to change notification settings - Fork 1
fix: Add management command monitoring pipeline #16
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
base: main
Are you sure you want to change the base?
Changes from 16 commits
02ad9fb
1c76575
59a6f4a
75ceb43
fa0c178
2d7cff4
cbf2c90
4fb153c
f576d94
d898455
f85f733
f158b15
aa5521e
5ea2b55
79aec8c
5e28326
94c8d4a
58369d5
137b948
3964f01
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| """Management command filter pipeline integrations.""" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| """Pipeline steps for management command integrations.""" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,154 @@ | ||
| """Pipeline steps for management command observability.""" | ||
|
|
||
| import logging | ||
| import os | ||
| import time | ||
| from contextlib import contextmanager, nullcontext | ||
|
|
||
| from edx_django_utils.monitoring import ( | ||
| function_trace, | ||
| set_custom_attribute, | ||
| set_monitoring_transaction_name, | ||
| ) | ||
| from openedx_filters import PipelineStep | ||
|
|
||
| from edx_filters_pipelines.waffle import ENABLE_MANAGEMENT_COMMAND_MONITORING | ||
|
|
||
| log = logging.getLogger(__name__) | ||
|
|
||
| DEFAULT_OPERATION_NAME = 'django.management.command' | ||
|
|
||
|
|
||
| @contextmanager | ||
| def monitor_management_command( | ||
| command_name, | ||
| service_variant, | ||
| operation_name=DEFAULT_OPERATION_NAME, | ||
| ): | ||
| """ | ||
| Wrap a management command execution with monitoring metadata and logging. | ||
|
|
||
| The operation name identifies the type of operation, while the resource | ||
| name identifies the specific management command being executed. | ||
| """ | ||
| resource_name = f'{service_variant}.management.{command_name}' | ||
|
|
||
| set_monitoring_transaction_name(resource_name) | ||
| set_custom_attribute('management_command.name', command_name) | ||
| set_custom_attribute('management_command.service_variant', service_variant) | ||
|
|
||
| github_run_url = os.getenv('EDX_MC_GITHUB_RUN_URL', '').strip() | ||
| if github_run_url: | ||
| set_custom_attribute('management_command.github_run_url', github_run_url) | ||
|
|
||
| log.info( | ||
| 'Starting management command: %s service_variant=%s ' | ||
| 'operation_name=%s resource_name=%s', | ||
| command_name, | ||
| service_variant, | ||
| operation_name, | ||
| resource_name, | ||
| ) | ||
|
|
||
| start_time = time.monotonic() | ||
| status = 'failure' | ||
|
|
||
| try: | ||
| with function_trace(resource_name, operation_name=operation_name): | ||
| yield | ||
|
|
||
| status = 'success' | ||
|
|
||
| except SystemExit as exc: | ||
| if exc.code in (0, None): | ||
| status = 'success' | ||
| else: | ||
| set_custom_attribute('management_command.exception_class', exc.__class__.__name__) | ||
| set_custom_attribute('management_command.exit_code', exc.code) | ||
| set_custom_attribute('management_command.exception_message', str(exc)) | ||
| log.exception( | ||
| 'Management command failed: %s service_variant=%s ' | ||
| 'operation_name=%s resource_name=%s exit_code=%s error=%s', | ||
| command_name, | ||
| service_variant, | ||
| operation_name, | ||
| resource_name, | ||
| exc.code, | ||
| exc, | ||
| ) | ||
| raise | ||
|
|
||
| except Exception as exc: | ||
| set_custom_attribute('management_command.exception_class', exc.__class__.__name__) | ||
| set_custom_attribute('management_command.exception_message', str(exc)) | ||
| log.exception( | ||
| 'Management command failed: %s service_variant=%s ' | ||
| 'operation_name=%s resource_name=%s exception_class=%s error=%s', | ||
| command_name, | ||
| service_variant, | ||
| operation_name, | ||
| resource_name, | ||
| exc.__class__.__name__, | ||
| exc, | ||
| ) | ||
| raise | ||
|
|
||
| finally: | ||
| duration = time.monotonic() - start_time | ||
| set_custom_attribute('management_command.status', status) | ||
| set_custom_attribute('management_command.duration_seconds', duration) | ||
| log.info( | ||
| 'Finished management command: %s service_variant=%s ' | ||
| 'operation_name=%s resource_name=%s status=%s duration_seconds=%s', | ||
| command_name, | ||
| service_variant, | ||
| operation_name, | ||
| resource_name, | ||
| status, | ||
| duration, | ||
| ) | ||
|
|
||
|
|
||
| class ManagementCommandMonitoringPipelineStep(PipelineStep): | ||
| """ | ||
| Add monitoring around Django management command execution. | ||
| """ | ||
|
|
||
| def run_filter( | ||
| self, | ||
| command_contextmanager, | ||
| command_name, | ||
| service_variant, | ||
| ): # pylint: disable=arguments-differ | ||
| """ | ||
| Return a wrapped context manager that applies monitoring when enabled. | ||
| """ | ||
| operation_name = self.extra_config.get('operation_name', DEFAULT_OPERATION_NAME) | ||
|
|
||
| @contextmanager | ||
| def wrapped_contextmanager(): | ||
| monitor_contextmanager = nullcontext() | ||
|
|
||
| try: | ||
| if ENABLE_MANAGEMENT_COMMAND_MONITORING.is_enabled(): | ||
| monitor_contextmanager = monitor_management_command( | ||
| command_name, | ||
| service_variant, | ||
| operation_name, | ||
| ) | ||
| except Exception: # pylint: disable=broad-except | ||
| log.exception( | ||
| 'Failed to initialize management command monitoring ' | ||
| 'for %s; continuing without monitoring.', | ||
| command_name, | ||
| ) | ||
|
|
||
| with monitor_contextmanager: | ||
| with command_contextmanager: | ||
| yield | ||
|
|
||
| return { | ||
| 'command_contextmanager': wrapped_contextmanager(), | ||
| 'command_name': command_name, | ||
| 'service_variant': service_variant, | ||
| } | ||
|
ktyagiapphelix2u marked this conversation as resolved.
|
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I checked this and agree with the intent, but CI currently cannot resolve edx-django-utils>=8.1.0 from the available package index (latest visible there is 8.0.1), so adding that minimum in base.in causes install failures. I have not added the 8.1.0 floor for now to keep the pipeline green. |
Uh oh!
There was an error while loading. Please reload this page.