optics-agent 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NDY0NThhZDNkNTNhNGYwOWEyMmFlMDhjOGFkNGVlNjM0MjAzYWNkNw==
5
+ data.tar.gz: !binary |-
6
+ YWIzNWIxMzNhMGRkMTA3ZGZjMWRiODNjYWVmNTY3OTY4ODA1ZmIxNg==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ Yzk3ODZjY2UzNWRlMzExMGEyMmY1MmRmNDBhNDA3MzVkYzg4ZjRkMWY1NTU3
10
+ ZjUwNzU1OWJhNjVhOGQ0OTYxZTk0YmQ0NWMzMDdmYzM4N2M3ZTdmMDQyMDMx
11
+ NWUyMDY4NmE1ODVlYjk1MWM4ZDY2MzY0Y2U1ZTM3ODljMzIzY2Q=
12
+ data.tar.gz: !binary |-
13
+ NGVlZGUyNTRmM2E3OGRjMWE1MGJkNmQ4ODQ2MjQ3MWIxOTY1YWM0ZDI1NDJi
14
+ NzUzYTk2MTc4ZDNmNzJmZDdkZjFiOWZmNDM2MzQwOGZhYzM4ZjExZWFkYjM3
15
+ ZmUxZjM0NDZkYjJmMDc2ODFiMjUyMTI2M2NiYzY5ZGY5MmFiOWM=
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2016 Meteor Development Group, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,139 @@
1
+ # optics-agent-ruby
2
+ Optics Agent for GraphQL Monitoring in Ruby.
3
+
4
+ This is an alpha release, suitable for use in development contexts. There are still some outstanding improvements to make it ready for production contexts; see the [known limitations](#known-limitations) section below.
5
+
6
+ [![Build Status](https://travis-ci.org/apollostack/optics-agent-ruby.svg?branch=master)](https://travis-ci.org/apollostack/optics-agent-ruby)
7
+
8
+ ## Installing
9
+
10
+ Add
11
+
12
+ ```ruby
13
+ gem 'optics-agent'
14
+ ```
15
+
16
+ To your `Gemfile`
17
+
18
+ ## Setup
19
+
20
+ ### API key
21
+
22
+ You'll need to run your app with the `OPTICS_API_KEY` environment variable set to the name of your Apollo Optics service; at the moment Optics is in early access alpha--[get in touch](http://www.apollostack.com/optics) if you want to be part of our early access program.
23
+
24
+ ### Basic Rack/Sinatra
25
+
26
+ Create an agent
27
+
28
+ ```ruby
29
+ # we expect one day there'll be some options
30
+ agent = OpticsAgent::Agent.instance
31
+ ```
32
+
33
+ Register the Rack middleware (say in a `config.ru`):
34
+
35
+ ```ruby
36
+ use agent.rack_middleware
37
+ ```
38
+
39
+ Register the Graphql middleware:
40
+
41
+ ```ruby
42
+ agent.instrument_schema(YourSchema)
43
+ ```
44
+
45
+ Add something like this to your route:
46
+
47
+ ```ruby
48
+ post '/graphql' do
49
+ request.body.rewind
50
+ document = JSON.parse request.body.read
51
+ result = Schema.execute(document["query"],
52
+ variables: document["variables"],
53
+ context: { optics_agent: env[:optics_agent].with_document(document) }
54
+ )
55
+ JSON.generate(result)
56
+ end
57
+ ```
58
+
59
+ ## Rails
60
+
61
+ The equivalent of the above for Rails is (I welcome better solutions!)
62
+
63
+ Create an agent in `config/apllication.rb`, and register the rack middleware:
64
+
65
+ ```ruby
66
+ module XXXRails
67
+ class Application < Rails::Application
68
+ # ...
69
+
70
+ config.optics_agent = OpticsAgent::Agent.instance
71
+ config.middleware.use config.optics_agent.rack_middleware
72
+ end
73
+ end
74
+
75
+ ```
76
+
77
+ Register the Graphql middleware when you create your schema:
78
+
79
+ ```ruby
80
+ Rails.application.config.optics_agent.instrument_schema(YourSchema)
81
+ ```
82
+
83
+ Add something like this to your `graphql` action:
84
+
85
+ ```ruby
86
+ def create
87
+ query_string = params[:query]
88
+ query_variables = ensure_hash(params[:variables])
89
+ result = YourSchema.execute(query_string,
90
+ variables: query_variables,
91
+ context: {
92
+ optics_agent: env[:optics_agent].with_document(params)
93
+ }
94
+ )
95
+ render json: result
96
+ end
97
+ ```
98
+
99
+ You can check out the GitHunt Rails API server example here: https://github.com/apollostack/githunt-api-rails
100
+
101
+ ## Known limitations
102
+
103
+ Currently the agent is in alpha state; it is intended for early access use in development or basic (non-performance oriented) staging testing.
104
+
105
+ We are working on resolving a [list of issues](https://github.com/apollostack/optics-agent-ruby/projects/1) to put together a production-ready beta launch. The headline issues as things stand are:
106
+
107
+ - The agent is overly chatty and uses a naive threading mechanism that may lose reporting data when threads exit/etc.
108
+ - Instrumentation timings may not be correct in all uses cases, and query times include the full rack request time.
109
+
110
+ You can follow along with our [Beta Release Project](https://github.com/apollostack/optics-agent-ruby/projects/1), or even get in touch if you want to help out getting there!
111
+
112
+ ## Development
113
+
114
+ ### Running tests
115
+
116
+ ```
117
+ bundle install
118
+ bundle exec rspec
119
+ ```
120
+
121
+ ### Building protobuf definitions
122
+
123
+ Ensure you've installed `protobuf` and development dependencies.
124
+
125
+ ```bash
126
+ # this works on OSX
127
+ brew install protobuf
128
+
129
+ # ensure it's version 3
130
+ protoc --version
131
+
132
+ bundle install
133
+ ````
134
+
135
+ Compile the `.proto` definitions with
136
+
137
+ ```bash
138
+ bundle exec rake proto:compile
139
+ ```
@@ -0,0 +1,145 @@
1
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
2
+ # source: reports.proto
3
+
4
+ require 'google/protobuf'
5
+
6
+ Google::Protobuf::DescriptorPool.generated_pool.build do
7
+ add_message "apollo.optics.proto.Id128" do
8
+ optional :high, :sfixed64, 1
9
+ optional :low, :sfixed64, 2
10
+ end
11
+ add_message "apollo.optics.proto.Timestamp" do
12
+ optional :seconds, :int64, 1
13
+ optional :nanos, :int32, 2
14
+ end
15
+ add_message "apollo.optics.proto.Error" do
16
+ optional :message, :string, 1
17
+ end
18
+ add_message "apollo.optics.proto.Trace" do
19
+ optional :server_id, :message, 1, "apollo.optics.proto.Id128"
20
+ optional :client_id, :message, 2, "apollo.optics.proto.Id128"
21
+ optional :start_time, :message, 4, "apollo.optics.proto.Timestamp"
22
+ optional :signature, :string, 5
23
+ optional :details, :message, 6, "apollo.optics.proto.Trace.Details"
24
+ optional :client_name, :string, 7
25
+ optional :client_version, :string, 8
26
+ optional :client_address, :string, 9
27
+ optional :http, :message, 10, "apollo.optics.proto.Trace.HTTPInfo"
28
+ optional :parse, :message, 12, "apollo.optics.proto.Trace.Node"
29
+ optional :validate, :message, 13, "apollo.optics.proto.Trace.Node"
30
+ optional :execute, :message, 14, "apollo.optics.proto.Trace.Node"
31
+ end
32
+ add_message "apollo.optics.proto.Trace.Details" do
33
+ map :variables, :string, :bytes, 1
34
+ optional :raw_query, :string, 2
35
+ optional :operation_name, :string, 3
36
+ end
37
+ add_message "apollo.optics.proto.Trace.HTTPInfo" do
38
+ optional :method, :enum, 1, "apollo.optics.proto.Trace.HTTPInfo.Method"
39
+ optional :host, :string, 2
40
+ optional :path, :string, 3
41
+ map :headers, :string, :string, 4
42
+ optional :secure, :bool, 8
43
+ optional :protocol, :string, 9
44
+ end
45
+ add_enum "apollo.optics.proto.Trace.HTTPInfo.Method" do
46
+ value :UNKNOWN, 0
47
+ value :OPTIONS, 1
48
+ value :GET, 2
49
+ value :HEAD, 3
50
+ value :POST, 4
51
+ value :PUT, 5
52
+ value :DELETE, 6
53
+ value :TRACE, 7
54
+ value :CONNECT, 8
55
+ value :PATCH, 9
56
+ end
57
+ add_message "apollo.optics.proto.Trace.Node" do
58
+ optional :type, :string, 3
59
+ optional :alias, :string, 4
60
+ optional :start_time, :uint64, 8
61
+ optional :end_time, :uint64, 9
62
+ repeated :error, :message, 11, "apollo.optics.proto.Error"
63
+ repeated :child, :message, 12, "apollo.optics.proto.Trace.Node"
64
+ oneof :id do
65
+ optional :field_name, :string, 1
66
+ optional :index, :uint32, 2
67
+ end
68
+ end
69
+ add_message "apollo.optics.proto.ReportHeader" do
70
+ optional :service, :string, 3
71
+ optional :hostname, :string, 5
72
+ optional :agent_version, :string, 6
73
+ optional :service_version, :string, 7
74
+ optional :runtime_version, :string, 8
75
+ optional :uname, :string, 9
76
+ end
77
+ add_message "apollo.optics.proto.StatsPerClientName" do
78
+ repeated :latency_count, :uint64, 1
79
+ repeated :error_count, :uint64, 2
80
+ map :count_per_version, :string, :uint64, 3
81
+ end
82
+ add_message "apollo.optics.proto.FieldStat" do
83
+ optional :name, :string, 2
84
+ optional :returnType, :string, 3
85
+ repeated :latency_count, :uint64, 8
86
+ end
87
+ add_message "apollo.optics.proto.TypeStat" do
88
+ optional :name, :string, 1
89
+ repeated :field, :message, 2, "apollo.optics.proto.FieldStat"
90
+ end
91
+ add_message "apollo.optics.proto.StatsPerSignature" do
92
+ map :per_client_name, :string, :message, 1, "apollo.optics.proto.StatsPerClientName"
93
+ repeated :per_type, :message, 2, "apollo.optics.proto.TypeStat"
94
+ end
95
+ add_message "apollo.optics.proto.TracesReport" do
96
+ optional :header, :message, 1, "apollo.optics.proto.ReportHeader"
97
+ repeated :trace, :message, 2, "apollo.optics.proto.Trace"
98
+ end
99
+ add_message "apollo.optics.proto.Field" do
100
+ optional :name, :string, 2
101
+ optional :returnType, :string, 3
102
+ end
103
+ add_message "apollo.optics.proto.Type" do
104
+ optional :name, :string, 1
105
+ repeated :field, :message, 2, "apollo.optics.proto.Field"
106
+ end
107
+ add_message "apollo.optics.proto.StatsReport" do
108
+ optional :header, :message, 1, "apollo.optics.proto.ReportHeader"
109
+ optional :start_time, :message, 8, "apollo.optics.proto.Timestamp"
110
+ optional :end_time, :message, 9, "apollo.optics.proto.Timestamp"
111
+ optional :realtime_duration, :uint64, 10
112
+ map :per_signature, :string, :message, 12, "apollo.optics.proto.StatsPerSignature"
113
+ repeated :type, :message, 13, "apollo.optics.proto.Type"
114
+ end
115
+ add_message "apollo.optics.proto.SchemaReport" do
116
+ optional :header, :message, 1, "apollo.optics.proto.ReportHeader"
117
+ optional :introspection_result, :string, 8
118
+ repeated :type, :message, 9, "apollo.optics.proto.Type"
119
+ end
120
+ end
121
+
122
+ module Apollo
123
+ module Optics
124
+ module Proto
125
+ Id128 = Google::Protobuf::DescriptorPool.generated_pool.lookup("apollo.optics.proto.Id128").msgclass
126
+ Timestamp = Google::Protobuf::DescriptorPool.generated_pool.lookup("apollo.optics.proto.Timestamp").msgclass
127
+ Error = Google::Protobuf::DescriptorPool.generated_pool.lookup("apollo.optics.proto.Error").msgclass
128
+ Trace = Google::Protobuf::DescriptorPool.generated_pool.lookup("apollo.optics.proto.Trace").msgclass
129
+ Trace::Details = Google::Protobuf::DescriptorPool.generated_pool.lookup("apollo.optics.proto.Trace.Details").msgclass
130
+ Trace::HTTPInfo = Google::Protobuf::DescriptorPool.generated_pool.lookup("apollo.optics.proto.Trace.HTTPInfo").msgclass
131
+ Trace::HTTPInfo::Method = Google::Protobuf::DescriptorPool.generated_pool.lookup("apollo.optics.proto.Trace.HTTPInfo.Method").enummodule
132
+ Trace::Node = Google::Protobuf::DescriptorPool.generated_pool.lookup("apollo.optics.proto.Trace.Node").msgclass
133
+ ReportHeader = Google::Protobuf::DescriptorPool.generated_pool.lookup("apollo.optics.proto.ReportHeader").msgclass
134
+ StatsPerClientName = Google::Protobuf::DescriptorPool.generated_pool.lookup("apollo.optics.proto.StatsPerClientName").msgclass
135
+ FieldStat = Google::Protobuf::DescriptorPool.generated_pool.lookup("apollo.optics.proto.FieldStat").msgclass
136
+ TypeStat = Google::Protobuf::DescriptorPool.generated_pool.lookup("apollo.optics.proto.TypeStat").msgclass
137
+ StatsPerSignature = Google::Protobuf::DescriptorPool.generated_pool.lookup("apollo.optics.proto.StatsPerSignature").msgclass
138
+ TracesReport = Google::Protobuf::DescriptorPool.generated_pool.lookup("apollo.optics.proto.TracesReport").msgclass
139
+ Field = Google::Protobuf::DescriptorPool.generated_pool.lookup("apollo.optics.proto.Field").msgclass
140
+ Type = Google::Protobuf::DescriptorPool.generated_pool.lookup("apollo.optics.proto.Type").msgclass
141
+ StatsReport = Google::Protobuf::DescriptorPool.generated_pool.lookup("apollo.optics.proto.StatsReport").msgclass
142
+ SchemaReport = Google::Protobuf::DescriptorPool.generated_pool.lookup("apollo.optics.proto.SchemaReport").msgclass
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,4 @@
1
+ module OpticsAgent
2
+ end
3
+
4
+ require 'optics-agent/agent'
@@ -0,0 +1,56 @@
1
+ require 'singleton'
2
+ require 'optics-agent/rack-middleware'
3
+ require 'optics-agent/graphql-middleware'
4
+ require 'optics-agent/reporting/report_job'
5
+ require 'optics-agent/reporting/schema_job'
6
+ require 'optics-agent/reporting/query-trace'
7
+
8
+ module OpticsAgent
9
+ # XXX: this is a class but acts as a singleton right now.
10
+ # Need to figure out how to pass the agent into the middleware
11
+ # (for instance we could dynamically generate a middleware class,
12
+ # or ask the user to pass the agent as an option) to avoid it
13
+ class Agent
14
+ include Singleton
15
+ include OpticsAgent::Reporting
16
+
17
+ attr_reader :schema
18
+
19
+ def initialize
20
+ @query_queue = []
21
+ @semaphone = Mutex.new
22
+ ReportJob.perform_in(60, self)
23
+ end
24
+
25
+ def instrument_schema(schema)
26
+ @schema = schema
27
+ schema.middleware << graphql_middleware
28
+
29
+ puts 'scheduling schema job'
30
+ SchemaJob.perform_in(10, self)
31
+ end
32
+
33
+ def add_query(query, rack_env, start_time, end_time)
34
+ @semaphone.synchronize {
35
+ @query_queue << [query, rack_env, start_time, end_time]
36
+ }
37
+ end
38
+
39
+ def clear_query_queue
40
+ @semaphone.synchronize {
41
+ queue = @query_queue
42
+ @query_queue = []
43
+ queue
44
+ }
45
+ end
46
+
47
+ def rack_middleware
48
+ OpticsAgent::RackMiddleware
49
+ end
50
+
51
+ def graphql_middleware
52
+ # graphql middleware doesn't seem to need the agent but certainly could have it
53
+ OpticsAgent::GraphqlMiddleware.new
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,18 @@
1
+ module OpticsAgent
2
+ class GraphqlMiddleware
3
+ def call(parent_type, parent_object, field_definition, field_args, query_context, next_middleware)
4
+ # This happens when an introspection query occurs (reporting schema)
5
+ # However, we could also use it to tell people if they've set things up wrong.
6
+ return next_middleware.call unless query_context[:optics_agent]
7
+
8
+ start_time = Time.now
9
+ result = next_middleware.call
10
+ end_time = Time.now
11
+
12
+ query = query_context[:optics_agent][:query]
13
+ query.report_field(parent_type.to_s, field_definition.name, start_time, end_time)
14
+
15
+ result
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,91 @@
1
+ query ShorterIntrospectionQuery {
2
+ __schema {
3
+ queryType { name }
4
+ mutationType { name }
5
+ subscriptionType { name }
6
+ types {
7
+ ...FullType
8
+ }
9
+ directives {
10
+ name
11
+ # description
12
+ locations
13
+ args {
14
+ ...InputValue
15
+ }
16
+ }
17
+ }
18
+ }
19
+
20
+ fragment FullType on __Type {
21
+ kind
22
+ name
23
+ # description
24
+ fields(includeDeprecated: true) {
25
+ name
26
+ # description
27
+ args {
28
+ ...InputValue
29
+ }
30
+ type {
31
+ ...TypeRef
32
+ }
33
+ isDeprecated
34
+ # deprecationReason
35
+ }
36
+ inputFields {
37
+ ...InputValue
38
+ }
39
+ interfaces {
40
+ ...TypeRef
41
+ }
42
+ enumValues(includeDeprecated: true) {
43
+ name
44
+ # description
45
+ isDeprecated
46
+ # deprecationReason
47
+ }
48
+ possibleTypes {
49
+ ...TypeRef
50
+ }
51
+ }
52
+
53
+ fragment InputValue on __InputValue {
54
+ name
55
+ # description
56
+ type { ...TypeRef }
57
+ # defaultValue
58
+ }
59
+
60
+ fragment TypeRef on __Type {
61
+ kind
62
+ name
63
+ ofType {
64
+ kind
65
+ name
66
+ ofType {
67
+ kind
68
+ name
69
+ ofType {
70
+ kind
71
+ name
72
+ ofType {
73
+ kind
74
+ name
75
+ ofType {
76
+ kind
77
+ name
78
+ ofType {
79
+ kind
80
+ name
81
+ ofType {
82
+ kind
83
+ name
84
+ }
85
+ }
86
+ }
87
+ }
88
+ }
89
+ }
90
+ }
91
+ }