Class: OpticsAgent::Agent

Inherits:
Object
  • Object
show all
Includes:
Reporting
Defined in:
lib/optics-agent/agent.rb

Class Attribute Summary collapse

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Reporting

#add_latency, #client_info, #duration_micros, #duration_nanos, #generate_report_header, #generate_timestamp, #latency_bucket_for_duration

Constructor Details

#initializeAgent

Returns a new instance of Agent.



24
25
26
27
28
29
30
31
# File 'lib/optics-agent/agent.rb', line 24

def initialize
  @stats_reporting_thread = nil
  @query_queue = []
  @semaphone = Mutex.new

  # set defaults
  @configuration = Configuration.new
end

Class Attribute Details

.loggerObject

Returns the value of attribute logger.



17
18
19
# File 'lib/optics-agent/agent.rb', line 17

def logger
  @logger
end

Instance Attribute Details

#report_tracesObject (readonly)

Returns the value of attribute report_traces.



22
23
24
# File 'lib/optics-agent/agent.rb', line 22

def report_traces
  @report_traces
end

#schemaObject (readonly)

Returns the value of attribute schema.



22
23
24
# File 'lib/optics-agent/agent.rb', line 22

def schema
  @schema
end

Instance Method Details

#add_query(*args) ⇒ Object



166
167
168
169
170
171
# File 'lib/optics-agent/agent.rb', line 166

def add_query(*args)
  @semaphone.synchronize {
    debug { "adding query to queue, queue length was #{@query_queue.length}" }
    @query_queue << args
  }
end

#clear_query_queueObject



173
174
175
176
177
178
179
180
# File 'lib/optics-agent/agent.rb', line 173

def clear_query_queue
  @semaphone.synchronize {
    debug { "clearing query queue, queue length was #{@query_queue.length}" }
    queue = @query_queue
    @query_queue = []
    queue
  }
end

#configure(&block) ⇒ Object



33
34
35
36
37
38
39
# File 'lib/optics-agent/agent.rb', line 33

def configure(&block)
  @configuration.instance_eval(&block)

  if @configuration.schema && @schema != @configuration.schema
    instrument_schema(@configuration.schema)
  end
end

#debug(message = nil) ⇒ Object

Should we be using built in debug levels rather than our own “debug” flag?



220
221
222
223
224
225
# File 'lib/optics-agent/agent.rb', line 220

def debug(message = nil)
  if @configuration.debug
    message = yield unless message
    self.class.logger.info "optics-agent: DEBUG: #{message} <#{Process.pid} | #{Thread.current.object_id}>"
  end
end

#disabled?Boolean

Returns:

  • (Boolean)


41
42
43
# File 'lib/optics-agent/agent.rb', line 41

def disabled?
  @configuration.disable_reporting || !@configuration.api_key || !@schema
end

#ensure_reporting!Object

Deprecated.

Use ensure_reporting_stats! instead



95
96
97
# File 'lib/optics-agent/agent.rb', line 95

def ensure_reporting!
  ensure_reporting_stats!
end

#ensure_reporting_stats!Object

We call this method on every request to ensure that the reporting thread is active in the correct process for pre-forking webservers like unicorn



81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/optics-agent/agent.rb', line 81

def ensure_reporting_stats!
  unless @schema
    warn """No schema instrumented.
Use the `schema` configuration setting, or call `agent.instrument_schema`
"""
    return
  end

  unless reporting_stats? || disabled?
    schedule_report
  end
end

#graphql_middlewareObject



189
190
191
# File 'lib/optics-agent/agent.rb', line 189

def graphql_middleware
  warn "You no longer need to pass the optics agent middleware, it now attaches itself"
end

#instrument_schema(schema) ⇒ Object



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/optics-agent/agent.rb', line 49

