You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by wu...@apache.org on 2020/03/05 03:32:48 UTC

[skywalking-nginx-lua] branch master updated: used the nginx lua code style. (#10)

This is an automated email from the ASF dual-hosted git repository.

wusheng pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/skywalking-nginx-lua.git


The following commit(s) were added to refs/heads/master by this push:
     new c6aca51  used the nginx lua code style. (#10)
c6aca51 is described below

commit c6aca516200573a64db7060cefb5d3608c571c3a
Author: 吴晟 Wu Sheng <wu...@foxmail.com>
AuthorDate: Thu Mar 5 11:32:39 2020 +0800

    used the nginx lua code style. (#10)
    
    * used the nginx lua code style for tracing context.
    
    * fixed test cases.
    
    * fixed
    
    * fixed test cases
    
    * fixed
    
    * fixed code style of register.
    
    * code style of segment.
    
    * add test cases for segment ref.
    
    * fixed
    
    * fixed bug in TracingContext:drainAfterFinished
    
    Co-authored-by: 吴晟 Wu Sheng <wu...@foxmail.com>
    Co-authored-by: Daming <zt...@foxmail.com>
---
 .gitignore                              |   5 +-
 README.md                               |  20 +--
 lib/skywalking/client.lua               |  32 ++--
 lib/skywalking/register.lua             |  22 +--
 lib/skywalking/segment.lua              |  74 ++++----
 lib/skywalking/segment_ref.lua          | 193 +++++++++++----------
 lib/skywalking/segment_ref_test.lua     |  22 +--
 lib/skywalking/span.lua                 | 298 ++++++++++++++++----------------
 lib/skywalking/span_test.lua            |  60 +++----
 lib/skywalking/tracer.lua               |  51 +++---
 lib/skywalking/tracing_context.lua      | 249 ++++++++++++--------------
 lib/skywalking/tracing_context_test.lua |  42 ++---
 t/segment_ref.t                         | 117 +++++++++++++
 t/util.t                                |   1 +
 14 files changed, 636 insertions(+), 550 deletions(-)

diff --git a/.gitignore b/.gitignore
index 162d14d..e072784 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,9 +13,10 @@ packages/
 /dist/
 /docker/snapshot/*.gz
 .mvn/wrapper/*.jar
-.factorypath  
-.vscode 
+.factorypath
+.vscode
 .checkstyle
 .externalToolBuilders
 /test/plugin/dist
 /test/plugin/workspace
+/t/servroot/
diff --git a/README.md b/README.md
index e0ee074..45fe6ed 100644
--- a/README.md
+++ b/README.md
@@ -8,9 +8,9 @@ Apache SkyWalking Nginx Agent
 ![CI](https://github.com/apache/skywalking-nginx-lua/workflows/CI/badge.svg?branch=master)
 
 
-[**SkyWalking**](https://github.com/apache/skywalking) Nginx Agent provides the native tracing capability for Nginx powered by Nginx LUA module. 
+[**SkyWalking**](https://github.com/apache/skywalking) Nginx Agent provides the native tracing capability for Nginx powered by Nginx LUA module.
 
-This agent follows the SkyWalking tracing and header protocol. It reports tracing data to SkyWalking APM through HTTP protocol. 
+This agent follows the SkyWalking tracing and header protocol. It reports tracing data to SkyWalking APM through HTTP protocol.
 All HTTP 1.1 requests go through Nginx could be collected by this agent.
 
 # Setup Doc
@@ -18,9 +18,9 @@ All HTTP 1.1 requests go through Nginx could be collected by this agent.
 http {
     lua_package_path "/Path/to/.../skywalking-nginx-lua/lib/skywalking/?.lua;;";
 
-    # Buffer represents the register inform and the queue of the finished segment 
+    # Buffer represents the register inform and the queue of the finished segment
     lua_shared_dict tracing_buffer 100m;
-    
+
     # Init is the timer setter and keeper
     # Setup an infinite loop timer to do register and trace report.
     init_worker_by_lua_block {
@@ -112,13 +112,13 @@ The following APIs are for developers or using this lib out of the Nginx case.
 
 ## Tracing APIs at LUA level
 **TracingContext** is the entrance API for lua level tracing.
-- `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.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.
 
 Create 2 kinds of span
-- `TracingContext:createEntrySpan(operationName, parent, contextCarrier)`
-- `TracingContext:createExitSpan(operationName, parent, peer, contextCarrier)`
+- `TracingContext.createEntrySpan(operationName, parent, contextCarrier)`
+- `TracingContext.createExitSpan(operationName, parent, peer, contextCarrier)`
 
 
 # Download
@@ -131,4 +131,4 @@ Have no release yet.
 * QQ Group: 392443393(2000/2000, not available), 901167865(available)
 
 # License
-Apache 2.0
\ No newline at end of file
+Apache 2.0
diff --git a/lib/skywalking/client.lua b/lib/skywalking/client.lua
index 502e198..42b8f7f 100644
--- a/lib/skywalking/client.lua
+++ b/lib/skywalking/client.lua
@@ -1,19 +1,19 @@
--- 
+--
 -- Licensed to the Apache Software Foundation (ASF) under one or more
 -- contributor license agreements.  See the NOTICE file distributed with
 -- this work for additional information regarding copyright ownership.
 -- The ASF licenses this file to You under the Apache License, Version 2.0
 -- (the "License"); you may not use this file except in compliance with
 -- the License.  You may obtain a copy of the License at
--- 
+--
 --    http://www.apache.org/licenses/LICENSE-2.0
--- 
+--
 -- Unless required by applicable law or agreed to in writing, software
 -- distributed under the License is distributed on an "AS IS" BASIS,
 -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
--- 
+--
 
 local Client = {}
 
@@ -78,9 +78,9 @@ function Client:registerService(metadata_buffer, backend_http_uri)
     local ERR = ngx.ERR
 
     local serviceName = metadata_buffer:get('serviceName')
-    
+
     local cjson = require('cjson')
-    local serviceRegister = require("register"):newServiceRegister(serviceName)
+    local serviceRegister = require("register").newServiceRegister(serviceName)
     local serviceRegisterParam = cjson.encode(serviceRegister)
 
     local http = require('resty.http')
@@ -102,7 +102,7 @@ function Client:registerService(metadata_buffer, backend_http_uri)
         for i, result in ipairs(registerResults)
         do
             if result.key == serviceName then
-                local serviceId = result.value 
+                local serviceId = result.value
                 log(DEBUG, "Service registered, service id = " .. serviceId)
                 metadata_buffer:set('serviceId', serviceId)
             end
@@ -122,9 +122,9 @@ function Client:registerServiceInstance(metadata_buffer, backend_http_uri)
     metadata_buffer:set('serviceInstanceUUID', serviceInstName)
 
     local cjson = require('cjson')
-    local serviceInstanceRegister = require("register"):newServiceInstanceRegister(
-        metadata_buffer:get('serviceId'), 
-        serviceInstName, 
+    local serviceInstanceRegister = require("register").newServiceInstanceRegister(
+        metadata_buffer:get('serviceId'),
+        serviceInstName,
         ngx.now() * 1000)
     local serviceInstanceRegisterParam = cjson.encode(serviceInstanceRegister)
 
@@ -146,7 +146,7 @@ function Client:registerServiceInstance(metadata_buffer, backend_http_uri)
             for i, result in ipairs(registerResults)
             do
                 if result.key == serviceInstName then
-                    local serviceId = result.value 
+                    local serviceId = result.value
                     log(DEBUG, "Service Instance registered, service instance id = " .. serviceId)
                     metadata_buffer:set('serviceInstId', serviceId)
                 end
@@ -166,9 +166,9 @@ function Client:ping(metadata_buffer, backend_http_uri)
     local ERR = ngx.ERR
 
     local cjson = require('cjson')
-    local pingPkg = require("register"):newServiceInstancePingPkg(
-        metadata_buffer:get('serviceInstId'), 
-        metadata_buffer:get('serviceInstanceUUID'), 
+    local pingPkg = require("register").newServiceInstancePingPkg(
+        metadata_buffer:get('serviceInstId'),
+        metadata_buffer:get('serviceInstanceUUID'),
         ngx.now() * 1000)
     local pingPkgParam = cjson.encode(pingPkg)
 
@@ -214,7 +214,7 @@ function Client:reportTraces(metadata_buffer, backend_http_uri)
                 ["Content-Type"] = "application/json",
             },
         })
-        
+
         if err == nil then
             if res.status ~= 200 then
                 log(ERR, "Segment report fails, response code " .. res.status)
@@ -235,4 +235,4 @@ function Client:reportTraces(metadata_buffer, backend_http_uri)
     end
 end
 
-return Client
\ No newline at end of file
+return Client
diff --git a/lib/skywalking/register.lua b/lib/skywalking/register.lua
index 988552f..8223ebc 100644
--- a/lib/skywalking/register.lua
+++ b/lib/skywalking/register.lua
@@ -1,28 +1,28 @@
--- 
+--
 -- Licensed to the Apache Software Foundation (ASF) under one or more
 -- contributor license agreements.  See the NOTICE file distributed with
 -- this work for additional information regarding copyright ownership.
 -- The ASF licenses this file to You under the Apache License, Version 2.0
 -- (the "License"); you may not use this file except in compliance with
 -- the License.  You may obtain a copy of the License at
--- 
+--
 --    http://www.apache.org/licenses/LICENSE-2.0
--- 
+--
 -- Unless required by applicable law or agreed to in writing, software
 -- distributed under the License is distributed on an "AS IS" BASIS,
 -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
--- 
+--
 
-local Register = {}
+local _M = {}
 
 -- Return Services as service register parameter
-function Register:newServiceRegister(unRegisterServiceName)
+function _M.newServiceRegister(unRegisterServiceName)
     local serv = {
         services = {}
     }
-    
+
     local service = {
         serviceName = unRegisterServiceName,
         -- Field type is optional, default value is `normal`
@@ -34,11 +34,11 @@ function Register:newServiceRegister(unRegisterServiceName)
     return serv
 end
 
-function Register:newServiceInstanceRegister(registeredServiceId, serviceInstUUID, registerTime)
+function _M.newServiceInstanceRegister(registeredServiceId, serviceInstUUID, registerTime)
     local serviceInstances = {
         instances = {}
     }
-    
+
     local serviceInstance = {
         serviceId = registeredServiceId,
         instanceUUID = serviceInstUUID,
@@ -53,7 +53,7 @@ function Register:newServiceInstanceRegister(registeredServiceId, serviceInstUUI
     return serviceInstances
 end
 
-function Register:newServiceInstancePingPkg(registeredServiceInstId, serviceInstUUID, updateTime)
+function _M.newServiceInstancePingPkg(registeredServiceInstId, serviceInstUUID, updateTime)
     local serviceInstancePingPkg = {
         serviceInstanceId = registeredServiceInstId,
         time = updateTime,
@@ -63,4 +63,4 @@ function Register:newServiceInstancePingPkg(registeredServiceInstId, serviceInst
     return serviceInstancePingPkg
 end
 
-return Register
\ No newline at end of file
+return _M
diff --git a/lib/skywalking/segment.lua b/lib/skywalking/segment.lua
index ea2f33e..7842f2c 100644
--- a/lib/skywalking/segment.lua
+++ b/lib/skywalking/segment.lua
@@ -17,65 +17,49 @@
 
 -- Segment represents a finished tracing context
 -- Including all information to send to the SkyWalking OAP server.
-local Util = require('util')
+local Span = require('span')
 
-local Segment = {
-    trace_id,
-    segment_id,
-    service_id,
-    service_inst_id,
-    spans,
-}
+local _M = {}
+-- local Segment = {
+--     trace_id,
+--     segment_id,
+--     service_id,
+--     service_inst_id,
+--     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
-local SegmentProtocol = {
-    globalTraceIds,
-    traceSegmentId,
-    serviceId,
-    serviceInstanceId,
-    spans,
-}
-
-function Segment:new()
-    local o = {}
-    setmetatable(o, self)
-    self.__index = self
-
-    return o
-end
-
-function SegmentProtocol:new()
-    local o = {}
-    setmetatable(o, self)
-    self.__index = self
-
-    o.globalTraceIds = {}
-
-    return o
-end
+-- local SegmentProtocol = {
+--     globalTraceIds,
+--     traceSegmentId,
+--     serviceId,
+--     serviceInstanceId,
+--     spans,
+-- }
 
 -- Return SegmentProtocol
-function Segment:transform()
-    local segmentBuilder = SegmentProtocol:new()
-    segmentBuilder.serviceId = self.service_id
-    segmentBuilder.globalTraceIds[1] = { idParts = self.trace_id}
-    segmentBuilder.traceSegmentId = { idParts = self.segment_id}
-    segmentBuilder.serviceId = self.service_id
-    segmentBuilder.serviceInstanceId = self.service_inst_id
+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.spans = {}
 
-    if self.spans ~= nil and #self.spans > 0 then
-        for i, span in ipairs(self.spans)
-        do 
-            segmentBuilder.spans[#segmentBuilder.spans + 1] = span:transform()
+    if segment.spans ~= nil and #segment.spans > 0 then
+        for i, span in ipairs(segment.spans)
+        do
+            segmentBuilder.spans[#segmentBuilder.spans + 1] = Span.transform(span)
         end
     end
 
     return segmentBuilder
 end
 
-return Segment
+return _M
diff --git a/lib/skywalking/segment_ref.lua b/lib/skywalking/segment_ref.lua
index 7709e90..7f85167 100644
--- a/lib/skywalking/segment_ref.lua
+++ b/lib/skywalking/segment_ref.lua
@@ -16,142 +16,147 @@
 --
 local Util = require('util')
 local Base64 = require('dependencies/base64')
+local encode_base64 = Base64.encode
+local decode_base64 = Base64.decode
 
-local SegmentRef = {
-    -- There is no multiple-threads scenario in the LUA, no only hard coded as CROSS_PROCESS
-    type = 'CROSS_PROCESS',
-    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,
-}
-
--- 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
-local RefProtocol = {
-    -- Constant in LUA, no cross-thread
-    refType = 'CrossProcess',
-    parentTraceSegmentId,
-    parentSpanId,
-    parentServiceInstanceId,
-    networkAddress,
-    networkAddressId,
-    entryServiceInstanceId,
-    entryEndpoint,
-    entryEndpointId,
-    parentEndpoint,
-    parentEndpointId,
-}
-
-function SegmentRef:new()
-    local o = {}
-    setmetatable(o, self)
-    self.__index = self
-
-    return o
+if Util.is_ngx_lua then
+    encode_base64 = ngx.encode_base64
+    decode_base64 = ngx.decode_base64
 end
 
-function RefProtocol:new()
-    local o = {}
-    setmetatable(o, self)
-    self.__index = self
-
-    return o
+local _M = {}
+-- local SegmentRef = {
+--     -- There is no multiple-threads scenario in the LUA, no only hard coded as CROSS_PROCESS
+--     type = 'CROSS_PROCESS',
+--     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,
+-- }
+
+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 SegmentRef:fromSW6Value(value)
+function _M.fromSW6Value(value)
+    local ref = _M.new()
+
     local parts = Util.split(value, '-')
     if #parts ~= 9 then
         return nil
     end
 
-    self.trace_id = Util.formatID(Base64.decode(parts[2]))
-    self.segment_id = Util.formatID(Base64.decode(parts[3]))
-    self.span_id = tonumber(parts[4])
-    self.parent_service_instance_id = tonumber(parts[5])
-    self.entry_service_instance_id = tonumber(parts[6])
-    local peerStr = Base64.decode(parts[7])
+    ref.trace_id = Util.formatID(decode_base64(parts[2]))
+    ref.segment_id = Util.formatID(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
-        self.network_address = string.sub(peerStr, 2)
+        ref.network_address = string.sub(peerStr, 2)
     else
-        self.network_address_id = tonumber(peerStr)
+        ref.network_address_id = tonumber(peerStr)
     end
-    local entryEndpointStr = Base64.decode(parts[8])
+    local entryEndpointStr = decode_base64(parts[8])
     if string.sub(entryEndpointStr, 1, 1) == '#' then
-        self.entry_endpoint_name = string.sub(entryEndpointStr, 2)
+        ref.entry_endpoint_name = string.sub(entryEndpointStr, 2)
     else
-        self.entry_endpoint_id = tonumber(entryEndpointStr)
+        ref.entry_endpoint_id = tonumber(entryEndpointStr)
     end
-    local parentEndpointStr = Base64.decode(parts[9])
+    local parentEndpointStr = decode_base64(parts[9])
     if string.sub(parentEndpointStr, 1, 1) == '#' then
-        self.parent_endpoint_name = string.sub(parentEndpointStr, 2)
+        ref.parent_endpoint_name = string.sub(parentEndpointStr, 2)
     else
-        self.parent_endpoint_id = tonumber(parentEndpointStr)
+        ref.parent_endpoint_id = tonumber(parentEndpointStr)
     end
 
-    return self
+    return ref
 end
 
 -- Return string to represent this ref.
-function SegmentRef:serialize()
+function _M.serialize(ref)
     local encodedRef = '1'
-    encodedRef = encodedRef .. '-' .. Base64.encode(Util.id2String(self.trace_id))
-    encodedRef = encodedRef .. '-' .. Base64.encode(Util.id2String(self.segment_id))
-    encodedRef = encodedRef .. '-' .. self.span_id
-    encodedRef = encodedRef .. '-' .. self.parent_service_instance_id
-    encodedRef = encodedRef .. '-' .. self.entry_service_instance_id
+    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 self.network_address_id ~= 0 then
-        networkAddress = self.network_address_id .. ''
+    if ref.network_address_id ~= 0 then
+        networkAddress = ref.network_address_id .. ''
     else
-        networkAddress = '#' .. self.network_address
+        networkAddress = '#' .. ref.network_address
     end
-    encodedRef = encodedRef .. '-' .. Base64.encode(networkAddress)
+    encodedRef = encodedRef .. '-' .. encode_base64(networkAddress)
 
     local entryEndpoint
-    if self.entry_endpoint_id ~= 0 then
-        entryEndpoint = self.entry_endpoint_id .. ''
+    if ref.entry_endpoint_id ~= 0 then
+        entryEndpoint = ref.entry_endpoint_id .. ''
     else
-        entryEndpoint = '#' .. self.entry_endpoint_name
+        entryEndpoint = '#' .. ref.entry_endpoint_name
     end
-    encodedRef = encodedRef .. '-' .. Base64.encode(entryEndpoint)
+    encodedRef = encodedRef .. '-' .. encode_base64(entryEndpoint)
 
     local parentEndpoint
-    if self.parent_endpoint_id ~= 0 then
-        parentEndpoint = self.parent_endpoint_id .. ''
+    if ref.parent_endpoint_id ~= 0 then
+        parentEndpoint = ref.parent_endpoint_id .. ''
     else
-        parentEndpoint = '#' .. self.parent_endpoint_name
+        parentEndpoint = '#' .. ref.parent_endpoint_name
     end
-    encodedRef = encodedRef .. '-' .. Base64.encode(parentEndpoint)
+    encodedRef = encodedRef .. '-' .. encode_base64(parentEndpoint)
 
     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
+-- local RefProtocol = {
+--     -- Constant in LUA, no cross-thread
+--     refType = 'CrossProcess',
+--     parentTraceSegmentId,
+--     parentSpanId,
+--     parentServiceInstanceId,
+--     networkAddress,
+--     networkAddressId,
+--     entryServiceInstanceId,
+--     entryEndpoint,
+--     entryEndpointId,
+--     parentEndpoint,
+--     parentEndpointId,
+-- }
 -- Return RefProtocol
-function SegmentRef:transform()
-    local refBuilder = RefProtocol:new()
-    refBuilder.parentTraceSegmentId = {idParts = self.segment_id }
-    refBuilder.parentSpanId = self.span_id
-    refBuilder.parentServiceInstanceId = self.parent_service_instance_id
-    refBuilder.networkAddress = self.network_address
-    refBuilder.networkAddressId = self.network_address_id
-    refBuilder.entryServiceInstanceId = self.entry_service_instance_id
-    refBuilder.entryEndpoint = self.entry_endpoint_name
-    refBuilder.entryEndpointId = self.entry_endpoint_id
-    refBuilder.parentEndpoint = self.parent_endpoint_name
-    refBuilder.parentEndpointId = self.parent_endpoint_id
+function _M.transform(ref)
+    local refBuilder = {}
+    refBuilder.refType = 'CrossProcess'
+    refBuilder.parentTraceSegmentId = {idParts = 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
     return refBuilder
 end
 
-return SegmentRef
+return _M
diff --git a/lib/skywalking/segment_ref_test.lua b/lib/skywalking/segment_ref_test.lua
index 94fc0c0..72c664e 100644
--- a/lib/skywalking/segment_ref_test.lua
+++ b/lib/skywalking/segment_ref_test.lua
@@ -1,19 +1,19 @@
--- 
+--
 -- Licensed to the Apache Software Foundation (ASF) under one or more
 -- contributor license agreements.  See the NOTICE file distributed with
 -- this work for additional information regarding copyright ownership.
 -- The ASF licenses this file to You under the Apache License, Version 2.0
 -- (the "License"); you may not use this file except in compliance with
 -- the License.  You may obtain a copy of the License at
--- 
+--
 --    http://www.apache.org/licenses/LICENSE-2.0
--- 
+--
 -- Unless required by applicable law or agreed to in writing, software
 -- distributed under the License is distributed on an "AS IS" BASIS,
 -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
--- 
+--
 
 
 local lu = require('luaunit')
@@ -23,7 +23,7 @@ local cjson = require("cjson")
 TestSegmentRef = {}
     -- This test is originally from ContextCarrierV2HeaderTest in the Java agent.
     function TestSegmentRef:testFromSW6Value()
-        local ref = SegmentRef:new():fromSW6Value('1-My40LjU=-MS4yLjM=-4-1-1-IzEyNy4wLjAuMTo4MDgw-Iy9wb3J0YWw=-MTIz')
+        local ref = SegmentRef.fromSW6Value('1-My40LjU=-MS4yLjM=-4-1-1-IzEyNy4wLjAuMTo4MDgw-Iy9wb3J0YWw=-MTIz')
         lu.assertNotNil(ref)
         lu.assertEquals(ref.trace_id, {"3", "4", "5"})
         lu.assertEquals(ref.segment_id, {"1", "2", "3"})
@@ -37,12 +37,12 @@ TestSegmentRef = {}
         lu.assertEquals(ref.parent_endpoint_name, nil)
         lu.assertEquals(ref.parent_endpoint_id, 123)
 
-        ref = SegmentRef:new():fromSW6Value('1-My40LjU=-MS')
+        ref = SegmentRef.fromSW6Value('1-My40LjU=-MS')
         lu.assertNil(ref)
     end
 
     function TestSegmentRef:testSerialize()
-        local ref = SegmentRef:new()
+        local ref = SegmentRef.new()
         ref.trace_id = {3, 4, 5}
         ref.segment_id = {1, 2, 3}
         ref.span_id = 4
@@ -52,11 +52,11 @@ TestSegmentRef = {}
         ref.entry_endpoint_name = "/portal"
         ref.parent_endpoint_id = 123
 
-        lu.assertEquals(ref:serialize(), '1-My40LjU=-MS4yLjM=-4-1-1-IzEyNy4wLjAuMTo4MDgw-Iy9wb3J0YWw=-MTIz')
+        lu.assertEquals(SegmentRef.serialize(ref), '1-My40LjU=-MS4yLjM=-4-1-1-IzEyNy4wLjAuMTo4MDgw-Iy9wb3J0YWw=-MTIz')
     end
 
     function TestSegmentRef:testTransform()
-        local ref = SegmentRef:new()
+        local ref = SegmentRef.new()
         ref.trace_id = {3, 4, 5}
         ref.segment_id = {1, 2, 3}
         ref.span_id = 4
@@ -66,11 +66,11 @@ TestSegmentRef = {}
         ref.entry_endpoint_name = "/portal"
         ref.parent_endpoint_id = 123
 
-        local refProtocol = ref:transform()
+        local refProtocol = SegmentRef.transform(ref)
         local inJSON = cjson.encode(refProtocol)
         lu.assertTrue(string.len(inJSON) > 0)
     end
 -- end TestSegmentRef
 
 
-os.exit( lu.LuaUnit.run() )
\ No newline at end of file
+os.exit( lu.LuaUnit.run() )
diff --git a/lib/skywalking/span.lua b/lib/skywalking/span.lua
index cd5ab4c..6dd30ec 100644
--- a/lib/skywalking/span.lua
+++ b/lib/skywalking/span.lua
@@ -1,19 +1,19 @@
--- 
+--
 -- Licensed to the Apache Software Foundation (ASF) under one or more
 -- contributor license agreements.  See the NOTICE file distributed with
 -- this work for additional information regarding copyright ownership.
 -- The ASF licenses this file to You under the Apache License, Version 2.0
 -- (the "License"); you may not use this file except in compliance with
 -- the License.  You may obtain a copy of the License at
--- 
+--
 --    http://www.apache.org/licenses/LICENSE-2.0
--- 
+--
 -- Unless required by applicable law or agreed to in writing, software
 -- distributed under the License is distributed on an "AS IS" BASIS,
 -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
--- 
+--
 
 local spanLayer = require("span_layer")
 local Util = require('util')
@@ -21,61 +21,62 @@ local SegmentRef = require("segment_ref")
 
 local CONTEXT_CARRIER_KEY = 'sw6'
 
-local Span = {
-    span_id,
-    parent_span_id,
-    operation_name,
-    tags,
-    logs,
-    layer = spanLayer.NONE,
-    is_entry = false,
-    is_exit = false,
-    peer,
-    start_time,
-    end_time,
-    error_occurred = false,
-    component_id,
-    refs,
-    is_noop = false,
-    -- owner is a TracingContext reference
-    owner,
-}
+local _M = {}
+-- local Span = {
+--     span_id,
+--     parent_span_id,
+--     operation_name,
+--     tags,
+--     logs,
+--     layer = spanLayer.NONE,
+--     is_entry = false,
+--     is_exit = false,
+--     peer,
+--     start_time,
+--     end_time,
+--     error_occurred = false,
+--     component_id,
+--     refs,
+--     is_noop = false,
+--     -- owner is a TracingContext reference
+--     owner,
+-- }
 
 -- 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
-local SpanProtocol = {
-    spanId,
-    parentSpanId,
-    startTime,
-    endTime,
-    -- Array of RefProtocol
-    refs,
-    operationName,
-    peer,
-    spanType,
-    spanLayer,
-    componentId,
-    isError,
-    tags,
-    logs,
-}
+-- local SpanProtocol = {
+--     spanId,
+--     parentSpanId,
+--     startTime,
+--     endTime,
+--     -- Array of RefProtocol
+--     refs,
+--     operationName,
+--     peer,
+--     spanType,
+--     spanLayer,
+--     componentId,
+--     isError,
+--     tags,
+--     logs,
+-- }
 
 -- Create an entry span. Represent the HTTP incoming request.
 -- @param contextCarrier, HTTP request header, which could carry the `sw6` context
-function Span:createEntrySpan(operationName, context, parent, contextCarrier)
-    local span = self:new(operationName, context, parent)
+function _M.createEntrySpan(operationName, context, parent, contextCarrier)
+    local span = _M.new(operationName, context, parent)
     span.is_entry = true
 
     if contextCarrier ~= nil then
         local propagatedContext = contextCarrier[CONTEXT_CARRIER_KEY]
         if propagatedContext ~= nil then
-            local ref = SegmentRef:new():fromSW6Value(propagatedContext)
+            local ref = SegmentRef.fromSW6Value(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.
-                context.internal:addRefIfFirst(ref)
+                context.internal.addRefIfFirst(context.internal, ref)
                 span.refs[#span.refs + 1] = ref
             end
         end
@@ -85,14 +86,14 @@ function Span:createEntrySpan(operationName, context, parent, contextCarrier)
 end
 
 -- Create an exit span. Represent the HTTP outgoing request.
-function Span:createExitSpan(operationName, context, parent, peer, contextCarrier)
-    local span = self:new(operationName, context, parent)
+function _M.createExitSpan(operationName, context, parent, peer, contextCarrier)
+    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()
+        local injectableRef = SegmentRef.new()
         injectableRef.trace_id = context.trace_id
         injectableRef.segment_id = context.segment_id
         injectableRef.span_id = span.span_id
@@ -106,8 +107,8 @@ function Span:createExitSpan(operationName, context, parent, peer, contextCarrie
         local entryEndpointId = -1
 
         local firstSpan = context.internal.first_span
-        if context.internal:hasRef() then
-            local firstRef = context.internal:getFirstRef()
+        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
@@ -120,7 +121,7 @@ function Span:createExitSpan(operationName, context, parent, peer, contextCarrie
             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
@@ -128,7 +129,7 @@ function Span:createExitSpan(operationName, context, parent, peer, contextCarrie
 
         local parentEndpointName
         local parentEndpointId = -1
-        
+
         if firstSpan.is_entry then
             parentEndpointName = firstSpan.operation_name
             parentEndpointId = 0
@@ -136,194 +137,185 @@ function Span:createExitSpan(operationName, context, parent, peer, contextCarrie
         injectableRef.parent_endpoint_name = parentEndpointName
         injectableRef.parent_endpoint_id = parentEndpointId
 
-        contextCarrier[CONTEXT_CARRIER_KEY] = injectableRef:serialize()
+        contextCarrier[CONTEXT_CARRIER_KEY] = SegmentRef.serialize(injectableRef)
     end
 
     return span
 end
 
--- Create an local span. Local span is usually not used. 
+-- Create an local span. Local span is usually not used.
 -- Typically, only one entry span and one exit span in the Nginx tracing segment.
-function Span:createLocalSpan(operationName, context, parent)
-    local span = self:new(operationName, context, parent) 
+function _M.createLocalSpan(operationName, context, parent)
+    local span = _M.new(operationName, context, parent)
     return span
 end
 
 -- Create a default span.
 -- Usually, this method wouldn't be called by outside directly.
 -- Read newEntrySpan, newExitSpan and newLocalSpan for more details
-function Span:new(operationName, context, parent)
-    local o = {}
-    setmetatable(o, self)
-    self.__index = self
-
-    o.operation_name = operationName
-    o.span_id = context.internal:nextSpanID()
-    
+function _M.new(operationName, context, parent)
+    local span = _M.newNoOP()
+    span.is_noop = false
+
+    span.operation_name = operationName
+    span.span_id = context.internal.nextSpanID(context.internal)
+
     if parent == nil then
         -- As the root span, the parent span id is -1
-        o.parent_span_id = -1
+        span.parent_span_id = -1
     else
-        o.parent_span_id = parent.span_id
-    end 
-
-    context.internal:addActive(o)
-    -- o.start_time = Util.timestamp()
-    o.refs = {}
-    o.owner = context
-
-    return o
-end
+        span.parent_span_id = parent.span_id
+    end
 
-function Span:newNoOP()
-    local o = {}
-    setmetatable(o, self)
-    self.__index = self
+    context.internal.addActive(context.internal, span)
+    span.refs = {}
+    span.owner = context
 
-    o.is_noop = true
-    return o
+    return span
 end
 
-function SpanProtocol:new()
-    local o = {}
-    setmetatable(o, self)
-    self.__index = self
-
-    return o
+function _M.newNoOP()
+    return {
+        layer = spanLayer.NONE,
+        is_entry = false,
+        is_exit = false,
+        error_occurred = false,
+        is_noop = true
+    }
 end
 
 ---- All belowing are instance methods
 
 -- Set start time explicitly
-function Span:start(startTime)
-    if self.is_noop then
-        return self
+function _M.start(span, startTime)
+    if span.is_noop then
+        return span
     end
 
-    self.start_time = startTime
+    span.start_time = startTime
 
-    return self
+    return span
 end
 
-function Span:finishWithDuration(duration)
-    if self.is_noop then
-        return self
+function _M.finishWithDuration(span, duration)
+    if span.is_noop then
+        return span
     end
 
-    self:finish(self.start_time + duration)
-    
-    return self
+    _M.finish(span, span.start_time + duration)
+
+    return span
 end
 
 -- @param endTime, optional.
-function Span:finish(endTime)
-    if self.is_noop then
-        return self
+function _M.finish(span, endTime)
+    if span.is_noop then
+        return span
     end
 
     if endTime == nil then
-        self.end_time = Util.timestamp()
+        span.end_time = Util.timestamp()
     else
-        self.end_time = endTime
+        span.end_time = endTime
     end
-    self.owner.internal:finishSpan(self)
+    span.owner.internal.finishSpan(span.owner.internal, span)
 
-    return self
+    return span
 end
 
-function Span:setComponentId(componentId)
-    if self.is_noop then
-        return self
+function _M.setComponentId(span, componentId)
+    if span.is_noop then
+        return span
     end
-    self.component_id = componentId
+    span.component_id = componentId
 
-    return self
+    return span
 end
 
-function Span:setLayer(spanLayer)
-    if self.is_noop then
-        return self
+function _M.setLayer(span, span_layer)
+    if span.is_noop then
+        return span
     end
-    self.layer = spanLayer
+    span.layer = span_layer
 
-    return self
+    return span
 end
 
-function Span:errorOccurred()
-    if self.is_noop then
-        return self
+function _M.errorOccurred(span)
+    if span.is_noop then
+        return span
     end
-    self.error_occurred = true
+    span.error_occurred = true
 
-    return self
+    return span
 end
 
-function Span:tag(tagKey, tagValue)
-    if self.is_noop then
-        return self
+function _M.tag(span, tagKey, tagValue)
+    if span.is_noop then
+        return span
     end
 
-    if self.tags == nil then
-        self.tags = {}
+    if span.tags == nil then
+        span.tags = {}
     end
 
     local tag = {key = tagKey, value = tagValue}
-    self.tags[#self.tags + 1] = tag
+    span.tags[#span.tags + 1] = tag
 
-    return self
+    return span
 end
 
 -- @param keyValuePairs, keyValuePairs is a typical {key=value, key1=value1}
-function Span:log(timestamp, keyValuePairs)
-    if self.is_noop then
-        return self
+function _M.log(span, timestamp, keyValuePairs)
+    if span.is_noop then
+        return span
     end
 
-    if self.logs == nil then
-        self.logs = {}
+    if span.logs == nil then
+        span.logs = {}
     end
 
     local logEntity = {time = timestamp, data = keyValuePairs}
-    self.logs[#self.logs + 1] = logEntity
+    span.logs[#span.logs + 1] = logEntity
 
-    return self
+    return span
 end
 
 -- Return SpanProtocol
-function Span:transform()
-    local spanBuilder = SpanProtocol:new()
-    spanBuilder.spanId = self.span_id
-    spanBuilder.parentSpanId = self.parent_span_id
-    spanBuilder.startTime = self.start_time
-    spanBuilder.endTime = self.end_time
+function _M.transform(span)
+    local spanBuilder = {}
+    spanBuilder.spanId = span.span_id
+    spanBuilder.parentSpanId = span.parent_span_id
+    spanBuilder.startTime = span.start_time
+    spanBuilder.endTime = span.end_time
     -- Array of RefProtocol
-    if #self.refs > 0 then
+    if #span.refs > 0 then
         spanBuilder.refs = {}
-        for i, ref in ipairs(self.refs)
-        do 
-            spanBuilder.refs[#spanBuilder.refs + 1] = ref:transform()
+        for i, ref in ipairs(span.refs)
+        do
+            spanBuilder.refs[#spanBuilder.refs + 1] = SegmentRef.transform(ref)
         end
     end
 
-    spanBuilder.operationName = self.operation_name
-    spanBuilder.peer = self.peer
-    if self.is_entry then
+    spanBuilder.operationName = span.operation_name
+    spanBuilder.peer = span.peer
+    if span.is_entry then
         spanBuilder.spanType = 'Entry'
-    elseif self.is_exit then
+    elseif span.is_exit then
         spanBuilder.spanType = 'Exit'
     else
         spanBuilder.spanType = 'Local'
     end
-    if self.layer ~= spanLayer.NONE then
-        spanBuilder.spanLayer = self.layer.name
+    if span.layer ~= spanLayer.NONE then
+        spanBuilder.spanLayer = span.layer.name
     end
-    spanBuilder.componentId = self.component_id
-    spanBuilder.isError = self.error_occurred
+    spanBuilder.componentId = span.component_id
+    spanBuilder.isError = span.error_occurred
 
-    spanBuilder.tags = self.tags
-    spanBuilder.logs = self.logs
+    spanBuilder.tags = span.tags
+    spanBuilder.logs = span.logs
 
     return spanBuilder
 end
 
-return Span
+return _M
diff --git a/lib/skywalking/span_test.lua b/lib/skywalking/span_test.lua
index 274f3cc..56e2fd2 100644
--- a/lib/skywalking/span_test.lua
+++ b/lib/skywalking/span_test.lua
@@ -1,19 +1,19 @@
--- 
+--
 -- Licensed to the Apache Software Foundation (ASF) under one or more
 -- contributor license agreements.  See the NOTICE file distributed with
 -- this work for additional information regarding copyright ownership.
 -- The ASF licenses this file to You under the Apache License, Version 2.0
 -- (the "License"); you may not use this file except in compliance with
 -- the License.  You may obtain a copy of the License at
--- 
+--
 --    http://www.apache.org/licenses/LICENSE-2.0
--- 
+--
 -- Unless required by applicable law or agreed to in writing, software
 -- distributed under the License is distributed on an "AS IS" BASIS,
 -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
--- 
+--
 
 local lu = require('luaunit')
 local TC = require('tracing_context')
@@ -22,10 +22,10 @@ local SpanLayer = require("span_layer")
 
 TestSpan = {}
     function TestSpan:testNewEntry()
-        local context = TC:new(1, 1)
+        local context = TC.new(1, 1)
         lu.assertNotNil(context)
 
-        local span1 = Span:createEntrySpan("operation_name", context, nil, nil)
+        local span1 = Span.createEntrySpan("operation_name", context, nil, nil)
         lu.assertNotNil(span1)
         lu.assertEquals(span1.is_entry, true)
         lu.assertEquals(span1.is_exit, false)
@@ -35,13 +35,13 @@ TestSpan = {}
     end
 
     function TestSpan:testNewEntryWithContextCarrier()
-        local context = TC:new(1, 1)
+        local context = TC.new(1, 1)
         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 span1 = Span:createEntrySpan("operation_name", context, nil, header)
+        local span1 = Span.createEntrySpan("operation_name", context, nil, header)
         lu.assertNotNil(span1)
         lu.assertEquals(span1.is_entry, true)
         lu.assertEquals(span1.is_exit, false)
@@ -66,11 +66,11 @@ TestSpan = {}
     end
 
     function TestSpan:testNewExit()
-        local context = TC:new(1, 1)
+        local context = TC.new(1, 1)
         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, '127.0.0.1:80', contextCarrier)
         lu.assertNotNil(span1)
         lu.assertEquals(span1.is_entry, false)
         lu.assertEquals(span1.is_exit, true)
@@ -82,44 +82,44 @@ TestSpan = {}
     end
 
     function TestSpan:testNew()
-        local context = TC:new(1, 1)
+        local context = TC.new(1, 1)
         lu.assertNotNil(context)
 
-        local span1 = Span:new("operation_name", context, nil)
+        local span1 = Span.new("operation_name", context, nil)
         lu.assertNotNil(span1)
         lu.assertEquals(span1.parent_span_id, -1)
         lu.assertEquals(span1.span_id, 0)
         lu.assertEquals(span1.operation_name, "operation_name")
-        local span2 = Span:new("operation_name", context, span1)
+        local span2 = Span.new("operation_name", context, span1)
         lu.assertEquals(span2.parent_span_id, 0)
         lu.assertEquals(span2.span_id, 1)
         lu.assertNil(span2.start_time)
-        span2:start(123456)
+        Span.start(span2, 123456)
         lu.assertNotNil(span2.start_time)
 
         -- Use new context to check again
-        context = TC:new(1, 1)
+        context = TC.new(1, 1)
         lu.assertNotNil(context)
 
-        span1 = Span:new("operation_name", context, nil)
+        span1 = Span.new("operation_name", context, nil)
         lu.assertNotNil(span1)
         lu.assertEquals(span1.parent_span_id, -1)
         lu.assertEquals(span1.span_id, 0)
     end
 
     function TestSpan:testProperties()
-        local context = TC:new(1, 1)
+        local context = TC.new(1, 1)
 
         local header = {sw6='1-My40LjU=-MS4yLjM=-4-1-1-IzEyNy4wLjAuMTo4MDgw-Iy9wb3J0YWw=-MTIz'}
-        local span1 = Span:createEntrySpan("operation_name", context, nil, header)
-        span1:start(1234567)
+        local span1 = Span.createEntrySpan("operation_name", context, nil, header)
+        Span.start(span1, 1234567)
         lu.assertEquals(span1.start_time, 1234567)
-        span1:finish(2222222)
+        Span.finish(span1, 2222222)
         lu.assertEquals(span1.end_time, 2222222)
-        span1:finishWithDuration(123)
+        Span.finishWithDuration(span1, 123)
         lu.assertEquals(span1.end_time, 1234690)
 
-        span1:tag("key1", "value1")
+        Span.tag(span1, "key1", "value1")
         lu.assertEquals(span1.tags[1].value, 'value1')
 
         lu.assertEquals(#span1.refs, 1)
@@ -127,16 +127,16 @@ TestSpan = {}
     end
 
     function TestSpan:testTransform()
-        local context = TC:new(1, 1)
+        local context = TC.new(1, 1)
 
         local header = {sw6='1-My40LjU=-MS4yLjM=-4-1-1-IzEyNy4wLjAuMTo4MDgw-Iy9wb3J0YWw=-MTIz'}
-        local span1 = Span:createEntrySpan("operation_name", context, nil, header)
-        span1:start(1234567)
-        span1:finish(2222222)
-        span1:tag("key", "value")
-        span1:log(123, {logkey="logvalue", logkey1="logvalue2"})
+        local span1 = Span.createEntrySpan("operation_name", context, nil, header)
+        Span.start(span1, 1234567)
+        Span.finish(span1, 2222222)
+        Span.tag(span1, "key", "value")
+        Span.log(span1, 123, {logkey="logvalue", logkey1="logvalue2"})
 
-        local spanBuilder = span1:transform()
+        local spanBuilder = Span.transform(span1)
         lu.assertEquals(#spanBuilder.refs, 1)
         lu.assertNil(spanBuilder.spanLayer)
         lu.assertEquals(spanBuilder.spanType, "Entry")
@@ -147,4 +147,4 @@ TestSpan = {}
 -- end TestSpan
 
 
-os.exit( lu.LuaUnit.run() )
\ No newline at end of file
+os.exit( lu.LuaUnit.run() )
diff --git a/lib/skywalking/tracer.lua b/lib/skywalking/tracer.lua
index 1689d79..f0a1a1e 100644
--- a/lib/skywalking/tracer.lua
+++ b/lib/skywalking/tracer.lua
@@ -1,19 +1,20 @@
--- 
+--
 -- Licensed to the Apache Software Foundation (ASF) under one or more
 -- contributor license agreements.  See the NOTICE file distributed with
 -- this work for additional information regarding copyright ownership.
 -- The ASF licenses this file to You under the Apache License, Version 2.0
 -- (the "License"); you may not use this file except in compliance with
 -- the License.  You may obtain a copy of the License at
--- 
+--
 --    http://www.apache.org/licenses/LICENSE-2.0
--- 
+--
 -- Unless required by applicable law or agreed to in writing, software
 -- distributed under the License is distributed on an "AS IS" BASIS,
 -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
--- 
+--
+local Span = require('span')
 
 local Tracer = {}
 
@@ -27,9 +28,9 @@ function Tracer:start(upstream_name)
     local serviceInstId = metadata_buffer:get("serviceInstId")
     local serviceId = metadata_buffer:get('serviceId')
     if (serviceInstId ~= nil and serviceInstId ~= 0) then
-        tracingContext = TC:new(serviceId, serviceInstId)
+        tracingContext = TC.new(serviceId, serviceInstId)
     else
-        tracingContext = TC:newNoOP()
+        tracingContext = TC.newNoOP()
     end
 
     -- Constant pre-defined in SkyWalking main repo
@@ -38,14 +39,14 @@ function Tracer:start(upstream_name)
 
     local contextCarrier = {}
     contextCarrier["sw6"] = ngx.req.get_headers()["sw6"]
-    local entrySpan = tracingContext:createEntrySpan(ngx.var.uri, nil, contextCarrier)
-    entrySpan:start(ngx.now() * 1000)
-    entrySpan:setComponentId(nginxComponentId)
-    entrySpan:setLayer(Layer.HTTP)
-    
-    entrySpan:tag('http.method', ngx.req.get_method())
-    entrySpan:tag('http.params', ngx.var.scheme .. '://' .. ngx.var.host .. ngx.var.request_uri )
-    
+    local entrySpan = TC.createEntrySpan(tracingContext, ngx.var.uri, nil, contextCarrier)
+    Span.start(entrySpan, ngx.now() * 1000)
+    Span.setComponentId(entrySpan, nginxComponentId)
+    Span.setLayer(entrySpan, Layer.HTTP)
+
+    Span.tag(entrySpan, 'http.method', ngx.req.get_method())
+    Span.tag(entrySpan, 'http.params', ngx.var.scheme .. '://' .. ngx.var.host .. ngx.var.request_uri )
+
     contextCarrier = {}
     -- Use the same URI to represent incoming and forwarding requests
     -- Change it if you need.
@@ -53,11 +54,11 @@ function Tracer:start(upstream_name)
 
     local upstreamServerName = upstream_name
     ------------------------------------------------------
-    local exitSpan = tracingContext:createExitSpan(upstreamUri, entrySpan, upstreamServerName, contextCarrier)
-    exitSpan:start(ngx.now() * 1000)
-    exitSpan:setComponentId(nginxComponentId)
-    exitSpan:setLayer(Layer.HTTP)
-    
+    local exitSpan = TC.createExitSpan(tracingContext, upstreamUri, entrySpan, upstreamServerName, contextCarrier)
+    Span.start(exitSpan, ngx.now() * 1000)
+    Span.setComponentId(exitSpan, nginxComponentId)
+    Span.setLayer(exitSpan, Layer.HTTP)
+
     for name, value in pairs(contextCarrier) do
         ngx.req.set_header(name, value)
     end
@@ -71,17 +72,19 @@ end
 function Tracer:finish()
     -- Finish the exit span when received the first response package from upstream
     if ngx.ctx.exitSpan ~= nil then
-        ngx.ctx.exitSpan:finish(ngx.now() * 1000)
+        Span.finish(ngx.ctx.exitSpan, ngx.now() * 1000)
         ngx.ctx.exitSpan = nil
     end
 end
 
 function Tracer:prepareForReport()
+    local TC = require('tracing_context')
+    local Segment = require('segment')
     if ngx.ctx.entrySpan ~= nil then
-        ngx.ctx.entrySpan:finish(ngx.now() * 1000)
-        local status, segment = ngx.ctx.tracingContext:drainAfterFinished()
+        Span.finish(ngx.ctx.entrySpan, ngx.now() * 1000)
+        local status, segment = TC.drainAfterFinished(ngx.ctx.tracingContext)
         if status then
-            local segmentJson = require('cjson').encode(segment:transform())
+            local segmentJson = require('cjson').encode(Segment.transform(segment))
             ngx.log(ngx.DEBUG, 'segment = ' .. segmentJson)
 
             local queue = ngx.shared.tracing_buffer
@@ -91,4 +94,4 @@ function Tracer:prepareForReport()
     end
 end
 
-return Tracer
\ No newline at end of file
+return Tracer
diff --git a/lib/skywalking/tracing_context.lua b/lib/skywalking/tracing_context.lua
index 427202e..23ecfb0 100644
--- a/lib/skywalking/tracing_context.lua
+++ b/lib/skywalking/tracing_context.lua
@@ -17,83 +17,129 @@
 
 local Util = require('util')
 local Span = require('span')
-local Segment = require('segment')
-
-local TracingContext = {
-    trace_id,
-    segment_id,
-    service_id,
-    service_inst_id,
-
-    is_noop = false,
-
-    internal,
-}
 
 -------------- Internal Object-------------
+local Internal = {}
 -- Internal Object hosts the methods for SkyWalking LUA internal APIs only.
-local Internal = {
-    self_generated_trace_id,
-    -- span id starts from 0
-    span_id_seq,
-    -- Owner means the Context instance holding this Internal object.
-    owner,
-    -- The first created span.
-    first_span,
-    -- The first ref injected in this context
-    first_ref,
-    -- Created span and still active
-    active_spans,
-    active_count,
-    -- Finished spans
-    finished_spans,
-}
-
-function TracingContext:new(serviceId, serviceInstID)
-    local o = {}
-    setmetatable(o, self)
-    self.__index = self
+-- local Internal = {
+--     self_generated_trace_id,
+--     -- span id starts from 0
+--     span_id_seq,
+--     -- Owner means the Context instance holding this Internal object.
+--     owner,
+--     -- The first created span.
+--     first_span,
+--     -- The first ref injected in this context
+--     first_ref,
+--     -- Created span and still active
+--     active_spans,
+--     active_count,
+--     -- Finished spans
+--     finished_spans,
+-- }
 
-    if serviceInstID == nil then
-        return TracingContext:newNoOP()
+
+-- add the segment ref if this is the first ref of this context
+local function addRefIfFirst(internal, ref)
+    if internal.self_generated_trace_id == true then
+        internal.self_generated_trace_id = false
+        internal.owner.trace_id = ref.trace_id
+        internal.first_ref = ref
     end
+end
 
-    o.trace_id = Util.newID()
-    o.segment_id = o.trace_id
-    o.service_id = serviceId
-    o.service_inst_id = serviceInstID
-    o.internal = Internal:new()
-    o.internal.owner = o
-    return o
+local function addActive(internal, span)
+    if internal.first_span == nil then
+        internal.first_span = span
+    end
+
+    -- span id starts at 0, to fit LUA, we need to plus one.
+    internal.active_spans[span.span_id + 1] = span
+    internal.active_count = internal.active_count + 1
+    return internal.owner
 end
 
-function TracingContext:newNoOP()
-    local o = {}
-    setmetatable(o, self)
-    self.__index = self
+local function finishSpan(internal, span)
+    -- span id starts at 0, to fit LUA, we need to plus one.
+    internal.active_spans[span.span_id + 1] = nil
+    internal.active_count = internal.active_count - 1
+    internal.finished_spans[#internal.finished_spans + 1] = span
 
-    o.is_noop = true
-    return o
+    return internal.owner
+end
+
+-- Generate the next span ID.
+local function nextSpanID(internal)
+    local nextSpanId = internal.span_id_seq
+    internal.span_id_seq = internal.span_id_seq + 1
+    return nextSpanId
 end
 
--- Delegate to Span:createEntrySpan
+-- Create an internal instance
+function Internal.new()
+    local internal = {}
+
+    internal.self_generated_trace_id = true
+    internal.span_id_seq = 0
+    internal.active_spans = {}
+    internal.active_count = 0
+    internal.finished_spans = {}
+    internal.addRefIfFirst = addRefIfFirst
+    internal.addActive = addActive
+    internal.finishSpan = finishSpan
+    internal.nextSpanID = nextSpanID
+    return internal
+end
+
+
+local _M = {}
+
+-- local TracingContext = {
+--     trace_id,
+--     segment_id,
+--     service_id,
+--     service_inst_id,
+--     is_noop = false,
+--     internal,
+-- }
+
+function _M.newNoOP()
+    return {is_noop = true}
+end
+
+function _M.new(serviceId, serviceInstID)
+    if serviceInstID == 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.internal = Internal.new()
+    tracing_context.internal.owner = tracing_context
+    return tracing_context
+end
+
+-- Delegate to Span.createEntrySpan
 -- @param contextCarrier could be nil if there is no downstream propagated context
-function TracingContext:createEntrySpan(operationName, parent, contextCarrier)
-    if self.is_noop then
-        return Span:newNoOP()
+function _M.createEntrySpan(tracingContext, operationName, parent, contextCarrier)
+    if tracingContext.is_noop then
+        return Span.newNoOP()
     end
 
-    return Span:createEntrySpan(operationName, self, parent, contextCarrier)
+    return Span.createEntrySpan(operationName, tracingContext, parent, contextCarrier)
 end
 
--- Delegate to Span:createExitSpan
+-- Delegate to Span.createExitSpan
 -- @param contextCarrier could be nil if don't need to inject any context to propagate
-function TracingContext:createExitSpan(operationName, parent, peer, contextCarrier)
-    if self.is_noop then
-        return Span:newNoOP()
+function _M.createExitSpan(tracingContext, operationName, parent, peer, contextCarrier)
+    if tracingContext.is_noop then
+        return Span.newNoOP()
     end
 
-    return Span:createExitSpan(operationName, self, parent, peer, contextCarrier)
+    return Span.createExitSpan(operationName, tracingContext, parent, peer, contextCarrier)
 end
 
 -- After all active spans finished, this segment will be treated as finished status.
@@ -103,87 +149,22 @@ end
 -- Return (boolean isSegmentFinished, Segment segment).
 -- Segment has value only when the isSegmentFinished is true
 -- if isSegmentFinished == false, SpanList = nil
-function TracingContext:drainAfterFinished()
-    if self.is_noop then
-        return true, Segment:new()
+function _M.drainAfterFinished(tracingContext)
+    if tracingContext.is_noop then
+        return false, nil
     end
 
-    if self.internal.active_count ~= 0 then
-        return false, nil
-    elseif #self.internal.finished_spans == 0 then
+    if tracingContext.internal.active_count ~= 0 then
         return false, nil
     else
-        local segment = Segment:new()
-        segment.trace_id = self.trace_id
-        segment.segment_id = self.segment_id
-        segment.service_id = self.service_id
-        segment.service_inst_id = self.service_inst_id
-        segment.spans = self.internal.finished_spans
+        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.spans = tracingContext.internal.finished_spans
         return true, segment
     end
 end
 
--------------- Internal Object-------------
--- Internal Object hosts the methods for SkyWalking LUA internal APIs only.
-
--- Create an internal instance
-function Internal:new()
-    local o = {}
-    setmetatable(o, self)
-    self.__index = self
-
-    o.self_generated_trace_id = true
-    o.span_id_seq = 0
-    o.active_spans = {}
-    o.active_count = 0
-    o.finished_spans = {}
-
-    return o
-end
-
--- add the segment ref if this is the first ref of this context
-function Internal:addRefIfFirst(ref)
-    if self.self_generated_trace_id == true then
-        self.self_generated_trace_id = false
-        self.owner.trace_id = ref.trace_id
-        self.first_ref = ref
-    end
-end
-
-function Internal:hasRef()
-    return first_ref ~= nil
-end
-
-function Internal:getFirstRef()
-    return first_ref
-end
-
-function Internal:addActive(span)
-    if self.first_span == nil then
-        self.first_span = span
-    end
-
-    -- span id starts at 0, to fit LUA, we need to plus one.
-    self.active_spans[span.span_id + 1] = span
-    self.active_count = self.active_count + 1
-    return self.owner
-end
-
-function Internal:finishSpan(span)
-    -- span id starts at 0, to fit LUA, we need to plus one.
-    self.active_spans[span.span_id + 1] = nil
-    self.active_count = self.active_count - 1
-    self.finished_spans[#self.finished_spans + 1] = span
-
-    return self.owner
-end
-
--- Generate the next span ID.
-function Internal:nextSpanID()
-    local nextSpanId = self.span_id_seq
-    self.span_id_seq = self.span_id_seq + 1;
-    return nextSpanId
-end
----------------------------------------------
-
-return TracingContext
+return _M
diff --git a/lib/skywalking/tracing_context_test.lua b/lib/skywalking/tracing_context_test.lua
index 2308e94..c5d4420 100644
--- a/lib/skywalking/tracing_context_test.lua
+++ b/lib/skywalking/tracing_context_test.lua
@@ -1,26 +1,28 @@
--- 
+--
 -- Licensed to the Apache Software Foundation (ASF) under one or more
 -- contributor license agreements.  See the NOTICE file distributed with
 -- this work for additional information regarding copyright ownership.
 -- The ASF licenses this file to You under the Apache License, Version 2.0
 -- (the "License"); you may not use this file except in compliance with
 -- the License.  You may obtain a copy of the License at
--- 
+--
 --    http://www.apache.org/licenses/LICENSE-2.0
--- 
+--
 -- Unless required by applicable law or agreed to in writing, software
 -- distributed under the License is distributed on an "AS IS" BASIS,
 -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
--- 
+--
 
 local lu = require('luaunit')
 local TC = require('tracing_context')
+local Segment = require('segment')
+local Span = require('span')
 
 TestTracingContext = {}
     function TestTracingContext:testNew()
-        local context = TC:new(1, 1)
+        local context = TC.new(1, 1)
         lu.assertNotNil(context)
         lu.assertNotNil(context.segment_id[1])
         lu.assertNotNil(context.segment_id[2])
@@ -30,24 +32,24 @@ TestTracingContext = {}
     end
 
     function TestTracingContext:testInternal_NextSpanSeqID()
-        local context = TC:new(1, 1)
+        local context = TC.new(1, 1)
 
-        lu.assertEquals(context.internal:nextSpanID(), 0)
+        lu.assertEquals(context.internal.nextSpanID(context.internal), 0)
     end
 
     function TestTracingContext:testInternal_addActive()
-        local context = TC:new(1, 1)
+        local context = TC.new(1, 1)
 
         local mockSpan = {span_id = 0}
-        context.internal:addActive(mockSpan)
+        context.internal.addActive(context.internal, mockSpan)
 
-        lu.assertEquals(#(context.internal.active_spans), 1)
+        lu.assertEquals(#context.internal.active_spans, 1)
     end
 
     function TestTracingContext:testSpanStack()
-        local context = TC:new(1, 1)
-        local span1 = context:createEntrySpan('entry_op')
-        local span2 = context:createExitSpan("exit_op", span1, "127.0.0.1")
+        local context = TC.new(1, 1)
+        local span1 = TC.createEntrySpan(context, 'entry_op')
+        local span2 = TC.createExitSpan(context, "exit_op", span1, "127.0.0.1")
 
         local activeSpans = context.internal.active_spans
         local finishedSpans = context.internal.finished_spans
@@ -56,30 +58,30 @@ TestTracingContext = {}
         lu.assertEquals(span1, activeSpans[1])
         lu.assertEquals(span2, activeSpans[2])
 
-        span2:finish()
+        Span.finish(span2)
         lu.assertNotNil(span2.end_time)
         lu.assertEquals(#(activeSpans), 1)
         lu.assertEquals(#(finishedSpans), 1)
 
-        span1:finish()
+        Span.finish(span1)
         lu.assertNotNil(span1.end_time)
         lu.assertEquals(#(activeSpans), 0)
         lu.assertEquals(#(finishedSpans), 2)
 
-        local isSegmentFinished, segment = context:drainAfterFinished()
+        local isSegmentFinished, segment = TC.drainAfterFinished(context)
         lu.assertEquals(span2, segment.spans[1])
         lu.assertEquals(span1, segment.spans[2])
 
-        local segmentBuilder = segment:transform()
+        local segmentBuilder = Segment.transform(segment)
         local JSON = require('cjson').encode(segmentBuilder)
         lu.assertTrue(#JSON > 0)
     end
 
     function TestTracingContext:testNewNoOP()
-        local noopContext = TC:newNoOP()
+        local noopContext = TC.newNoOP()
 
-        local span1 = noopContext:createEntrySpan('entry_op')
-        local span2 = noopContext:createExitSpan("exit_op", span1, "127.0.0.1")
+        local span1 = TC.createEntrySpan(noopContext, 'entry_op')
+        local span2 = TC.createExitSpan(noopContext, "exit_op", span1, "127.0.0.1")
 
         lu.assertEquals(true, span1.is_noop)
         lu.assertEquals(true, span2.is_noop)
diff --git a/t/segment_ref.t b/t/segment_ref.t
new file mode 100644
index 0000000..8c9f76a
--- /dev/null
+++ b/t/segment_ref.t
@@ -0,0 +1,117 @@
+use Test::Nginx::Socket 'no_plan';
+
+use Cwd qw(cwd);
+my $pwd = cwd();
+
+repeat_each(1);
+no_long_string();
+no_shuffle();
+no_root_location();
+log_level('info');
+
+our $HttpConfig = qq{
+    lua_package_path "$pwd/lib/skywalking/?.lua;;";
+    error_log logs/error.log debug;
+    resolver 114.114.114.114 8.8.8.8 ipv6=off;
+    lua_shared_dict tracing_buffer 100m;
+};
+
+run_tests;
+
+__DATA__
+
+=== TEST 1: fromSW6Value
+--- 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')
+            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)
+        }
+    }
+--- request
+GET /t
+--- response_body
+345
+123
+4
+1
+1
+127.0.0.1:8080
+0
+/portal
+0
+nil
+123
+--- no_error_log
+[error]
+
+
+
+=== TEST 2: Serialize
+--- http_config eval: $::HttpConfig
+--- config
+    location /t {
+        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.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
+            ngx.say(SegmentRef.serialize(ref))
+        }
+    }
+--- request
+GET /t
+--- response_body
+1-My40LjU=-MS4yLjM=-4-1-1-IzEyNy4wLjAuMTo4MDgw-Iy9wb3J0YWw=-MTIz
+--- no_error_log
+[error]
+
+
+
+=== TEST 3: Transform
+--- http_config eval: $::HttpConfig
+--- config
+    location /t {
+        content_by_lua_block {
+            local SegmentRef = require('segment_ref')
+            local cjson = require("cjson")
+
+            local ref = SegmentRef.new()
+            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
+
+            local refProtocol = SegmentRef.transform(ref)
+            local inJSON = cjson.encode(refProtocol)
+            ngx.say(string.len(inJSON) > 0)
+        }
+    }
+--- request
+GET /t
+--- response_body
+true
+--- no_error_log
+[error]
diff --git a/t/util.t b/t/util.t
index 53f6094..e526e4f 100644
--- a/t/util.t
+++ b/t/util.t
@@ -19,6 +19,7 @@ our $HttpConfig = qq{
 run_tests;
 
 __DATA__
+
 === TEST 1: timestamp
 --- http_config eval: $::HttpConfig
 --- config