未验证 提交 d754b896 编写于 作者: 静夜思朝颜's avatar 静夜思朝颜 提交者: GitHub

Adapt the new v3 protocol (#30)

* adapt the new v3 protocol
上级 507eea2c
......@@ -31,9 +31,13 @@ jobs:
strategy:
fail-fast: true
steps:
- uses: actions/checkout@v1
with:
submodules: true
- uses: actions/checkout@v2
# In the checkout@v2, it doesn't support git submodule. Execute the commands manually.
- name: checkout submodules
shell: bash
run: |
git submodule sync --recursive
git -c protocol.version=2 submodule update --init --force --recursive --depth=1
- uses: leafo/gh-actions-lua@v5
with:
luaVersion: "5.3.5"
......@@ -48,6 +52,7 @@ jobs:
run: |
sudo luarocks install luaunit
sudo luarocks install lua-cjson 2.1.0-1
sudo luarocks install lua-resty-jit-uuid
- name: "Install OpenResty"
run: |
wget -qO - https://openresty.org/package/pubkey.gpg | sudo apt-key add -
......
......@@ -31,9 +31,13 @@ jobs:
strategy:
fail-fast: true
steps:
- uses: actions/checkout@v1
with:
submodules: true
- uses: actions/checkout@v2
# In the checkout@v2, it doesn't support git submodule. Execute the commands manually.
- name: checkout submodules
shell: bash
run: |
git submodule sync --recursive
git -c protocol.version=2 submodule update --init --force --recursive --depth=1
- uses: actions/setup-java@v1
with:
java-version: 8
......
......@@ -216,7 +216,7 @@ The following components are provided under a MIT license. See project link for
The text of each license is also included at licenses/LICENSE-[project].txt.
base64 dependencies/base64.lua https://github.com/iskolbin/lbase64 MIT
lua-resty-jit-uuid lib/resty/jit-uuid.lua https://github.com/thibaultcha/lua-resty-jit-uuid MIT
========================================================================
BSD 2-Clause License
......
......@@ -71,6 +71,9 @@ http {
# Download
Please head to the [releases page](http://skywalking.apache.org/downloads/) to download a release of Apache SkyWalking.
* 0.1.0 release requires SkyWalking 7
* 0.2.0+ releases require SkyWalking 8
# Compatible backend
SkyWalking OAP begins to support LUA agent in 7.0.0 release.
......@@ -80,22 +83,22 @@ You could choose master branch before the official 7.0.0 release.
### Debug Startup
By using the `/examples/nginx.conf`, you could start the Nginx with LUA module or OpenResty. Such as `nginx -c /path/to/skywalking-nginx-lua/examples/nginx.conf`
Then you could
1. See the `register logs` happens on the console log.
1. See the `instance properties update logs` happens on the console log.
```
2020/04/04 15:15:37 [debug] 12089#1446111: *4 [lua] content_by_lua(nginx.conf:175):4: Instance report request = {"service":"User Service Name","serviceInstance":"User Service Instance Name","properties":[{"language":"Lua"}]}
2020/04/04 15:15:37 [debug] 12089#1446111: *2 [lua] client.lua:89: reportServiceInstance(): Instance report response = {}
```
2020/02/27 09:22:06 [debug] 20676#11799235: *4 [lua] content_by_lua(nginx.conf:118):4: Service register request = {"services":[{"type":"normal","serviceName":"User Service Name"}]}
2020/02/27 09:22:06 [debug] 20676#11799235: *2 [lua] client.lua:99: registerService(): Service register response = [{"key":"User Service Name","value":1}]
2020/02/27 09:22:06 [debug] 20676#11799235: *2 [lua] client.lua:106: registerService(): Service registered, service id = 1
2020/02/27 09:22:06 [debug] 20676#11799235: *4 [lua] content_by_lua(nginx.conf:134):3: Service instance register request = {"instances":[{"time":1582766526928,"instanceUUID":"name:User Service Instance Name","properties":[{"key":"language","value":"Lua"}],"serviceId":1}]}
2020/02/27 09:22:06 [debug] 20676#11799235: *2 [lua] client.lua:143: registerServiceInstance(): Service Instance register response = [{"key":"name:User Service Instance Name","value":1}]
2020/02/27 09:22:06 [debug] 20676#11799235: *2 [lua] client.lua:150: registerServiceInstance(): Service Instance registered, service instance id = 1
2. See the `heartbeat logs` happens after the `register logs`
```
2020/04/04 15:15:40 [debug] 12089#1446111: *4 [lua] content_by_lua(nginx.conf:188):3: KeepAlive request = {"service":"User Service Name","serviceInstance":"User Service Instance Name"}
```
2. Access the `http://127.0.0.1:8080/ingress` then you could see the tracing happens and reported spans in the logs.
3. Access the `http://127.0.0.1:8080/ingress` then you could see the tracing happens and reported spans in the logs.
```
2020/02/27 09:22:10 [debug] 20676#11799235: *9 [lua] tracer.lua:91: prepareForReport(): segment = {"spans":[{"operationName":"\/tier2\/lb","startTime":1582766530125,"endTime":1582766530139,"spanType":"Exit","spanId":1,"isError":false,"parentSpanId":0,"componentId":6000,"peer":"User Service Name-nginx:upstream_ip:port","spanLayer":"HTTP"},{"operationName":"\/tier2\/lb","startTime":1582766530125,"tags":[{"key":"http.method","value":"GET"},{"key":"http.params","value":"http:\/\/127.0.0.1\/tier2\/lb"}],"endTime":1582766530139,"spanType":"Entry","spanId":0,"isError":false,"parentSpanId":-1,"componentId":6000,"refs":[{"parentTraceSegmentId":{"idParts":[1582766530044,794206293,69887]},"parentEndpointId":0,"entryEndpointId":0,"parentServiceInstanceId":1,"parentEndpoint":"\/ingress","networkAddress":"#User Service Name-nginx:upstream_ip:port","parentSpanId":1,"entryServiceInstanceId":1,"networkAddressId":0,"entryEndpoint":"\/ingress"}],"spanLayer":"HTTP"}],"serviceInstanceId":1,"serviceId":1,"traceSegmentId":{"idParts":[1582766530052,794206293,69887]},"globalTraceIds":[{"idParts":[1582766530044,794206293,69887]}]}
2020/02/27 09:22:10 [debug] 20676#11799235: *9 [lua] tracer.lua:95: prepareForReport(): segment buffer size = 1
2020/02/27 09:22:10 [debug] 20676#11799235: *6 [lua] tracer.lua:91: prepareForReport(): segment = {"spans":[{"operationName":"\/ingress","startTime":1582766530114,"endTime":1582766530140,"spanType":"Exit","spanId":1,"isError":false,"parentSpanId":0,"componentId":6000,"peer":"User Service Name-nginx:upstream_ip:port","spanLayer":"HTTP"},{"operationName":"\/ingress","startTime":1582766530114,"tags":[{"key":"http.method","value":"GET"},{"key":"http.params","value":"http:\/\/127.0.0.1\/ingress"}],"endTime":1582766530140,"spanType":"Entry","spanId":0,"parentSpanId":-1,"isError":false,"spanLayer":"HTTP","componentId":6000}],"serviceInstanceId":1,"serviceId":1,"traceSegmentId":{"idParts":[1582766530044,794206293,69887]},"globalTraceIds":[{"idParts":[1582766530044,794206293,69887]}]}
2020/04/04 15:15:46 [debug] 12089#1446111: *11 [lua] tracer.lua:83: prepareForReport(): segment = {"traceId":"1585984546953.410917649.45972","serviceInstance":"User Service Instance Name","spans":[{"operationName":"\/tier2\/lb","startTime":1585984546967,"endTime":1585984546968,"spanType":"Exit","spanId":1,"isError":false,"parentSpanId":0,"componentId":6000,"peer":"backend service","spanLayer":"Http"},{"operationName":"\/tier2\/lb","startTime":1585984546967,"tags":[{"key":"http.method","value":"GET"},{"key":"http.params","value":"http:\/\/127.0.0.1\/tier2\/lb"}],"endTime":1585984546968,"spanType":"Entry","spanId":0,"isError":false,"parentSpanId":-1,"componentId":6000,"refs":[{"traceId":"1585984546953.410917649.45972","networkAddressUsedAtPeer":"upstream service","parentEndpoint":"\/ingress","parentServiceInstance":"User Service Instance Name","parentSpanId":1,"parentService":"User Service Name","parentTraceSegmentId":"1585984546953.410917649.45972","refType":"CrossProcess"}],"spanLayer":"Http"}],"service":"User Service Name","traceSegmentId":"1585984546967.449397702.9959"}
2020/04/04 15:15:46 [debug] 12089#1446111: *11 [lua] tracer.lua:87: prepareForReport(): segment buffer size = 1
2020/04/04 15:15:46 [debug] 12089#1446111: *8 [lua] tracer.lua:83: prepareForReport(): segment = {"traceId":"1585984546953.410917649.45972","serviceInstance":"User Service Instance Name","spans":[{"operationName":"\/ingress","startTime":1585984546953,"endTime":1585984546968,"spanType":"Exit","spanId":1,"isError":false,"parentSpanId":0,"componentId":6000,"peer":"upstream service","spanLayer":"Http"},{"operationName":"\/ingress","startTime":1585984546953,"tags":[{"key":"http.method","value":"GET"},{"key":"http.params","value":"http:\/\/localhost\/ingress"}],"endTime":1585984546968,"spanType":"Entry","spanId":0,"parentSpanId":-1,"isError":false,"spanLayer":"Http","componentId":6000}],"service":"User Service Name","traceSegmentId":"1585984546953.410917649.45972"}
```
### Local Development and Unit Tests
......@@ -106,6 +109,7 @@ All codes in the `lib/skywalking` require the `*_test.lua` to do the UnitTest. T
The following libs are required in runtime or test cases, please use `LuaRocks` to install them.
- lua-cjson. NOTICE, some platforms such as MacOS 10.15 may have issue with the latest release of this lib, consider to install an old release.(`luarocks install lua-cjson 2.1.0-1`)
- luaunit
- lua-resty-jit-uuid
# APIs
This LUA tracing lib is originally designed for Nginx+LUA/OpenResty ecosystems. But we write it to support more complex cases.
......
......@@ -101,7 +101,7 @@ http {
default_type text/html;
content_by_lua_block {
ngx.say("<p>Backend service for testing only.</p>")
ngx.say("<p>Backend sw6 received headers: " .. ngx.req.get_headers()["sw6"] .. "</p>")
ngx.say("<p>Backend sw8 received headers: " .. ngx.req.get_headers()["sw8"] .. "</p>")
}
}
......@@ -109,49 +109,34 @@ http {
# ------------------------------------------------------
# -- Mock OAP server to provide register and trace collection
# ------------------------------------------------------
location /v2/service/register {
location /v3/management/reportProperties {
default_type text/html;
lua_need_request_body on;
content_by_lua_block {
local cjson = require('cjson')
ngx.log(ngx.DEBUG, 'Service register request = ', ngx.req.get_body_data())
local param = cjson.decode(ngx.req.get_body_data())
local registeredInfo = {}
registeredInfo[1] = {key=param.services[1].serviceName, value=1}
ngx.say(cjson.encode(registeredInfo))
}
}
location /v2/instance/register {
default_type text/html;
lua_need_request_body on;
ngx.log(ngx.DEBUG, 'Instance report request = ', ngx.req.get_body_data())
content_by_lua_block {
local cjson = require('cjson')
ngx.log(ngx.DEBUG, 'Service instance register request = ', ngx.req.get_body_data())
local param = cjson.decode(ngx.req.get_body_data())
local registeredInfo = {}
registeredInfo[1] = {key=param.instances[1].instanceUUID, value=1}
ngx.say(cjson.encode(registeredInfo))
local reportInfo = {}
ngx.say(cjson.encode(reportInfo))
}
}
location /v2/instance/heartbeat {
location /v3/management/keepAlive {
default_type text/html;
lua_need_request_body on;
content_by_lua_block {
local cjson = require('cjson')
--ngx.log(ngx.DEBUG, 'Service instance ping request = ', ngx.req.get_body_data())
ngx.log(ngx.DEBUG, 'KeepAlive request = ', ngx.req.get_body_data())
local keepAliveInfo = {}
ngx.say(cjson.encode(keepAliveInfo))
}
}
location /v2/segments {
location /v3/segments {
default_type text/html;
lua_need_request_body on;
......
......@@ -17,8 +17,8 @@
local Client = {}
-- Tracing timer does the service and instance register
-- After register successfully, it sends traces and heart beat
-- Tracing timer reports instance properties report, keeps alive and sends traces
-- After report instance properties successfully, it sends keep alive packages.
function Client:startBackendTimer(backend_http_uri)
local metadata_buffer = ngx.shared.tracing_buffer
......@@ -33,27 +33,15 @@ function Client:startBackendTimer(backend_http_uri)
check = function(premature)
if not premature then
local serviceId = metadata_buffer:get('serviceId')
if (serviceId == nil or serviceId == 0) then
self:registerService(metadata_buffer, backend_http_uri)
end
-- Register is in the async way, if register successfully, go for instance register
serviceId = metadata_buffer:get('serviceId')
if (serviceId ~= nil and serviceId ~= 0) then
local serviceInstId = metadata_buffer:get('serviceInstId')
if (serviceInstId == nil or serviceInstId == 0) then
self:registerServiceInstance(metadata_buffer, backend_http_uri)
end
end
-- After all register successfully, begin to send trace segments
local serviceInstId = metadata_buffer:get('serviceInstId')
if (serviceInstId ~= nil and serviceInstId ~= 0) then
self:reportTraces(metadata_buffer, backend_http_uri)
local instancePropertiesSubmitted = metadata_buffer:get('instancePropertiesSubmitted')
if (instancePropertiesSubmitted == nil or instancePropertiesSubmitted == false) then
self:reportServiceInstance(metadata_buffer, backend_http_uri)
else
self:ping(metadata_buffer, backend_http_uri)
end
self:reportTraces(metadata_buffer, backend_http_uri)
-- do the health check
local ok, err = new_timer(delay, check)
if not ok then
......@@ -72,91 +60,39 @@ function Client:startBackendTimer(backend_http_uri)
end
end
-- Register service
function Client:registerService(metadata_buffer, backend_http_uri)
function Client:reportServiceInstance(metadata_buffer, backend_http_uri)
local log = ngx.log
local DEBUG = ngx.DEBUG
local ERR = ngx.ERR
local serviceName = metadata_buffer:get('serviceName')
local serviceInstanceName = metadata_buffer:get('serviceInstanceName')
local cjson = require('cjson')
local serviceRegister = require("register").newServiceRegister(serviceName)
local serviceRegisterParam = cjson.encode(serviceRegister)
local reportInstance = require("management").newReportInstanceProperties(serviceName, serviceInstanceName)
local reportInstanceParam, err = cjson.encode(reportInstance)
if err then
log(ERR, "Request to report instance fails, ", err)
return
end
local http = require('resty.http')
local httpc = http.new()
local res, err = httpc:request_uri(backend_http_uri .. '/v2/service/register', {
local res, err = httpc:request_uri(backend_http_uri .. '/v3/management/reportProperties', {
method = "POST",
body = serviceRegisterParam,
body = reportInstanceParam,
headers = {
["Content-Type"] = "application/json",
},
})
if not res then
log(ERR, "Service register fails, " .. err)
log(ERR, "Instance report fails, ", err)
elseif res.status == 200 then
log(DEBUG, "Service register response = " .. res.body)
local registerResults = cjson.decode(res.body)
for i, result in ipairs(registerResults)
do
if result.key == serviceName then
local serviceId = result.value
log(DEBUG, "Service registered, service id = " .. serviceId)
metadata_buffer:set('serviceId', serviceId)
end
end
log(DEBUG, "Instance report response = ", res.body)
metadata_buffer:set('instancePropertiesSubmitted', true)
else
log(ERR, "Service register fails, response code " .. res.status)
end
end
-- Register service instance
function Client:registerServiceInstance(metadata_buffer, backend_http_uri)
local log = ngx.log
local DEBUG = ngx.DEBUG
local ERR = ngx.ERR
local serviceInstName = 'name:' .. metadata_buffer:get('serviceInstanceName')
metadata_buffer:set('serviceInstanceUUID', serviceInstName)
local cjson = require('cjson')
local serviceInstanceRegister = require("register").newServiceInstanceRegister(
metadata_buffer:get('serviceId'),
serviceInstName,
ngx.now() * 1000)
local serviceInstanceRegisterParam = cjson.encode(serviceInstanceRegister)
local http = require('resty.http')
local httpc = http.new()
local res, err = httpc:request_uri(backend_http_uri .. '/v2/instance/register', {
method = "POST",
body = serviceInstanceRegisterParam,
headers = {
["Content-Type"] = "application/json",
},
})
if err == nil then
if res.status == 200 then
log(DEBUG, "Service Instance register response = " .. res.body)
local registerResults = cjson.decode(res.body)
for i, result in ipairs(registerResults)
do
if result.key == serviceInstName then
local serviceId = result.value
log(DEBUG, "Service Instance registered, service instance id = " .. serviceId)
metadata_buffer:set('serviceInstId', serviceId)
end
end
else
log(ERR, "Service Instance register fails, response code " .. res.status)
end
else
log(ERR, "Service Instance register fails, " .. err)
log(ERR, "Instance report fails, response code ", res.status)
end
end
......@@ -166,16 +102,19 @@ function Client:ping(metadata_buffer, backend_http_uri)
local DEBUG = ngx.DEBUG
local ERR = ngx.ERR
local serviceName = metadata_buffer:get('serviceName')
local serviceInstanceName = metadata_buffer:get('serviceInstanceName')
local cjson = require('cjson')
local pingPkg = require("register").newServiceInstancePingPkg(
metadata_buffer:get('serviceInstId'),
metadata_buffer:get('serviceInstanceUUID'),
ngx.now() * 1000)
local pingPkgParam = cjson.encode(pingPkg)
local pingPkg = require("management").newServiceInstancePingPkg(serviceName, serviceInstanceName)
local pingPkgParam, err = cjson.encode(pingPkg)
if err then
log(ERR, "Agent ping fails, ", err)
end
local http = require('resty.http')
local httpc = http.new()
local res, err = httpc:request_uri(backend_http_uri .. '/v2/instance/heartbeat', {
local res, err = httpc:request_uri(backend_http_uri .. '/v3/management/keepAlive', {
method = "POST",
body = pingPkgParam,
headers = {
......@@ -185,10 +124,10 @@ function Client:ping(metadata_buffer, backend_http_uri)
if err == nil then
if res.status ~= 200 then
log(ERR, "Agent ping fails, response code " .. res.status)
log(ERR, "Agent ping fails, response code ", res.status)
end
else
log(ERR, "Agent ping fails, " .. err)
log(ERR, "Agent ping fails, ", err)
end
end
......@@ -208,7 +147,7 @@ function Client:reportTraces(metadata_buffer, backend_http_uri)
while segment ~= nil
do
local res, err = httpc:request_uri(backend_http_uri .. '/v2/segments', {
local res, err = httpc:request_uri(backend_http_uri .. '/v3/segments', {
method = "POST",
body = segment,
headers = {
......@@ -218,13 +157,13 @@ function Client:reportTraces(metadata_buffer, backend_http_uri)
if err == nil then
if res.status ~= 200 then
log(ERR, "Segment report fails, response code " .. res.status)
log(ERR, "Segment report fails, response code ", res.status)
break
else
count = count + 1
end
else
log(ERR, "Segment report fails, " .. err)
log(ERR, "Segment report fails, ", err)
break
end
......
......@@ -18,49 +18,24 @@
local _M = {}
-- Return Services as service register parameter
function _M.newServiceRegister(unRegisterServiceName)
local serv = {
services = {}
function _M.newReportInstanceProperties(serviceName, serviceInstance)
local allProperties = {
key = "language",
value = "lua"
}
local service = {
serviceName = unRegisterServiceName,
-- Field type is optional, default value is `normal`
type = 'normal'
return {
service = serviceName,
serviceInstance = serviceInstance,
properties = {allProperties}
}
serv.services[#serv.services + 1] = service
return serv
end
function _M.newServiceInstanceRegister(registeredServiceId, serviceInstUUID, registerTime)
local serviceInstances = {
instances = {}
}
local serviceInstance = {
serviceId = registeredServiceId,
instanceUUID = serviceInstUUID,
time = registerTime,
properties = {}
function _M.newServiceInstancePingPkg(serviceName, serviceInstance)
return {
service = serviceName,
serviceInstance = serviceInstance,
}
serviceInstance.properties[#serviceInstance.properties + 1] = {key = "language", value = "Lua"}
serviceInstances.instances[#serviceInstances.instances + 1] = serviceInstance
return serviceInstances
end
function _M.newServiceInstancePingPkg(registeredServiceInstId, serviceInstUUID, updateTime)
local serviceInstancePingPkg = {
serviceInstanceId = registeredServiceInstId,
time = updateTime,
serviceInstanceUUID = serviceInstUUID,
}
return serviceInstancePingPkg
end
return _M
--- jit-uuid
-- Fast and dependency-free UUID library for LuaJIT/ngx_lua.
-- @module jit-uuid
-- @author Thibault Charbonnier
-- @license MIT
-- @release 0.0.7
local bit = require 'bit'
local tohex = bit.tohex
local band = bit.band
local bor = bit.bor
local _M = {
_VERSION = '0.0.7'
}
----------
-- seeding
----------
--- Seed the random number generator.
-- Under the hood, this function calls `math.randomseed`.
-- It makes sure to use the most appropriate seeding technique for
-- the current environment, guaranteeing a unique seed.
--
-- To guarantee unique UUIDs, you must have correctly seeded
-- the Lua pseudo-random generator (with `math.randomseed`).
-- You are free to seed it any way you want, but this function
-- can do it for you if you'd like, with some added guarantees.
--
-- @param[type=number] seed (Optional) A seed to use. If none given, will
-- generate one trying to use the most appropriate technique.
-- @treturn number `seed`: the seed given to `math.randomseed`.
-- @usage
-- local uuid = require 'resty.jit-uuid'
-- uuid.seed()
--
-- -- in ngx_lua, seed in the init_worker context:
-- init_worker_by_lua {
-- local uuid = require 'resty.jit-uuid'
-- uuid.seed()
-- }
function _M.seed(seed)
if not seed then
if ngx then
seed = ngx.time() + ngx.worker.pid()
elseif package.loaded['socket'] and package.loaded['socket'].gettime then
seed = package.loaded['socket'].gettime()*10000
else
seed = os.time()
end
end
math.randomseed(seed)
return seed
end
-------------
-- validation
-------------
do
if ngx and string.find(ngx.config.nginx_configure(),'--with-pcre-jit',nil,true) then
local type = type
local re_find = ngx.re.find
local regex = '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'
--- Validate a string as a UUID.
-- To be considered valid, a UUID must be given in its canonical
-- form (hexadecimal digits including the hyphen characters).
-- This function validates UUIDs disregarding their generation algorithm,
-- and in a case-insensitive manner, but checks the variant field.
--
-- Use JIT PCRE if available in OpenResty or fallbacks on Lua patterns.
--
-- @param[type=string] str String to verify.
-- @treturn boolean `valid`: true if valid UUID, false otherwise.
-- @usage
-- local uuid = require 'resty.jit-uuid'
--
-- uuid.is_valid 'cbb297c0-a956-486d-ad1d-f9bZZZZZZZZZ' --> false
-- uuid.is_valid 'cbb297c0-a956-486d-dd1d-f9b42df9465a' --> false (invalid variant)
-- uuid.is_valid 'cbb297c0a956486dad1df9b42df9465a' --> false (no dashes)
-- uuid.is_valid 'cbb297c0-a956-486d-ad1d-f9b42df9465a' --> true
function _M.is_valid(str)
-- it has proven itself efficient to first check the length with an
-- evenly distributed set of valid and invalid uuid lengths.
if type(str) ~= 'string' or #str ~= 36 then
return false
end
return re_find(str, regex, 'ioj') ~= nil
end
else
local match = string.match
local d = '[0-9a-fA-F]'
local p = '^' .. table.concat({
d:rep(8),
d:rep(4),
d:rep(4),
'[89ab]' .. d:rep(3),
d:rep(12)
}, '%-') .. '$'
function _M.is_valid(str)
if type(str) ~= 'string' or #str ~= 36 then
return false
end
return match(str, p) ~= nil
end
end
end
----------------
-- v4 generation
----------------
do
local fmt = string.format
local random = math.random
--- Generate a v4 UUID.
-- v4 UUIDs are created from randomly generated numbers.
--
-- @treturn string `uuid`: a v4 (randomly generated) UUID.
-- @usage
-- local uuid = require 'resty.jit-uuid'
--
-- local u1 = uuid() ---> __call metamethod
-- local u2 = uuid.generate_v4()
function _M.generate_v4()
return (fmt('%s%s%s%s-%s%s-%s%s-%s%s-%s%s%s%s%s%s',
tohex(random(0, 255), 2),
tohex(random(0, 255), 2),
tohex(random(0, 255), 2),
tohex(random(0, 255), 2),
tohex(random(0, 255), 2),
tohex(random(0, 255), 2),
tohex(bor(band(random(0, 255), 0x0F), 0x40), 2),
tohex(random(0, 255), 2),
tohex(bor(band(random(0, 255), 0x3F), 0x80), 2),
tohex(random(0, 255), 2),
tohex(random(0, 255), 2),
tohex(random(0, 255), 2),
tohex(random(0, 255), 2),
tohex(random(0, 255), 2),
tohex(random(0, 255), 2),
tohex(random(0, 255), 2)))
end
end
----------------
-- v3/v5 generation
----------------
do
if ngx then
local ffi = require 'ffi'
local tonumber = tonumber
local assert = assert
local error = error
local concat = table.concat
local type = type
local char = string.char
local fmt = string.format
local sub = string.sub
local gmatch = ngx.re.gmatch
local sha1_bin = ngx.sha1_bin
local md5 = ngx.md5
local C = ffi.C
local ffi_new = ffi.new
local ffi_str = ffi.string
local ffi_cast = ffi.cast
local new_tab
do
local ok
ok, new_tab = pcall(require, 'table.new')
if not ok then
new_tab = function(narr, nrec) return {} end
end
end
ffi.cdef [[
typedef unsigned char u_char;
typedef intptr_t ngx_int_t;
u_char * ngx_hex_dump(u_char *dst, const u_char *src, size_t len);
ngx_int_t ngx_hextoi(u_char *line, size_t n);
]]
local str_type = ffi.typeof('uint8_t[?]')
local u_char_type = ffi.typeof('u_char *')
local function bin_tohex(s)
local slen = #s
local blen = slen * 2
local buf = ffi_new(str_type, blen)
C.ngx_hex_dump(buf, s, slen)
return ffi_str(buf, blen)
end
local function hex_to_i(s)
local buf = ffi_cast(u_char_type, s)
local n = tonumber(C.ngx_hextoi(buf, #s))
if n == -1 then
error("could not convert hex to number")
end
return n
end
local buf = new_tab(16, 0)
local function factory(namespace, hash_fn)
if not _M.is_valid(namespace) then
return nil, 'namespace must be a valid UUID'
end
local i = 0
local iter, err = gmatch(namespace, [[([\da-f][\da-f])]])
if not iter then
return nil, 'could not create iter: ' .. err
end
while true do
local m, err = iter()
if err then
return nil, err
end
if not m then
break
end
i = i + 1
buf[i] = char(tonumber(m[0], 16))
end
assert(i == 16, "invalid binary namespace buffer length")
local ns = concat(buf)
return function(name)
if type(name) ~= 'string' then
return nil, 'name must be a string'
end
local hash, ver, var = hash_fn(ns, name)
return (fmt('%s-%s-%s%s-%s%s-%s', sub(hash, 1, 8),
sub(hash, 9, 12),
ver,
sub(hash, 15, 16),
var,
sub(hash, 19, 20),
sub(hash, 21, 32)))
end
end
local function v3_hash(binary, name)
local hash = md5(binary .. name)
return hash,
tohex(bor(band(hex_to_i(sub(hash, 13, 14)), 0x0F), 0x30), 2),
tohex(bor(band(hex_to_i(sub(hash, 17, 18)), 0x3F), 0x80), 2)
end
local function v5_hash(binary, name)
local hash = bin_tohex(sha1_bin(binary .. name))
return hash,
tohex(bor(band(hex_to_i(sub(hash, 13, 14)), 0x0F), 0x50), 2),
tohex(bor(band(hex_to_i(sub(hash, 17, 18)), 0x3F), 0x80), 2)
end
--- Instanciate a v3 UUID factory.
-- @function factory_v3
-- Creates a closure generating namespaced v3 UUIDs.
-- @param[type=string] namespace (must be a valid UUID according to `is_valid`)
-- @treturn function `factory`: a v3 UUID generator.
-- @treturn string `err`: a string describing an error
-- @usage
-- local uuid = require 'resty.jit-uuid'
--
-- local fact = assert(uuid.factory_v3('e6ebd542-06ae-11e6-8e82-bba81706b27d'))
--
-- local u1 = fact('hello')
-- ---> 3db7a435-8c56-359d-a563-1b69e6802c78
--
-- local u2 = fact('foobar')
-- ---> e8d3eeba-7723-3b72-bbc5-8f598afa6773
function _M.factory_v3(namespace)
return factory(namespace, v3_hash)
end
--- Instanciate a v5 UUID factory.
-- @function factory_v5
-- Creates a closure generating namespaced v5 UUIDs.
-- @param[type=string] namespace (must be a valid UUID according to `is_valid`)
-- @treturn function `factory`: a v5 UUID generator.
-- @treturn string `err`: a string describing an error
-- @usage
-- local uuid = require 'resty.jit-uuid'
--
-- local fact = assert(uuid.factory_v5('e6ebd542-06ae-11e6-8e82-bba81706b27d'))
--
-- local u1 = fact('hello')
-- ---> 4850816f-1658-5890-8bfd-1ed14251f1f0
--
-- local u2 = fact('foobar')
-- ---> c9be99fc-326b-5066-bdba-dcd31a6d01ab
function _M.factory_v5(namespace)
return factory(namespace, v5_hash)
end
--- Generate a v3 UUID.
-- v3 UUIDs are created from a namespace and a name (a UUID and a string).
-- The same name and namespace result in the same UUID. The same name and
-- different namespaces result in different UUIDs, and vice-versa.
-- The resulting UUID is derived using MD5 hashing.
--
-- This is a sugar function which instanciates a short-lived v3 UUID factory.
-- It is an expensive operation, and intensive generation using the same
-- namespaces should prefer allocating their own long-lived factory with
-- `factory_v3`.
--
-- @param[type=string] namespace (must be a valid UUID according to `is_valid`)
-- @param[type=string] name
-- @treturn string `uuid`: a v3 (namespaced) UUID.
-- @treturn string `err`: a string describing an error
-- @usage
-- local uuid = require 'resty.jit-uuid'
--
-- local u = uuid.generate_v3('e6ebd542-06ae-11e6-8e82-bba81706b27d', 'hello')
-- ---> 3db7a435-8c56-359d-a563-1b69e6802c78
function _M.generate_v3(namespace, name)
local fact, err = _M.factory_v3(namespace)
if not fact then
return nil, err
end
return fact(name)
end
--- Generate a v5 UUID.
-- v5 UUIDs are created from a namespace and a name (a UUID and a string).
-- The same name and namespace result in the same UUID. The same name and
-- different namespaces result in different UUIDs, and vice-versa.
-- The resulting UUID is derived using SHA-1 hashing.
--
-- This is a sugar function which instanciates a short-lived v5 UUID factory.
-- It is an expensive operation, and intensive generation using the same
-- namespaces should prefer allocating their own long-lived factory with
-- `factory_v5`.
--
-- @param[type=string] namespace (must be a valid UUID according to `is_valid`)
-- @param[type=string] name
-- @treturn string `uuid`: a v5 (namespaced) UUID.
-- @treturn string `err`: a string describing an error
-- @usage
-- local uuid = require 'resty.jit-uuid'
--
-- local u = uuid.generate_v5('e6ebd542-06ae-11e6-8e82-bba81706b27d', 'hello')
-- ---> 4850816f-1658-5890-8bfd-1ed14251f1f0
function _M.generate_v5(namespace, name)
local fact, err = _M.factory_v5(namespace)
if not fact then
return nil, err
end
return fact(name)
end
else
function _M.factory_v3() error('v3 UUID generation only supported in ngx_lua', 2) end
function _M.generate_v3() error('v3 UUID generation only supported in ngx_lua', 2) end
function _M.factory_v5() error('v5 UUID generation only supported in ngx_lua', 2) end
function _M.generate_v5() error('v5 UUID generation only supported in ngx_lua', 2) end
end
end
return setmetatable(_M, {
__call = _M.generate_v4
})
\ No newline at end of file
......@@ -23,32 +23,30 @@ local _M = {}
-- local Segment = {
-- trace_id,
-- segment_id,
-- service_id,
-- service_inst_id,
-- service,
-- service_instance,
-- spans,
-- }
-- Due to nesting relationship inside Segment/Span/TracingContext at the runtime,
-- SegmentProtocol is created to prepare JSON format serialization.
-- Following SkyWalking official trace protocol v2
-- https://github.com/apache/skywalking-data-collect-protocol/blob/master/language-agent-v2/trace.proto
-- Following SkyWalking official trace protocol v3
-- https://github.com/apache/skywalking-data-collect-protocol/blob/master/language-agent/Tracing.proto
-- local SegmentProtocol = {
-- globalTraceIds,
-- traceId,
-- traceSegmentId,
-- serviceId,
-- serviceInstanceId,
-- service,
-- serviceInstance,
-- spans,
-- }
-- Return SegmentProtocol
function _M.transform(segment)
local segmentBuilder = {}
segmentBuilder.serviceId = segment.service_id
segmentBuilder.globalTraceIds = {}
segmentBuilder.globalTraceIds[1] = {idParts = segment.trace_id}
segmentBuilder.traceSegmentId = {idParts = segment.segment_id}
segmentBuilder.serviceId = segment.service_id
segmentBuilder.serviceInstanceId = segment.service_inst_id
segmentBuilder.traceId = segment.trace_id
segmentBuilder.traceSegmentId = segment.segment_id
segmentBuilder.service = segment.service
segmentBuilder.serviceInstance = segment.service_instance
segmentBuilder.spans = {}
......
......@@ -31,59 +31,34 @@ local _M = {}
-- trace_id,
-- segment_id,
-- span_id,
-- network_address,
-- network_address_id = 0,
-- entry_service_instance_id = 0,
-- parent_service_instance_id = 0,
-- entry_endpoint_name,
-- entry_endpoint_id = 0,
-- parent_endpoint_name,
-- parent_endpoint_id = 0,
-- parent_service,
-- parent_service_instance,
-- parent_endpoint,
-- address_used_at_client,
-- }
function _M.new()
return {
type = 'CROSS_PROCESS',
network_address_id = 0,
entry_service_instance_id = 0,
parent_service_instance_id = 0,
entry_endpoint_id = 0,
parent_endpoint_id = 0,
}
end
-- Deserialize value from the propagated context and initialize the SegmentRef
function _M.fromSW6Value(value)
function _M.fromSW8Value(value)
local ref = _M.new()
local parts = Util.split(value, '-')
if #parts ~= 9 then
if #parts ~= 8 then
return nil
end
ref.trace_id = Util.formatID(decode_base64(parts[2]))
ref.segment_id = Util.formatID(decode_base64(parts[3]))
ref.trace_id = decode_base64(parts[2])
ref.segment_id = decode_base64(parts[3])
ref.span_id = tonumber(parts[4])
ref.parent_service_instance_id = tonumber(parts[5])
ref.entry_service_instance_id = tonumber(parts[6])
local peerStr = decode_base64(parts[7])
if string.sub(peerStr, 1, 1) == '#' then
ref.network_address = string.sub(peerStr, 2)
else
ref.network_address_id = tonumber(peerStr)
end
local entryEndpointStr = decode_base64(parts[8])
if string.sub(entryEndpointStr, 1, 1) == '#' then
ref.entry_endpoint_name = string.sub(entryEndpointStr, 2)
else
ref.entry_endpoint_id = tonumber(entryEndpointStr)
end
local parentEndpointStr = decode_base64(parts[9])
if string.sub(parentEndpointStr, 1, 1) == '#' then
ref.parent_endpoint_name = string.sub(parentEndpointStr, 2)
else
ref.parent_endpoint_id = tonumber(parentEndpointStr)
end
ref.parent_service = decode_base64(parts[5])
ref.parent_service_instance = decode_base64(parts[6])
ref.parent_endpoint = decode_base64(parts[7])
ref.address_used_at_client = decode_base64(parts[8])
return ref
end
......@@ -91,71 +66,43 @@ end
-- Return string to represent this ref.
function _M.serialize(ref)
local encodedRef = '1'
encodedRef = encodedRef .. '-' .. encode_base64(Util.id2String(ref.trace_id))
encodedRef = encodedRef .. '-' .. encode_base64(Util.id2String(ref.segment_id))
encodedRef = encodedRef .. '-' .. ref.span_id
encodedRef = encodedRef .. '-' .. ref.parent_service_instance_id
encodedRef = encodedRef .. '-' .. ref.entry_service_instance_id
local networkAddress
if ref.network_address_id ~= 0 then
networkAddress = ref.network_address_id .. ''
else
networkAddress = '#' .. ref.network_address
end
encodedRef = encodedRef .. '-' .. encode_base64(networkAddress)
local entryEndpoint
if ref.entry_endpoint_id ~= 0 then
entryEndpoint = ref.entry_endpoint_id .. ''
else
entryEndpoint = '#' .. ref.entry_endpoint_name
end
encodedRef = encodedRef .. '-' .. encode_base64(entryEndpoint)
local parentEndpoint
if ref.parent_endpoint_id ~= 0 then
parentEndpoint = ref.parent_endpoint_id .. ''
else
parentEndpoint = '#' .. ref.parent_endpoint_name
end
encodedRef = encodedRef .. '-' .. encode_base64(parentEndpoint)
.. '-' .. encode_base64(ref.trace_id)
.. '-' .. encode_base64(ref.segment_id)
.. '-' .. ref.span_id
.. '-' .. encode_base64(ref.parent_service)
.. '-' .. encode_base64(ref.parent_service_instance)
.. '-' .. encode_base64(ref.parent_endpoint)
.. '-' .. encode_base64(ref.address_used_at_client)
return encodedRef
end
-- Due to nesting relationship inside Segment/Span/TracingContext at the runtime,
-- RefProtocol is created to prepare JSON format serialization.
-- Following SkyWalking official trace protocol v2
-- https://github.com/apache/skywalking-data-collect-protocol/blob/master/language-agent-v2/trace.proto
-- Following SkyWalking official trace protocol v3
-- https://github.com/apache/skywalking-data-collect-protocol/blob/master/language-agent/Tracing.proto
-- local RefProtocol = {
-- -- Constant in LUA, no cross-thread
-- refType = 'CrossProcess',
-- traceId,
-- parentTraceSegmentId,
-- parentSpanId,
-- parentServiceInstanceId,
-- networkAddress,
-- networkAddressId,
-- entryServiceInstanceId,
-- entryEndpoint,
-- entryEndpointId,
-- parentService,
-- parentServiceInstance,
-- parentEndpoint,
-- parentEndpointId,
-- networkAddressUsedAtPeer,
-- }
-- Return RefProtocol
function _M.transform(ref)
local refBuilder = {}
refBuilder.refType = 'CrossProcess'
refBuilder.parentTraceSegmentId = {idParts = ref.segment_id }
refBuilder.traceId = ref.trace_id
refBuilder.parentTraceSegmentId = ref.segment_id
refBuilder.parentSpanId = ref.span_id
refBuilder.parentServiceInstanceId = ref.parent_service_instance_id
refBuilder.networkAddress = ref.network_address
refBuilder.networkAddressId = ref.network_address_id
refBuilder.entryServiceInstanceId = ref.entry_service_instance_id
refBuilder.entryEndpoint = ref.entry_endpoint_name
refBuilder.entryEndpointId = ref.entry_endpoint_id
refBuilder.parentEndpoint = ref.parent_endpoint_name
refBuilder.parentEndpointId = ref.parent_endpoint_id
refBuilder.parentService = ref.parent_service
refBuilder.parentServiceInstance = ref.parent_service_instance
refBuilder.parentEndpoint = ref.parent_endpoint
refBuilder.networkAddressUsedAtPeer = ref.address_used_at_client
return refBuilder
end
......
......@@ -22,49 +22,43 @@ local cjson = require("cjson")
TestSegmentRef = {}
-- This test is originally from ContextCarrierV2HeaderTest in the Java agent.
function TestSegmentRef:testFromSW6Value()
local ref = SegmentRef.fromSW6Value('1-My40LjU=-MS4yLjM=-4-1-1-IzEyNy4wLjAuMTo4MDgw-Iy9wb3J0YWw=-MTIz')
function TestSegmentRef:testFromSW8Value()
local ref = SegmentRef.fromSW8Value('1-My40LjU=-MS4yLjM=-4-c2VydmljZQ==-aW5zdGFuY2U=-L2FwcA==-MTI3LjAuMC4xOjgwODA=')
lu.assertNotNil(ref)
lu.assertEquals(ref.trace_id, {"3", "4", "5"})
lu.assertEquals(ref.segment_id, {"1", "2", "3"})
lu.assertEquals(ref.trace_id, "3.4.5")
lu.assertEquals(ref.segment_id, "1.2.3")
lu.assertEquals(ref.span_id, 4)
lu.assertEquals(ref.parent_service_instance_id, 1)
lu.assertEquals(ref.entry_service_instance_id, 1)
lu.assertEquals(ref.network_address, '127.0.0.1:8080')
lu.assertEquals(ref.network_address_id, 0)
lu.assertEquals(ref.entry_endpoint_name, '/portal')
lu.assertEquals(ref.entry_endpoint_id, 0)
lu.assertEquals(ref.parent_endpoint_name, nil)
lu.assertEquals(ref.parent_endpoint_id, 123)
lu.assertEquals(ref.parent_service, "service")
lu.assertEquals(ref.parent_service_instance, "instance")
lu.assertEquals(ref.parent_endpoint, '/app')
lu.assertEquals(ref.address_used_at_client, '127.0.0.1:8080')
ref = SegmentRef.fromSW6Value('1-My40LjU=-MS')
ref = SegmentRef.fromSW8Value('1-My40LjU=-MS')
lu.assertNil(ref)
end
function TestSegmentRef:testSerialize()
local ref = SegmentRef.new()
ref.trace_id = {3, 4, 5}
ref.segment_id = {1, 2, 3}
ref.trace_id = "3.4.5"
ref.segment_id = "1.2.3"
ref.span_id = 4
ref.entry_service_instance_id = 1
ref.parent_service_instance_id = 1
ref.network_address = "127.0.0.1:8080"
ref.entry_endpoint_name = "/portal"
ref.parent_endpoint_id = 123
ref.parent_service = "service"
ref.parent_service_instance = "instance"
ref.parent_endpoint = "/app"
ref.address_used_at_client = "127.0.0.1:8080"
lu.assertEquals(SegmentRef.serialize(ref), '1-My40LjU=-MS4yLjM=-4-1-1-IzEyNy4wLjAuMTo4MDgw-Iy9wb3J0YWw=-MTIz')
lu.assertEquals(SegmentRef.serialize(ref), '1-My40LjU=-MS4yLjM=-4-c2VydmljZQ==-aW5zdGFuY2U=-L2FwcA==-MTI3LjAuMC4xOjgwODA=')
end
function TestSegmentRef:testTransform()
local ref = SegmentRef.new()
ref.trace_id = {3, 4, 5}
ref.segment_id = {1, 2, 3}
ref.trace_id = "3.4.5"
ref.segment_id = "1.2.3"
ref.span_id = 4
ref.entry_service_instance_id = 1
ref.parent_service_instance_id = 1
ref.network_address = "127.0.0.1:8080"
ref.entry_endpoint_name = "/portal"
ref.parent_endpoint_id = 123
ref.parent_service = "service"
ref.parent_service_instance = "instance"
ref.parent_endpoint = "/app"
ref.address_used_at_client = "127.0.0.1:8080"
local refProtocol = SegmentRef.transform(ref)
local inJSON = cjson.encode(refProtocol)
......
......@@ -19,7 +19,7 @@ local spanLayer = require("span_layer")
local Util = require('util')
local SegmentRef = require("segment_ref")
local CONTEXT_CARRIER_KEY = 'sw6'
local CONTEXT_CARRIER_KEY = 'sw8'
local _M = {}
-- local Span = {
......@@ -27,7 +27,6 @@ local _M = {}
-- parent_span_id,
-- operation_name,
-- tags,
-- logs,
-- layer = spanLayer.NONE,
-- is_entry = false,
-- is_exit = false,
......@@ -44,8 +43,8 @@ local _M = {}
-- Due to nesting relationship inside Segment/Span/TracingContext at the runtime,
-- SpanProtocol is created to prepare JSON format serialization.
-- Following SkyWalking official trace protocol v2
-- https://github.com/apache/skywalking-data-collect-protocol/blob/master/language-agent-v2/trace.proto
-- Following SkyWalking official trace protocol v3
-- https://github.com/apache/skywalking-data-collect-protocol/blob/master/language-agent/Tracing.proto
-- local SpanProtocol = {
-- spanId,
-- parentSpanId,
......@@ -64,7 +63,7 @@ local _M = {}
-- }
-- Create an entry span. Represent the HTTP incoming request.
-- @param contextCarrier, HTTP request header, which could carry the `sw6` context
-- @param contextCarrier, HTTP request header, which could carry the `sw8` context
function _M.createEntrySpan(operationName, context, parent, contextCarrier)
local span = _M.new(operationName, context, parent)
span.is_entry = true
......@@ -72,7 +71,7 @@ function _M.createEntrySpan(operationName, context, parent, contextCarrier)
if contextCarrier ~= nil then
local propagatedContext = contextCarrier[CONTEXT_CARRIER_KEY]
if propagatedContext ~= nil then
local ref = SegmentRef.fromSW6Value(propagatedContext)
local ref = SegmentRef.fromSW8Value(propagatedContext)
if ref ~= nil then
-- If current trace id is generated by the context, in LUA case, mostly are yes
-- use the ref trace id to override it, in order to keep trace id consistently same.
......@@ -97,45 +96,18 @@ function _M.createExitSpan(operationName, context, parent, peer, contextCarrier)
injectableRef.trace_id = context.trace_id
injectableRef.segment_id = context.segment_id
injectableRef.span_id = span.span_id
-- injectableRef.network_address_id wouldn't be set. Right now, there is no network_address register mechanism
injectableRef.network_address = peer
local entryServiceInstanceId
local entryEndpointName
-- -1 represent the endpoint id doesn't exist, but it is a meaningful value.
-- Once -1 is here, the entryEndpointName will be ignored.
local entryEndpointId = -1
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
if context.internal.first_ref then
local firstRef = context.internal.first_ref
injectableRef.entry_service_instance_id = firstRef.entry_service_instance_id
entryEndpointName = firstRef.entry_endpoint_name
entryEndpointId = firstRef.entry_endpoint_id
entryServiceInstanceId = firstRef.entry_service_instance_id
parentEndpointName = context.internal.first_ref.entry_endpoint_name
else
injectableRef.entry_service_instance_id = context.service_inst_id
if firstSpan.is_entry then
entryEndpointId = 0
entryEndpointName = firstSpan.operation_name
end
entryServiceInstanceId = context.service_inst_id
end
injectableRef.entry_service_instance_id = entryServiceInstanceId
injectableRef.parent_service_instance_id = context.service_inst_id
injectableRef.entry_endpoint_name = entryEndpointName
injectableRef.entry_endpoint_id = entryEndpointId
local parentEndpointName
local parentEndpointId = -1
if firstSpan.is_entry then
parentEndpointName = firstSpan.operation_name
parentEndpointId = 0
end
injectableRef.parent_endpoint_name = parentEndpointName
injectableRef.parent_endpoint_id = parentEndpointId
injectableRef.parent_endpoint = parentEndpointName
contextCarrier[CONTEXT_CARRIER_KEY] = SegmentRef.serialize(injectableRef)
end
......
......@@ -22,7 +22,7 @@ local SpanLayer = require("span_layer")
TestSpan = {}
function TestSpan:testNewEntry()
local context = TC.new(1, 1)
local context = TC.new("service", "instance")
lu.assertNotNil(context)
local span1 = Span.createEntrySpan("operation_name", context, nil, nil)
......@@ -35,11 +35,11 @@ TestSpan = {}
end
function TestSpan:testNewEntryWithContextCarrier()
local context = TC.new(1, 1)
local context = TC.new("service", "instance")
lu.assertNotNil(context)
-- Typical header from the SkyWalking Java Agent test case
local header = {sw6='1-My40LjU=-MS4yLjM=-4-1-1-IzEyNy4wLjAuMTo4MDgw-Iy9wb3J0YWw=-MTIz'}
local header = {sw8='1-My40LjU=-MS4yLjM=-4-c2VydmljZQ==-aW5zdGFuY2U=-L2FwcA==-MTI3LjAuMC4xOjgwODA='}
local span1 = Span.createEntrySpan("operation_name", context, nil, header)
lu.assertNotNil(span1)
......@@ -48,25 +48,21 @@ TestSpan = {}
lu.assertEquals(span1.layer, SpanLayer.NONE)
local ref = span1.refs[1]
lu.assertNotNil(ref)
lu.assertEquals(ref.trace_id, {"3", "4", "5"})
lu.assertEquals(ref.trace_id, "3.4.5")
-- Context trace id will be overrided by the ref trace id
lu.assertEquals(context.trace_id, {"3", "4", "5"})
lu.assertEquals(ref.segment_id, {"1", "2", "3"})
lu.assertEquals(context.trace_id, "3.4.5")
lu.assertEquals(ref.segment_id, "1.2.3")
lu.assertEquals(ref.span_id, 4)
lu.assertEquals(ref.parent_service_instance_id, 1)
lu.assertEquals(ref.entry_service_instance_id, 1)
lu.assertEquals(ref.network_address, '127.0.0.1:8080')
lu.assertEquals(ref.network_address_id, 0)
lu.assertEquals(ref.entry_endpoint_name, '/portal')
lu.assertEquals(ref.entry_endpoint_id, 0)
lu.assertEquals(ref.parent_endpoint_name, nil)
lu.assertEquals(ref.parent_endpoint_id, 123)
lu.assertEquals(ref.parent_service, "service")
lu.assertEquals(ref.parent_service_instance, "instance")
lu.assertEquals(ref.address_used_at_client, '127.0.0.1:8080')
lu.assertEquals(ref.parent_endpoint, '/app')
lu.assertEquals(#(context.internal.active_spans), 1)
end
function TestSpan:testNewExit()
local context = TC.new(1, 1)
local context = TC.new("service", "instance")
lu.assertNotNil(context)
local contextCarrier = {}
......@@ -78,11 +74,11 @@ TestSpan = {}
lu.assertEquals(span1.peer, '127.0.0.1:80')
lu.assertEquals(#(context.internal.active_spans), 1)
lu.assertNotNil(contextCarrier['sw6'])
lu.assertNotNil(contextCarrier['sw8'])
end
function TestSpan:testNew()
local context = TC.new(1, 1)
local context = TC.new("service", "instance")
lu.assertNotNil(context)
local span1 = Span.new("operation_name", context, nil)
......@@ -98,7 +94,7 @@ TestSpan = {}
lu.assertNotNil(span2.start_time)
-- Use new context to check again
context = TC.new(1, 1)
context = TC.new("service", "instance")
lu.assertNotNil(context)
span1 = Span.new("operation_name", context, nil)
......@@ -108,9 +104,9 @@ TestSpan = {}
end
function TestSpan:testProperties()
local context = TC.new(1, 1)
local context = TC.new("service", "instance")
local header = {sw6='1-My40LjU=-MS4yLjM=-4-1-1-IzEyNy4wLjAuMTo4MDgw-Iy9wb3J0YWw=-MTIz'}
local header = {sw8='1-My40LjU=-MS4yLjM=-4-c2VydmljZQ==-aW5zdGFuY2U=-L2FwcA==-MTI3LjAuMC4xOjgwODA='}
local span1 = Span.createEntrySpan("operation_name", context, nil, header)
Span.start(span1, 1234567)
lu.assertEquals(span1.start_time, 1234567)
......@@ -123,13 +119,13 @@ TestSpan = {}
lu.assertEquals(span1.tags[1].value, 'value1')
lu.assertEquals(#span1.refs, 1)
lu.assertEquals(span1.refs[1].network_address, '127.0.0.1:8080')
lu.assertEquals(span1.refs[1].address_used_at_client, '127.0.0.1:8080')
end
function TestSpan:testTransform()
local context = TC.new(1, 1)
local context = TC.new("service", "instance")
local header = {sw6='1-My40LjU=-MS4yLjM=-4-1-1-IzEyNy4wLjAuMTo4MDgw-Iy9wb3J0YWw=-MTIz'}
local header = {sw8='1-My40LjU=-MS4yLjM=-4-c2VydmljZQ==-aW5zdGFuY2U=-L2FwcA==-MTI3LjAuMC4xOjgwODA='}
local span1 = Span.createEntrySpan("operation_name", context, nil, header)
Span.start(span1, 1234567)
Span.finish(span1, 2222222)
......
......@@ -25,20 +25,15 @@ function Tracer:start(upstream_name)
local tracingContext
local serviceName = metadata_buffer:get("serviceName")
local serviceInstId = metadata_buffer:get("serviceInstId")
local serviceId = metadata_buffer:get('serviceId')
if (serviceInstId ~= nil and serviceInstId ~= 0) then
tracingContext = TC.new(serviceId, serviceInstId)
else
tracingContext = TC.newNoOP()
end
local serviceInstanceName = metadata_buffer:get('serviceInstanceName')
tracingContext = TC.new(serviceName, serviceInstanceName)
-- Constant pre-defined in SkyWalking main repo
-- 84 represents Nginx
local nginxComponentId = 6000
local contextCarrier = {}
contextCarrier["sw6"] = ngx.req.get_headers()["sw6"]
contextCarrier["sw8"] = ngx.req.get_headers()["sw8"]
local entrySpan = TC.createEntrySpan(tracingContext, ngx.var.uri, nil, contextCarrier)
Span.start(entrySpan, ngx.now() * 1000)
Span.setComponentId(entrySpan, nginxComponentId)
......@@ -85,11 +80,11 @@ function Tracer:prepareForReport()
local status, segment = TC.drainAfterFinished(ngx.ctx.tracingContext)
if status then
local segmentJson = require('cjson').encode(Segment.transform(segment))
ngx.log(ngx.DEBUG, 'segment = ' .. segmentJson)
ngx.log(ngx.DEBUG, 'segment = ', segmentJson)
local queue = ngx.shared.tracing_buffer
local length = queue:lpush('segment', segmentJson)
ngx.log(ngx.DEBUG, 'segment buffer size = ' .. queue:llen('segment'))
ngx.log(ngx.DEBUG, 'segment buffer size = ', queue:llen('segment'))
end
end
end
......
......@@ -97,8 +97,8 @@ local _M = {}
-- local TracingContext = {
-- trace_id,
-- segment_id,
-- service_id,
-- service_inst_id,
-- service,
-- service_instance,
-- is_noop = false,
-- internal,
-- }
......@@ -107,16 +107,16 @@ function _M.newNoOP()
return {is_noop = true}
end
function _M.new(serviceId, serviceInstID)
if serviceInstID == nil then
function _M.new(serviceName, serviceInstanceName)
if serviceInstanceName == nil or serviceName == nil then
return _M.newNoOP()
end
local tracing_context = {}
tracing_context.trace_id = Util.newID()
tracing_context.segment_id = tracing_context.trace_id
tracing_context.service_id = serviceId
tracing_context.service_inst_id = serviceInstID
tracing_context.service = serviceName
tracing_context.service_instance = serviceInstanceName
tracing_context.internal = Internal.new()
tracing_context.internal.owner = tracing_context
return tracing_context
......@@ -160,8 +160,8 @@ function _M.drainAfterFinished(tracingContext)
local segment = {}
segment.trace_id = tracingContext.trace_id
segment.segment_id = tracingContext.segment_id
segment.service_id = tracingContext.service_id
segment.service_inst_id = tracingContext.service_inst_id
segment.service = tracingContext.service
segment.service_instance = tracingContext.service_instance
segment.spans = tracingContext.internal.finished_spans
return true, segment
end
......
......@@ -22,23 +22,21 @@ local Span = require('span')
TestTracingContext = {}
function TestTracingContext:testNew()
local context = TC.new(1, 1)
local context = TC.new("service", "instance")
lu.assertNotNil(context)
lu.assertNotNil(context.segment_id[1])
lu.assertNotNil(context.segment_id[2])
lu.assertNotNil(context.segment_id[3])
lu.assertNotNil(context.segment_id)
lu.assertEquals(context.trace_id, context.segment_id)
end
function TestTracingContext:testInternal_NextSpanSeqID()
local context = TC.new(1, 1)
local context = TC.new("service", "instance")
lu.assertEquals(context.internal.nextSpanID(context.internal), 0)
end
function TestTracingContext:testInternal_addActive()
local context = TC.new(1, 1)
local context = TC.new("service", "instance")
local mockSpan = {span_id = 0}
context.internal.addActive(context.internal, mockSpan)
......@@ -47,7 +45,7 @@ TestTracingContext = {}
end
function TestTracingContext:testSpanStack()
local context = TC.new(1, 1)
local context = TC.new("service", "instance")
local span1 = TC.createEntrySpan(context, 'entry_op')
local span2 = TC.createExitSpan(context, "exit_op", span1, "127.0.0.1")
......
......@@ -80,30 +80,23 @@ local random_seed = function ()
return seed
end
math.randomseed(random_seed())
function _M.newID()
return {timestamp(), math.random(0, MAX_ID_PART2), math.random(0, MAX_ID_PART3)}
end
math.randomseed(random_seed())
-- Format a trace/segment id into an array.
-- An official ID should have three parts separated by '.' and each part of it is a number
function _M.formatID(str)
local regex = '.'
if _M.is_ngx_lua then
regex = [[\.]]
local newID
-- for Nginx Lua
local ok, uuid = pcall(require, "resty.jit-uuid")
if ok then
uuid.seed()
newID = function()
return uuid.generate_v4()
end
local parts = split(str, regex)
if #parts ~= 3 then
return nil
else
newID = function()
return timestamp() .. '.' .. math.random(0, MAX_ID_PART2) .. '.' .. math.random(0, MAX_ID_PART3)
end
return parts
end
-- @param id is an array with length = 3
function _M.id2String(id)
return id[1] .. '.' .. id[2] .. '.' .. id[3]
end
_M.newID = newID
return _M
......@@ -22,9 +22,7 @@ TestUtil = {}
function TestUtil.testNewID()
local id = Util.newID()
lu.assertNotNil(id[1])
lu.assertNotNil(id[2])
lu.assertNotNil(id[3])
lu.assertNotNil(id)
end
function TestUtil.testTimestamp()
......
The MIT License (MIT)
Copyright (c) 2016-2019 Thibault Charbonnier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
\ No newline at end of file
package = "skywalking-nginx-lua"
version = "2.0-0"
source = {
url = "git://github.com/apache/skywalking-nginx-lua",
branch = "master",
}
description = {
summary = "The Nginx Lua agent for Apache SkyWalking",
homepage = "https://github.com/apache/skywalking-nginx-lua",
license = "Apache License 2.0"
}
dependencies = {
"lua-resty-http = 0.15"
"lua-resty-jit-uuid = 0.0.7"
}
build = {
type = "builtin",
modules = {
["skywalking.management"] = "lib/skywalking/management.lua",
["skywalking.segment_ref"] = "lib/skywalking/segment_ref.lua",
["skywalking.segment"] = "lib/skywalking/segment.lua",
["skywalking.span_layer"] = "lib/skywalking/span_layer.lua",
["skywalking.span"] = "lib/skywalking/span.lua",
["skywalking.tracing_context"] = "lib/skywalking/tracing_context.lua",
["skywalking.util"] = "lib/skywalking/util.lua",
}
}
......@@ -20,40 +20,32 @@ run_tests;
__DATA__
=== TEST 1: fromSW6Value
=== TEST 1: fromSW8Value
--- http_config eval: $::HttpConfig
--- config
location /t {
content_by_lua_block {
local SegmentRef = require('segment_ref')
local ref = SegmentRef.fromSW6Value('1-My40LjU=-MS4yLjM=-4-1-1-IzEyNy4wLjAuMTo4MDgw-Iy9wb3J0YWw=-MTIz')
local ref = SegmentRef.fromSW8Value('1-My40LjU=-MS4yLjM=-4-c2VydmljZQ==-aW5zdGFuY2U=-L2FwcA==-MTI3LjAuMC4xOjgwODA=')
ngx.say(ref.trace_id)
ngx.say(ref.segment_id)
ngx.say(ref.span_id)
ngx.say(ref.parent_service_instance_id)
ngx.say(ref.entry_service_instance_id)
ngx.say(ref.network_address)
ngx.say(ref.network_address_id)
ngx.say(ref.entry_endpoint_name)
ngx.say(ref.entry_endpoint_id)
ngx.say(ref.parent_endpoint_name)
ngx.say(ref.parent_endpoint_id)
ngx.say(ref.parent_service)
ngx.say(ref.parent_service_instance)
ngx.say(ref.parent_endpoint)
ngx.say(ref.address_used_at_client)
}
}
--- request
GET /t
--- response_body
345
123
3.4.5
1.2.3
4
1
1
service
instance
/app
127.0.0.1:8080
0
/portal
0
nil
123
--- no_error_log
[error]
......@@ -66,21 +58,20 @@ nil
content_by_lua_block {
local SegmentRef = require('segment_ref')
local ref = SegmentRef.new()
ref.trace_id = {3, 4, 5}
ref.segment_id = {1, 2, 3}
ref.trace_id = "3.4.5"
ref.segment_id = "1.2.3"
ref.span_id = 4
ref.entry_service_instance_id = 1
ref.parent_service_instance_id = 1
ref.network_address = "127.0.0.1:8080"
ref.entry_endpoint_name = "/portal"
ref.parent_endpoint_id = 123
ref.parent_service = "service"
ref.parent_service_instance = "instance"
ref.parent_endpoint = "/app"
ref.address_used_at_client = "127.0.0.1:8080"
ngx.say(SegmentRef.serialize(ref))
}
}
--- request
GET /t
--- response_body
1-My40LjU=-MS4yLjM=-4-1-1-IzEyNy4wLjAuMTo4MDgw-Iy9wb3J0YWw=-MTIz
1-My40LjU=-MS4yLjM=-4-c2VydmljZQ==-aW5zdGFuY2U=-L2FwcA==-MTI3LjAuMC4xOjgwODA=
--- no_error_log
[error]
......@@ -95,14 +86,13 @@ GET /t
local cjson = require("cjson")
local ref = SegmentRef.new()
ref.trace_id = {3, 4, 5}
ref.segment_id = {1, 2, 3}
ref.trace_id = "3.4.5"
ref.segment_id = "1.2.3"
ref.span_id = 4
ref.entry_service_instance_id = 1
ref.parent_service_instance_id = 1
ref.network_address = "127.0.0.1:8080"
ref.entry_endpoint_name = "/portal"
ref.parent_endpoint_id = 123
ref.parent_service = "service"
ref.parent_service_instance = "instance"
ref.parent_endpoint = "/app"
ref.address_used_at_client = "127.0.0.1:8080"
local refProtocol = SegmentRef.transform(ref)
local inJSON = cjson.encode(refProtocol)
......
......@@ -47,43 +47,13 @@ true
=== TEST 2: newID
--- http_config eval: $::HttpConfig
--- config
location /t {
content_by_lua_block {
local util = require('util')
local new_id = util.newID()
local regex = [[^\d+$]]
ngx.say(#new_id)
for i = 1, #new_id, 1 do
local m = ngx.re.match(new_id[i], regex)
if m and tonumber(m[0]) == new_id[i] then
ngx.say(i)
end
end
}
}
--- request
GET /t
--- response_body
3
1
2
3
--- no_error_log
[error]
=== TEST 3: id2String
--- http_config eval: $::HttpConfig
--- config
location /t {
content_by_lua_block {
local util = require('util')
local id = util.newID()
local id_str = util.id2String(id)
local regex = [[^\d+\.\d+\.\d+$]]
local m = ngx.re.match(id_str, regex)
local regex = [[^[0-9a-f]+\-[0-9a-f]+\-[0-9a-f]+\-[0-9a-f]+\-[0-9a-f]+$]]
local m = ngx.re.match(id, regex)
if m then
ngx.say(true)
end
......@@ -95,24 +65,3 @@ GET /t
true
--- no_error_log
[error]
=== TEST 4: formatID
--- http_config eval: $::HttpConfig
--- config
location /t {
content_by_lua_block {
local util = require('util')
local id = util.newID()
local id_str = util.id2String(id)
local parts = util.formatID(id_str)
ngx.say(#parts)
}
}
--- request
GET /t
--- response_body
3
--- no_error_log
[error]
......@@ -38,7 +38,7 @@
<docker.image.name>skywalking-collector</docker.image.name>
<docker-maven-plugin.version>0.4.13</docker-maven-plugin.version>
<exec-maven-plugin.version>1.6.0</exec-maven-plugin.version>
<agent-test-tools.version>14b1499e65788d570ce5914b2ca7320cd36f961e</agent-test-tools.version>
<agent-test-tools.version>1f009d692b99896c08cf9f26440f92287f0364e7</agent-test-tools.version>
<agent-test-tools.workingDirectory>${project.basedir}/target/agent-test-tools</agent-test-tools.workingDirectory>
<agent-test-tools.repos>https://github.com/apache/skywalking-agent-test-tool.git</agent-test-tools.repos>
</properties>
......
......@@ -49,9 +49,10 @@ segmentItems:
parentSpanId: -1
componentId: 6000
refs:
- {parentEndpointId: 0, entryEndpointId: 0, parentServiceInstanceId: 1, parentEndpoint: /ingress, parentTraceSegmentId: not null,
- {parentEndpoint: /ingress, parentTraceSegmentId: not null, refType: 'CrossProcess',
networkAddress: 'e2e-test-with-mock-collector:upstream_ip:port', parentSpanId: 1,
entryServiceInstanceId: 1, networkAddressId: 0, entryEndpoint: /ingress}
parentServiceInstance: 'e2e-test-with-mock-collector-instanceA', traceId: not null,
parentService: 'e2e-test-with-mock-collector'}
spanLayer: Http
- segmentId: not null
spans:
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册