julewire-active_job 1.0.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 +7 -0
- data/CHANGELOG.md +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +45 -0
- data/docs/advanced-configuration.md +12 -0
- data/docs/boundaries.md +13 -0
- data/docs/configuration.md +27 -0
- data/docs/continuations.md +21 -0
- data/docs/propagation.md +17 -0
- data/julewire-active_job.gemspec +41 -0
- data/lib/julewire/active_job/configuration.rb +24 -0
- data/lib/julewire/active_job/installer.rb +88 -0
- data/lib/julewire/active_job/job_attributes.rb +50 -0
- data/lib/julewire/active_job/job_execution.rb +118 -0
- data/lib/julewire/active_job/job_serialization.rb +72 -0
- data/lib/julewire/active_job/log_subscriber_silencer.rb +24 -0
- data/lib/julewire/active_job/railtie.rb +23 -0
- data/lib/julewire/active_job/subscribers/event.rb +185 -0
- data/lib/julewire/active_job/version.rb +7 -0
- data/lib/julewire/active_job.rb +38 -0
- data/lib/julewire-active_job.rb +3 -0
- metadata +122 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: a82446be20c0ec6d8ac39b204411837b4759cb57f94ddb9b2138173d16b5fc3d
|
|
4
|
+
data.tar.gz: 949f9196d2e54d90248b66bce79f34d20bd4be6fbae26667febe548527829cd1
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: f0acd8d095c2b529f8bb865b3757bafa62a97e7997e5009d8a8960bd2b8c96e4ffbb2c3e7eb6345318f2150b0113f28e4236212571c0251eab74a89075dad694
|
|
7
|
+
data.tar.gz: fcf079813dfde620139088138f2705db70ad6a0ab62aa8f2655b977a3b9ccbcd04c27790d9582aabc6928f211fc2d182dfb488261d99d3e006fa5164571d70d1
|
data/CHANGELOG.md
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Alexander Grebennik
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Julewire Active Job
|
|
2
|
+
|
|
3
|
+
Active Job integration for Julewire.
|
|
4
|
+
|
|
5
|
+
It records job execution summaries, Active Job structured events, and Julewire
|
|
6
|
+
propagation carriers in serialized job data.
|
|
7
|
+
|
|
8
|
+
## Quickstart
|
|
9
|
+
|
|
10
|
+
```ruby
|
|
11
|
+
gem "julewire-active_job"
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
In Rails, the Railtie installs the integration. Defaults are on:
|
|
15
|
+
|
|
16
|
+
```ruby
|
|
17
|
+
config.julewire_active_job.execution = true
|
|
18
|
+
config.julewire_active_job.structured_events = true
|
|
19
|
+
config.julewire_active_job.propagation = true
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Outside Rails, install it after Active Job is loaded:
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
Julewire::ActiveJob.install!(base: ActiveJob::Base)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Default behavior:
|
|
29
|
+
|
|
30
|
+
- job execution scopes emit `job.completed` summaries
|
|
31
|
+
- Active Job structured events become point records
|
|
32
|
+
- carriers restore upstream Julewire context before `perform`
|
|
33
|
+
- Active Job default text subscriber output is detached
|
|
34
|
+
|
|
35
|
+
Generic job metadata also appears in the record's `neutral` section as `job.*`
|
|
36
|
+
formatter-coordination fields. Full Active Job detail lives under
|
|
37
|
+
`attributes.active_job`. Propagated Julewire context stays separate and small.
|
|
38
|
+
|
|
39
|
+
## Docs
|
|
40
|
+
|
|
41
|
+
- [Configuration](docs/configuration.md)
|
|
42
|
+
- [Advanced Configuration](docs/advanced-configuration.md)
|
|
43
|
+
- [Propagation](docs/propagation.md)
|
|
44
|
+
- [Continuations](docs/continuations.md)
|
|
45
|
+
- [Boundaries](docs/boundaries.md)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Advanced Configuration
|
|
2
|
+
|
|
3
|
+
These options are stable knobs for unusual job serialization or event naming
|
|
4
|
+
needs.
|
|
5
|
+
|
|
6
|
+
| Option | Default | Purpose |
|
|
7
|
+
| --- | --- | --- |
|
|
8
|
+
| `carrier_key` | `Julewire::Core::Propagation::Carrier::DEFAULT_KEY` | Carrier key inside propagation envelopes. |
|
|
9
|
+
| `serialized_carrier_key` | `"julewire.carrier"` | Key used in serialized Active Job data. |
|
|
10
|
+
| `summary_event` | `"job.completed"` | Event name for job summaries. |
|
|
11
|
+
| `summary_severity` | `:info` | Severity for successful job summaries. |
|
|
12
|
+
| `event_prefixes` | `["active_job."]` | Structured event prefixes to emit. |
|
data/docs/boundaries.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Boundaries
|
|
2
|
+
|
|
3
|
+
This gem instruments Active Job.
|
|
4
|
+
|
|
5
|
+
It does not own:
|
|
6
|
+
|
|
7
|
+
- queue backend transport
|
|
8
|
+
- process supervisors
|
|
9
|
+
- queue-backend lifecycle hooks
|
|
10
|
+
- durable job delivery
|
|
11
|
+
- retry policy
|
|
12
|
+
|
|
13
|
+
Application logger calls made inside jobs remain normal Julewire records.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
Rails apps configure this gem through `config.julewire_active_job`. Non-Rails
|
|
4
|
+
apps pass a `Configuration` instance to `install!`.
|
|
5
|
+
|
|
6
|
+
## Default Path
|
|
7
|
+
|
|
8
|
+
| Option | Default | Purpose |
|
|
9
|
+
| --- | --- | --- |
|
|
10
|
+
| `enabled` | `true` | Install the integration. |
|
|
11
|
+
| `execution` | `true` | Wrap job perform calls in Julewire executions. |
|
|
12
|
+
| `structured_events` | `true` | Emit Active Job structured events. |
|
|
13
|
+
| `propagation` | `true` | Store and restore Julewire carriers in job data. |
|
|
14
|
+
| `source` | `"active_job"` | Source value on Active Job records. |
|
|
15
|
+
|
|
16
|
+
## Common Knobs
|
|
17
|
+
|
|
18
|
+
| Option | Default | Purpose |
|
|
19
|
+
| --- | --- | --- |
|
|
20
|
+
| `carrier_max_bytes` | `nil` | Omit oversized carriers from serialized jobs. |
|
|
21
|
+
| `silence_log_subscriber` | `true` | Detach Active Job default text subscriber output. |
|
|
22
|
+
|
|
23
|
+
Example:
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
config.julewire_active_job.carrier_max_bytes = 16_384
|
|
27
|
+
```
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Continuations
|
|
2
|
+
|
|
3
|
+
Rails continuation events run inside the Julewire job execution scope when the
|
|
4
|
+
execution callback is installed.
|
|
5
|
+
|
|
6
|
+
The job summary records these values under `attributes.active_job`:
|
|
7
|
+
|
|
8
|
+
- `continuation_steps_started`
|
|
9
|
+
- `continuation_steps_completed`
|
|
10
|
+
- `continuation_steps_interrupted`
|
|
11
|
+
- `continuation_steps_failed`
|
|
12
|
+
- `continuation_steps_skipped`
|
|
13
|
+
- `continuation_interruptions`
|
|
14
|
+
- `continuation_resumptions`
|
|
15
|
+
- `continuation_description`
|
|
16
|
+
- `continuation_interrupt_reason`
|
|
17
|
+
- `continuation_last_step`
|
|
18
|
+
- `continuation_last_step_cursor`
|
|
19
|
+
- `continuation_last_step_resumed`
|
|
20
|
+
- `continuation_last_step_state`
|
|
21
|
+
- `continuation_status`
|
data/docs/propagation.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Propagation
|
|
2
|
+
|
|
3
|
+
When a job is serialized, the gem stores a Julewire carrier under
|
|
4
|
+
`julewire.carrier`. When the job performs, the carrier is restored before the
|
|
5
|
+
job execution scope starts.
|
|
6
|
+
|
|
7
|
+
That lets upstream context flow into the job without emitting propagation-only
|
|
8
|
+
data by default.
|
|
9
|
+
|
|
10
|
+
Set `carrier_max_bytes` to omit oversized carriers from serialized job payloads.
|
|
11
|
+
When omitted, the job still runs normally; it starts without upstream Julewire
|
|
12
|
+
context.
|
|
13
|
+
|
|
14
|
+
Generic job metadata such as class, id, queue, priority, execution count,
|
|
15
|
+
timestamps, and status is emitted in the record's `neutral` section as `job.*`
|
|
16
|
+
formatter-coordination fields. Full Active Job metadata, including framework-
|
|
17
|
+
specific status and exception fields, is emitted under `attributes.active_job`.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "lib/julewire/active_job/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "julewire-active_job"
|
|
7
|
+
spec.version = Julewire::ActiveJob::VERSION
|
|
8
|
+
spec.authors = ["Alexander Grebennik"]
|
|
9
|
+
spec.email = ["slbug@users.noreply.github.com", "sl.bug.sl@gmail.com"]
|
|
10
|
+
|
|
11
|
+
spec.summary = "Active Job integration for Julewire structured logging."
|
|
12
|
+
spec.description =
|
|
13
|
+
"Execution-scoped Active Job instrumentation, structured event capture, " \
|
|
14
|
+
"and propagation carrier support for Julewire."
|
|
15
|
+
spec.homepage = "https://github.com/slbug/julewire"
|
|
16
|
+
spec.license = "MIT"
|
|
17
|
+
spec.required_ruby_version = ">= 3.4"
|
|
18
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
19
|
+
spec.metadata["source_code_uri"] = "https://github.com/slbug/julewire/tree/main/gems/active_job"
|
|
20
|
+
spec.metadata["changelog_uri"] = "https://github.com/slbug/julewire/blob/main/gems/active_job/CHANGELOG.md"
|
|
21
|
+
|
|
22
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
|
23
|
+
|
|
24
|
+
spec.files = Dir.chdir(__dir__) do
|
|
25
|
+
Dir[
|
|
26
|
+
"CHANGELOG.md",
|
|
27
|
+
"LICENSE.txt",
|
|
28
|
+
"README.md",
|
|
29
|
+
"docs/**/*.md",
|
|
30
|
+
"julewire-active_job.gemspec",
|
|
31
|
+
"lib/**/*.rb"
|
|
32
|
+
]
|
|
33
|
+
end
|
|
34
|
+
spec.executables = []
|
|
35
|
+
spec.require_paths = ["lib"]
|
|
36
|
+
|
|
37
|
+
spec.add_dependency "activejob", ">= 8.1"
|
|
38
|
+
spec.add_dependency "julewire-core", ">= 1.0"
|
|
39
|
+
spec.add_dependency "julewire-rails_support", ">= 1.0"
|
|
40
|
+
spec.add_dependency "zeitwerk", ">= 2.8.1"
|
|
41
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Julewire
|
|
4
|
+
module ActiveJob
|
|
5
|
+
class Configuration
|
|
6
|
+
DEFAULT_SERIALIZED_CARRIER_KEY = "julewire.carrier"
|
|
7
|
+
|
|
8
|
+
include Julewire::Core::Integration::Settings
|
|
9
|
+
|
|
10
|
+
setting :enabled, default: true, predicate: true
|
|
11
|
+
setting :execution, default: true, predicate: true
|
|
12
|
+
setting :structured_events, default: true, predicate: true
|
|
13
|
+
setting :silence_log_subscriber, default: true, predicate: true
|
|
14
|
+
setting :propagation, default: true, predicate: true
|
|
15
|
+
setting :carrier_key, default: Julewire::Core::Propagation::Carrier::DEFAULT_KEY
|
|
16
|
+
setting :carrier_max_bytes, validate: byte_limit
|
|
17
|
+
setting :serialized_carrier_key, default: DEFAULT_SERIALIZED_CARRIER_KEY
|
|
18
|
+
setting :source, default: "active_job"
|
|
19
|
+
setting :summary_event, default: "job.completed"
|
|
20
|
+
setting :summary_severity, default: :info
|
|
21
|
+
setting :event_prefixes, default: ["active_job."]
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Julewire
|
|
4
|
+
module ActiveJob
|
|
5
|
+
module Installer
|
|
6
|
+
EXECUTION_INSTALL = Core::Integration::IvarState.new(:@julewire_active_job_execution)
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
def install!(base: nil, event_reporter: nil, configuration: Configuration.new)
|
|
10
|
+
return unless configuration.enabled?
|
|
11
|
+
|
|
12
|
+
Julewire::ActiveJob.config = configuration
|
|
13
|
+
base ||= active_job_base
|
|
14
|
+
raise Error, "ActiveJob::Base is not available" unless base
|
|
15
|
+
|
|
16
|
+
install_serialization(base, configuration)
|
|
17
|
+
install_execution_callback(base, configuration)
|
|
18
|
+
if configuration.structured_events?
|
|
19
|
+
Subscribers::Event.install!(configuration, event_reporter: event_reporter)
|
|
20
|
+
else
|
|
21
|
+
Subscribers::Event.reset!
|
|
22
|
+
end
|
|
23
|
+
LogSubscriberSilencer.silence! if configuration.silence_log_subscriber?
|
|
24
|
+
base
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def active_job_base
|
|
30
|
+
require "active_job/base"
|
|
31
|
+
::ActiveJob::Base
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def install_serialization(base, configuration)
|
|
35
|
+
install_serialization_configuration(base, configuration)
|
|
36
|
+
return if base < JobSerialization
|
|
37
|
+
|
|
38
|
+
base.prepend(JobSerialization)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def install_serialization_configuration(base, configuration)
|
|
42
|
+
if base.respond_to?(:class_attribute)
|
|
43
|
+
unless base.respond_to?(JobSerialization::CONFIGURATION_METHOD)
|
|
44
|
+
base.class_attribute(
|
|
45
|
+
JobSerialization::CONFIGURATION_METHOD,
|
|
46
|
+
instance_accessor: false,
|
|
47
|
+
instance_predicate: false
|
|
48
|
+
)
|
|
49
|
+
end
|
|
50
|
+
base.public_send("#{JobSerialization::CONFIGURATION_METHOD}=", configuration)
|
|
51
|
+
else
|
|
52
|
+
base.instance_variable_set(JobSerialization::CONFIGURATION_IVAR, configuration)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def install_execution_callback(base, configuration)
|
|
57
|
+
return unless configuration.execution?
|
|
58
|
+
|
|
59
|
+
installed = EXECUTION_INSTALL.fetch(base)
|
|
60
|
+
if installed
|
|
61
|
+
installed.configuration = configuration
|
|
62
|
+
return installed
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
callback = ExecutionCallback.new(configuration)
|
|
66
|
+
# Rails callbacks are easier to update in place than to remove safely.
|
|
67
|
+
base.around_perform do |job, block|
|
|
68
|
+
callback.call(job, &block)
|
|
69
|
+
end
|
|
70
|
+
EXECUTION_INSTALL.store(base, callback)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
class ExecutionCallback
|
|
75
|
+
def initialize(configuration)
|
|
76
|
+
@configuration = configuration
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
attr_writer :configuration
|
|
80
|
+
|
|
81
|
+
def call(job, &)
|
|
82
|
+
Julewire::ActiveJob::JobExecution.call(job, configuration: @configuration, &)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
private_constant :ExecutionCallback
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
|
|
4
|
+
module Julewire
|
|
5
|
+
module ActiveJob
|
|
6
|
+
module JobAttributes
|
|
7
|
+
JOB_NAME_KEYS = %i[job_class class_name job_name].freeze
|
|
8
|
+
JOB_ID_KEYS = %i[job_id id].freeze
|
|
9
|
+
JOB_PROVIDER_ID_KEYS = %i[provider_job_id].freeze
|
|
10
|
+
JOB_QUEUE_NAME_KEYS = %i[queue queue_name].freeze
|
|
11
|
+
JOB_PRIORITY_KEYS = %i[priority].freeze
|
|
12
|
+
JOB_EXECUTION_COUNT_KEYS = %i[executions].freeze
|
|
13
|
+
JOB_ENQUEUED_AT_KEYS = %i[enqueued_at].freeze
|
|
14
|
+
JOB_SCHEDULED_AT_KEYS = %i[scheduled_at].freeze
|
|
15
|
+
JOB_STATUS_KEYS = %i[status].freeze
|
|
16
|
+
private_constant :JOB_NAME_KEYS
|
|
17
|
+
private_constant :JOB_ID_KEYS
|
|
18
|
+
private_constant :JOB_PROVIDER_ID_KEYS
|
|
19
|
+
private_constant :JOB_QUEUE_NAME_KEYS
|
|
20
|
+
private_constant :JOB_PRIORITY_KEYS
|
|
21
|
+
private_constant :JOB_EXECUTION_COUNT_KEYS
|
|
22
|
+
private_constant :JOB_ENQUEUED_AT_KEYS
|
|
23
|
+
private_constant :JOB_SCHEDULED_AT_KEYS
|
|
24
|
+
private_constant :JOB_STATUS_KEYS
|
|
25
|
+
|
|
26
|
+
class << self
|
|
27
|
+
def call(fields)
|
|
28
|
+
Core::Fields::AttributeKeys.fields(
|
|
29
|
+
Core::Fields::AttributeKeys::JOB_SYSTEM => "active_job",
|
|
30
|
+
Core::Fields::AttributeKeys::JOB_NAME => first_value(fields, keys: JOB_NAME_KEYS),
|
|
31
|
+
Core::Fields::AttributeKeys::JOB_ID => first_value(fields, keys: JOB_ID_KEYS),
|
|
32
|
+
Core::Fields::AttributeKeys::JOB_PROVIDER_ID => first_value(fields, keys: JOB_PROVIDER_ID_KEYS),
|
|
33
|
+
Core::Fields::AttributeKeys::JOB_QUEUE_NAME => first_value(fields, keys: JOB_QUEUE_NAME_KEYS),
|
|
34
|
+
Core::Fields::AttributeKeys::JOB_PRIORITY => first_value(fields, keys: JOB_PRIORITY_KEYS),
|
|
35
|
+
Core::Fields::AttributeKeys::JOB_EXECUTION_COUNT => first_value(fields, keys: JOB_EXECUTION_COUNT_KEYS),
|
|
36
|
+
Core::Fields::AttributeKeys::JOB_ENQUEUED_AT => first_value(fields, keys: JOB_ENQUEUED_AT_KEYS),
|
|
37
|
+
Core::Fields::AttributeKeys::JOB_SCHEDULED_AT => first_value(fields, keys: JOB_SCHEDULED_AT_KEYS),
|
|
38
|
+
Core::Fields::AttributeKeys::JOB_STATUS => first_value(fields, keys: JOB_STATUS_KEYS)
|
|
39
|
+
)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def first_value(fields, keys:)
|
|
45
|
+
Core::Integration::Values::Read.first_value(fields, keys: keys)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "time"
|
|
4
|
+
|
|
5
|
+
module Julewire
|
|
6
|
+
module ActiveJob
|
|
7
|
+
module JobExecution
|
|
8
|
+
class << self
|
|
9
|
+
def call(job, configuration: Configuration.new, &)
|
|
10
|
+
carrier = carrier_for(job)
|
|
11
|
+
return perform_job(job, configuration, &) unless configuration.propagation?
|
|
12
|
+
|
|
13
|
+
Julewire::Core::Propagation::Carrier.restore(carrier, key: configuration.carrier_key) do
|
|
14
|
+
perform_job(job, configuration, &)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def perform_job(job, configuration, &)
|
|
21
|
+
fields = job_fields(job)
|
|
22
|
+
Core::Integration::Facade.with_execution(**execution_options(job, configuration, fields)) do
|
|
23
|
+
install_context(fields)
|
|
24
|
+
perform_with_summary(&)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def carrier_for(job)
|
|
29
|
+
job.instance_variable_get(CARRIER_IVAR) || {}
|
|
30
|
+
rescue StandardError
|
|
31
|
+
{}
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def execution_options(job, configuration, fields)
|
|
35
|
+
options = {
|
|
36
|
+
type: :job,
|
|
37
|
+
fields: { job_class: fields[:job_class] || job.class.name },
|
|
38
|
+
attributes: attributes_for(fields),
|
|
39
|
+
neutral: neutral_for(fields),
|
|
40
|
+
inherit_attributes: false,
|
|
41
|
+
summary_event: configuration.summary_event,
|
|
42
|
+
summary_severity: configuration.summary_severity,
|
|
43
|
+
summary_source: configuration.source
|
|
44
|
+
}
|
|
45
|
+
job_id = fields[:job_id]
|
|
46
|
+
options[:id] = job_id if job_id
|
|
47
|
+
options
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def install_context(fields)
|
|
51
|
+
job_id = fields[:job_id]
|
|
52
|
+
Core::Integration::Facade.add_context(job_id: job_id) if job_id
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def perform_with_summary
|
|
56
|
+
result = yield
|
|
57
|
+
add_summary(status: "ok")
|
|
58
|
+
result
|
|
59
|
+
rescue StandardError => e
|
|
60
|
+
add_summary(status: "error", exception_class: e.class.name)
|
|
61
|
+
raise
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def add_summary(fields)
|
|
65
|
+
add_summary_neutral(fields)
|
|
66
|
+
Core::Integration::Facade.add_summary_attributes(completion_attributes(fields))
|
|
67
|
+
rescue StandardError
|
|
68
|
+
nil
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def job_fields(job)
|
|
72
|
+
values = Core::Integration::Values::Shape
|
|
73
|
+
fields = { job_class: job.class.name }
|
|
74
|
+
values.append_field(fields, :job_id, job_id(job))
|
|
75
|
+
values.append_field(fields, :provider_job_id, safe_call(job, :provider_job_id))
|
|
76
|
+
values.append_field(fields, :queue, safe_call(job, :queue_name))
|
|
77
|
+
values.append_field(fields, :priority, safe_call(job, :priority))
|
|
78
|
+
values.append_field(fields, :executions, safe_call(job, :executions))
|
|
79
|
+
values.append_field(
|
|
80
|
+
fields,
|
|
81
|
+
:enqueued_at,
|
|
82
|
+
values.timestamp(safe_call(job, :enqueued_at))
|
|
83
|
+
)
|
|
84
|
+
values.append_field(
|
|
85
|
+
fields,
|
|
86
|
+
:scheduled_at,
|
|
87
|
+
values.timestamp(safe_call(job, :scheduled_at))
|
|
88
|
+
)
|
|
89
|
+
fields
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def job_id(job)
|
|
93
|
+
safe_call(job, :job_id)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def attributes_for(fields)
|
|
97
|
+
{ active_job: fields }
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def neutral_for(fields)
|
|
101
|
+
JobAttributes.call(fields)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def add_summary_neutral(fields)
|
|
105
|
+
Core::Integration::Facade.add_summary_neutral(JobAttributes.call(fields))
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def completion_attributes(fields)
|
|
109
|
+
{ active_job: fields }
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def safe_call(object, method_name)
|
|
113
|
+
Core::Integration::Values::Read.value(object, method_name)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Julewire
|
|
4
|
+
module ActiveJob
|
|
5
|
+
module JobSerialization
|
|
6
|
+
CONFIGURATION_IVAR = :@julewire_active_job_configuration
|
|
7
|
+
CONFIGURATION_METHOD = :julewire_active_job_configuration
|
|
8
|
+
|
|
9
|
+
def serialize
|
|
10
|
+
super.tap do |job_data|
|
|
11
|
+
inject_julewire_carrier(job_data)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def deserialize(job_data)
|
|
16
|
+
extract_julewire_carrier(job_data)
|
|
17
|
+
super
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def inject_julewire_carrier(job_data)
|
|
23
|
+
configuration = julewire_active_job_configuration
|
|
24
|
+
return unless configuration.propagation?
|
|
25
|
+
|
|
26
|
+
carrier = Julewire::Core::Propagation::Carrier.inject(
|
|
27
|
+
{},
|
|
28
|
+
key: configuration.carrier_key,
|
|
29
|
+
max_bytes: configuration.carrier_max_bytes
|
|
30
|
+
)
|
|
31
|
+
return unless carrier
|
|
32
|
+
|
|
33
|
+
value = carrier[configuration.carrier_key.to_s]
|
|
34
|
+
job_data[configuration.serialized_carrier_key] = value if value
|
|
35
|
+
IntegrationHealth.record_success(action: :carrier_inject, component: :job_serialization)
|
|
36
|
+
rescue StandardError => e
|
|
37
|
+
IntegrationHealth.record_failure(e, action: :carrier_inject, component: :job_serialization)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def extract_julewire_carrier(job_data)
|
|
41
|
+
configuration = julewire_active_job_configuration
|
|
42
|
+
unless configuration.propagation?
|
|
43
|
+
instance_variable_set(CARRIER_IVAR, {})
|
|
44
|
+
return
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
value = job_data[configuration.serialized_carrier_key]
|
|
48
|
+
instance_variable_set(CARRIER_IVAR, value ? { configuration.carrier_key => value } : {})
|
|
49
|
+
IntegrationHealth.record_success(action: :carrier_extract, component: :job_serialization)
|
|
50
|
+
rescue StandardError => e
|
|
51
|
+
IntegrationHealth.record_failure(e, action: :carrier_extract, component: :job_serialization)
|
|
52
|
+
instance_variable_set(CARRIER_IVAR, {})
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def julewire_active_job_configuration
|
|
56
|
+
if self.class.respond_to?(CONFIGURATION_METHOD)
|
|
57
|
+
configuration = self.class.public_send(CONFIGURATION_METHOD)
|
|
58
|
+
return configuration if configuration
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
self.class.ancestors.each do |ancestor|
|
|
62
|
+
next unless ancestor.instance_variable_defined?(CONFIGURATION_IVAR)
|
|
63
|
+
|
|
64
|
+
return ancestor.instance_variable_get(CONFIGURATION_IVAR)
|
|
65
|
+
end
|
|
66
|
+
Julewire::ActiveJob.config
|
|
67
|
+
rescue StandardError
|
|
68
|
+
Julewire::ActiveJob.config
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Julewire
|
|
4
|
+
module ActiveJob
|
|
5
|
+
module LogSubscriberSilencer
|
|
6
|
+
class << self
|
|
7
|
+
def silence!
|
|
8
|
+
Core::Integration::Lifecycle.require_optional("active_job/log_subscriber")
|
|
9
|
+
subscriber_class = active_job_log_subscriber
|
|
10
|
+
return unless subscriber_class
|
|
11
|
+
|
|
12
|
+
subscriber_class.detach_from(:active_job) if subscriber_class.respond_to?(:detach_from)
|
|
13
|
+
Julewire::RailsSupport::EventReporter.unsubscribe_log_subscriber(subscriber_class)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def active_job_log_subscriber
|
|
19
|
+
::ActiveJob::LogSubscriber if defined?(::ActiveJob::LogSubscriber)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Julewire
|
|
4
|
+
module ActiveJob
|
|
5
|
+
class Railtie < ::Rails::Railtie
|
|
6
|
+
config.julewire_active_job = Julewire::ActiveJob.config
|
|
7
|
+
|
|
8
|
+
initializer "julewire.active_job" do |app|
|
|
9
|
+
Julewire::ActiveJob::Railtie.install_active_job!(app.config.julewire_active_job)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class << self
|
|
13
|
+
def install_active_job!(settings)
|
|
14
|
+
return unless settings.enabled?
|
|
15
|
+
|
|
16
|
+
ActiveSupport.on_load(:active_job) do
|
|
17
|
+
Julewire::ActiveJob.install!(base: self, configuration: settings)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Julewire
|
|
4
|
+
module ActiveJob
|
|
5
|
+
module Subscribers
|
|
6
|
+
class Event
|
|
7
|
+
include Core::Integration::EventSubscriber
|
|
8
|
+
|
|
9
|
+
STRUCTURED_EVENT_FILE = "active_job/structured_event_subscriber"
|
|
10
|
+
ERROR_EVENTS = %w[
|
|
11
|
+
active_job.retry_stopped
|
|
12
|
+
active_job.discarded
|
|
13
|
+
].freeze
|
|
14
|
+
|
|
15
|
+
event_subscriber integration_health: IntegrationHealth, configuration_class: Configuration
|
|
16
|
+
|
|
17
|
+
class << self
|
|
18
|
+
include Core::Integration::SubscriberInstall
|
|
19
|
+
|
|
20
|
+
def install!(configuration, event_reporter: nil)
|
|
21
|
+
return reset! unless configuration.structured_events?
|
|
22
|
+
|
|
23
|
+
Core::Integration::Lifecycle.require_optional(STRUCTURED_EVENT_FILE)
|
|
24
|
+
reporter = event_reporter || Julewire::RailsSupport::EventReporter.default
|
|
25
|
+
return unless Julewire::RailsSupport::EventReporter.subscribable?(reporter)
|
|
26
|
+
|
|
27
|
+
install_subscriber(configuration, enabled: true) do |subscriber|
|
|
28
|
+
Julewire::RailsSupport::EventReporter.subscribe(reporter, subscriber) { subscriber.accept?(it) }
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def accept?(event)
|
|
34
|
+
return false unless @configuration.structured_events?
|
|
35
|
+
|
|
36
|
+
prefixes = @configuration.event_prefixes
|
|
37
|
+
return true if prefixes.nil?
|
|
38
|
+
|
|
39
|
+
name = event[:name].to_s
|
|
40
|
+
Array(prefixes).any? { name.start_with?(it.to_s) }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def emit_event(event)
|
|
46
|
+
name = event[:name].to_s
|
|
47
|
+
record = record_for(event, name)
|
|
48
|
+
enrich_continuation_summary(name, record.dig(:attributes, :active_job) || {})
|
|
49
|
+
Core::Integration::Facade.emit(record)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def record_for(event, name)
|
|
53
|
+
values = Core::Integration::Values::Shape
|
|
54
|
+
payload = values.payload_hash(event[:payload])
|
|
55
|
+
record = base_record(event, name, payload)
|
|
56
|
+
record[:error] = error_payload(payload) if exception_payload?(payload)
|
|
57
|
+
record
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def base_record(event, name, payload)
|
|
61
|
+
values = Core::Integration::Values::Shape
|
|
62
|
+
record = {
|
|
63
|
+
severity: severity_for(name, payload),
|
|
64
|
+
event: name,
|
|
65
|
+
logger: "ActiveJob.event",
|
|
66
|
+
context: values.hash_or_empty(event[:context]),
|
|
67
|
+
attributes: attributes_for(event, payload),
|
|
68
|
+
neutral: neutral_for(event, payload)
|
|
69
|
+
}
|
|
70
|
+
values.append_field(record, :timestamp, values.timestamp(event[:timestamp]))
|
|
71
|
+
values.append_field(record, :source, @configuration.source)
|
|
72
|
+
record
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def attributes_for(event, payload)
|
|
76
|
+
values = Core::Integration::Values::Shape
|
|
77
|
+
active_job = payload.empty? ? {} : payload
|
|
78
|
+
values.append_compact_field(active_job, :tags, values.hash_or_empty(event[:tags]))
|
|
79
|
+
{ active_job: active_job }
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def neutral_for(event, payload)
|
|
83
|
+
values = Core::Integration::Values::Shape
|
|
84
|
+
Core::Fields::FieldSet.merge!(
|
|
85
|
+
JobAttributes.call(payload),
|
|
86
|
+
values.source_location_attributes(event[:source_location])
|
|
87
|
+
)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def severity_for(name, payload)
|
|
91
|
+
return :error if ERROR_EVENTS.include?(name)
|
|
92
|
+
return :error if exception_payload?(payload)
|
|
93
|
+
|
|
94
|
+
:info
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def exception_payload?(payload)
|
|
98
|
+
return false unless payload.is_a?(Hash)
|
|
99
|
+
|
|
100
|
+
payload.key?(:exception_class) ||
|
|
101
|
+
payload.key?(:exception_message) ||
|
|
102
|
+
payload.key?(:exception_backtrace)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def error_payload(payload)
|
|
106
|
+
{
|
|
107
|
+
class: payload[:exception_class],
|
|
108
|
+
message: payload[:exception_message],
|
|
109
|
+
backtrace: payload[:exception_backtrace]
|
|
110
|
+
}.compact
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def enrich_continuation_summary(name, payload)
|
|
114
|
+
return unless Core::Integration::Facade.summary_active?
|
|
115
|
+
|
|
116
|
+
case name
|
|
117
|
+
when "active_job.step_started"
|
|
118
|
+
increment_summary(:continuation_steps_started)
|
|
119
|
+
add_step_summary(payload, state: "started")
|
|
120
|
+
when "active_job.step"
|
|
121
|
+
enrich_completed_step_summary(payload)
|
|
122
|
+
when "active_job.step_skipped"
|
|
123
|
+
increment_summary(:continuation_steps_skipped)
|
|
124
|
+
add_step_summary(payload, state: "skipped")
|
|
125
|
+
when "active_job.interrupt"
|
|
126
|
+
enrich_interrupt_summary(payload)
|
|
127
|
+
when "active_job.resume"
|
|
128
|
+
enrich_resume_summary(payload)
|
|
129
|
+
end
|
|
130
|
+
rescue StandardError
|
|
131
|
+
nil
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def enrich_interrupt_summary(payload)
|
|
135
|
+
increment_summary(:continuation_interruptions)
|
|
136
|
+
Core::Integration::Facade.add_summary_attributes(
|
|
137
|
+
active_job: {
|
|
138
|
+
continuation_status: "interrupted",
|
|
139
|
+
continuation_description: payload[:description],
|
|
140
|
+
continuation_interrupt_reason: payload[:reason]
|
|
141
|
+
}
|
|
142
|
+
)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def enrich_resume_summary(payload)
|
|
146
|
+
increment_summary(:continuation_resumptions)
|
|
147
|
+
Core::Integration::Facade.add_summary_attributes(
|
|
148
|
+
active_job: {
|
|
149
|
+
continuation_status: "resumed",
|
|
150
|
+
continuation_description: payload[:description]
|
|
151
|
+
}
|
|
152
|
+
)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def enrich_completed_step_summary(payload)
|
|
156
|
+
if payload[:interrupted]
|
|
157
|
+
increment_summary(:continuation_steps_interrupted)
|
|
158
|
+
add_step_summary(payload, state: "interrupted")
|
|
159
|
+
elsif exception_payload?(payload)
|
|
160
|
+
increment_summary(:continuation_steps_failed)
|
|
161
|
+
add_step_summary(payload, state: "failed")
|
|
162
|
+
else
|
|
163
|
+
increment_summary(:continuation_steps_completed)
|
|
164
|
+
add_step_summary(payload, state: "completed")
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def add_step_summary(payload, state:)
|
|
169
|
+
Core::Integration::Facade.add_summary_attributes(
|
|
170
|
+
active_job: {
|
|
171
|
+
continuation_last_step: payload[:step],
|
|
172
|
+
continuation_last_step_cursor: payload[:cursor],
|
|
173
|
+
continuation_last_step_state: state,
|
|
174
|
+
continuation_last_step_resumed: payload[:resumed]
|
|
175
|
+
}
|
|
176
|
+
)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def increment_summary(key)
|
|
180
|
+
Core::Integration::Facade.increment_summary_attribute(:active_job, key)
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "zeitwerk"
|
|
4
|
+
require "julewire/core"
|
|
5
|
+
require "julewire/rails_support"
|
|
6
|
+
|
|
7
|
+
module Julewire
|
|
8
|
+
module ActiveJob
|
|
9
|
+
class Error < Julewire::Error; end
|
|
10
|
+
CARRIER_IVAR = :@julewire_carrier
|
|
11
|
+
IntegrationHealth = Core::Integration::Health.scoped(:active_job)
|
|
12
|
+
private_constant :CARRIER_IVAR
|
|
13
|
+
|
|
14
|
+
extend Core::Integration::Configurable
|
|
15
|
+
|
|
16
|
+
configurable_with { Configuration }
|
|
17
|
+
|
|
18
|
+
class << self
|
|
19
|
+
def install!(base: nil, event_reporter: nil, configuration: config)
|
|
20
|
+
Installer.install!(base: base, event_reporter: event_reporter, configuration: configuration)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def perform(job, &)
|
|
24
|
+
JobExecution.call(job, configuration: config, &)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def load_railtie_if_rails!
|
|
28
|
+
Railtie if defined?(::Rails::Railtie)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
loader = Zeitwerk::Loader.for_gem_extension(self)
|
|
34
|
+
# Rails-only autoload, skipped when non-Rails processes eager load the gem.
|
|
35
|
+
loader.do_not_eager_load("#{__dir__}/active_job/railtie.rb")
|
|
36
|
+
loader.setup
|
|
37
|
+
Julewire::ActiveJob.load_railtie_if_rails!
|
|
38
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: julewire-active_job
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Alexander Grebennik
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: activejob
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '8.1'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '8.1'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: julewire-core
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '1.0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '1.0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: julewire-rails_support
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '1.0'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '1.0'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: zeitwerk
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: 2.8.1
|
|
61
|
+
type: :runtime
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: 2.8.1
|
|
68
|
+
description: Execution-scoped Active Job instrumentation, structured event capture,
|
|
69
|
+
and propagation carrier support for Julewire.
|
|
70
|
+
email:
|
|
71
|
+
- slbug@users.noreply.github.com
|
|
72
|
+
- sl.bug.sl@gmail.com
|
|
73
|
+
executables: []
|
|
74
|
+
extensions: []
|
|
75
|
+
extra_rdoc_files: []
|
|
76
|
+
files:
|
|
77
|
+
- CHANGELOG.md
|
|
78
|
+
- LICENSE.txt
|
|
79
|
+
- README.md
|
|
80
|
+
- docs/advanced-configuration.md
|
|
81
|
+
- docs/boundaries.md
|
|
82
|
+
- docs/configuration.md
|
|
83
|
+
- docs/continuations.md
|
|
84
|
+
- docs/propagation.md
|
|
85
|
+
- julewire-active_job.gemspec
|
|
86
|
+
- lib/julewire-active_job.rb
|
|
87
|
+
- lib/julewire/active_job.rb
|
|
88
|
+
- lib/julewire/active_job/configuration.rb
|
|
89
|
+
- lib/julewire/active_job/installer.rb
|
|
90
|
+
- lib/julewire/active_job/job_attributes.rb
|
|
91
|
+
- lib/julewire/active_job/job_execution.rb
|
|
92
|
+
- lib/julewire/active_job/job_serialization.rb
|
|
93
|
+
- lib/julewire/active_job/log_subscriber_silencer.rb
|
|
94
|
+
- lib/julewire/active_job/railtie.rb
|
|
95
|
+
- lib/julewire/active_job/subscribers/event.rb
|
|
96
|
+
- lib/julewire/active_job/version.rb
|
|
97
|
+
homepage: https://github.com/slbug/julewire
|
|
98
|
+
licenses:
|
|
99
|
+
- MIT
|
|
100
|
+
metadata:
|
|
101
|
+
homepage_uri: https://github.com/slbug/julewire
|
|
102
|
+
source_code_uri: https://github.com/slbug/julewire/tree/main/gems/active_job
|
|
103
|
+
changelog_uri: https://github.com/slbug/julewire/blob/main/gems/active_job/CHANGELOG.md
|
|
104
|
+
rubygems_mfa_required: 'true'
|
|
105
|
+
rdoc_options: []
|
|
106
|
+
require_paths:
|
|
107
|
+
- lib
|
|
108
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
109
|
+
requirements:
|
|
110
|
+
- - ">="
|
|
111
|
+
- !ruby/object:Gem::Version
|
|
112
|
+
version: '3.4'
|
|
113
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
114
|
+
requirements:
|
|
115
|
+
- - ">="
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: '0'
|
|
118
|
+
requirements: []
|
|
119
|
+
rubygems_version: 4.0.14
|
|
120
|
+
specification_version: 4
|
|
121
|
+
summary: Active Job integration for Julewire structured logging.
|
|
122
|
+
test_files: []
|