def __init__(self, plugins: List[str], requestor_getter=None): with opentracing.global_tracer().start_active_span( "PluginsManager.__init__"): self.all_plugins = [] self.global_plugins = [] self.plugins_per_channel = defaultdict(list) global_db_configs, channel_db_configs = self._get_db_plugin_configs( ) channels = Channel.objects.all() for plugin_path in plugins: with opentracing.global_tracer().start_active_span( f"{plugin_path}"): PluginClass = import_string(plugin_path) if not getattr(PluginClass, "CONFIGURATION_PER_CHANNEL", False): plugin = self._load_plugin( PluginClass, global_db_configs, requestor_getter=requestor_getter, ) self.global_plugins.append(plugin) self.all_plugins.append(plugin) else: for channel in channels: channel_configs = channel_db_configs.get( channel, {}) plugin = self._load_plugin(PluginClass, channel_configs, channel, requestor_getter) self.plugins_per_channel[channel.slug].append( plugin) self.all_plugins.append(plugin) for channel in channels: self.plugins_per_channel[channel.slug].extend( self.global_plugins)
def init_tracer(service_name): global _tracer if _tracer is not None: return _tracer config = jaeger_client.Config(config={}, service_name=service_name, validate=True) config.initialize_tracer() _tracer = opentracing.global_tracer() # A nasty hack to ensure enough time for the tracing data to be flushed atexit.register(_fini_tracer) return _tracer
def tracing_wrapper(execute, sql, params, many, context): conn: DatabaseWrapper = context["connection"] operation = f"{conn.alias} {conn.display_name}" with opentracing.global_tracer().start_active_span(operation) as scope: span = scope.span span.set_tag(opentracing.tags.COMPONENT, "db") span.set_tag(opentracing.tags.DATABASE_STATEMENT, sql) span.set_tag(opentracing.tags.DATABASE_TYPE, conn.display_name) span.set_tag(opentracing.tags.PEER_HOSTNAME, conn.settings_dict.get("HOST")) span.set_tag(opentracing.tags.PEER_PORT, conn.settings_dict.get("PORT")) span.set_tag("service.name", "postgres") span.set_tag("span.type", "sql") return execute(sql, params, many, context)
def __run_method_on_plugins( self, method_name: str, default_value: Any, *args, **kwargs ): """Try to run a method with the given name on each declared plugin.""" with opentracing.global_tracer().start_active_span( f"PluginsManager.{method_name}" ): value = default_value for plugin in self.plugins: value = self.__run_method_on_single_plugin( plugin, method_name, value, *args, **kwargs ) return value
async def test_publish_injects_tracing(self): app = kafkaesk.Application(kafka_servers=["foo"]) producer = AsyncMock() app._get_producer = AsyncMock(return_value=producer) tracer = opentracing.global_tracer() tracer._scope_manager = ContextVarsScopeManager() tracer.scope_manager.activate("foobar", True) with patch.object(tracer, "inject") as mock: await app.raw_publish("foobar", b"foobar") mock.assert_called_once()
def enrich_context_with_span(rpc_name, args): """ Method to extract the Span embedded in Kafka RPC request on the receiver side. If span is found embedded in the KV args (with key as "span"), it is de-serialized and injected into the Context to be carried forward by the RPC request processor thread. If no span is found embedded, even then a span is created with name as "kafka-rpc-<rpc-name>" to enrich the Context for RPC calls coming from components currently not sending the span (e.g. openonu adapter) """ tracer = global_tracer() try: for arg in args: if arg.key == SPAN_ARG and arg.value is not None: text_map_string = StrType() arg.value.Unpack(text_map_string) span_dict = json.loads(text_map_string.val) span_ctx = tracer.extract(Format.TEXT_MAP, span_dict) if span_ctx is not None: rx_rpc_name = span_ctx.baggage.get('rpc-span-name') return tracer.start_active_span(rx_rpc_name, child_of=span_ctx, finish_on_close=False) except Exception as e: log.info('exception-during-context-decode', err=str(e)) # If here, no active span found in request, start a new span instead span_name = 'kafka-' if rpc_name == PROCESS_IA_MSG_RPC: for arg in args: if arg.key == MESSAGE_KEY: ia_msg = InterAdapterMessage() arg.value.Unpack(ia_msg) span_name += 'inter-adapter-' msg_type = ia_msg.header.type try: rpc_name = IA_MSG_ENUM.values_by_number[msg_type].name except Exception as _e: rpc_name = 'msg-type-{}'.format(msg_type) break span_name += 'rpc-' + rpc_name return tracer.start_active_span(span_name, ignore_active_span=True, finish_on_close=False)
def _create_new_item(db, name, price): tracer = opentracing.global_tracer() with tracer.start_span("db._create_new_item", child_of=tracer.active_span) as span: cursor = db.cursor() sql = "INSERT INTO ITEM(NAME, PRICE) \ VALUES ('%s', '%d')" % (name, price) try: cursor.execute(sql) db.commit() except Exception as e: print(e) db.rollback() raise Exception("failed to insert item in db")
def get_client_token( config: GatewayConfig, token_config: Optional[TokenConfig] = None ) -> str: gateway = get_braintree_gateway(**config.connection_params) with opentracing.global_tracer().start_active_span( "braintree.client_token.generate" ) as scope: span = scope.span span.set_tag(opentracing.tags.COMPONENT, "payment") span.set_tag("service.name", "braintree") if not token_config: return gateway.client_token.generate() parameters = create_token_params(config, token_config) return gateway.client_token.generate(parameters)
def send_requests(url): with global_tracer().start_active_span("client") as client_scope_shim: client_scope_shim.span.set_baggage_item("key_client", "value_client") print("client shim key_client: {}".format( client_scope_shim.span.get_baggage_item("key_client"))) try: res = get(url) print(f"Request to {url}, got {len(res.content)} bytes") except Exception as e: print(f"Request to {url} failed {e}") pass
def tracing_wrapper(execute, sql, params, many, context): with opentracing.global_tracer().start_span( operation_name="query") as span: span.set_tag("component", "db") span.set_tag("db.statement", sql) try: result = execute(sql, params, many, context) except Exception as e: span.set_tag("error", True) span.set_tag("error.object", e) raise else: span.set_tag("error", False) return result
def preprocess_order_creation( self, checkout_info: "CheckoutInfo", discounts: Iterable[DiscountInfo], lines: Optional[Iterable["CheckoutLineInfo"]], previous_value: Any, ): """Ensure all the data is correct and we can proceed with creation of order. Raise an error when can't receive taxes. """ if lines is None: lines = fetch_checkout_lines(checkout_info.checkout) if self._skip_plugin(previous_value): return previous_value data = generate_request_data_from_checkout( checkout_info, lines, self.config, transaction_token=str(checkout_info.checkout.token), transaction_type=TransactionType.ORDER, discounts=discounts, ) if not data.get("createTransactionModel", {}).get("lines"): return previous_value transaction_url = urljoin( get_api_url(self.config.use_sandbox), "transactions/createoradjust" ) with opentracing.global_tracer().start_active_span( "avatax.transactions.crateoradjust" ) as scope: span = scope.span span.set_tag(opentracing.tags.COMPONENT, "tax") span.set_tag("service.name", "avatax") response = api_post_request(transaction_url, data, self.config) if not response or "error" in response: msg = response.get("error", {}).get("message", "") error_code = response.get("error", {}).get("code", "") logger.warning( "Unable to calculate taxes for checkout %s, error_code: %s, " "error_msg: %s", checkout_info.checkout.token, error_code, msg, ) customer_msg = CustomerErrors.get_error_msg(response.get("error", {})) raise TaxError(customer_msg) return previous_value
def _process_additional_action(self, payment_information: "PaymentData", kind: str): config = self._get_gateway_config() additional_data = payment_information.data if not additional_data: raise PaymentError("Unable to finish the payment.") with opentracing.global_tracer().start_active_span( "adyen.checkout.payment_details" ) as scope: span = scope.span span.set_tag(opentracing.tags.COMPONENT, "payment") span.set_tag("service.name", "adyen") result = api_call(additional_data, self.adyen.checkout.payments_details) result_code = result.message["resultCode"].strip().lower() is_success = result_code not in FAILED_STATUSES action_required = "action" in result.message if result_code in PENDING_STATUSES: kind = TransactionKind.PENDING elif ( is_success and config.auto_capture and self.order_auto_confirmation and not action_required ): # For enabled auto_capture on Saleor side we need to proceed an additional # action kind = TransactionKind.CAPTURE result = call_capture( payment_information=payment_information, merchant_account=self.config.connection_params["merchant_account"], token=result.message.get("pspReference"), adyen_client=self.adyen, ) payment_method_info = get_payment_method_info(payment_information, result) action = result.message.get("action") return GatewayResponse( is_success=is_success, action_required=action_required, action_required_data=action, kind=kind, amount=payment_information.amount, currency=payment_information.currency, transaction_id=result.message.get("pspReference", ""), error=result.message.get("refusalReason"), raw_response=result.message, searchable_key=result.message.get("pspReference", ""), payment_method_info=payment_method_info, )
def list_client_sources(config: GatewayConfig, customer_id: str) -> List[CustomerSource]: gateway = get_braintree_gateway(**config.connection_params) with opentracing.global_tracer().start_active_span( "braintree.customer.find") as scope: span = scope.span span.set_tag(opentracing.tags.COMPONENT, "payment") span.set_tag("service.name", "braintree") customer = gateway.customer.find(customer_id) if not customer: return [] return [ extract_credit_card_data(card, "braintree") for card in customer.credit_cards ]
def execute_graphql_request( self, request, data, query, variables, operation_name, ): if not query: raise HttpError( HttpResponseBadRequest("Must provide query string.")) with opentracing.global_tracer().start_active_span( "graphql_query") as scope: span = scope.span span.set_tag(opentracing.tags.COMPONENT, "GraphQL") try: document = parse(query) except GraphQLError as e: return ExecutionResult(errors=[e], data=dict(invalid=True)) if request.method.lower() == "get": operation_ast = get_operation_ast(document, operation_name) if operation_ast and operation_ast.operation != OperationType.QUERY: raise HttpError( HttpResponseNotAllowed( ["POST"], "Can only perform a {} operation from a POST request." .format(operation_ast.operation.value), )) validation_errors = validate(self.schema.graphql_schema, document) if validation_errors: return ExecutionResult(data=None, errors=validation_errors) try: with connection.execute_wrapper(tracing_wrapper): return self.schema.execute( source=query, root_value=self.get_root_value(request), variable_values=variables, operation_name=operation_name, context_value=self.get_context(request), middleware=self.get_middleware(request), ) except GraphQLError as e: span.set_tag(opentracing.tags.ERROR, True) return ExecutionResult(errors=[e])
def _fetch_new_taxes_data(data: Dict[str, Dict], data_cache_key: str, config: AvataxConfiguration): transaction_url = urljoin(get_api_url(config.use_sandbox), "transactions/createoradjust") with opentracing.global_tracer().start_active_span( "avatax.transactions.crateoradjust") as scope: span = scope.span span.set_tag(opentracing.tags.COMPONENT, "tax") span.set_tag("service.name", "avatax") response = api_post_request(transaction_url, data, config) if response and "error" not in response: cache.set(data_cache_key, (data, response), CACHE_TIME) else: # cache failed response to limit hits to avatax. cache.set(data_cache_key, (data, response), 10) return response
def _get_all_plugin_configs(self): with opentracing.global_tracer().start_active_span( "_get_all_plugin_configs"): if not hasattr(self, "_plugin_configs"): plugin_configurations = PluginConfiguration.objects.prefetch_related( "channel").all() self._plugin_configs_per_channel = defaultdict(dict) self._global_plugin_configs = {} for pc in plugin_configurations: channel = pc.channel if channel is None: self._global_plugin_configs[pc.identifier] = pc else: self._plugin_configs_per_channel[channel][ pc.identifier] = pc return self._global_plugin_configs, self._plugin_configs_per_channel
def webhook(self, request: WSGIRequest, path: str, previous_value) -> HttpResponse: config = self._get_gateway_config() if path.startswith(WEBHOOK_PATH): return handle_webhook(request, config) elif path.startswith(ADDITIONAL_ACTION_PATH): with opentracing.global_tracer().start_active_span( "adyen.checkout.payment_details" ) as scope: span = scope.span span.set_tag(opentracing.tags.COMPONENT, "payment") span.set_tag("service.name", "adyen") return handle_additional_actions( request, self.adyen.checkout.payments_details, ) return HttpResponseNotFound()
def get_price_range( self, discounts: Optional[Iterable[DiscountInfo]] = None ) -> MoneyRange: import opentracing with opentracing.global_tracer().start_active_span("get_price_range"): if self.variants.all(): prices = [variant.get_price(discounts) for variant in self] return MoneyRange(min(prices), max(prices)) price = calculate_discounted_price( product=self, price=self.price, collections=self.collections.all(), discounts=discounts, ) return MoneyRange(start=price, stop=price)
def handle_query(self, request: HttpRequest) -> JsonResponse: tracer = opentracing.global_tracer() # Disable extending spans from header due to: # https://github.com/DataDog/dd-trace-py/issues/2030 # span_context = tracer.extract( # format=Format.HTTP_HEADERS, carrier=dict(request.headers) # ) # We should: # Add `from opentracing.propagation import Format` to imports # Add `child_of=span_ontext` to `start_active_span` with tracer.start_active_span("http") as scope: span = scope.span span.set_tag(opentracing.tags.COMPONENT, "http") span.set_tag(opentracing.tags.HTTP_METHOD, request.method) span.set_tag( opentracing.tags.HTTP_URL, request.build_absolute_uri(request.get_full_path()), ) span.set_tag("http.useragent", request.META.get("HTTP_USER_AGENT", "")) span.set_tag("span.type", "web") request_ips = request.META.get(settings.REAL_IP_ENVIRON, "") for ip in request_ips.split(","): if is_valid_ipv4(ip): span.set_tag(opentracing.tags.PEER_HOST_IPV4, ip) elif is_valid_ipv6(ip): span.set_tag(opentracing.tags.PEER_HOST_IPV6, ip) else: continue span.set_tag("http.client_ip", ip) span.set_tag("http.client_ip_originated_from", settings.REAL_IP_ENVIRON) break response = self._handle_query(request) span.set_tag(opentracing.tags.HTTP_STATUS_CODE, response.status_code) # RFC2616: Content-Length is defined in bytes, # we can calculate the RAW UTF-8 size using the length of # response.content of type 'bytes' span.set_tag("http.content_length", len(response.content)) return response
def preprocess_order_creation( self, checkout: "Checkout", discounts: Iterable[DiscountInfo], previous_value: TaxedMoney, ): """Ensure all the data is correct and we can proceed with creation of order. Raise an error when can't receive taxes. """ if self._skip_plugin(previous_value): return previous_value data = generate_request_data_from_checkout( checkout, transaction_type=TRANSACTION_TYPE, discounts=discounts, ) if not data.TransactionLines: return previous_value transaction_url = urljoin(get_api_url(self.config.use_sandbox), "AvaTaxExcise/transactions/create") with opentracing.global_tracer().start_active_span( "avatax_excise.transactions.create") as scope: span = scope.span span.set_tag(opentracing.tags.COMPONENT, "tax") span.set_tag("service.name", "avatax_excise") response = api_post_request(transaction_url, data, self.config) if not response or response.get("Status") != "Success": transaction_errors = response.get("TransactionErrors") customer_msg = "" if isinstance(transaction_errors, list): for error in transaction_errors: error_message = error.get("ErrorMessage") if error_message: customer_msg += error_message error_code = response.get("ErrorCode", "") logger.warning( "Unable to calculate taxes for checkout %s, error_code: %s, " "error_msg: %s", checkout.token, error_code, error_message, ) raise TaxError(customer_msg) return previous_value
async def test_basic(tracer): assert opentracing.global_tracer() == tracer async def coro(): span = tracer.start_span(operation_name='foobar') with span_in_context(span): span.finish() return span assert get_current_span() is None span = await coro() assert get_current_span() is None assert tracer.finished_spans() == [span]
def commit_transaction( user_tran_id: str, config: AvataxConfiguration, ) -> Dict[str, Any]: commit_url = urljoin( get_api_url(config.use_sandbox), f"AvaTaxExcise/transactions/{user_tran_id}/commit", ) with opentracing.global_tracer().start_active_span( "avatax_excise.transactions.commit") as scope: span = scope.span span.set_tag(opentracing.tags.COMPONENT, "tax") span.set_tag("service.name", "avatax_excise") response = api_commit_transaction(commit_url, config) return response
def _get_db_plugin_configs(self): with opentracing.global_tracer().start_active_span( "_get_db_plugin_configs"): qs = (PluginConfiguration.objects.all().using( settings.DATABASE_CONNECTION_REPLICA_NAME).prefetch_related( "channel")) channel_configs = defaultdict(dict) global_configs = {} for db_plugin_config in qs: channel = db_plugin_config.channel if channel is None: global_configs[ db_plugin_config.identifier] = db_plugin_config else: channel_configs[channel][ db_plugin_config.identifier] = db_plugin_config return global_configs, channel_configs
def api_post_request_task(transaction_url, data, config, order_id): config = AvataxConfiguration(**config) order = Order.objects.filter(id=order_id).first() if not order: logger.error( "Unable to send the order %s to Avatax Excise. Order doesn't exist.", order_id, ) return if not data.get("TransactionLines"): msg = "The order doesn't have any line which should be sent to Avatax Excise." external_notification_event( order=order, user=None, message=msg, parameters=None ) return with opentracing.global_tracer().start_active_span( "avatax_excise.transactions.create" ) as scope: span = scope.span span.set_tag(opentracing.tags.COMPONENT, "tax") span.set_tag("service.name", "avatax_excise") response = api_post_request(transaction_url, data, config) msg = f"Order sent to Avatax Excise. Order ID: {order.token}" if not response or "Error" in response.get("Status"): errors = response.get("TransactionErrors", []) avatax_msg = "" for error in errors: avatax_msg += error.get("ErrorMessage", "") msg = f"Unable to send order to Avatax Excise. {avatax_msg}" logger.warning( "Unable to send order %s to Avatax Excise. Response %s", order.token, response, ) tax_item = {get_metadata_key("itemized_taxes"): json.dumps( response.get("TransactionTaxes"))} order.store_value_in_metadata(items=tax_item) order.save() external_notification_event( order=order, user=None, message=msg, parameters=None) if not response or "Error" in response.get("Status"): raise TaxError
def single(url: str, duration: int, frequency: float = 1.0, configuration: Configuration = None, secrets: Secrets = None): """ Run a loop for up to the given duration in a thread and call the remote service following the given frequency (in seconds). Send traces along the way to OpenTracing. """ end = time.time() + duration tracer = opentracing.global_tracer() scope = tracer.scope_manager.active parent = scope.span def f(): logger.info("Calling '{}' for {}s every {}s".format( url, duration, frequency )) while True: headers = {} with tracer.start_span("call-service1", child_of=parent) as span: span.set_tag('http.method','GET') span.set_tag('http.url', url) span.set_tag('span.kind', 'client') span.tracer.inject(span, 'http_headers', headers) try: r = requests.get(url, headers=headers, timeout=1) if r.status_code == 200: logger.debug("Response from service: {}".format( r.json())) else: logger.debug("Failed to get response from server") span.set_tag('http.status_code', r.status_code) except Exception: logger.debug( "Failed to talk to '{}'".format(url), exc_info=True) if time.time() > end: return time.sleep(frequency) loop = threading.Thread(target=f) loop.start()
def get_product_price_range( *, product: Product, variants: Iterable[ProductVariant], collections: Iterable[Collection], discounts: Iterable[DiscountInfo]) -> Optional[MoneyRange]: with opentracing.global_tracer().start_active_span( "get_product_price_range"): if variants: prices = [ get_variant_price( variant=variant, product=product, collections=collections, discounts=discounts, ) for variant in variants ] return MoneyRange(min(prices), max(prices)) return None
def transaction_for_existing_customer(payment_information: PaymentData, config: GatewayConfig): gateway = get_braintree_gateway(**config.connection_params) with opentracing.global_tracer().start_active_span( "braintree.transaction.sale") as scope: span = scope.span span.set_tag(opentracing.tags.COMPONENT, "payment") span.set_tag("service.name", "braintree") return gateway.transaction.sale({ "amount": str(payment_information.amount), "customer_id": payment_information.customer_id, "options": { "submit_for_settlement": config.auto_capture }, **get_customer_data(payment_information), })
def get_payment_gateways(self, currency: Optional[str], checkout: Optional["Checkout"], previous_value) -> List["PaymentGateway"]: """Fetch current configuration for given checkout. It calls an Adyen API to fetch all available payment methods for given checkout. Adyen defines available payment methods based on the data that we send like amount, currency, and country. Some payment methods are only available if the given data matches their conditions. Like to display payment method X, which is available in UK, we need to set GBP as currency, and country-code needs to point to UK. We don't fetch anything if checkout is none, as we don't have enough info to provide the required data in the request. """ local_config = self._get_gateway_config() config = [{ "field": "client_key", "value": local_config.connection_params["client_key"], }] if checkout: # If checkout is available, fetch available payment methods from Adyen API # and append them to the config object returned for the gateway. request = request_data_for_gateway_config( checkout, local_config.connection_params["merchant_account"]) with opentracing.global_tracer().start_active_span( "adyen.checkout.payment_methods") as scope: span = scope.span span.set_tag(opentracing.tags.COMPONENT, "payment") span.set_tag("service.name", "adyen") response = api_call(request, self.adyen.checkout.payment_methods) adyen_payment_methods = json.dumps(response.message) config.append({ "field": "config", "value": adyen_payment_methods }) gateway = PaymentGateway( id=self.PLUGIN_ID, name=self.PLUGIN_NAME, config=config, currencies=self.get_supported_currencies([]), ) return [gateway]
def check_payment_balance(self, data: dict, previous_value=None) -> dict: request_data = get_request_data_for_check_payment( data, self.config.connection_params["merchant_account"]) with opentracing.global_tracer().start_active_span( "adyen.checkout.payment_methods_balance") as scope: span = scope.span span.set_tag(opentracing.tags.COMPONENT, "payment") span.set_tag("service.name", "adyen") try: result = api_call( request_data, self.adyen.checkout.client.call_checkout_api, action="paymentMethods/balance", ) return result.message except PaymentError as e: return e.message
def call_capture( payment_information: "PaymentData", merchant_account: str, token: str, adyen_client: Adyen.Adyen, ): # https://docs.adyen.com/checkout/capture#make-an-api-call-to-capture-a-payment request = request_for_payment_capture( payment_information=payment_information, merchant_account=merchant_account, token=token, ) with opentracing.global_tracer().start_active_span( "adyen.payment.capture") as scope: span = scope.span span.set_tag(opentracing.tags.COMPONENT, "payment") span.set_tag("service.name", "adyen") return api_call(request, adyen_client.payment.capture)