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/01 15:02:12 UTC

[skywalking-rust] branch extract updated: Finish the core APIs and part of document.

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

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


The following commit(s) were added to refs/heads/extract by this push:
     new 73f8497  Finish the core APIs and part of document.
73f8497 is described below

commit 73f84976700d0e414376138eb80079c37364b2d8
Author: Wu Sheng <wu...@foxmail.com>
AuthorDate: Sun Mar 1 23:02:00 2020 +0800

    Finish the core APIs and part of document.
---
 README.md                       |  53 +++++++++++++++++++++
 tracing-core/src/context.rs     |  82 +++++++++++++++++++++++++-------
 tracing-core/src/segment_ref.rs | 100 +++++++++++++++++++++++++++++++++++++++-
 tracing-core/src/span.rs        |  29 +++++++-----
 4 files changed, 234 insertions(+), 30 deletions(-)

diff --git a/README.md b/README.md
index 205f5e9..efef7df 100644
--- a/README.md
+++ b/README.md
@@ -12,5 +12,58 @@ Apache SkyWalking Rust Agent
 including tracing, metrics, topology map for distributed system and alert.
 It uses SkyWalking native formats and core concepts to keep best compatibility and performance.
 
+# Concepts
+All concepts are from the official SkyWalking definitions.
+## Span
+Span is an important and common concept in distributed tracing system. Learn Span from Google Dapper Paper.
+For better performance, we extend the span into 3 kinds.
+   
+1. EntrySpan EntrySpan represents a service provider, also the endpoint of server side. As an APM system, we are targeting the application servers. So almost all the services and MQ-consumer are EntrySpan(s).
+2. LocalSpan LocalSpan represents a normal Java method, which does not relate to remote service, neither a MQ producer/consumer nor a service(e.g. HTTP service) provider/consumer.
+3. ExitSpan ExitSpan represents a client of service or MQ-producer, as named as LeafSpan at early age of SkyWalking. e.g. accessing DB by JDBC, reading Redis/Memcached are cataloged an ExitSpan.
+
+Tag and Log are similar attributes of the span. 
+- Tag is a key:value pair to indicate the attribute with a string value.
+- Log is heavier than tag, with one timestamp and multiple key:value pairs. Log represents an event, typically an error happens.
+
+## TracingContext
+TracingContext is the context of the tracing process. Span should only be created through context, and be archived into the
+context after the span finished.
+
+## Injectable
+Injectable is used(optional) when the exit span creates. This Injectable received the notification from tracing context,
+including the key and value for tracing context across process propagation. Typically, Injectable implementation would 
+manipulate the RPC header/metadata to make the key/value sent to the server side.
+
+## Extractable
+Extractable is used(optional) when the entry span creates. The Extractable fetches the value of the given key from the propagated
+context. Typically, Extractable implementation would read the RPC header/metadata, which sent from the client side.   
+
+# APIs
+## Tracing Core APIs
+Tracing core APIs are 100% manual control tracing APIs. Users could use them to trace any process by following SkyWalking
+core concepts.
+
+```rust
+let mut context = TracingContext::new(&reporter).unwrap();
+let span1 = context.create_entry_span("op1", None, Some(&MockerHeader {}));
+{
+    assert_eq!(span1.span_id(), 0);
+    let mut span2 = context.create_local_span("op2", Some(&span1));
+    span2.tag(Tag::new(String::from("tag1"), String::from("value1")));
+    {
+        assert_eq!(span2.span_id(), 1);
+        let mut span3 = context.create_exit_span("op3", Some(&span2), "127.0.0.1:8080", Some(&HeaderCarrier {}));
+        assert_eq!(span3.span_id(), 2);
+
+        context.finish_span(span3);
+    }
+    context.finish_span(span2);
+}
+context.finish_span(span1);
+
+reporter.report_trace(context);
+```
+
 # License
 Apache 2.0 
