|
| 1 | +# Copyright 2010 New Relic, Inc. |
| 2 | +# |
| 3 | +# Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | +# you may not use this file except in compliance with the License. |
| 5 | +# You may obtain a copy of the License at |
| 6 | +# |
| 7 | +# http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | +# |
| 9 | +# Unless required by applicable law or agreed to in writing, software |
| 10 | +# distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | +# See the License for the specific language governing permissions and |
| 13 | +# limitations under the License. |
| 14 | + |
| 15 | + |
| 16 | +import logging |
| 17 | + |
| 18 | +from newrelic.api.application import application_instance |
| 19 | +from newrelic.api.web_transaction import WebTransaction |
| 20 | +from newrelic.api.function_trace import FunctionTrace |
| 21 | +from newrelic.api.transaction import current_transaction |
| 22 | +from newrelic.common.object_wrapper import wrap_function_wrapper |
| 23 | + |
| 24 | +_logger = logging.getLogger(__name__) |
| 25 | + |
| 26 | +CLIENT_ATTRIBUTES_DEPLOY_RESOURCE_LOG_MSG = "Exception occurred in PyZeebe instrumentation: Failed to extract resource count/file for method `deploy_resource`. Report this issue to New Relic support." |
| 27 | + |
| 28 | + |
| 29 | +# Adds client method params as txn or span attributes |
| 30 | +def _add_client_input_attributes(method_name, trace, args, kwargs): |
| 31 | + bpmn_id = extract_agent_attribute_from_methods(args, kwargs, method_name, ("run_process", "run_process_with_result"), "bpmn_process_id", 0) |
| 32 | + if bpmn_id: |
| 33 | + trace._add_agent_attribute("zeebe.client.bpmnProcessId", bpmn_id) |
| 34 | + |
| 35 | + msg_name = extract_agent_attribute_from_methods(args, kwargs, method_name, ("publish_message"), "name", 0) |
| 36 | + if msg_name: |
| 37 | + trace._add_agent_attribute("zeebe.client.messageName", msg_name) |
| 38 | + |
| 39 | + correlation_key = extract_agent_attribute_from_methods(args, kwargs, method_name, ("publish_message"), "correlation_key", 1) |
| 40 | + if correlation_key: |
| 41 | + trace._add_agent_attribute("zeebe.client.correlationKey", correlation_key) |
| 42 | + |
| 43 | + message_id = extract_agent_attribute_from_methods(args, kwargs, method_name, ("publish_message"), "message_id", 4) |
| 44 | + if message_id: |
| 45 | + trace._add_agent_attribute("zeebe.client.messageId", message_id) |
| 46 | + |
| 47 | + resource = extract_agent_attribute_from_methods(args, {}, method_name, ("deploy_resource"), None, 0) |
| 48 | + if resource: |
| 49 | + try: |
| 50 | + trace._add_agent_attribute("zeebe.client.resourceFile", resource) |
| 51 | + trace._add_agent_attribute("zeebe.client.resourceCount", len(list(args))) |
| 52 | + except Exception: |
| 53 | + _logger.warning(CLIENT_ATTRIBUTES_DEPLOY_RESOURCE_LOG_MSG, exc_info=True) |
| 54 | + |
| 55 | + |
| 56 | +def extract_agent_attribute_from_methods(args, kwargs, method_name, methods, param, index): |
| 57 | + try: |
| 58 | + if method_name in methods: |
| 59 | + value = kwargs.get(param) |
| 60 | + if not value and args and len(args) > index: |
| 61 | + value = args[index] |
| 62 | + return value |
| 63 | + except Exception: |
| 64 | + _logger.warning("Exception occurred in PyZeebe instrumentation: failed to extract %s from %s. Report this issue to New Relic support.", param, method_name, exc_info=True) |
| 65 | + |
| 66 | +# Async wrapper that instruments router/worker annotations` |
| 67 | +async def _nr_wrapper_execute_one_job(wrapped, instance, args, kwargs): |
| 68 | + job = args[0] if args else kwargs.get("job") |
| 69 | + process_id = getattr(job, "bpmn_process_id", None) or "UnknownProcess" |
| 70 | + task_type = getattr(job, "type", None) or "UnknownType" |
| 71 | + txn_name = f"{process_id}/{task_type}" |
| 72 | + |
| 73 | + with WebTransaction(application_instance(), txn_name, group="ZeebeTask") as txn: |
| 74 | + if job is not None: |
| 75 | + if hasattr(job, "key"): |
| 76 | + txn.add_custom_attribute("zeebe.job.key", job.key) |
| 77 | + if hasattr(job, "type"): |
| 78 | + txn.add_custom_attribute("zeebe.job.type", job.type) |
| 79 | + if hasattr(job, "bpmn_process_id"): |
| 80 | + txn.add_custom_attribute("zeebe.job.bpmnProcessId", job.bpmn_process_id) |
| 81 | + if hasattr(job, "process_instance_key"): |
| 82 | + txn.add_custom_attribute("zeebe.job.processInstanceKey", job.process_instance_key) |
| 83 | + if hasattr(job, "element_id"): |
| 84 | + txn.add_custom_attribute("zeebe.job.elementId", job.element_id) |
| 85 | + |
| 86 | + return await wrapped(*args, **kwargs) |
| 87 | + |
| 88 | + |
| 89 | +# Async wrapper that instruments a ZeebeClient method. |
| 90 | +def _nr_client_wrapper(method_name): |
| 91 | + async def _client_wrapper(wrapped, instance, args, kwargs): |
| 92 | + txn = current_transaction() |
| 93 | + if not txn: |
| 94 | + return await wrapped(*args, **kwargs) |
| 95 | + |
| 96 | + with FunctionTrace(name=method_name, group="ZeebeClient") as trace: |
| 97 | + _add_client_input_attributes(method_name, trace, args, kwargs) |
| 98 | + return await wrapped(*args, **kwargs) |
| 99 | + |
| 100 | + return _client_wrapper |
| 101 | + |
| 102 | + |
| 103 | +# Instrument JobExecutor.execute_one_job to create a background transaction per job (invoked from @router.task or @worker.task annotations) |
| 104 | +def instrument_pyzeebe_worker_job_executor(module): |
| 105 | + if hasattr(module, "JobExecutor"): |
| 106 | + wrap_function_wrapper(module, "JobExecutor.execute_one_job", _nr_wrapper_execute_one_job) |
| 107 | + |
| 108 | + |
| 109 | +# Instrument ZeebeClient methods to trace client calls. |
| 110 | +def instrument_pyzeebe_client_client(module): |
| 111 | + target_methods = ("run_process", "run_process_with_result", "deploy_resource", "publish_message") |
| 112 | + |
| 113 | + for method_name in target_methods: |
| 114 | + if hasattr(module, "ZeebeClient"): |
| 115 | + if hasattr(module.ZeebeClient, method_name): |
| 116 | + wrap_function_wrapper(module, f"ZeebeClient.{method_name}", _nr_client_wrapper(method_name)) |
0 commit comments