def test_common_tags_are_applied_to_new_spans(self): common_tags = {"a": "common_tag", "another": "common_tag"} tracer = HaystackTracer("any_service", NoopRecorder(), common_tags=common_tags) span = tracer.start_span("any_operation") self.assertDictEqual(common_tags, span.tags)
def test_common_tags_are_overridden_if_span_specifies_the_same(self): common_tags = {"a": "common_tag", "another": "common_tag"} tracer = HaystackTracer("any_service", NoopRecorder(), common_tags=common_tags) span_tags = {"another": "span_tag"} span = tracer.start_span("any_operation", tags=span_tags) expected_tags = {**common_tags, **span_tags} self.assertDictEqual(expected_tags, span.tags)
def test_non_shared_spans_are_created_by_default(self): tracer = HaystackTracer("any_service", NoopRecorder()) trace_id = "123" span_id = "1234" parent_id = "12345" upstream_ctx = SpanContext(trace_id=trace_id, span_id=span_id, parent_id=parent_id) span = tracer.start_span("any_operation", child_of=upstream_ctx) self.assertEqual(span.context.trace_id, trace_id) self.assertNotEqual(span.context.span_id, span_id) self.assertEqual(span.context.parent_id, span_id)
def setup_tracer(): global recorder recorder = LoggerRecorder() # instantiate a haystack tracer for this service and set a common tag which applies to all traces tracer = HaystackTracer("Service-A", recorder, common_tags={"app.version": "1234"}) # now set the global tracer, so we can reference it with opentracing.tracer anywhere in our app opentracing.set_global_tracer(tracer)
def act_as_remote_service(headers): # remote service would have it"s own tracer with HaystackTracer("Service-B", recorder) as tracer: # simulate network transfer delay time.sleep(.25) # now as-if this was executing on the remote service, extract the parent span ctx from headers upstream_span_ctx = tracer.extract(opentracing.Format.HTTP_HEADERS, headers) with tracer.start_active_span("controller_method", child_of=upstream_span_ctx) as parent_scope: parent_scope.span.set_tag(tags.SPAN_KIND, "server") # simulate downstream service doing some work before replying time.sleep(1)
def test_haystack_agent(self): span_name = "agent-test-span" service_name = "agent-test" opentracing.tracer = HaystackTracer( service_name, HaystackAgentRecorder("sandbox_haystack_agent_1")) thread = threading.Thread(target=send_a_span, args=(span_name, )) thread.start() retrieved_spans = get_spans() client_span = retrieved_spans["client"] server_span = retrieved_spans["server"] self.assertEqual(f"{span_name}-client", client_span.operationName) self.assertEqual(span_name, server_span.operationName) self.assertEqual(service_name, server_span.serviceName) self.assertEqual(service_name, client_span.serviceName) self.assertEqual(client_span.parentSpanId, server_span.spanId) self.assertEqual(client_span.traceId, server_span.traceId)
def test_http_collector(self): span_name = "http-test-span" service_name = "http-test" opentracing.tracer = HaystackTracer( service_name, AsyncHttpRecorder( collector_url="http://sandbox_haystack_collector_1:8080/span")) thread = threading.Thread(target=send_a_span, args=(span_name, )) thread.start() retrieved_spans = get_spans() client_span = retrieved_spans["client"] server_span = retrieved_spans["server"] self.assertEqual(f"{span_name}-client", client_span.operationName) self.assertEqual(span_name, server_span.operationName) self.assertEqual(service_name, server_span.serviceName) self.assertEqual(service_name, client_span.serviceName) self.assertEqual(client_span.parentSpanId, server_span.spanId) self.assertEqual(client_span.traceId, server_span.traceId)
consideration. For Example, in AWS, due to the way AWS lambda freezes execution context, it's not reliable to send requests via the AsyncHttpRecorder. If the function is not time-sensitive in reply or is async, SyncHttpRecorder is a good fit as shown below. If the function cannot afford to dispatch the span in-process, then it is recommended to either setup a haystack agent in the network and utilize HaystackAgentRecorder or offload the span record dispatching via Queue -> Worker model. In AWS this could mean implementing a SQSRecorder which puts the finished span onto a SQS queue. The queue could then notify a lambda implementing SyncHttpRecorder to dispatch the records. """ recorder = SyncHttpRecorder(os.env["COLLECTOR_URL"]) # suppose it is desired to tag all traces with the application version common_tags = {"svc_ver": os["APP_VERSION"]} tracer = HaystackTracer("example-service", recorder, common_tags=common_tags) opentracing.set_global_tracer(tracer) def invoke_downstream(headers): return "done" def process_downstream_response(response): return "done" def handler(event, context): # extract the span context from headers if this is a downstream service parent_ctx = opentracing.tracer.extract(opentracing.Format.HTTP_HEADERS,