gql_metrics_tracer 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/gql_metrics_tracer.rb +47 -175
  3. metadata +57 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8a0d96d8b78c4b80c1625c6b5500cebac944464197884576612f00ba6bc584cf
4
- data.tar.gz: cf69077f4fc23379bf50e058df072aab751d4e676ef9c28e664d4632b2bd820f
3
+ metadata.gz: 932d56d4a22e85406cba06faebc9e220ea9da7389049fca1266cc7796684a0ba
4
+ data.tar.gz: 91f73c97ec02642e48576da8f0c24522b137b4b83a64dc705ab339bb4dae6edc
5
5
  SHA512:
6
- metadata.gz: a65dd23fd1f46112ea4f389e373326e7da910df2f024135f5c49220e8becb7f85b21a35758432e37dc05583afd6342827f5f358ce2364f665c5a4f3ece3ba1ad
7
- data.tar.gz: 2cb1aa8955955c854c8b864fb9c9f7dae8aeaf4ea0396104c6f0bbb5a0547fc75b7753154e52f8828d3359acdc95e23cfb39f65dd55fd74177693c8cb84cd34c
6
+ metadata.gz: 16d5cf2b5e49c0c09a7fe5ad356d09432baf46400c4b48ff86e517a17121cfead10a088718a84696ff400d283730de27f8bd6f6d49f1111aeb1d2178fe9e123e
7
+ data.tar.gz: f47e07317b13d0130e298d41d4171990c8670af9d8b3090822e251460a938a092020caf822d82b41f57aa478a029effa74471f7b887d0bb12d1b9cad9d8b5e54
@@ -1,194 +1,66 @@
1
- # frozen_string_literal: true
2
-
3
1
  require "typhoeus"
4
2
  require "json"
5
-
6
- class GQLMetricsTracer
7
- def self.use(schema_definition, _options = {})
8
- tracer = new(schema_definition)
9
- schema_definition.instrument(:query, tracer)
10
- schema_definition.tracer(tracer)
11
- end
12
-
13
- def initialize(schema_definition)
14
- @schema_id = nil
15
- @schema_definition = schema_definition
16
-
17
- publish_schema!
18
- end
19
-
20
- def before_query(query)
21
- query.context["gqlmetrics-trace"] = {
22
- "starts-ns" => precise_time,
23
- "starts-timestamp" => Time.current,
24
- "traces" => [],
25
- }
26
- end
27
-
28
- def after_query(query)
29
- ended_at_ns = precise_time
30
- ended_timestamp = Time.current
31
- started_at = query.context.dig("gqlmetrics-trace", "starts-timestamp")
32
- started_at_ns = query.context.dig("gqlmetrics-trace", "starts-ns")
33
- traces = query.context.dig("gqlmetrics-trace", "traces")
34
-
35
- send_trace!(
36
- query: query.document.to_query_string,
37
- duration_ns: ended_at_ns - started_at_ns,
38
- started_at: started_at,
39
- ended_at: ended_timestamp,
40
- traces: traces,
41
- )
42
- end
43
-
44
- def trace(key, data, &block)
45
- if key == "execute_field"
46
- field = data[:context].present? ? data[:context].field : data[:field]
47
- path = data[:context].present? ? data[:context].path : data[:path]
48
- parent_type = data[:context].present? ? data[:context].parent_type : data[:owner].graphql_name
49
- context = data[:context] || data[:object].context
50
-
51
- trace_field!(field: field, path: path, parent_type: parent_type, context: context, &block)
52
- else
53
- yield
3
+ require "active_support/all"
4
+
5
+ require "gql_metrics_tracer/field_tracer"
6
+ require "gql_metrics_tracer/query_tracer"
7
+ require "gql_metrics_tracer/api_client"
8
+
9
+ module GQLMetrics
10
+ class Tracer
11
+ def self.use(schema_definition, _options = {})
12
+ tracer = new(schema_definition)
13
+ schema_definition.instrument(:query, tracer)
14
+ schema_definition.tracer(tracer)
54
15
  end
