feat: support update the peer before requesting outgoing (#95)
diff --git a/README.md b/README.md
index ab21317..9f30f44 100644
--- a/README.md
+++ b/README.md
@@ -131,7 +131,8 @@
## Nginx APIs
- **startTimer**, `require("skywalking.client"):startBackendTimer("http://127.0.0.1:8080")`. Start the backend timer. This timer register the metadata and report traces to the backend.
- **destroyBackendTimer**, `require("skywalking.client"):destroyBackendTimer()`. Stop the timer created by `startBackendTimer`, and clean unreported data.
-- **start**, `require("skywalking.tracer"):start("upstream service", correlation)`. Begin the tracing before the upstream begin. The custom data (table type) can be injected as the second parameter, and then they will be propagated to the downstream service.
+- **start**, `require("skywalking.tracer"):start("upstream service", correlation)`. Begin the tracing before the upstream beginning. The custom data (table type) can be injected as the second parameter, and then they will be propagated to the downstream service. If `upstream service` could be determined precisely later, keep it as `nil` and call `inject` method when peer(upstream address) is resolved by load balancer and DNS resolver.
+- **inject**, `require("skywalking.tracer"):inject(exitSpan, peer, correlation)`. Inject an exit span context and correlation context into carrier, and then they will be propagated to the downstream service. (**Since v1.0**, advanced API, called when you update the peer of exit span.)
- **finish**, `require("skywalking.tracer"):finish()`. Finish the tracing for this HTTP request.
- **prepareForReport**, `require("skywalking.tracer"):prepareForReport()`. Prepare the finished segment for further report.
@@ -140,11 +141,15 @@
- `TracingContext.new(serviceId, serviceInstID)`, create an active tracing context.
- `TracingContext.newNoOP()`, create a no OP tracing context.
- `TracingContext.drainAfterFinished()`, fetch the segment includes all finished spans.
+- `TracingContext.inject(exitSpan, peer, correlation)`, inject an exit span context and correlation context into carrier, and then they will be propagated to the downstream service by outgoing HTTP request. (**Since v1.0**, advanced API, called when you update the peer of exit span.)
Create 2 kinds of span
- `TracingContext.createEntrySpan(operationName, parent, contextCarrier)`
- `TracingContext.createExitSpan(operationName, parent, peer, contextCarrier)`
+Create 2 kinds of span API v1
+- `TracingContext.createEntrySpan(operationName, parent, contextCarrier)`
+- `TracingContext.createExitSpan(operationName, parent)`
# Contact Us
* Submit an [issue](https://github.com/apache/skywalking/issues) with `[NIGNX-LUA]` as the issue title prefix.
diff --git a/lib/skywalking/correlation_context_test.lua b/lib/skywalking/correlation_context_test.lua
index a959f9a..87c08f0 100644
--- a/lib/skywalking/correlation_context_test.lua
+++ b/lib/skywalking/correlation_context_test.lua
@@ -18,9 +18,10 @@
local lu = require('luaunit')
local correlationContext = require('skywalking.correlation_context')
local TC = require('skywalking.tracing_context')
+local Span = require("skywalking.span")
-TestCorelationContext = {}
- function TestCorelationContext:testFromSW8Value()
+TestCorrelationContext = {}
+ function TestCorrelationContext:testFromSW8Value()
-- simple analyze
local context = correlationContext.fromSW8Value('dGVzdDE=:dDE=,dGVzdDI=:dDI=')
lu.assertNotNil(context)
@@ -38,7 +39,7 @@
lu.assertNotNil(#context == 0)
end
- function TestCorelationContext:testSerialize()
+ function TestCorrelationContext:testSerialize()
-- serialize empty correlation
local context = correlationContext.fromSW8Value('')
local encode_context = correlationContext.serialize(context)
@@ -64,7 +65,7 @@
lu.assertEquals(encode_context, "")
end
- function TestCorelationContext:testPut()
+ function TestCorrelationContext:testPut()
-- put with empty key and value
local context = correlationContext.fromSW8Value('')
correlationContext.put(context, nil, nil)
@@ -88,7 +89,7 @@
lu.assertEquals(context["test3"], "t3")
end
- function TestCorelationContext:testTracingContext()
+ function TestCorrelationContext:testTracingContext()
-- transform data
local context = TC.new("service", "instance")
local header = {}
@@ -96,14 +97,25 @@
TC.createEntrySpan(context, 'operation_name', nil, header)
lu.assertNotNil(context.correlation)
local contextCarrier = {}
- TC.createExitSpan(context, 'operation_name', nil, 'peer', contextCarrier)
+
+ -- mock ngx.req.set_header(k, v)
+ ngx = {
+ req = {
+ set_header = function(k, v)
+ contextCarrier[k] = v
+ end
+ }
+ }
+ local exitSpan = TC.createExitSpan(context, 'operation_name', nil)
+ TC.inject(context, exitSpan, 'peer', context.correlation)
lu.assertNotNil(contextCarrier['sw8-correlation'])
local correlation = correlationContext.fromSW8Value(contextCarrier['sw8-correlation'])
lu.assertEquals(correlation["test1"], "t1")
lu.assertEquals(correlation["test2"], "t2")
-- transform data with adding data
- TC.createExitSpan(context, 'operation_name', nil, 'peer', contextCarrier, {
+ exitSpan = TC.createExitSpan(context, 'operation_name', nil)
+ TC.inject(context, exitSpan, 'peer', {
test3 = "t3"
})
lu.assertNotNil(contextCarrier['sw8-correlation'])
diff --git a/lib/skywalking/segment_ref.lua b/lib/skywalking/segment_ref.lua
index d3917fb..cfd5a49 100644
--- a/lib/skywalking/segment_ref.lua
+++ b/lib/skywalking/segment_ref.lua
@@ -57,6 +57,20 @@
return ref
end
+-- Create and initialize an injectable reference
+function _M.createInjectableRef(context, span)
+ local injectableRef = _M.new()
+ injectableRef.trace_id = context.trace_id
+ injectableRef.segment_id = context.segment_id
+ injectableRef.span_id = span.span_id
+ injectableRef.address_used_at_client = span.peer
+ injectableRef.parent_service = context.service
+ injectableRef.parent_service_instance = context.service_instance
+ injectableRef.parent_endpoint = span.parent_endpoint_name
+
+ return injectableRef
+end
+
-- Return string to represent this ref.
function _M.serialize(ref)
local encodedRef = '1'
diff --git a/lib/skywalking/span.lua b/lib/skywalking/span.lua
index ce52b01..15d8978 100644
--- a/lib/skywalking/span.lua
+++ b/lib/skywalking/span.lua
@@ -86,29 +86,12 @@
end
-- Create an exit span. Represent the HTTP outgoing request.
-function _M.createExitSpan(operationName, context, parent, peer, contextCarrier)
+function _M.createExitSpan(operationName, context, parent)
local span = _M.new(operationName, context, parent)
span.is_exit = true
- span.peer = peer
- if contextCarrier ~= nil then
- -- if there is contextCarrier container, the Span will inject the value based on the current tracing context
- local injectableRef = SegmentRef.new()
- injectableRef.trace_id = context.trace_id
- injectableRef.segment_id = context.segment_id
- injectableRef.span_id = span.span_id
- injectableRef.address_used_at_client = peer
- injectableRef.parent_service = context.service
- injectableRef.parent_service_instance = context.service_instance
-
- local firstSpan = context.internal.first_span
- local parentEndpointName
- parentEndpointName = firstSpan.operation_name
- injectableRef.parent_endpoint = parentEndpointName
-
- contextCarrier[CONTEXT_CARRIER_KEY] = SegmentRef.serialize(injectableRef)
- end
-
+ local firstSpan = context.internal.first_span
+ span.parent_endpoint_name = firstSpan.operation_name
return span
end
@@ -252,6 +235,15 @@
return span
end
+function _M.setPeer(span, peer)
+ if span.is_noop then
+ return span
+ end
+ span.peer = peer
+
+ return span
+end
+
-- Return SpanProtocol
function _M.transform(span)
local spanBuilder = Util.tablepool_fetch("sw_spanBuilder", 0, 32)
diff --git a/lib/skywalking/span_test.lua b/lib/skywalking/span_test.lua
index c544441..0c7456f 100644
--- a/lib/skywalking/span_test.lua
+++ b/lib/skywalking/span_test.lua
@@ -65,16 +65,11 @@
local context = TC.new("service", "instance")
lu.assertNotNil(context)
- local contextCarrier = {}
- local span1 = Span.createExitSpan("operation_name", context, nil, '127.0.0.1:80', contextCarrier)
+ local span1 = Span.createExitSpan("operation_name", context, nil)
lu.assertNotNil(span1)
lu.assertEquals(span1.is_entry, false)
lu.assertEquals(span1.is_exit, true)
lu.assertEquals(span1.layer, SpanLayer.NONE)
- lu.assertEquals(span1.peer, '127.0.0.1:80')
-
- lu.assertEquals(#(context.internal.active_spans), 1)
- lu.assertNotNil(contextCarrier['sw8'])
end
function TestSpan:testNew()
diff --git a/lib/skywalking/tracer.lua b/lib/skywalking/tracer.lua
index 8f62a67..cc08e6f 100644
--- a/lib/skywalking/tracer.lua
+++ b/lib/skywalking/tracer.lua
@@ -29,8 +29,6 @@
function Tracer:start(upstream_name, correlation)
- local log = ngx.log
- local WARN = ngx.WARN
local serviceName = metadata_shdict:get("serviceName")
local serviceInstanceName = metadata_shdict:get('serviceInstanceName')
local req_uri = ngx.var.uri
@@ -67,19 +65,18 @@
Span.tag(entrySpan, 'http.params',
ngx.var.scheme .. '://' .. ngx.var.host .. ngx.var.request_uri )
+ ------------------------------------------------------
contextCarrier = Util.tablepool_fetch("sw_contextCarrier")
-- Use the same URI to represent incoming and forwarding requests
-- Change it if you need.
- local upstreamServerName = upstream_name
- ------------------------------------------------------
- local exitSpan = TC.createExitSpan(tracingContext, req_uri, entrySpan,
- upstreamServerName, contextCarrier, correlation)
+ local exitSpan = TC.createExitSpan(tracingContext, req_uri, entrySpan)
Span.start(exitSpan, time_now)
Span.setComponentId(exitSpan, nginxComponentId)
Span.setLayer(exitSpan, Layer.HTTP)
- for name, value in pairs(contextCarrier) do
- ngx.req.set_header(name, value)
+ local upstreamServerName = upstream_name
+ if upstreamServerName then
+ TC.inject(tracingContext, exitSpan, upstreamServerName, correlation)
end
-- Push the data in the context
@@ -90,6 +87,17 @@
ctx.is_finished = false
end
+-- inject an exit span context and correlation context into carrier
+-- since v1.0.0
+function Tracer:inject(exitSpan, peer, correlation)
+ local ctx = ngx.ctx
+ local context = ctx.tracingContext
+
+ if not context.is_noop and exitSpan ~= nil and not ctx.is_finished then
+ TC.inject(context, exitSpan, peer, correlation)
+ end
+end
+
function Tracer:finish()
-- Finish the exit span when received the first response package from upstream
if ngx.ctx.exitSpan ~= nil and not ngx.ctx.is_finished then
diff --git a/lib/skywalking/tracing_context.lua b/lib/skywalking/tracing_context.lua
index 7c7374d..6d16209 100644
--- a/lib/skywalking/tracing_context.lua
+++ b/lib/skywalking/tracing_context.lua
@@ -17,8 +17,10 @@
local Util = require('skywalking.util')
local Span = require('skywalking.span')
+local SegmentRef = require('skywalking.segment_ref')
local CorrelationContext = require('skywalking.correlation_context')
+local CONTEXT_CARRIER_KEY = 'sw8'
local CONTEXT_CORRELATION_KEY = 'sw8-correlation'
-------------- Internal Object-------------
@@ -140,22 +142,29 @@
-- Delegate to Span.createExitSpan
-- @param contextCarrier could be nil if don't need to inject any context to propagate
-function _M.createExitSpan(tracingContext, operationName, parent, peer, contextCarrier, correlation)
+function _M.createExitSpan(tracingContext, operationName, parent)
if tracingContext.is_noop then
return Span.newNoOP()
end
- if contextCarrier then
- if correlation then
- for name, value in pairs(correlation) do
- CorrelationContext.put(tracingContext.correlation, name, value)
- end
- end
+ return Span.createExitSpan(operationName, tracingContext, parent)
+end
- contextCarrier[CONTEXT_CORRELATION_KEY] = CorrelationContext.serialize(tracingContext.correlation)
+-- Inject an exit span context and correlation context into context carrier to propagate
+-- @param correlation is used to transport custom data to downstream service
+function _M.inject(tracingContext, exitSpan, peer, correlation)
+ Span.setPeer(exitSpan, peer)
+
+ local injectableRef = SegmentRef.createInjectableRef(tracingContext, exitSpan)
+ local correlationData = tracingContext.correlation
+ if correlation then
+ for name, value in pairs(correlation) do
+ CorrelationContext.put(correlationData, name, value)
+ end
end
- return Span.createExitSpan(operationName, tracingContext, parent, peer, contextCarrier)
+ ngx.req.set_header(CONTEXT_CARRIER_KEY, SegmentRef.serialize(injectableRef))
+ ngx.req.set_header(CONTEXT_CORRELATION_KEY, CorrelationContext.serialize(correlationData))
end
-- After all active spans finished, this segment will be treated as finished status.