1 module hunt.trace.Span;
2 
3 import hunt.trace.Endpoint;
4 import hunt.trace.Constrants;
5 import hunt.trace.Annotation;
6 import hunt.trace.Helpers;
7 
8 import hunt.logging;
9 import hunt.serialization.JsonSerializer;
10 import hunt.serialization.Common;
11 
12 import std.json;
13 import std.range;
14 import std.socket;
15 import std.string;
16 
17 /**
18  * 
19  */
20 class Span {
21 
22     /// 16bytes
23     string traceId;
24     string name;
25     @Ignore string parentId;
26     /// 8bytes
27     string id;
28     string kind;
29     long timestamp;
30     long duration;
31     @Ignore bool debug_;
32     @Ignore bool shared_;
33 
34     EndPoint localEndpoint;
35     EndPoint remoteEndpoint;
36     Annotation[] annotations;
37     string[string] tags;
38 
39     string samplingState = "1";
40 
41     string defaultId() {
42         // b3={TraceId}-{SpanId}-{SamplingState}-{ParentSpanId}
43         if(parentId.empty) {
44             return traceId ~ "-" ~ id ~ "-" ~ samplingState;
45         } else {
46             return traceId ~ "-" ~ id ~ "-" ~ samplingState ~ "-" ~ parentId;
47         }
48     }
49 
50     void addTag(string key, string value) {
51         tags[key] = value;
52     }
53 
54     void addAnnotation(string value, long timestamp = 0) {
55         auto anno = new Annotation();
56         anno.value = value;
57         if (timestamp == 0)
58             timestamp = usecs;
59         anno.timestamp = timestamp;
60         annotations ~= anno;
61     }
62 
63     void start(long timestamp = 0) {
64         if (timestamp != 0)
65             this.timestamp = timestamp;
66         else
67             this.timestamp = usecs;
68     }
69 
70     void finish(long timestamp = 0) {
71         if (timestamp != 0)
72             this.duration = timestamp - this.timestamp;
73         else
74             this.duration = usecs - this.timestamp;
75 
76     }
77 
78     static EndPoint buildLocalEndPoint(string name) {
79         EndPoint endpoint = new EndPoint();
80         endpoint.serviceName = name;
81 
82         try {
83             auto addresses = getAddress("localhost");
84             foreach (address; addresses) {
85                 // writefln("  IP: %s", address.toAddrString());
86                 string ip = address.toAddrString();
87                 if(ip.startsWith("::")) {
88                     // localEndpoint.ipv6 = ip; // todo
89                 } else {
90                     endpoint.ipv4 = ip;
91                 }
92             }
93         } catch(Exception ex) {
94             warning(ex.msg);
95         }
96 
97         return endpoint;
98     }
99 
100     override string toString() {
101         auto json = toJson(this);
102         json["debug"] = (debug_);
103         json["shared"] = (shared_);
104 
105         // import hunt.logging;
106         // warning("parentId: ", parentId);
107         // warning("traceId: ", traceId);
108         if (parentId.length != 0)
109             json["parentId"] = parentId;
110         return json.toString;
111     }
112 }
113 
114 
115 
116 void traceSpanAfter(Span span, string[string] tags, string error = "") {
117     assert(span !is null);
118     
119     foreach (k, v; tags) {
120         span.addTag(k, v);
121     }
122 
123     if (error != "") {
124         span.addTag(SPAN_ERROR, error);
125     }
126     span.finish();
127 
128     // warning(span.toString());
129 }