55
- end
56
16
 
57
- private
58
-
59
- attr_reader :schema_id, :schema_definition
60
-
61
- def send_trace!(query:, duration_ns:, started_at:, ended_at:, traces:)
62
- return unless graph_token_configured?
63
- return if @schema_id.nil?
17
+ def initialize(schema_definition)
18
+ @api_client = ApiClient.new schema_definition: schema_definition
19
+ Thread.new { @api_client.publish_schema! }
20
+ end
64
21
 
65
- Thread.new do
66
- response = Typhoeus.post(
67
- "#{gqlmetrics_http_host}/api/traces",
68
- headers: {
69
- 'Content-Type': "application/json",
70
- 'Authorization': "Bearer #{ENV['GQLMETRICS_GRAPH_ID']}",
71
- },
72
- timeout: 30,
73
- body: JSON.dump(
74
- {
75
- schema_id: @schema_id,
76
- query: query,
77
- started_at_ms: timestamp_to_ms(started_at),
78
- ended_at_ms: timestamp_to_ms(ended_at),
79
- duration_ns: duration_ns,
80
- traces: traces,
81
- },
22
+ def before_query(query)
23
+ query.context["gqlmetrics-trace"] = {
24
+ "query-tracer" => QueryTracer.new(
25
+ schema_id: schema_id,
26
+ query: query.document.to_query_string,
27
+ operation_name: query.context[:operation_name],
82
28
  ),
83
- )
84
-
85
- trace_id = JSON.parse(response.body)["id"]
86
-
87
- if trace_id.present?
88
- Rails.logger.info "[GQLMetricsTracer] Sent trace #{trace_id}"
89
- else
90
- Rails.logger.warn "[GQLMetricsTracer] Failed to send the trace..."
91
- end
29
+ }
92
30
  end
93
- end
94
31
 
95
- def trace_field!(field:, path:, parent_type:, context:)
96
- query_starts_at = context["gqlmetrics-trace"]["starts-ns"]
32
+ def after_query(query)
33
+ query_tracer = query.context.dig("gqlmetrics-trace", "query-tracer")
34
+ query_tracer.end_trace!
97
35
 
98
- sql_queries = []
99
- ActiveSupport::Notifications.subscribe "sql.active_record" do |event|
100
- unless /(pg_)|(max_identifier_length)/.match?(event.payload[:sql])
101
- sql_queries.push(
102
- {
103
- sql: event.payload[:sql],
104
- start_time: event.time,
105
- end_time: event.end,
106
- duration_ns: event.duration * 1_000_000,
107
- allocations_count: event.allocations,
108
- },
109
- )
110
- end
36
+ Thread.new { api_client.send_trace!(query_tracer) }
111
37
  end
112
38
 
113
- starts_at = precise_time
114
- yield
115
- ensure
116
- ends_at = precise_time
117
- ActiveSupport::Notifications.unsubscribe("sql.active_record")
118
-
119
- trace_node = {
120
- "response_name" => field.name,
121
- "original_field_name" => field.name,
122
- "type" => field.type.to_type_signature,
123
- "parent_type" => parent_type.to_s,
124
- "start_time" => starts_at - query_starts_at,
125
- "end_time" => ends_at - query_starts_at,
126
- "sql_queries" => sql_queries,
127
- "path" => path,
128
- }
129
-
130
- context["gqlmetrics-trace"]["traces"] << trace_node
131
- end
132
-
133
- # Introspect the schema and upload to the ingress server
134
- def publish_schema!
135
- return unless graph_token_configured?
136
-
137
- Thread.new do
138
- # Sleep for 50ms so that the definition has time enough to initialize
139
- sleep(0.05)
140
- schema = schema_definition.execute(GraphQL::Introspection::INTROSPECTION_QUERY)
141
-
142
- payload = {
143
- query: PUBLISH_SCHEMA_QUERY,
144
- variables: {
145
- schema: schema.to_json,
146
- },
147
- }
148
-
149
- response = Typhoeus.post(
150
- "#{gqlmetrics_http_host}/graphql",
151
- headers: {
152
- 'Content-Type': "application/json",
153
- 'Authorization': "Bearer #{ENV['GQLMETRICS_GRAPH_ID']}",
154
- },
155
- timeout: 10,
156
- body: payload.to_json,
157
- )
158
-
159
- json_response = JSON.parse(response.body)
160
- schema_id = json_response.dig("data", "publishSchema", "id")
161
-
162
- if schema_id.present?
163
- @schema_id = schema_id
39
+ def trace(key, data, &block)
40
+ if key == "execute_field"
41
+ context = data[:context] || data[:object].context
42
+ query_tracer = context.dig("gqlmetrics-trace", "query-tracer")
43
+
44
+ field = !data[:context].nil? ? data[:context].field : data[:field]
45
+ path = !data[:context].nil? ? data[:context].path : data[:path]
46
+ parent_type = !data[:context].nil? ? data[:context].parent_type : data[:owner].graphql_name
47
+
48
+ query_tracer.trace_field!(
49
+ field: field,
50
+ path: path,
51
+ parent_type: parent_type,
52
+ context: context,
53
+ &block
54
+ )
164
55
  else
165
- Rails.logger.warn "[GQLMetricsTracer] Failed to publish the latest schema..."
166
- nil
56
+ yield
167
57
  end
168
58
  end
169
- end
170
-
171
- def graph_token_configured?
172
- ENV["GQLMETRICS_GRAPH_ID"].present?
173
- end
174
59
 
175
- def gqlmetrics_http_host
176
- ENV.fetch("GQLMETRICS_HTTP_HOST", "http://localhost:5000")
177
- end
60
+ private
178
61
 
179
- def precise_time
180
- Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond)
181
- end
62
+ attr_reader :api_client
182
63
 
