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")) },