Class: Ditliti::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/ditliti.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(endpoint:, api_key:, project_id:, release: nil, environment: nil, timeout: 1.5) ⇒ Client

Returns a new instance of Client.



101
102
103
104
105
106
107
108
109
110
# File 'lib/ditliti.rb', line 101

def initialize(endpoint:, api_key:, project_id:, release: nil, environment: nil, timeout: 1.5)
  @endpoint = endpoint.sub(%r{/$}, "")
  @api_key = api_key
  @project_id = project_id
  @release = release
  @environment = environment
  @timeout = timeout
  @breadcrumbs = []
  @trace = nil
end

Instance Attribute Details

#session_idObject

Returns the value of attribute session_id.



98
99
100
# File 'lib/ditliti.rb', line 98

def session_id
  @session_id
end

#traceObject (readonly)

Returns the value of attribute trace.



99
100
101
# File 'lib/ditliti.rb', line 99

def trace
  @trace
end

#userObject

Returns the value of attribute user.



98
99
100
# File 'lib/ditliti.rb', line 98

def user
  @user
end

Instance Method Details

#add_breadcrumb(message:, category: nil, level: "info", data: {}) ⇒ Object



161
162
163
164
165
166
167
168
169
170
# File 'lib/ditliti.rb', line 161

def add_breadcrumb(message:, category: nil, level: "info", data: {})
  @breadcrumbs << {
    timestamp: Time.now.utc.iso8601,
    category: category,
    message: message.to_s,
    level: level,
    data: data || {}
  }
  @breadcrumbs.shift while @breadcrumbs.length > 100
end

#capture_exception(error, context: {}, tags: {}, transaction: nil, user_agent: nil) ⇒ Object



172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/ditliti.rb', line 172

def capture_exception(error, context: {}, tags: {}, transaction: nil, user_agent: nil)
  payload = {
    id: SecureRandom.uuid,
    project_id: @project_id,
    level: "error",
    message: error.message.to_s,
    exception_type: error.class.name,
    stack_trace: stack_frames(error),
    tags: { runtime: "ruby" }.merge(current_trace_tags).merge(tags || {}),
    context: context || {},
    user_agent: user_agent,
    release: @release,
    environment: @environment,
    transaction: transaction,
    session_id: @session_id,
    breadcrumbs: @breadcrumbs,
    user: @user
  }
  post("/api/v1/ingest", payload, stable_id(payload))
end

#capture_profile(duration_ms:, trace_id: nil, transaction: nil, platform: "ruby", samples: [], tags: {}, release: @release, environment: @environment) ⇒ Object



240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/ditliti.rb', line 240

def capture_profile(duration_ms:, trace_id: nil, transaction: nil, platform: "ruby", samples: [], tags: {}, release: @release, environment: @environment)
  send_envelope({
    profiles: [{
      project_id: @project_id,
      trace_id: trace_id || @trace&.trace_id,
      transaction: transaction,
      started_at: Time.now.utc.iso8601,
      duration_ms: duration_ms,
      platform: platform,
      samples: samples || [],
      tags: { runtime: "ruby" }.merge(tags || {}),
      release: release,
      environment: environment
    }]
  })
end

#capture_rack_exception(error, env) ⇒ Object



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

def capture_rack_exception(error, env)
  request_method = env["REQUEST_METHOD"]
  path = env["PATH_INFO"] || env["REQUEST_URI"]
  capture_exception(
    error,
    context: {
      framework: "rack",
      method: request_method,
      path: path,
      query: env["QUERY_STRING"]
    },
    tags: { framework: "rack" },
    transaction: "#{request_method || "HTTP"} #{path || "/"}",
    user_agent: env["HTTP_USER_AGENT"]
  )
end

#capture_replay_segment(duration_ms:, session_id: @session_id, segment_index: 0, events: [], tags: {}, user: @user, release: @release, environment: @environment) ⇒ Object

Raises:

  • (ArgumentError)


221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/ditliti.rb', line 221

def capture_replay_segment(duration_ms:, session_id: @session_id, segment_index: 0, events: [], tags: {}, user: @user, release: @release, environment: @environment)
  raise ArgumentError, "replay session_id is required" if session_id.nil? || session_id.to_s.empty?

  send_envelope({
    replays: [{
      project_id: @project_id,
      session_id: session_id,
      started_at: Time.now.utc.iso8601,
      duration_ms: duration_ms,
      segment_index: segment_index,
      events: events || [],
      tags: { runtime: "ruby" }.merge(tags || {}),
      user: user,
      release: release,
      environment: environment
    }]
  })
end

#get_traceparentObject



122
123
124
# File 'lib/ditliti.rb', line 122

def get_traceparent
  @trace&.to_traceparent
end

#record_span(trace_id:, span_id:, operation_name:, duration_ms:, parent_span_id: nil, started_at: nil, tags: {}) ⇒ Object



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/ditliti.rb', line 135

def record_span(trace_id:, span_id:, operation_name:, duration_ms:, parent_span_id: nil, started_at: nil, tags: {})
  base_tags = { runtime: "ruby" }
  base_tags[:environment] = @environment if @environment
  base_tags[:release] = @release if @release
  send_envelope({
    spans: [{
      trace_id: trace_id,
      span_id: span_id,
      parent_span_id: parent_span_id,
      project_id: @project_id,
      operation_name: operation_name,
      start_time: (started_at || Time.now.utc).iso8601(3),
      duration_ms: duration_ms,
      tags: base_tags.merge(tags)
    }]
  })
end

#send_envelope(envelope) ⇒ Object



210
211
212
213
214
215
216
217
218
219
# File 'lib/ditliti.rb', line 210

def send_envelope(envelope)
  post("/api/v1/envelope", {
    project_id: @project_id,
    events: envelope.fetch(:events, []),
    spans: envelope.fetch(:spans, []),
    logs: envelope.fetch(:logs, []),
    replays: envelope.fetch(:replays, []),
    profiles: envelope.fetch(:profiles, [])
  })
end

#start_span(operation_name, tags: {}) ⇒ Object

Start a child span under the active trace (starting one implicitly if none is active yet).



128
129
130
131
132
133
# File 'lib/ditliti.rb', line 128

def start_span(operation_name, tags: {})
  start_trace if @trace.nil?
  parent_span_id = @trace.span_id
  ctx = @trace.child
  Span.new(self, operation_name, parent_span_id, ctx, { runtime: "ruby" }.merge(tags))
end

#start_trace(incoming_traceparent = nil) ⇒ Object

Start (or continue, if an inbound traceparent header is given) the active trace. Downstream calls should read #get_traceparent and forward it as the traceparent header of any outgoing HTTP request to propagate the trace into the next service.



116
117
118
119
120
# File 'lib/ditliti.rb', line 116

def start_trace(incoming_traceparent = nil)
  parent = TraceContext.from_traceparent(incoming_traceparent)
  @trace = parent ? parent.child : TraceContext.start
  @trace
end