|
| 1 | +# Appdash in other languages |
| 2 | + |
| 3 | +It is possible to have other programming languages communicate with a Go-based Appdash collection server (e.g. `cmd/appdash serve`). |
| 4 | + |
| 5 | +This enables other applications, not written in Go, to communicate performance, debug information, and logs to a Appdash collection server. |
| 6 | + |
| 7 | +Currently only a Python client is available, see the `python/` sub-directory for more information. The rest of this document will describe the details of how using Appdash from other languages would be achieved. |
| 8 | + |
| 9 | +# Wire Protocol |
| 10 | + |
| 11 | +Appdash collection servers communicate with clients via Google's protobuf (varint delimited) messages. |
| 12 | + |
| 13 | +1. A varint is sent over the wire to communicate _how large the protobuf message is_. This is done because protobuf doesn't actually handle _streams of messages_, rather just the encoding/decoding of _single messages_. |
| 14 | +2. The actual protobuf-encoded message is sent. |
| 15 | + |
| 16 | +The actual protobuf file (which can be used to generate code for most languages) can be found in the `internal/wire/collector.proto` file. |
| 17 | + |
| 18 | +We will now discuss in-depth the protobuf format, and how everything works. |
| 19 | + |
| 20 | +# CollectPacket |
| 21 | + |
| 22 | +A CollectPacket is the high-level message structure. It is sent from a Appdash client to a remote Appdash collection server (e.g. `cmd/appdash serve`). It is composed of a single _SpanID_ and any number of _Annotations_ associated with the identified span. For example, a _CollectPacket_ would be sent to the server to say what a span's name was, how long it took, etc. |
| 23 | + |
| 24 | +When the server receives a CollectPacket, it stores the annotations associated with the span for later (in _a Store_). These annotations are unmarshaled into _Events_ which are then displayed nicely inside Appdash's web UI, which lets you analyse the trace, etc. |
| 25 | + |
| 26 | +# SpanID |
| 27 | + |
| 28 | +The SpanID portion of the protobuf file looks like: |
| 29 | + |
| 30 | +``` |
| 31 | +// SpanID is the group of information which can uniquely identify the exact |
| 32 | +// span being collected. |
| 33 | +required group SpanID = 1 { |
| 34 | + // trace is the root ID of the tree that contains all of the spans |
| 35 | + // related to this one. |
| 36 | + required fixed64 trace = 2; |
| 37 | +
|
| 38 | + // span is an ID that probabilistically uniquely identifies this span. |
| 39 | + required fixed64 span = 3; |
| 40 | +
|
| 41 | + // parent is the ID of the parent span, if any. |
| 42 | + optional fixed64 parent = 4; |
| 43 | +} |
| 44 | +``` |
| 45 | + |
| 46 | +A SpanID is made up of three ID's in total: |
| 47 | + |
| 48 | +1. The _trace ID_ (also called the _root ID_). |
| 49 | +2. The _span ID_. |
| 50 | +3. The _parent ID_, or zero. |
| 51 | + |
| 52 | +Each ID is an unsigned 64-bit integer which has no special quality other than _uniquely identifying that span_. They are random numbers and are chosen purely to avoid collision with one another. |
| 53 | + |
| 54 | +All spans in a trace share the same _trace ID_, and each span is a distant child of a parent span or the _root span (aka. trace)_. |
| 55 | + |
| 56 | +# Annotation |
| 57 | + |
| 58 | +The Annotation portion of the protobuf file looks like: |
| 59 | + |
| 60 | +``` |
| 61 | +// Annotation is any number of annotations for the span to be collected. |
| 62 | +repeated group Annotation = 5 { |
| 63 | + // key is the annotation's key. |
| 64 | + required string key = 6; |
| 65 | +
|
| 66 | + // value is the annotation's value, which may be either human or |
| 67 | + // machine readable, depending on the schema of the event that |
| 68 | + // generated it. |
| 69 | + optional bytes value = 7; |
| 70 | +} |
| 71 | +``` |
| 72 | + |
| 73 | +As it looks, a annotation is a very arbitrary value which _puts meaning (or "annotates")_ a specific span. It has a key and a value that is either human or machine readable. You can define your own annotations as you see fit, but for most purposes you will utilize the ones exposed by apptrace by default. |
| 74 | + |
| 75 | +Make explicit note that although annotations _may be_ ordered by specific clients -- there is no such requirement. Any robust client or server should appropriately _handle annotations as a list_ and _expect no specific order_ of them. |
| 76 | + |
| 77 | +# Events |
| 78 | + |
| 79 | +Events (things like associating a name, message, log event, SQL event, or HTTP event) are _marshaled_ into a set of multiple _annotations_. These annotations are then sent over the wire in the form of a CollectPacket, described above. |
| 80 | + |
| 81 | +What follows is a description of the events which Appdash recognizes and renders neatly in the UI, and exactly how they are marshaled into annotations. |
| 82 | + |
| 83 | +Note that the code is psuedo code, not actual code. |
| 84 | + |
| 85 | +## SpanNameEvent |
| 86 | + |
| 87 | +A SpanNameEvent sets the name of a span. It is marshaled into two span annotations: |
| 88 | + |
| 89 | +``` |
| 90 | +Annotation(key="Name", value="theNameOfTheSpan") |
| 91 | +Annotation(key="_schema:name", value="") |
| 92 | +``` |
| 93 | + |
| 94 | +## MsgEvent |
| 95 | + |
| 96 | +A MsgEvent represents a message event, with human readable text. Most clients |
| 97 | +will emit a LogEvent instead, which also contains a timestamp. |
| 98 | + |
| 99 | +``` |
| 100 | +Annotation(key="Msg", value="hello") |
| 101 | +Annotation(key="_schema:msg", value="") |
| 102 | +``` |
| 103 | + |
| 104 | +## LogEvent |
| 105 | + |
| 106 | +A LogEvent represents a log message event, with human readable text and a timestamp. |
| 107 | + |
| 108 | +``` |
| 109 | +Annotation(key="Msg", value="hello") |
| 110 | +Annotation(key="Time", value="2015-02-19T19:31:17.451675861-07:00") |
| 111 | +Annotation(key="_schema:log", value="") |
| 112 | +``` |
| 113 | + |
| 114 | +## SQLEvent |
| 115 | + |
| 116 | +A SQLEvent represents a SQL query event. It is marshaled into several annotations: |
| 117 | + |
| 118 | +``` |
| 119 | +Annotation(key="Tag", value="fakeTag0") |
| 120 | +Annotation(key="ClientSend", value="2015-02-19T19:31:17.449917809-07:00") |
| 121 | +Annotation(key="ClientRecv", value="2015-02-19T19:31:18.442917809-07:00") |
| 122 | +Annotation(key="SQL", value="SELECT * FROM table_name;") |
| 123 | +Annotation(key="_schema:SQL", value="") |
| 124 | +``` |
| 125 | + |
| 126 | +## HTTPServerEvent |
| 127 | + |
| 128 | +A HTTPServerEvent represents an HTTP server serving a single client. It includes several annotations with information about the request, it's headers, etc. |
| 129 | + |
| 130 | +Typically it will be preceded by a SpanNameEvent with the HTTP path requested, for example: |
| 131 | + |
| 132 | +``` |
| 133 | +Annotation(key="Name", value="localhost:8699") |
| 134 | +Annotation(key="_schema:name", value="") |
| 135 | +``` |
| 136 | + |
| 137 | +Information about the request is placed into `Request.Foo` keys, information about the response is placed into `Response.Foo` keys, etc. A server handling a request to `/endpoint-A` would for instance generate annotations like: |
| 138 | + |
| 139 | +``` |
| 140 | +Annotation(key="Request.Method", value="GET") |
| 141 | +Annotation(key="Request.URI", value="/endpoint-A") |
| 142 | +Annotation(key="Request.Proto", value="HTTP/1.1") |
| 143 | +Annotation(key="Request.Headers.Accept-Encoding", value="gzip") |
| 144 | +Annotation(key="Request.Headers.User-Agent", value="Go 1.1 package http") |
| 145 | +Annotation(key="Request.Headers.Span-Id", value="3b83e3e091f8946a/76dc6cbdb3863717/a4475c5cc57a69d4") |
| 146 | +Annotation(key="Request.Host", value="localhost:8699") |
| 147 | +Annotation(key="Request.RemoteAddr", value="127.0.0.1:35741") |
| 148 | +Annotation(key="Request.ContentLength", value="0") |
| 149 | +Annotation(key="Response.StatusCode", value="200") |
| 150 | +Annotation(key="Response.Headers.Span-Id", value="3b83e3e091f8946a/76dc6cbdb3863717/a4475c5cc57a69d4") |
| 151 | +Annotation(key="Response.ContentLength", value="23") |
| 152 | +Annotation(key="Route", value="/endpoint-A") |
| 153 | +Annotation(key="User", value="") |
| 154 | +Annotation(key="ServerRecv", value="2015-02-21T16:36:13.248971779-07:00") |
| 155 | +Annotation(key="ServerSend", value="2015-02-21T16:36:13.499282101-07:00") |
| 156 | +Annotation(key="_schema:HTTPServer", value="") |
| 157 | +``` |
| 158 | + |
| 159 | +TODO: describe `Request.Headers.Span-Id`. |
| 160 | + |
| 161 | +## HTTPClientEvent |
| 162 | + |
| 163 | +A HTTPClientEvent represents a HTTP client making an outbound request. Very similiar to HTTPServerEvent, it includes several annotations with information about the request, it's headers, etc. |
| 164 | + |
| 165 | +For example, a outbound request to `/endpoint-A`: |
| 166 | + |
| 167 | +``` |
| 168 | +Annotation(key="ClientSend", value="2015-02-21T16:36:13.113231752-07:00") |
| 169 | +Annotation(key="ClientRecv", value="2015-02-21T16:36:13.500518641-07:00") |
| 170 | +Annotation(key="Request.Host", value="localhost:8699") |
| 171 | +Annotation(key="Request.RemoteAddr", value="") |
| 172 | +Annotation(key="Request.ContentLength", value="0") |
| 173 | +Annotation(key="Request.Method", value="GET") |
| 174 | +Annotation(key="Request.URI", value="/endpoint-A") |
| 175 | +Annotation(key="Request.Proto", value="HTTP/1.1") |
| 176 | +Annotation(key="Request.Headers.Span-Id", value="3b83e3e091f8946a/76dc6cbdb3863717/a4475c5cc57a69d4") |
| 177 | +Annotation(key="Response.Headers.Content-Type", value="text/plain; charset=utf-8") |
| 178 | +Annotation(key="Response.Headers.Date", value="Sat, 21 Feb 2015 23:36:13 GMT") |
| 179 | +Annotation(key="Response.Headers.Content-Length", value="23") |
| 180 | +Annotation(key="Response.ContentLength", value="23") |
| 181 | +Annotation(key="Response.StatusCode", value="200") |
| 182 | +Annotation(key="_schema:HTTPClient", value="") |
| 183 | +``` |
| 184 | + |
| 185 | +## Larger-Scale Example |
| 186 | + |
| 187 | +A larger-scale example of the annotations generated via running `cmd/appdash demo` is available. See the `demo-annotations.md` file for more information. |
0 commit comments