From 837792fc031e10b1d4d8393d0ff85783850c627c Mon Sep 17 00:00:00 2001 From: David Elner Date: Tue, 18 Dec 2018 17:47:18 -0500 Subject: [PATCH 1/2] Added: Datadog::Correlation. --- lib/ddtrace/correlation.rb | 17 ++++++++++++++ spec/ddtrace/correlation_spec.rb | 39 ++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 lib/ddtrace/correlation.rb create mode 100644 spec/ddtrace/correlation_spec.rb diff --git a/lib/ddtrace/correlation.rb b/lib/ddtrace/correlation.rb new file mode 100644 index 0000000000..97efe8f8f3 --- /dev/null +++ b/lib/ddtrace/correlation.rb @@ -0,0 +1,17 @@ +module Datadog + # Contains behavior for managing correlations with tracing + # e.g. Retrieve a correlation to the current trace for logging, etc. + module Correlation + # Struct representing correlation + Identifier = Struct.new(:trace_id, :span_id) + NULL_IDENTIFIER = Identifier.new.freeze + + module_function + + # Produces a CorrelationIdentifier from the Context provided + def identifier_from_context(context) + return NULL_IDENTIFIER if context.nil? + Identifier.new(context.trace_id, context.span_id).freeze + end + end +end diff --git a/spec/ddtrace/correlation_spec.rb b/spec/ddtrace/correlation_spec.rb new file mode 100644 index 0000000000..bd7868b1e5 --- /dev/null +++ b/spec/ddtrace/correlation_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' + +require 'ddtrace/correlation' +require 'ddtrace/context' + +RSpec.describe Datadog::Correlation do + describe '::identifier_from_context' do + subject(:correlation_ids) { described_class.identifier_from_context(context) } + + context 'given nil' do + let(:context) { nil } + + it 'returns an empty Correlation::Identifier' do + is_expected.to be_a_kind_of(Datadog::Correlation::Identifier) + expect(correlation_ids.trace_id).to be nil + expect(correlation_ids.span_id).to be nil + end + end + + context 'given a Context object' do + let(:context) do + instance_double( + Datadog::Context, + trace_id: trace_id, + span_id: span_id + ) + end + + let(:trace_id) { double('trace id') } + let(:span_id) { double('span id') } + + it 'returns a Correlation::Identifier matching the Context' do + is_expected.to be_a_kind_of(Datadog::Correlation::Identifier) + expect(correlation_ids.trace_id).to eq(trace_id) + expect(correlation_ids.span_id).to eq(span_id) + end + end + end +end From dc6cbdbf10489d577d2fa55aceb3db466803af6d Mon Sep 17 00:00:00 2001 From: David Elner Date: Tue, 18 Dec 2018 17:47:51 -0500 Subject: [PATCH 2/2] Added: Tracer#active_correlation_ids. --- docs/GettingStarted.md | 36 ++++++++++++++++++++++++++++++++++++ lib/ddtrace/tracer.rb | 6 ++++++ spec/ddtrace/tracer_spec.rb | 29 +++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+) diff --git a/docs/GettingStarted.md b/docs/GettingStarted.md index 2ab8796504..ad5480d910 100644 --- a/docs/GettingStarted.md +++ b/docs/GettingStarted.md @@ -62,6 +62,7 @@ For descriptions of terminology used in APM, take a look at the [official docume - [Processing pipeline](#processing-pipeline) - [Filtering](#filtering) - [Processing](#processing) + - [Trace correlation](#trace-correlation) - [OpenTracing](#opentracing) ## Compatibility @@ -1605,6 +1606,41 @@ Datadog::Pipeline.before_flush( ) ``` +### Trace correlation + +In many cases, such as logging, it may be useful to correlate trace IDs to other events or data streams, for easier cross referencing. The tracer can produce a correlation identifier for the currently active trace via `active_correlation_ids`, which can be used to decorate these other data sources. + +An example of this for the purpose of logging: + +```ruby +require 'ddtrace' +require 'logger' + +logger = Logger.new(STDOUT) +logger.progname = 'my_app' +logger.formatter = proc do |severity, datetime, progname, msg| + # Returns Datadog::Correlation::Identifier + ids = Datadog.tracer.active_correlation_ids + "[#{datetime}][#{progname}][#{severity}][dd.trace_id=#{ids.trace_id} dd.span_id=#{ids.span_id}] #{msg}\n" +end + +# When a trace is active... +Datadog.tracer.trace('logging.example') do + # And a warning is produced... + logger.warn('This is a warning.') + + # Prints: + # [2018-12-18 22:42:25 +0000][my_app][WARN][dd.trace_id=5963550561812073440 dd.span_id=2232727802607726424] This is a warning. +end + +# When no trace is active... +# And a warning is produced... +logger.warn('This is a warning.') + +# Prints: +# [2018-12-18 22:44:19 +0000][my_app][WARN][dd.trace_id= dd.span_id=] This is a warning. +``` + ### OpenTracing For setting up Datadog with OpenTracing, see out [Quickstart for OpenTracing](#quickstart-for-opentracing) section for details. diff --git a/lib/ddtrace/tracer.rb b/lib/ddtrace/tracer.rb index a7c19882aa..2e919c03bf 100644 --- a/lib/ddtrace/tracer.rb +++ b/lib/ddtrace/tracer.rb @@ -10,6 +10,7 @@ require 'ddtrace/logger' require 'ddtrace/writer' require 'ddtrace/sampler' +require 'ddtrace/correlation' # \Datadog global namespace that includes all tracing functionality for Tracer and Span classes. module Datadog @@ -357,6 +358,11 @@ def active_root_span call_context.current_root_span end + # Return a CorrelationIdentifier for active span + def active_correlation_ids + Datadog::Correlation.identifier_from_context(call_context) + end + # Send the trace to the writer to enqueue the spans list in the agent # sending queue. def write(trace) diff --git a/spec/ddtrace/tracer_spec.rb b/spec/ddtrace/tracer_spec.rb index fbc377faa9..0cc61f0aae 100644 --- a/spec/ddtrace/tracer_spec.rb +++ b/spec/ddtrace/tracer_spec.rb @@ -96,4 +96,33 @@ is_expected.to be(span) end end + + describe '#active_correlation_ids' do + subject(:active_correlation_ids) { tracer.active_correlation_ids } + + context 'when a trace is active' do + let(:span) { @span } + + around(:each) do |example| + tracer.trace('test') do |span| + @span = span + example.run + end + end + + it 'produces an Datadog::Correlation::Identifier with data' do + is_expected.to be_a_kind_of(Datadog::Correlation::Identifier) + expect(active_correlation_ids.trace_id).to eq(span.trace_id) + expect(active_correlation_ids.span_id).to eq(span.span_id) + end + end + + context 'when no trace is active' do + it 'produces an empty Datadog::Correlation::Identifier' do + is_expected.to be_a_kind_of(Datadog::Correlation::Identifier) + expect(active_correlation_ids.trace_id).to be nil + expect(active_correlation_ids.span_id).to be nil + end + end + end end