def instrument_schema(schema)
  unless @configuration.api_key
    warn """No api_key set.
Either configure it or use the OPTICS_API_KEY environment variable.
"""
    return
  end

  if @schema
    warn """Agent has already instrumented a schema.
Perhaps you are calling both `agent.configure { schema YourSchema }` and
`agent.instrument_schema YourSchema`?
"""
    return
  end

  @schema = schema
  @schema._attach_optics_agent(self)

  unless disabled?
    debug "spawning schema thread"
    Thread.new do
      debug "schema thread spawned"
      sleep @configuration.schema_report_delay_ms / 1000.0
      debug "running schema job"
      SchemaJob.new.perform(self)
    end
  end
end

#log(message = nil) ⇒ Object



210
211
212
213
# File 'lib/optics-agent/agent.rb', line 210

def log(message = nil)
  message = yield unless message
  self.class.logger.info "optics-agent: #{message}"
end

#rack_middlewareObject



182
183
184
185
186
187
# File 'lib/optics-agent/agent.rb', line 182

def rack_middleware
  # We need to pass ourselves to the class we return here because
  # rack will instanciate it. (See comment at the top of RackMiddleware)
  OpticsAgent::RackMiddleware.agent = self
  OpticsAgent::RackMiddleware
end

#report_traces?Boolean

Returns:

  • (Boolean)


45
46
47
# File 'lib/optics-agent/agent.rb', line 45

def report_traces?
  @configuration.report_traces
end

#reporting_connectionObject



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/optics-agent/agent.rb', line 99

def reporting_connection
  @reporting_connection ||=
    Faraday.new(:url => @configuration.endpoint_url) do |conn|
      conn.request :retry,
                   :max => 5,
                   :interval => 0.1,
                   :max_interval => 10,
                   :backoff_factor => 2,
                   :exceptions => [Exception],
                   :retry_if => ->(env, exc) { true }
      conn.use OpticsAgent::Reporting::DetectServerSideError

      # XXX: allow setting adaptor in config
      conn.adapter :net_http_persistent
    end
end

#reporting_stats?Boolean

Returns:

  • (Boolean)


116
117
118
# File 'lib/optics-agent/agent.rb', line 116

def reporting_stats?
  @stats_reporting_thread != nil && !!@stats_reporting_thread.status
end

#schedule_reportObject



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/optics-agent/agent.rb', line 120

def schedule_report
  @semaphone.synchronize do
    return if reporting_stats?

    debug "spawning reporting thread"

    thread = Thread.new do
      begin
        debug "reporting thread spawned"

        report_interval = @configuration.report_interval_ms / 1000.0
        last_started = Time.now

        while true
          next_send = last_started + report_interval
          sleep_time = next_send - Time.now

          if sleep_time < 0
            warn 'Report took more than one interval! Some requests might appear at the wrong time.'
          else
            sleep sleep_time
          end

          debug "running reporting job"
          last_started = Time.now
          ReportJob.new.perform(self)
          debug "finished running reporting job"
        end
      rescue Exception => e
        warn "Stats report thread dying: #{e}"
      end
    end

    at_exit do
      if thread.status
        debug 'sending last stats report before exiting'
        thread.exit
        ReportJob.new.perform(self)
        debug 'last stats report sent'
      end
    end

    @stats_reporting_thread = thread
  end
end

#send_message(path, message) ⇒ Object



193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/optics-agent/agent.rb', line 193

def send_message(path, message)
  response = reporting_connection.post do |request|
    request.url path
    request.headers['x-api-key'] = @configuration.api_key
    request.headers['user-agent'] = "optics-agent-rb"
    request.body = message.class.encode(message)

    if @configuration.debug || @configuration.print_reports
      log "sending message: #{message.class.encode_json(message)}"
    end
  end

  if @configuration.debug || @configuration.print_reports
    log "got response body: #{response.body}"
  end
end

#warn(message = nil) ⇒ Object



215
216
217
# File 'lib/optics-agent/agent.rb', line 215

def warn(message = nil)
  self.class.logger.warn "optics-agent: WARNING: #{message}"
end