posthog-ruby 3.2.0 → 3.3.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 41c94649948ce4d629d17ccc7f5ed6649c7f9ed6e69ec491ca48bc638962cba4
4
- data.tar.gz: 9a152790208aed756496e031faa0c2bf5a8dfba9bc8f74a1ac3510830132ac8a
3
+ metadata.gz: e895ef67d6f423e89151951d6868fa464c1efe64cee6dd37be8923351bf88467
4
+ data.tar.gz: 7bf79b2a93e79c8717e52ed2a6ca3261f0d3933859e05e0cee1c9c22aaaa77db
5
5
  SHA512:
6
- metadata.gz: b50f6538f3e285387f33a68a4c792c615f95d77826d333481637c7d3321a5490ce0be5f4bdf2ccb4ea914ae9ee5302dc488c9b3bf24f057a6c7d9c72799142d1
7
- data.tar.gz: b34492ba3808627183010ca98d3e77356a873df98b5163854814e60f8225238db51cbafdb9786e9cf83a4295537c365e380094fe39d489bb518e979acd0578d4
6
+ metadata.gz: 881cd65e3634adc8fe539fad751591876ffd070ed712b4de8533fdf38e231ba15fd126daa0afece1b1f06b27306c3afa00c1129e37c98202391593f439cfa886
7
+ data.tar.gz: 2333ef174f7f08858cf518d66ac60bd6c5fa8246d06b61c73c89c5e7d08cf2986b5b45bc952d6d18476b3b73257402ee5838a362da100da1b64d7c72f4ae38eb
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'time'
4
+ require 'securerandom'
4
5
 
5
6
  require 'posthog/defaults'
6
7
  require 'posthog/logging'
@@ -9,6 +10,7 @@ require 'posthog/send_worker'
9
10
  require 'posthog/noop_worker'
10
11
  require 'posthog/feature_flags'
11
12
  require 'posthog/send_feature_flags_options'
13
+ require 'posthog/exception_capture'
12
14
 
13
15
  module PostHog
14
16
  class Client
@@ -144,6 +146,33 @@ module PostHog
144
146
  enqueue(FieldParser.parse_for_capture(attrs))
145
147
  end
146
148
 
149
+ # Captures an exception as an event
150
+ #
151
+ # @param [Exception, String, Object] exception The exception to capture, a string message, or exception-like object
152
+ # @param [String] distinct_id The ID for the user (optional, defaults to a generated UUID)
153
+ # @param [Hash] additional_properties Additional properties to include with the exception event (optional)
154
+ def capture_exception(exception, distinct_id = nil, additional_properties = {})
155
+ exception_info = ExceptionCapture.build_parsed_exception(exception)
156
+
157
+ return if exception_info.nil?
158
+
159
+ no_distinct_id_was_provided = distinct_id.nil?
160
+ distinct_id ||= SecureRandom.uuid
161
+
162
+ properties = { '$exception_list' => [exception_info] }
163
+ properties.merge!(additional_properties) if additional_properties && !additional_properties.empty?
164
+ properties['$process_person_profile'] = false if no_distinct_id_was_provided
165
+
166
+ event_data = {
167
+ distinct_id: distinct_id,
168
+ event: '$exception',
169
+ properties: properties,
170
+ timestamp: Time.now
171
+ }
172
+
173
+ capture(event_data)
174
+ end
175
+
147
176
  # Identifies a user
148
177
  #