183
- def timestamp_to_ms(timestamp)
184
- (timestamp.to_f * 1_000).to_i
64
+ delegate :schema_id, to: :api_client
185
65
  end
186
-
187
- PUBLISH_SCHEMA_QUERY = <<~PUBLISH_SCHEMA_QUERY
188
- mutation PublishSchema($schema: String!) {
189
- publishSchema(schema: $schema) {
190
- id
191
- }
192
- }
193
- PUBLISH_SCHEMA_QUERY
194
66
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gql_metrics_tracer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dan Andreasson
@@ -26,6 +26,34 @@ dependencies:
26
26
  - - "~>"
27
27
  - !ruby/object:Gem::Version
28
28
  version: '1.0'
29
+ - !ruby/object:Gem::Dependency
30
+ name: graphql
31
+ requirement: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: '0'
36
+ type: :runtime
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ - !ruby/object:Gem::Dependency
44
+ name: activesupport
45
+ requirement: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ type: :runtime
51
+ prerelease: false
52
+ version_requirements: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
29
57
  - !ruby/object:Gem::Dependency
30
58
  name: rspec
31
59
  requirement: !ruby/object:Gem::Requirement
@@ -40,6 +68,34 @@ dependencies:
40
68
  - - "~>"
41
69
  - !ruby/object:Gem::Version
42
70
  version: '3.9'
71
+ - !ruby/object:Gem::Dependency
72
+ name: rubocop
73
+ requirement: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - "~>"
76
+ - !ruby/object:Gem::Version
77
+ version: '1.0'
78
+ type: :development
79
+ prerelease: false
80
+ version_requirements: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - "~>"
83
+ - !ruby/object:Gem::Version
84
+ version: '1.0'
85
+ - !ruby/object:Gem::Dependency
86
+ name: timecop
87
+ requirement: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ type: :development
93
+ prerelease: false
94
+ version_requirements: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
43
99
  description: Explore your GraphQL metrics
44
100
  email: dan@saits.se
45
101
  executables: []