\ No newline at end of file
diff --git a/tracing-core/src/context.rs b/tracing-core/src/context.rs
index 6525c04..47a2ed8 100644
--- a/tracing-core/src/context.rs
+++ b/tracing-core/src/context.rs
@@ -25,11 +25,11 @@ use crate::span::TracingSpan;
 /// All new span belonging to this tracing context should be created through this context.
 pub trait Context {
     /// Create an entry span belonging this context
-    fn create_entry_span(&mut self, operation_name: String, parent: Option<&Box<dyn Span>>, extractor: &dyn Extractable) -> Box<dyn Span>;
+    fn create_entry_span(&mut self, operation_name: &str, parent: Option<&Box<dyn Span>>, extractor: Option<&dyn Extractable>) -> Box<dyn Span>;
     /// Create an exit span belonging this context
-    fn create_exit_span(&mut self, operation_name: String, parent: Option<&Box<dyn Span>>, peer: String, injector: &dyn Injectable) -> Box<dyn Span>;
+    fn create_exit_span(&mut self, operation_name: &str, parent: Option<&Box<dyn Span>>, peer: &str, injector: Option<&dyn Injectable>) -> Box<dyn Span>;
     /// Create an local span belonging this context
-    fn create_local_span(&mut self, operation_name: String, parent: Option<&Box<dyn Span>>) -> Box<dyn Span>;
+    fn create_local_span(&mut self, operation_name: &str, parent: Option<&Box<dyn Span>>) -> Box<dyn Span>;
     /// Finish the given span. The span is only being accept if it belongs to this context.
     /// Return err if the span was created by another context.
     fn finish_span(&mut self, span: Box<dyn Span>);
@@ -40,7 +40,12 @@ pub struct TracingContext {
     next_seq: i32,
 
     primary_trace_id: ID,
+    segment_id: ID,
     self_generated_id: bool,
+    entry_endpoint_name: Option<String>,
+    first_ref: Option<SegmentRef>,
+    service_instance_id: i32,
+
     finished_spans: Vec<Box<dyn Span>>,
 }
 
@@ -54,7 +59,11 @@ impl TracingContext {
                 Some(TracingContext {
                     next_seq: -1,
                     primary_trace_id: IDGenerator::new_id(id),
+                    segment_id: IDGenerator::new_id(id),
                     self_generated_id: true,
+                    entry_endpoint_name: None,
+                    first_ref: None,
+                    service_instance_id: id,
                     finished_spans: Vec::new(),
                 }
                 )
@@ -62,6 +71,26 @@ impl TracingContext {
         }
     }
 
+    pub fn service_instance_id(&self) -> i32 {
+        self.service_instance_id
+    }
+
+    pub fn first_ref(&self) -> &Option<SegmentRef> {
+        &self.first_ref
+    }
+
+    pub fn entry_endpoint_name(&self) -> &Option<String> {
+        &self.entry_endpoint_name
+    }
+
+    pub fn trace_id(&self) -> ID {
+        self.primary_trace_id.clone()
+    }
+
+    pub fn segment_id(&self) -> ID {
+        self.segment_id.clone()
+    }
+
     /// Fetch the next id for new span
     fn next_span_id(&mut self) -> i32 {
         self.next_seq = self.next_seq + 1;
@@ -71,35 +100,45 @@ impl TracingContext {
 
 /// Default implementation of Context
 impl Context for TracingContext {
-    fn create_entry_span(&mut self, operation_name: String, parent: Option<&Box<dyn Span>>, extractor: &dyn Extractable) -> Box<dyn Span> {
+    fn create_entry_span(&mut self, operation_name: &str, parent: Option<&Box<dyn Span>>, extractor: Option<&dyn Extractable>) -> Box<dyn Span> {
         let mut entry_span = TracingSpan::new_entry_span(operation_name, self.next_span_id(), match parent {
             None => { -1 }
             Some(s) => { s.span_id() }
         });
-        match SegmentRef::from_text(extractor.extract("sw6".to_string())) {
-            Some(reference) => {
-                if self.self_generated_id {
-                    self.self_generated_id = false;
-                    self.primary_trace_id = reference.get_trace_id();
+
+        if extractor.is_some() {
+            match SegmentRef::from_text(extractor.unwrap().extract("sw6".to_string())) {
+                Some(reference) => {
+                    if self.self_generated_id {
+                        self.self_generated_id = false;
+                        self.primary_trace_id = reference.get_trace_id();
+                    }
+                    if self.first_ref.is_none() {
+                        self.first_ref = Some(reference.clone());
+                        self.entry_endpoint_name = Some(String::from(operation_name))
+                    }
+                    entry_span._add_ref(reference);
                 }
-                entry_span._add_ref(reference);
+                _ => {}
             }
-            _ => {}
         }
         Box::new(entry_span)
     }
 
-    fn create_exit_span(&mut self, operation_name: String, parent: Option<&Box<dyn Span>>, peer: String, injector: &dyn Injectable) -> Box<dyn Span> {
+    fn create_exit_span(&mut self, operation_name: &str, parent: Option<&Box<dyn Span>>, peer: &str, injector: Option<&dyn Injectable>) -> Box<dyn Span> {
         let exit_span = TracingSpan::new_exit_span(operation_name, self.next_span_id(), match parent {
             None => { -1 }
             Some(s) => { s.span_id() }
         }, peer);
 
+        if injector.is_some() {
+            injector.unwrap().inject(String::from("sw6"), SegmentRef::for_across_process(self, &exit_span, &peer).serialize());
+        }
 
         Box::new(exit_span)
     }
 
-    fn create_local_span(&mut self, operation_name: String, parent: Option<&Box<dyn Span>>) -> Box<dyn Span> {
+    fn create_local_span(&mut self, operation_name: &str, parent: Option<&Box<dyn Span>>) -> Box<dyn Span> {
         Box::new(TracingSpan::new_local_span(operation_name, self.next_span_id(), match parent {
             None => { -1 }
             Some(s) => { s.span_id() }
@@ -119,20 +158,20 @@ mod context_tests {
     use std::sync::mpsc;
     use std::sync::mpsc::{Receiver, Sender};
 
-    use crate::{Context, ContextListener, Extractable, ID, Tag, TracingContext};
+    use crate::{Context, ContextListener, Extractable, ID, Injectable, Tag, TracingContext};
 
     #[test]
     fn test_context_stack() {
         let reporter = MockReporter::new();
         let mut context = TracingContext::new(&reporter).unwrap();
-        let span1 = context.create_entry_span(String::from("op1"), None, &MockerHeader {});
+        let span1 = context.create_entry_span("op1", None, Some(&MockerHeader {}));
         {
             assert_eq!(span1.span_id(), 0);
-            let mut span2 = context.create_local_span(String::from("op2"), Some(&span1));
+            let mut span2 = context.create_local_span("op2", Some(&span1));
             span2.tag(Tag::new(String::from("tag1"), String::from("value1")));
             {
                 assert_eq!(span2.span_id(), 1);
-                let mut span3 = context.create_local_span(String::from("op3"), Some(&span2));
+                let mut span3 = context.create_exit_span("op3", Some(&span2), "127.0.0.1:8080", Some(&HeaderCarrier {}));
                 assert_eq!(span3.span_id(), 2);
 
                 context.finish_span(span3);
@@ -188,6 +227,15 @@ mod context_tests {
         }
     }
 
+    struct HeaderCarrier {}
+
+    impl Injectable for HeaderCarrier {
+        fn inject(&self, key: String, value: String) {
+            assert_eq!(key, "sw6");
+            assert_eq!(value.len() > 0, true);
+        }
+    }
+
     struct MockRegisterPending {}
 
     impl ContextListener for MockRegisterPending {
diff --git a/tracing-core/src/segment_ref.rs b/tracing-core/src/segment_ref.rs
index 104060c..eca2be1 100644
--- a/tracing-core/src/segment_ref.rs
+++ b/tracing-core/src/segment_ref.rs
@@ -13,9 +13,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use crate::ID;
+use std::os::raw::c_long;
 
+use crate::{ID, Span, TracingContext};
+use crate::segment_ref::SegmentRefType::CROSS_PROCESS;
+
+#[derive(Clone, Hash)]
 pub struct SegmentRef {
+    ref_type: SegmentRefType,
     trace_id: ID,
     segment_id: ID,
     span_id: i32,
@@ -29,6 +34,12 @@ pub struct SegmentRef {
     parent_endpoint_id: i32,
 }
 
+#[derive(Clone, Hash)]
+enum SegmentRefType {
+    CROSS_PROCESS,
+    CROSS_THREAD,
+}
+
 impl SegmentRef {
     pub fn from_text(value: &str) -> Option<Self> {
         let strings: Vec<&str> = value.split("-").collect();
@@ -69,6 +80,7 @@ impl SegmentRef {
             };
 
             Some(SegmentRef {
+                ref_type: CROSS_PROCESS,
                 trace_id,
                 segment_id,
                 span_id,
@@ -86,6 +98,65 @@ impl SegmentRef {
         }
     }
 
+    pub fn for_across_process(context: &TracingContext, exit_span: &dyn Span, peer: &str) -> Self {
+        // -1 represent the object doesn't exist.
+        let inexistence = -1;
+        let (entry_endpoint, entry_endpoint_id) = match context.first_ref() {
+            None => {
+                match context.entry_endpoint_name() {
+                    None => { (None, inexistence) }
+                    Some(endpoint) => { (Some(endpoint.clone()), 0) }
+                }
+            }
+            Some(reference) => {
+                match &reference.entry_endpoint {
+                    None => { (None, reference.entry_endpoint_id) }
+                    Some(endpoint) => { (Some(endpoint.clone()), 0) }
+                }
+            }
+        };
+        let (parent_endpoint, parent_endpoint_id) = match context.entry_endpoint_name() {
+            None => { (None, inexistence) }
+            Some(endpoint) => { (Some(endpoint.clone()), 0) }
+        };
+
+        SegmentRef {
+            ref_type: CROSS_PROCESS,
+            trace_id: context.trace_id(),
+            segment_id: context.segment_id(),
+            span_id: exit_span.span_id(),
+            network_address: Some(String::from(peer.clone())),
+            // No network address register, the id always be 0
+            network_address_id: 0,
+            entry_service_instance_id: {
+                match context.first_ref() {
+                    None => { context.service_instance_id() }
+                    Some(reference) => { reference.entry_service_instance_id }
+                }
+            },
+            parent_service_instance_id: context.service_instance_id(),
+            entry_endpoint,
+            entry_endpoint_id,
+            parent_endpoint,
+            parent_endpoint_id,
+        }
+    }
+
+    pub fn serialize(&self) -> String {
+        let parts: Vec<String> = vec![
+            "1".to_string(),
+            base64::encode(self.trace_id.to_string().as_bytes()),
+            base64::encode(self.segment_id.to_string().as_bytes()),
+            self.span_id.to_string(),
+            self.parent_service_instance_id.to_string(),
+            self.entry_service_instance_id.to_string(),
+            SegmentRef::string_or_id_to_encode_base64(&self.network_address, self.network_address_id),
+            SegmentRef::string_or_id_to_encode_base64(&self.entry_endpoint, self.entry_endpoint_id),
+            SegmentRef::string_or_id_to_encode_base64(&self.parent_endpoint, self.parent_endpoint_id),
+        ];
+        parts.join("-")
+    }
+
     pub fn get_trace_id(&self) -> ID {
         self.trace_id.clone()
     }
@@ -128,6 +199,17 @@ impl SegmentRef {
             _ => { None }
         }
     }
+
+    fn string_or_id_to_encode_base64(text: &Option<String>, id: i32) -> String {
+        base64::encode(match text {
+            None => { id.to_string() }
+            Some(t) => {
+                let mut network = "#".to_string();
+                network.push_str(&t);
+                network
+            }
+        }.as_bytes())
+    }
 }
 
 #[cfg(test)]
@@ -147,4 +229,20 @@ mod segment_ref_tests {
         assert_eq!(carrier.entry_endpoint, Some("/portal".to_string()));
         assert_eq!(carrier.parent_endpoint_id, 123);
     }
+
+    #[test]
+    fn test_serialize_ref() {
+        let carrier = SegmentRef::from_text("1-My40LjU=-MS4yLjM=-4-1-1-IzEyNy4wLjAuMTo4MDgw-Iy9wb3J0YWw=-MTIz").unwrap();
+        assert_eq!(carrier.trace_id == ID::new(3, 4, 5), true);
+        assert_eq!(carrier.segment_id == ID::new(1, 2, 3), true);
+        assert_eq!(carrier.span_id, 4);
+        assert_eq!(carrier.entry_service_instance_id, 1);
+        assert_eq!(carrier.parent_service_instance_id, 1);
+        assert_eq!(carrier.network_address, Some("127.0.0.1:8080".to_string()));
+        assert_eq!(carrier.entry_endpoint, Some("/portal".to_string()));
+        assert_eq!(carrier.parent_endpoint_id, 123);
+
+        let carrier_text = carrier.serialize();
+        assert_eq!(carrier_text, "1-My40LjU=-MS4yLjM=-4-1-1-IzEyNy4wLjAuMTo4MDgw-Iy9wb3J0YWw=-MTIz");
+    }
 }
diff --git a/tracing-core/src/span.rs b/tracing-core/src/span.rs
index 623cdc6..a51c8eb 100644
--- a/tracing-core/src/span.rs
+++ b/tracing-core/src/span.rs
@@ -20,7 +20,12 @@ use crate::segment_ref::SegmentRef;
 use crate::Tag;
 
 /// Span is one of the tracing concept, representing a time duration.
-/// Span typically used in the certain scope, Typically, it represent a method invocation or a RPC.
+///Span is an important and common concept in distributed tracing system. Learn Span from Google Dapper Paper.
+/// For better performance, we extend the span into 3 kinds.
+///
+/// 1. EntrySpan EntrySpan represents a service provider, also the endpoint of server side. As an APM system, we are targeting the application servers. So almost all the services and MQ-consumer are EntrySpan(s).
+/// 2. LocalSpan LocalSpan represents a normal Java method, which does not relate to remote service, neither a MQ producer/consumer nor a service(e.g. HTTP service) provider/consumer.
+/// 3. ExitSpan ExitSpan represents a client of service or MQ-producer, as named as LeafSpan at early age of SkyWalking. e.g. accessing DB by JDBC, reading Redis/Memcached are cataloged an ExitSpan.
 pub trait Span {
     /// Start the span with the current system time
     fn start(&mut self);
@@ -86,30 +91,30 @@ pub struct TracingSpan {
 /// Tracing Span is only created inside TracingContext.
 impl TracingSpan {
     /// Create a new entry span
-    pub fn new_entry_span(operation_name: String, span_id: i32, parent_span_id: i32) -> TracingSpan {
+    pub fn new_entry_span(operation_name: &str, span_id: i32, parent_span_id: i32) -> TracingSpan {
         let mut span = TracingSpan::_new(operation_name, span_id, parent_span_id);
         span.is_entry = true;
         span
     }
 
     /// Create a new exit span
-    pub fn new_exit_span(operation_name: String, span_id: i32, parent_span_id: i32, peer: String) -> TracingSpan {
+    pub fn new_exit_span(operation_name: &str, span_id: i32, parent_span_id: i32, peer: &str) -> TracingSpan {
         let mut span = TracingSpan::_new(operation_name, span_id, parent_span_id);
         span.is_exit = true;
-        span.peer = Some(peer);
+        span.peer = Some(String::from(peer));
         span
     }
 
     /// Create a new local span
-    pub fn new_local_span(operation_name: String, span_id: i32, parent_span_id: i32) -> TracingSpan {
+    pub fn new_local_span(operation_name: &str, span_id: i32, parent_span_id: i32) -> TracingSpan {
         let span = TracingSpan::_new(operation_name, span_id, parent_span_id);
         span
     }
 
     /// Create a span
-    fn _new(operation_name: String, span_id: i32, parent_span_id: i32) -> Self {
+    fn _new(operation_name: &str, span_id: i32, parent_span_id: i32) -> Self {
         TracingSpan {
-            operation_name,
+            operation_name: String::from(operation_name),
             span_id,
             parent_span_id,
             start_time: 0,
@@ -210,14 +215,14 @@ mod span_tests {
 
     #[test]
     fn test_span_new() {
-        let mut span = TracingSpan::_new(String::from("op1"), 0, -1);
+        let mut span = TracingSpan::_new("op1", 0, -1);
         assert_eq!(span.parent_span_id, -1);
         assert_eq!(span.span_id, 0);
         assert_eq!(span.start_time, 0);
         span.start();
         assert_ne!(span.start_time, 0);
 
-        let mut span2 = TracingSpan::_new(String::from("op2"), 1, 0);
+        let mut span2 = TracingSpan::_new("op2", 1, 0);
         assert_eq!("op2", span2.operation_name);
         assert_eq!(span2.parent_span_id, 0);
         assert_eq!(span2.span_id, 1);
@@ -227,13 +232,13 @@ mod span_tests {
 
     #[test]
     fn test_new_entry_span() {
-        let span = TracingSpan::new_entry_span(String::from("op1"), 0, 1);
+        let span = TracingSpan::new_entry_span("op1", 0, 1);
         assert_eq!(span.is_entry(), true)
     }
 
     #[test]
     fn test_span_with_tags() {
-        let mut span = TracingSpan::new_entry_span(String::from("op1"), 0, 1);
+        let mut span = TracingSpan::new_entry_span("op1", 0, 1);
         span.tag(Tag::new(String::from("tag1"), String::from("value1")));
         span.tag(Tag::new(String::from("tag2"), String::from("value2")));
 
@@ -244,7 +249,7 @@ mod span_tests {
 
     #[test]
     fn test_span_with_logs() {
-        let mut span = TracingSpan::_new(String::from("op1"), 0, -1);
+        let mut span = TracingSpan::_new("op1", 0, -1);
 
         span.log(LogEvent::new(123, Box::new([
             { EventField::new(String::from("event1"), String::from("event description")) },