149
178
  # @param [Hash] attrs
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Portions of this file are derived from getsentry/sentry-ruby by Software, Inc. dba Sentry
4
+ # Licensed under the MIT License
5
+ # - sentry-ruby/lib/sentry/interfaces/single_exception.rb
6
+ # - sentry-ruby/lib/sentry/interfaces/stacktrace_builder.rb
7
+ # - sentry-ruby/lib/sentry/backtrace.rb
8
+ # - sentry-ruby/lib/sentry/interfaces/stacktrace.rb
9
+ # - sentry-ruby/lib/sentry/linecache.rb
10
+
11
+ # 💖 open source (under MIT License)
12
+
13
+ module PostHog
14
+ module ExceptionCapture
15
+ RUBY_INPUT_FORMAT = /
16
+ ^ \s* (?: [a-zA-Z]: | uri:classloader: )? ([^:]+ | <.*>):
17
+ (\d+)
18
+ (?: :in\s('|`)(?:([\w:]+)\#)?([^']+)')?$
19
+ /x
20
+
21
+ def self.build_parsed_exception(value)
22
+ title, message, backtrace = coerce_exception_input(value)
23
+ return nil if title.nil?
24
+
25
+ build_single_exception_from_data(title, message, backtrace)
26
+ end
27
+
28
+ def self.build_single_exception_from_data(title, message, backtrace)
29
+ {
30
+ 'type' => title,
31
+ 'value' => message || '',
32
+ 'mechanism' => {
33
+ 'type' => 'generic',
34
+ 'handled' => true
35
+ },
36
+ 'stacktrace' => build_stacktrace(backtrace)
37
+ }
38
+ end
39
+
40
+ def self.build_stacktrace(backtrace)
41
+ return nil unless backtrace && !backtrace.empty?
42
+
43
+ frames = backtrace.first(50).map do |line|
44
+ parse_backtrace_line(line)
45
+ end.compact.reverse
46
+
47
+ {
48
+ 'type' => 'raw',
49
+ 'frames' => frames
50
+ }
51
+ end
52
+
53
+ def self.parse_backtrace_line(line)
54
+ match = line.match(RUBY_INPUT_FORMAT)
55
+ return nil unless match
56
+
57
+ file = match[1]
58
+ lineno = match[2].to_i
59
+ method_name = match[5]
60
+
61
+ frame = {
62
+ 'filename' => File.basename(file),
63
+ 'abs_path' => file,
64
+ 'lineno' => lineno,
65
+ 'function' => method_name,
66
+ 'in_app' => !gem_path?(file),
67
+ 'platform' => 'ruby'
68
+ }
69
+
70
+ add_context_lines(frame, file, lineno) if File.exist?(file)
71
+
72
+ frame
73
+ end
74
+
75
+ def self.gem_path?(path)
76
+ path.include?('/gems/') ||
77
+ path.include?('/ruby/') ||
78
+ path.include?('/.rbenv/') ||
79
+ path.include?('/.rvm/')
80
+ end
81
+
82
+ def self.add_context_lines(frame, file_path, lineno, context_size = 5)
83
+ lines = File.readlines(file_path)
84
+ return if lines.empty?
85
+
86
+ return unless lineno.positive? && lineno <= lines.length
87
+
88
+ pre_context_start = [lineno - context_size, 1].max
89
+ post_context_end = [lineno + context_size, lines.length].min
90
+
91
+ frame['context_line'] = lines[lineno - 1].chomp
92
+
93
+ frame['pre_context'] = lines[(pre_context_start - 1)...(lineno - 1)].map(&:chomp) if pre_context_start < lineno
94
+
95
+ frame['post_context'] = lines[lineno...(post_context_end)].map(&:chomp) if post_context_end > lineno
96
+ rescue StandardError
97
+ # Silently ignore file read errors
98
+ end
99
+
100
+ def self.coerce_exception_input(value)
101
+ if value.is_a?(String)
102
+ title = 'Error'
103
+ message = value
104
+ backtrace = nil
105
+ elsif value.respond_to?(:backtrace) && value.respond_to?(:message)
106
+ title = value.class.to_s
107
+ message = value.message || ''
108
+ backtrace = value.backtrace
109
+ else
110
+ return [nil, nil, nil]
111
+ end
112
+
113
+ [title, message, backtrace]
114
+ end
115
+ end
116
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PostHog
4
- VERSION = '3.2.0'
4
+ VERSION = '3.3.0'
5
5
  end
data/lib/posthog.rb CHANGED
@@ -9,3 +9,4 @@ require 'posthog/send_worker'
9
9
  require 'posthog/transport'
10
10
  require 'posthog/response'
11
11
  require 'posthog/logging'
12
+ require 'posthog/exception_capture'
metadata CHANGED
@@ -1,13 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: posthog-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.0
4
+ version: 3.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ''
8
+ autorequire:
8
9
  bindir: bin
9
10
  cert_chain: []
10
- date: 2025-08-26 00:00:00.000000000 Z
11
+ date: 2025-09-23 00:00:00.000000000 Z
11
12
  dependencies:
12
13
  - !ruby/object:Gem::Dependency
13
14
  name: concurrent-ruby
@@ -38,6 +39,7 @@ files:
38
39
  - lib/posthog/backoff_policy.rb
39
40
  - lib/posthog/client.rb
40
41
  - lib/posthog/defaults.rb
42
+ - lib/posthog/exception_capture.rb
41
43
  - lib/posthog/feature_flag.rb
42
44
  - lib/posthog/feature_flags.rb
43
45
  - lib/posthog/field_parser.rb
@@ -55,6 +57,7 @@ licenses:
55
57
  - MIT
56
58
  metadata:
57
59
  rubygems_mfa_required: 'true'
60
+ post_install_message:
58
61
  rdoc_options: []
59
62
  require_paths:
60
63
  - lib
@@ -69,7 +72,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
69
72
  - !ruby/object:Gem::Version
70
73
  version: '0'
71
74
  requirements: []
72
- rubygems_version: 3.6.6
75
+ rubygems_version: 3.5.3
76
+ signing_key:
73
77
  specification_version: 4
74
78
  summary: PostHog library
75
79
  test_files: []