syntax = "proto2";

package code.justin.tv.release.trace.pbmsg;
option go_package = "pbmsg";

// An Event represents a single Trace-worthy event.  This includes RPC
// lifecycle events (client started to send a request, server finishes sending
// a response), and application-specific custom events.
message Event {

	// Kind describes the type of event instrumented.
	//
	// When a request-response RPC takes place, there are several identifiable
	// instants that can be observed by the client, and several that can be
	// observed by the server.  Each of those is assigned a different Kind
	// value.  An program may only be instrumented to measure a small subset
	// of the possible events.  The goal of the Kind enumeration is to
	// document all known events precisely, so that if two programs emit an
	// event with a particular Kind, that the results will be comparable.
	//
	// If a program wishes to emit an event which is not described by an
	// existing Kind value, a new Kind should be written to fit the bill.  The
	// new value should be reusable across other programs with similar
	// requirements and should not be particular to the language or framework
	// used.
	enum Kind {

		// Server-sent events

		// Server reads the beginning of a request.  The server has received
		// sufficient data from the client to determine which Trace
		// transaction the request should be attributed to, if any.  If the
		// protocol differentiates between a metadata preamble and a payload
		// that follows, this event is emitted immediately after the entire
		// metadata has been received.
		//
		// For an HTTP request, this would be when the transport has presented
		// the request URI and headers are presented to the application.
		REQUEST_HEAD_RECEIVED = 20;

		// Server finishes reading the last of the request body.
		//
		// For an HTTP request, this would be when the transport has presented
		// the last of the request body to the application and notified it
		// that no more body data will be received.  Trailers may be yet to
		// come.
		REQUEST_BODY_RECEIVED = 21;

		// Server responds to a request.  There has not yet been any direct
		// communication to the client about the client's request on the
		// application layer, but the server will change that immediately
		// after generating this event.  Transport-layer acknowledgement of
		// data receipt does not trigger this event.
		//
		// For an HTTP response, this would be when the status code and
		// headers are presented to the HTTP transport, before any bytes are
		// written to the wire.
		RESPONSE_HEAD_PREPARED = 30;

		// Server has sent the last of the response body to the client.
		//
		// For an HTTP response, this would be when the last of the response
		// body has been accepted by the HTTP transport, and the application
		// has notified the transport that no more body data will be sent.
		RESPONSE_BODY_SENT = 31;

		// Client-sent events

		// Client initiates a request.  There has been no indication given to
		// the outside world that this particular request is about to take
		// place.  The application knows enough about the request to begin
		// making externally-visible actions.
		//
		// For an HTTP request, this would be when the request URI and headers
		// are presented to the HTTP transport, before any bytes are written
		// to the wire.  The hostname may still need to be resolved, a TCP
		// connection may still need to be established, a TLS handshake may
		// still need to take place.
		REQUEST_HEAD_PREPARED = 40;

		// Client finishes the externally-visible actions required for the
		// request.
		//
		// For an HTTP request, this would be when the last of the request
		// body has been accepted by the HTTP transport, and the application
		// has notified the transport that no more body data will be sent.
		REQUEST_BODY_SENT = 41;

		// Client reads the beginning of the response.  The response metadata
		// has been read and is ready to be presented to the application's
		// business logic, giving some initial indication of if the RPC
		// succeeded.  The response body has not yet been read.
		//
		// For an HTTP response, this would be when the client's HTTP
		// transport has read and decoded the full response headers from the
		// wire and is presenting them to the application, but before the
		// response body has been consumed.
		RESPONSE_HEAD_RECEIVED = 50;

		// Client finishes reading the last of the response body.
		//
		// For an HTTP response, this would be when the transport has
		// presented the last of the response body to the application and
		// notified it that no more body data will be received.  Trailers may
		// be yet to come.
		RESPONSE_BODY_RECEIVED = 51;
	}

	// Lifecycle event this message is attached to
	optional Kind kind = 1;

	// Unix epoch time in nanoseconds, as measured by the process
	optional sfixed64 time = 2;

	// FQDN of the process's host
	optional string hostname = 3;

	// Service name of the process
	optional string svcname = 4;

	// PID of the process
	optional int32 pid = 5;

	// 128-bit identifier for the entire transaction.  This field is sent as
	// two 64-bit integers, least significant word first.
	repeated fixed64 transaction_id = 6 [packed=true];

	// Identifier for a single RPC span (one request leading to one response).
	// Each level of the distributed call stack adds a new element to the path
	// field.  The value at each level is the (zero-indexed) number of
	// outbound RPC requests made at that level.
	repeated uint32 path = 7 [packed=true];

	// Additional protocol-specific information
	optional Extra extra = 8;

	// Amazon Resource Name for the process's EC2 instance.  This may be sent
	// instead of the hostname field.
	optional string ec2_arn = 20;
}

