Module: Clamp::Analytics

Defined in:
lib/clamp_analytics.rb,
lib/clamp_analytics/money.rb,
lib/clamp_analytics/errors.rb,
lib/clamp_analytics/version.rb

Defined Under Namespace

Classes: Error, HTTPError, Money, NotInitializedError

Constant Summary collapse

DEFAULT_ENDPOINT =
"https://api.clamp.sh"
VERSION =
"0.2.0"

Class Method Summary collapse

Class Method Details

.capture_error(exception, context: {}, anonymous_id: nil, timestamp: nil) ⇒ true

Capture an exception as a ‘$error` event. Convenience wrapper that extracts message/type/backtrace from the exception and forwards to track. The server adds a stable fingerprint at ingest so the same bug groups across occurrences.

begin
  process_webhook(payload)
rescue => e
  Clamp::Analytics.capture_error(e, context: { webhook: "stripe" })
end

Parameters:

  • exception (Exception)

    the exception to capture

  • context (Hash) (defaults to: {})

    optional flat hash of additional properties. Values must be primitives (String, Integer, Float, true/false). The reserved key ‘:handled` is ignored if present.

  • anonymous_id (String, nil) (defaults to: nil)

    optional, links to a browser visitor

  • timestamp (Time, String, nil) (defaults to: nil)

    optional

Returns:

  • (true)

Raises:



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/clamp_analytics.rb', line 96

def capture_error(exception, context: {}, anonymous_id: nil, timestamp: nil)
  message = (exception.message || "Unknown error")[0, 1024]
  error_type = exception.class.name[0, 64]
  backtrace = (exception.backtrace || []).join("\n")
  stack = backtrace.empty? ? "" : backtrace[0, 16384]

  properties = {
    "error.message" => message,
    "error.type" => error_type,
    "error.stack" => stack,
    "error.handled" => true
  }
  context.each do |k, v|
    key = k.to_s
    next if key == "handled"
    if v.is_a?(String) || v.is_a?(Integer) || v.is_a?(Float) || v == true || v == false
      properties[key] = v
    end
  end

  track("$error", properties: properties, anonymous_id: anonymous_id, timestamp: timestamp)
end

.init(project_id:, api_key:, endpoint: nil) ⇒ Object

Initialize the SDK. Call once at application boot (Rails initializer, Sinatra setup block, etc.).



33
34
35
36
37
38
39
40
41
# File 'lib/clamp_analytics.rb', line 33

def init(project_id:, api_key:, endpoint: nil)
  @mutex.synchronize do
    @config = {
      project_id: project_id,
      api_key: api_key,
      endpoint: endpoint || DEFAULT_ENDPOINT
    }
  end
end

.reset!Object

Reset all SDK state. Intended for tests.



125
126
127
128
129
130
# File 'lib/clamp_analytics.rb', line 125

def reset!
  @mutex.synchronize do
    @config = nil
    @transport = nil
  end
end

.track(name, properties: {}, anonymous_id: nil, timestamp: nil) ⇒ true

Track a server-side event.

Parameters:

  • name (String)

    event name

  • properties (Hash) (defaults to: {})

    optional, values may be String, Integer, Float, true/false, or Money

  • anonymous_id (String, nil) (defaults to: nil)

    optional, links to a browser visitor

  • timestamp (Time, String, nil) (defaults to: nil)

    optional; if omitted, uses Time.now.utc

Returns:

  • (true)

Raises:



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/clamp_analytics.rb', line 53

def track(name, properties: {}, anonymous_id: nil, timestamp: nil)
  cfg = @mutex.synchronize { @config }
  raise NotInitializedError, "clamp_analytics: call Clamp::Analytics.init before track" if cfg.nil?

  payload = { p: cfg[:project_id], name: name }
  payload[:anonymousId] = anonymous_id unless anonymous_id.nil?
  payload[:properties] = serialize_properties(properties) unless properties.empty?
  payload[:timestamp] = serialize_timestamp(timestamp)

  response = transport.call(
    "#{cfg[:endpoint]}/e/s",
    { "content-type" => "application/json", "x-clamp-key" => cfg[:api_key] },
    JSON.generate(payload)
  )

  status = response[:status]
  if status < 200 || status >= 300
    raise HTTPError.new(status, response[:body].to_s)
  end

  true
end

.transport=(transport) ⇒ Object

Override the transport. Used by tests; pass nil to restore the default.



120
121
122
# File 'lib/clamp_analytics.rb', line 120

def transport=(transport)
  @mutex.synchronize { @transport = transport }
end