auditea 0.1.0.beta.2 → 0.1.0.beta.3

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: 42597f9a8f1014e6c58655f23f96fea08309ceeebfd1537bb346b3a418124279
4
- data.tar.gz: 8a360c4240d9c1a7b38ec9ff834074e8ed7edcf91ba52628fcfc28a00f85f3ec
3
+ metadata.gz: 587d3b57a35018f55c5e82c15a485715913e911a714971797a47fde50d788251
4
+ data.tar.gz: beae506f6609e933baa4d4513e9015cc7b11c919a75ed83ba912eeb546aae0e5
5
5
  SHA512:
6
- metadata.gz: cb4e6b2cb6444448ef2391a0a5321b9441e7522a0e1220a4cea089d154a66f58577a13ec245af1dfec7cd7b676bb2a2d525520a3831f4eb0bbef9e297591b995
7
- data.tar.gz: 787fa69e8fb02fafbe35bcba69b8e592fafa146f76b9cc87b3f7fc7f0e675b7d7b3660f9290ee4a475da6857a3d6e1022bb38966abbf0ebc7827760d78e05c62
6
+ metadata.gz: 16e6ebde275b1f03f12028ca1c746fdedd9efb907706560ecae52b1fed188e90944f0e827c40e7a1cb909742d381fe37d26deb385ee66d82965c8185444ac99f
7
+ data.tar.gz: e0c7097c1f146d724befdaf2df850e30a556c670a06406f459003b46a2b52fc77687b36d3fa366d3db4662e13718ddf70ea12698480555858bf51650ee9781c8
data/CHANGELOG.md CHANGED
@@ -1,6 +1,20 @@
1
1
  # Changelog
2
2
 
3
- ## 0.1.0.beta.2 (unreleased)
3
+ ## 0.1.0.beta.3 (unreleased)
4
+
5
+ ### Exception capture parity (AUD-ERROR-PARITY01)
6
+
7
+ - Public `Auditea.capture_exception(error, handled:, mechanism:, context:, metadata:)` → `exception.raised`
8
+ - Rails `ErrorReporter` subscriber via public `subscribe` API (Rails 7.2 / 8.1); graceful no-op when absent
9
+ - Identity-safe WeakMap dedupe of exception objects (no integer `object_id` reuse false negatives); cleared at request/job boundaries
10
+ - When `CurrentContext` is already cleared (post-unwind `application.action_dispatch`), derive method/path/request_id/controller/action from the ErrorReporter controller/request via public APIs only
11
+ - `process_action.action_controller` remains as fallback when ErrorReporter is not installed
12
+ - Structured evidence: type, message, bounded frames (≤40) with deterministic `in_app` when knowable, `handled`, `mechanism`, HTTP path/method (no query secrets), controller/action when available
13
+ - ActiveJob: ErrorReporter sources matching `active_job` are skipped when `capture_active_job` is enabled (dedicated `job.failed` path unchanged)
14
+ - Real Rails 7.2 / 8.1 integration coverage for unhandled/handled controller exceptions and ActiveJob no-duplicate behavior
15
+ - Provenance unchanged: sdk/ruby/rails versions + configured `release`
16
+
17
+ ## 0.1.0.beta.2
4
18
 
5
19
  ### Runtime / dependency inventory (opt-in, default OFF)