// An Extra describes protocol-specific information to be associated with an
// Event.
message Extra {

	// The address of the peer.  This could be an ipv4:port pair, [ipv6]:port
	// pair, or hostname:port pair for TCP/UDP-based protocols, a bare IP
	// address or hostname for IP- based protocols, or a path to a unix domain
	// socket.
	//
	// This field is NOT an attempt to identify the client, e.g. via the HTTP
	// X-Forwarded-For header.
	optional string peer = 1;

	// Additional HTTP-specific information for the Event.
	optional ExtraHTTP http = 2;

	// Additional SQL-specific information for the Event.
	optional ExtraSQL sql = 3;

	// Additional Memcached-specific information for the Event.
	optional ExtraMemcached memcached = 4;

	// Additional gRPC-specific information for the Event.
	optional ExtraGRPC grpc = 5;
}

// An ExtraHTTP describes HTTP-specific information to be associated with an
// Event.
message ExtraHTTP {
	// Status code of HTTP response.  Common values will include 200 or 404.
	// Values less than zero indicate an error observed by the client which
	// caused it to give up on the request before getting a status code from
	// the server.
	optional sint32 status_code = 1;

	// Method of HTTP request.
	optional Method method = 2;

	// Path of the HTTP request.  This is only the path - the scheme,
	// hostname, query, and fragment are not included.
	optional string uri_path = 16;

	// https://www.iana.org/assignments/http-methods/http-methods.xhtml
	enum Method {
		// invalid, default value
		UNKNOWN = 0;

		// https://tools.ietf.org/html/rfc7231#section-4.3.1  safe, idempotent
		GET = 1;

		// https://tools.ietf.org/html/rfc7231#section-4.3.2  safe, idempotent
		HEAD = 2;

		// https://tools.ietf.org/html/rfc7231#section-4.3.3
		POST = 3;

		// https://tools.ietf.org/html/rfc7231#section-4.3.4  idempotent
		PUT = 4;

		// https://tools.ietf.org/html/rfc7231#section-4.3.5  idempotent
		DELETE = 5;

		// https://tools.ietf.org/html/rfc7231#section-4.3.6
		CONNECT = 6;

		// https://tools.ietf.org/html/rfc7231#section-4.3.7  safe, idempotent
		OPTIONS = 7;

		// https://tools.ietf.org/html/rfc7231#section-4.3.8  safe, idempotent
		TRACE = 8;

		// https://tools.ietf.org/html/rfc5789
		PATCH = 9;
	}
}

// An ExtraSQL describes SQL-specific information to be associated with an
// Event.
message ExtraSQL {
	// Name of the target database.
	optional string database_name = 1;

	// Username used to connect to the target database.
	optional string database_user = 2;

	// Query string with all user data removed: e.g. strings replaced by '',
	// integers replaced by 0, etc.  This value may be truncated (particularly
	// if it's larger than 1kB).
	optional string stripped_query = 16;
}

// An ExtraMemcached describes memcached-specific information to be
// associated with an event. Most of this was written with the
// assistance of the memcached protocol spec at
// https://github.com/memcached/memcached/blob/c10feb9ebe2179f6fbe4ac2e3cd12bfefdd42631/doc/protocol.txt.
message ExtraMemcached {
	// The command sent to memcached by a client
	optional MemcachedCommand command = 1;

	// The key sent to memcached. Note that multiple keys can be
	// fetched in one `get` request; the keys will be
	// whitespace-separated per the memcached spec.
	//
	// key is currently commented-out because of concerns around
	// confidential data residing in memcached keys.
	//
	// optional string key = 4;

	// The number of keys in this memcached event. This is useful
	// for understanding bulk transactions: the request side might
	// ask for 150 keys, but the response side might have only
	// 100, indicating that the cache was hit for 100 out of the
	// 150 requested keys.
	optional uint32 n_keys = 2;

	// Some commands involve the client setting a 32-bit integer
	// expiration time. According to the memcached protocol spec,
	// this may be either a Unix time, or a number of seconds
	// starting from current time. The memcached server decides
	// which it is by the size of the expiration time: if the
	// value is less than or equal to 60*60*24*30 (30 days), then
	// it's taken as a relative expiration; if it's over this
	// amount then it is interpreted as Unix time. If its set to
	// zero, then the item never expires.
	optional fixed32 expiration = 3;

	enum MemcachedCommand {
		// Default value - unset. Probably an administrative
		// command like STAT.
		UNKNOWN_COMMAND = 0;

		// Set means "store this data."
		SET = 1;

		// Add means "store this data, but only if the server
		// doesn't already have data for this key."
		ADD = 2;

		// Replace means "store this data, but only if the
		// server *does* have data for this key."
		REPLACE = 3;

		// Append means "add this data to an existing key
		// after existing data." Append does not come with an
		// expiration time.
		APPEND = 4;

		// Prepend means "add this data to an existing key
		// before existing data." Prepend does not come with
		// an expiration time.
		PREPEND = 5;

		// Compare-and-set means "Store this data, if and only
		// if no one else has updated since I last fetched
		// it."
		CAS = 6;

		// Get retrieves one or more keys. If no_reply is set
		// to true, then the server will not respond in the
		// case of cache misses, so it is the client's
		// responsibility to match up which requested keys
		// actually got a VALUE response if they ask for
		// multiple keys at once.
		GET = 7;

		// Gets is like Get, but asks for the cas_unique to be
		// sent along with the response.
		GETS = 8;

		// Delete forcibly removes a key.
		DELETE = 9;

		// Incr means "increment this key by this amount." The
		// server returns the new value of the key.
		INCR = 10;

		// Decr means "decrement this key by this amount." The
		// server returns the new value of the key.
		DECR = 11;

	}
}

// An ExtraGRPC describes gRPC-specific information to be associated with an
// event.
message ExtraGRPC {
	// Fully-qualified method name, including proto package name, service
	// name, and method name.
	optional string method = 1;
}

// An UnusedExtra describes protocol-specific information to be associated
// with an Event.  This message is not yet ready for use, meaning that the
// field names, identities, and numbers are subject to change!
message UnusedExtra {
	option deprecated = true; // not ready yet!

	// The Extra message can be attached to any Event.  The Kind of Event
	// determines the meaning of some of the fields: when payload_size is
	// specified in a REQUEST_BODY_RECEIVED event, it refers to the size of
	// the request body.  The same annotation on a RESPONSE_BODY_SENT event
	// would refer to the size of the response body.

	// we'll save fields 1-15 for later, when we know what the popular fields
	// are

	// written by an engineer - "string" means utf-8 encoded, which should be
	// fine?
	optional string text = 20;
	optional bool text_truncated = 1020;

	// if there's a payload, how big is it?
	optional int64 payload_size = 21;

	// HTTP

	// request uri
	optional string http_uri = 41;
	optional bool http_uri_truncated = 1041;

	// headers, partial list
	repeated KV http_header = 43;

	// if there are headers that we expect to be very long, we may want to
	// pull them out into their own fields so we can note when we truncate
	// them
	optional string http_referer = 44;
	optional bool http_referer_truncated = 1044;


	// if we use proto3 we can just use maps
	message KV {
		// option map_entry = true;
		optional string key = 1;
		optional string value = 2;
	}
}

// An EventSet is a list of Events.  It may be used when sending multiple
// events in a datagram, or when saving them to disk.  There is no codified or
// implied relation between Events grouped into an EventSet.
message EventSet {
	// List of Events
	repeated Event event = 1;

	// A compressed EventSet. If this is set, then the event field should be empty.
	optional bytes compressed_event_set = 2;

	// The compression codec used to encode compressed_event_set.
	optional Compression compression = 3;
}

enum Compression {
	// The data is not compressed.
	NONE = 0;
	// The data is comressed with gzip.
	GZIP = 1;
}