6
20
 
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Ruby / Rails SDK for [AudiTea](https://github.com/reznis/AudiTea) event collection.
4
4
 
5
- Version: **0.1.0.beta.2** (not published yet)
5
+ Version: **0.1.0.beta.3** (not published yet)
6
6
 
7
7
  Captures durable technical and business evidence. AudiTea **server** owns taxonomy, dictionary, human labels, vulnerability matching, correlation, and reinterpretation.
8
8
 
@@ -12,7 +12,7 @@ The SDK does **not** perform vulnerability scanning, OS package/CVE inventory, d
12
12
 
13
13
  ```ruby
14
14
  # Gemfile
15
- gem "auditea", "0.1.0.beta.2"
15
+ gem "auditea", "0.1.0.beta.3"
16
16
  ```
17
17
 
18
18
  ```bash
@@ -114,9 +114,17 @@ Auditea.capture(
114
114
  evidence: { business: { from_status: "draft", to_status: "approved" } }
115
115
  )
116
116
 
117
+ Auditea.capture_exception(
118
+ error,
119
+ handled: true,
120
+ mechanism: "application_rescue"
121
+ )
122
+
117
123
  Auditea.log(:info, "Export finished", context: { export_id: export.id })
118
124
  ```
119
125
 
126
+ Automatic Rails exception observation uses `Rails.error` (ErrorReporter) when available, with `process_action.action_controller` as fallback. Same-exception dedupe is request/thread-local by object id so ErrorReporter and controller notifications do not double-count.
127
+
120
128
  Wire format follows AudiTea **EVENT_CONTRACT_V1**:
121
129
 
122
130
  - Bearer Source credential authentication
data/RELEASE.md CHANGED
@@ -1,4 +1,4 @@
1
- # Release: auditea 0.1.0.beta.2
1
+ # Release: auditea 0.1.0.beta.3
2
2
 
3
3
  Authoritative runbook for controlled beta publication of the Ruby SDK.
4
4
 
@@ -153,10 +153,10 @@ PR status may say `READY FOR HUMAN PUBLICATION REVIEW` while still listing exter
153
153
  ```bash
154
154
  git checkout main
155
155
  git pull
156
- # confirm VERSION and CHANGELOG still say 0.1.0.beta.2
156
+ # confirm VERSION and CHANGELOG still say 0.1.0.beta.3
157
157
  gem build auditea.gemspec
158
158
  # inspect package once more
159
- gem push auditea-0.1.0.beta.2.gem
159
+ gem push auditea-0.1.0.beta.3.gem
160
160
  ```
161
161
 
162
162
  Then mark CHANGELOG released with the publication date in a follow-up commit.
@@ -166,9 +166,9 @@ Then mark CHANGELOG released with the publication date in a follow-up commit.
166
166
  ## Post-publication verification
167
167
 
168
168
  ```bash
169
- gem install auditea -v 0.1.0.beta.2 --pre
169
+ gem install auditea -v 0.1.0.beta.3 --pre
170
170
  ruby -rauditea -e 'puts Auditea::VERSION'
171
- # expect: 0.1.0.beta.2
171
+ # expect: 0.1.0.beta.3
172
172
  ```
173
173
 
174
174
  Prefer installing the RubyGems artifact (not a git branch) into the controlled TEST app.
@@ -23,10 +23,13 @@ module Auditea
23
23
  end
24
24
 
25
25
  ::ActiveJob::Base.around_perform do |job, block|
26
+ Auditea::Instrumentation::Exception::Deduper.clear!
26
27
  block.call
27
28
  rescue StandardError => error
28
29
  Auditea::Instrumentation::ActiveJob.report_unhandled(job, error)
29
30
  raise
31
+ ensure
32
+ Auditea::Instrumentation::Exception::Deduper.clear!
30
33
  end
31
34
  end
32
35
 
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Auditea
4
+ module Instrumentation
5
+ # Rails / ActiveSupport::ErrorReporter subscriber (public API only).
6
+ # Installed when Rails.error / ActiveSupport.error_reporter#subscribe exists.
7
+ class ErrorSubscriber
8
+ ACTIVE_JOB_SOURCE = /active_job/i
9
+
10
+ class << self
11
+ def install!
12
+ return if @installed
13
+
14
+ reporter = resolve_reporter
15
+ return unless reporter.respond_to?(:subscribe)
16
+
17
+ @subscriber = new
18
+ reporter.subscribe(@subscriber)
19
+ @installed = true
20
+ true
21
+ rescue StandardError => error
22
+ Auditea.handle_error(error)
23
+ false
24
+ end
25
+
26
+ def installed?
27
+ !!@installed
28
+ end
29
+
30
+ def reset_for_tests!
31
+ reporter = resolve_reporter
32
+ reporter.unsubscribe(@subscriber) if @subscriber && reporter.respond_to?(:unsubscribe)
33
+ @subscriber = nil
34
+ @installed = false
35
+ end
36
+
37
+ def resolve_reporter
38
+ if defined?(::Rails) && ::Rails.respond_to?(:error)
39
+ ::Rails.error
40
+ elsif defined?(::ActiveSupport) && ::ActiveSupport.respond_to?(:error_reporter)
41
+ ::ActiveSupport.error_reporter
42
+ end
43
+ end
44
+ end
45
+
46
+ # ActiveSupport::ErrorReporter subscriber contract — must never raise.
47
+ def report(error, handled:, severity: :error, context: {}, source: nil)
48
+ return unless Auditea.configuration.capture_exceptions
49
+ return if skip_active_job?(source)
50
+
51
+ structural = StructuralRequest.from_reporter_context(context)
52
+
53
+ Auditea::Instrumentation::Exception.capture(
54
+ error,
55
+ handled: handled,
56
+ mechanism: mechanism_for(source),
57
+ severity: map_severity(severity, handled),
58
+ evidence: structural["evidence"],
59
+ context: structural["context"],
60
+ metadata: {
61
+ "sdk_event_kind" => "auto_observed"
62
+ }
63
+ )
64
+ rescue StandardError => error
65
+ Auditea.handle_error(error)
66
+ end
67
+
68
+ private
69
+
70
+ def skip_active_job?(source)
71
+ return false unless source.to_s.match?(ACTIVE_JOB_SOURCE)
72
+ return true if Auditea.configuration.capture_active_job
73
+
74
+ false
75
+ end
76
+
77
+ def mechanism_for(source)
78
+ value = source.to_s.strip
79
+ value.empty? ? "rails.error_reporter" : value
80
+ end
81
+
82
+ def map_severity(severity, handled)
83
+ case severity.to_s
84
+ when "warning" then "warning"
85
+ when "info" then "info"
86
+ when "error" then "error"
87
+ else
88
+ handled ? "warning" : "error"
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -2,28 +2,56 @@
2
2
 
3
3
  module Auditea
4
4
  module Instrumentation
5
+ # Structured exception → `exception.raised` evidence.
6
+ #
7
+ # Capture paths (fail-open at the Auditea boundary):
8
+ # - explicit Auditea.capture_exception
9
+ # - Rails ErrorReporter subscriber (preferred when available)
10
+ # - process_action.action_controller fallback when ErrorReporter is absent
5
11
  module Exception
12
+ MAX_FRAMES = 40
13
+ MESSAGE_MAX = 500
14
+
6
15
  module_function
7
16
 
17
+ def capture(error, handled: false, mechanism: nil, severity: "error",
18
+ context: nil, metadata: nil, evidence: nil, claim: true)
19
+ return { ok: false, skipped: true, reason: "disabled" } unless Auditea.configuration.capture_exceptions
20
+ return { ok: false, skipped: true, reason: "not_an_exception" } unless error.is_a?(::Exception)
21
+ return { ok: false, skipped: true, reason: "duplicate" } if claim && !Deduper.claim!(error)
22
+
23
+ merged_evidence = merge_evidence(error, handled: handled, mechanism: mechanism, evidence: evidence)
24
+ meta = { "sdk_event_kind" => "auto_observed" }
25
+ meta.merge!(stringify_keys(metadata)) if metadata
26
+
27
+ Auditea.capture(
28
+ "exception.raised",
29
+ category: "error",
30
+ severity: severity || "error",
31
+ evidence: merged_evidence,
32
+ context: context,
33
+ metadata: meta
34
+ )
35
+ rescue StandardError => error
36
+ Auditea.handle_error(error)
37
+ end
38
+
8
39
  def capture_from_notification(event)
40
+ # Prefer Rails ErrorReporter when installed so handled/source semantics stay
41
+ # consistent and ActionDispatch does not double-emit with process_action.
42
+ return if ErrorSubscriber.installed?
43
+
9
44
  payload = event.payload || {}
10
45
  exception = payload[:exception_object]
11
46
  exception ||= build_from_array(payload[:exception])
12
47
  return unless exception
13
48
 
14
- frames = safe_frames(exception)
15
49
  path = payload[:path].to_s.split("?", 2).first
16
-
17
- Auditea.capture(
18
- "exception.raised",
19
- category: "error",
20
- severity: "error",
50
+ capture(
51
+ exception,
52
+ handled: false,
53
+ mechanism: "action_controller.process_action",
21
54
  evidence: {
22
- "exception" => {
23
- "type" => exception.class.name,
24
- "message" => Sanitizer.truncate_string(exception.message.to_s, 500),
25
- "frames" => frames
26
- }.compact,
27
55
  "http" => {
28
56
  "method" => payload[:method],
29
57
  "path" => path,
@@ -33,15 +61,46 @@ module Auditea
33
61
  "controller" => payload[:controller],
34
62
  "action" => payload[:action]
35
63
  }.compact
36
- },
37
- metadata: {
38
- "sdk_event_kind" => "auto_observed"
39
64
  }
40
65
  )
41
66
  rescue StandardError => error
42
67
  Auditea.handle_error(error)
43
68
  end
44
69
 
70
+ def merge_evidence(error, handled:, mechanism:, evidence:)
71
+ base = stringify_keys(evidence || {})
72
+ exception = base["exception"].is_a?(Hash) ? base["exception"].dup : {}
73
+ exception["type"] ||= error.class.name
74
+ exception["message"] ||= Sanitizer.truncate_string(error.message.to_s, MESSAGE_MAX)
75
+ exception["frames"] ||= safe_frames(error)
76
+ exception["handled"] = handled ? true : false unless exception.key?("handled")
77
+ exception["mechanism"] = mechanism.to_s if mechanism && !exception.key?("mechanism")
78
+
79
+ http = base["http"].is_a?(Hash) ? base["http"].dup : {}
80
+ enrich_http_from_context!(http)
81
+
82
+ rails = base["rails"].is_a?(Hash) ? base["rails"].dup : {}
83
+
84
+ {
85
+ "exception" => exception.compact,
86
+ "http" => compact_hash(http),
87
+ "rails" => compact_hash(rails)
88
+ }.compact.merge(base.except("exception", "http", "rails"))
89
+ end
90
+
91
+ def compact_hash(hash)
92
+ cleaned = hash.compact
93
+ cleaned.empty? ? nil : cleaned
94
+ end
95
+
96
+ def enrich_http_from_context!(http)
97
+ ctx = CurrentContext.to_h
98
+ http_ctx = ctx["http"].is_a?(Hash) ? ctx["http"] : {}
99
+ http["method"] ||= http_ctx["method"]
100
+ path = http_ctx["path"].to_s.split("?", 2).first
101
+ http["path"] ||= path unless path.nil? || path.empty?
102
+ end
103
+
45
104
  def build_from_array(pair)
46
105
  return nil unless pair.is_a?(Array) && pair.size >= 2
47
106
 
@@ -56,9 +115,12 @@ module Auditea
56
115
  def safe_frames(exception)
57
116
  return [] unless exception.respond_to?(:backtrace) && exception.backtrace
58
117
 
59
- exception.backtrace.first(20).map do |line|
118
+ exception.backtrace.first(MAX_FRAMES).map do |line|
60
119
  file, linenum, method_name = parse_frame(line)
61
- { "file" => file, "line" => linenum, "method" => method_name }.compact
120
+ frame = { "file" => file, "line" => linenum, "method" => method_name }.compact
121
+ in_app = classify_in_app(file)
122
+ frame["in_app"] = in_app unless in_app.nil?
123
+ frame
62
124
  end
63
125
  end
64
126
 
@@ -70,6 +132,62 @@ module Auditea
70
132
  [line, nil, nil]
71
133
  end
72
134
  end
135
+
136
+ # Deterministic app-vs-framework marking. Returns nil when unknown (omit key).
137
+ def classify_in_app(file)
138
+ path = file.to_s
139
+ return nil if path.empty?
140
+
141
+ return true if path.start_with?("app/") || path.include?("/app/")
142
+ return false if path.include?("/gems/") || path.include?("/ruby/") ||
143
+ path.include?("/vendor/bundle/") || path.include?("/lib/ruby/")
144
+
145
+ if defined?(::Rails) && ::Rails.respond_to?(:root) && ::Rails.root
146
+ root = ::Rails.root.to_s
147
+ if path.start_with?(root)
148
+ return false if path.start_with?("#{root}/vendor")
149
+
150
+ return true
151
+ end
152
+ end
153
+
154
+ nil
155
+ end
156
+
157
+ def stringify_keys(value)
158
+ EventBuilder.stringify_keys(value)
159
+ end
160
+
161
+ # Identity-safe claim set: WeakMap keys are the exception objects themselves.
162
+ # GC removes entries when exceptions are collected, so recycled object_ids cannot
163
+ # falsely suppress a distinct raise. Still cleared at request/job boundaries.
164
+ module Deduper
165
+ class << self
166
+ def claim!(error)
167
+ map = store
168
+ return false if map.key?(error)
169
+
170
+ map[error] = true
171
+ true
172
+ end
173
+
174
+ def claimed?(error)
175
+ store.key?(error)
176
+ rescue StandardError
177
+ false
178
+ end
179
+
180
+ def clear!
181
+ Thread.current[:auditea_exception_claims] = ObjectSpace::WeakMap.new
182
+ end
183
+
184
+ private
185
+
186
+ def store
187
+ Thread.current[:auditea_exception_claims] ||= ObjectSpace::WeakMap.new
188
+ end
189
+ end
190
+ end
73
191
  end
74
192
  end
75
193
  end
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Auditea
4
+ module Instrumentation
5
+ # Derive scalar request/controller fields from Rails ErrorReporter context.
6
+ # Never retains controller/request objects; never reads params/body/cookies/session.
7
+ module StructuralRequest
8
+ module_function
9
+
10
+ def from_reporter_context(context)
11
+ hash = context.is_a?(Hash) ? context : {}
12
+ controller = hash[:controller] || hash["controller"]
13
+
14
+ rails = {}
15
+ http = {}
16
+ event_context = {}
17
+
18
+ if controller && !controller.is_a?(String) && controller.respond_to?(:class)
19
+ rails["controller"] = controller.class.name
20
+ if controller.respond_to?(:action_name)
21
+ action = controller.action_name
22
+ rails["action"] = action.to_s if present?(action)
23
+ end
24
+
25
+ request = safe_request(controller)
26
+ if request
27
+ method = safe_request_method(request)
28
+ http["method"] = method if present?(method)
29
+
30
+ path = safe_path(request)
31
+ http["path"] = path if present?(path)
32
+
33
+ request_id = safe_request_id(request)
34
+ event_context["request_id"] = request_id if present?(request_id)
35
+ end
36
+
37
+ status = safe_status(controller)
38
+ http["status"] = status unless status.nil?
39
+ end
40
+
41
+ {
42
+ "evidence" => {
43
+ "rails" => compact_hash(rails),
44
+ "http" => compact_hash(http)
45
+ }.compact,
46
+ "context" => compact_hash(event_context)
47
+ }
48
+ rescue StandardError
49
+ { "evidence" => {}, "context" => {} }
50
+ end
51
+
52
+ def safe_request(controller)
53
+ return nil unless controller.respond_to?(:request)
54
+
55
+ request = controller.request
56
+ return nil if request.nil?
57
+ return nil unless request.respond_to?(:request_method) || request.respond_to?(:path)
58
+
59
+ request
60
+ rescue StandardError
61
+ nil
62
+ end
63
+
64
+ def safe_request_method(request)
65
+ return request.request_method.to_s if request.respond_to?(:request_method)
66
+
67
+ nil
68
+ rescue StandardError
69
+ nil
70
+ end
71
+
72
+ def safe_path(request)
73
+ raw =
74
+ if request.respond_to?(:path)
75
+ request.path
76
+ elsif request.respond_to?(:fullpath)
77
+ request.fullpath
78
+ end
79
+ return nil unless present?(raw)
80
+
81
+ raw.to_s.split("?", 2).first
82
+ rescue StandardError
83
+ nil
84
+ end
85
+
86
+ def safe_request_id(request)
87
+ if request.respond_to?(:request_id)
88
+ value = request.request_id
89
+ return value.to_s if present?(value)
90
+ end
91
+
92
+ if request.respond_to?(:env)
93
+ env = request.env
94
+ if env.is_a?(Hash)
95
+ value = env["action_dispatch.request_id"] || env["HTTP_X_REQUEST_ID"]
96
+ return value.to_s if present?(value)
97
+ end
98
+ end
99
+
100
+ nil
101
+ rescue StandardError
102
+ nil
103
+ end
104
+
105
+ def safe_status(controller)
106
+ return nil unless controller.respond_to?(:response)
107
+
108
+ response = controller.response
109
+ return nil unless response
110
+ return nil unless response.respond_to?(:status)
111
+
112
+ status = response.status
113
+ return nil unless status.is_a?(Integer)
114
+ return nil unless status.between?(100, 599)
115
+
116
+ # Unhandled failures often still have the default 200 before rendering;
117
+ # only keep status when the response looks finalized or already erroneous.
118
+ committed = response.respond_to?(:committed?) && response.committed?
119
+ return status if committed || status >= 400
120
+
121
+ nil
122
+ rescue StandardError
123
+ nil
124
+ end
125
+
126
+ def compact_hash(hash)
127
+ cleaned = hash.compact
128
+ cleaned.empty? ? nil : cleaned
129
+ end
130
+
131
+ def present?(value)
132
+ !(value.nil? || (value.respond_to?(:empty?) && value.empty?))
133
+ end
134
+ end
135
+ end
136
+ end
@@ -12,6 +12,7 @@ module Auditea
12
12
 
13
13
  def call(env)
14
14
  CurrentContext.clear!
15
+ Instrumentation::Exception::Deduper.clear!
15
16
  request = ::Rack::Request.new(env)
16
17
  CurrentContext.set(
17
18
  "request_id" => env["action_dispatch.request_id"] || env["HTTP_X_REQUEST_ID"],
@@ -24,6 +25,7 @@ module Auditea
24
25
  ensure
25
26
  opportunistically_refresh_inventory
26
27
  CurrentContext.clear!
28
+ Instrumentation::Exception::Deduper.clear!
27
29
  end
28
30
 
29
31
  private
@@ -18,6 +18,7 @@ module Auditea
18
18
  initializer "auditea.instrumentation" do
19
19
  Auditea::Instrumentation::Request.install!
20
20
  Auditea::Instrumentation::ActiveJob.install!
21
+ Auditea::Instrumentation::ErrorSubscriber.install!
21
22
  end
22
23
 
23
24
  config.after_initialize do
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Auditea
4
- VERSION = "0.1.0.beta.2"
4
+ VERSION = "0.1.0.beta.3"
5
5
  end
data/lib/auditea.rb CHANGED
@@ -32,6 +32,8 @@ require_relative "auditea/inventory"
32
32
  require_relative "auditea/host_inventory"
33
33
  require_relative "auditea/instrumentation/request"
34
34
  require_relative "auditea/instrumentation/exception"
35
+ require_relative "auditea/instrumentation/structural_request"
36
+ require_relative "auditea/instrumentation/error_subscriber"
35
37
  require_relative "auditea/instrumentation/logger_adapter"
36
38
  require_relative "auditea/instrumentation/active_job"
37
39
  require_relative "auditea/middleware/context"
@@ -94,6 +96,25 @@ module Auditea
94
96
  # Alias kept for legacy familiarity (KEEP CONCEPT).
95
97
  alias track capture
96
98
 
99
+ # First-class exception capture → action `exception.raised` / category `error`.
100
+ # Rebuilds structured exception evidence (type, message, frames, handled, mechanism).
101
+ # Uses CurrentContext when present; sanitizes like normal events; fail-open by default.
102
+ def capture_exception(error, handled: false, mechanism: nil, severity: "error",
103
+ context: nil, metadata: nil, evidence: nil)
104
+ Instrumentation::Exception.capture(
105
+ error,
106
+ handled: handled,
107
+ mechanism: mechanism || "auditea.capture_exception",
108
+ severity: severity,
109
+ context: context,
110
+ metadata: (metadata || {}).merge("sdk_event_kind" => "explicit"),
111
+ evidence: evidence,
112
+ claim: true
113
+ )
114
+ rescue StandardError => error
115
+ handle_error(error)
116
+ end
117
+
97
118
  # Structured application log as a first-class event (not Rails.logger auto-forward).
98
119
  def log(level, message, context: {}, metadata: {})
99
120
  Log.record(level, message, context: context, metadata: metadata)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: auditea
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0.beta.2
4
+ version: 0.1.0.beta.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - AudiTea
@@ -35,9 +35,11 @@ files:
35
35
  - lib/auditea/host_inventory/collector.rb
36
36
  - lib/auditea/host_inventory/emitter.rb
37
37
  - lib/auditea/instrumentation/active_job.rb
38
+ - lib/auditea/instrumentation/error_subscriber.rb
38
39
  - lib/auditea/instrumentation/exception.rb
39
40
  - lib/auditea/instrumentation/logger_adapter.rb
40
41
  - lib/auditea/instrumentation/request.rb
42
+ - lib/auditea/instrumentation/structural_request.rb
41
43
  - lib/auditea/inventory.rb
42
44
  - lib/auditea/inventory/background_refresh.rb
43
45
  - lib/auditea/inventory/collector.rb