logsy 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: eee92d3c7715964e65bd1a5f5efb80e07328bd8fdf3de735d07664c429f19623
4
+ data.tar.gz: 3a5a149095b25d6cb00e662f96c5d235c1e35dcb1e728f9977676a49ed039e6b
5
+ SHA512:
6
+ metadata.gz: 6035309f83d88ad2a07745d01331d38ecff83254685e9b76d204de8f65b2cba0e86246b192dea878ee1231b8e7f98a0d435cd0d80166e1f37422046ab588db2a
7
+ data.tar.gz: 2fd3061b4df673dd3bc186d0f61bc4e4ac945de8d1f45a6b1d4cf24025f95db834a65a3e453e6aefdc088bcc079a06c0fe60e2635a2b510e830449dcee470a6d
data/CHANGELOG.md ADDED
@@ -0,0 +1,10 @@
1
+ # Changelog
2
+
3
+ ## [0.1.0] - Unreleased
4
+
5
+ - Initial release.
6
+ - `Logsy[]` / `Logsy[]=` / `Logsy.tags` dictionary-style API for per-request tags — no subclass required.
7
+ - `Logsy::JsonFormatter` for structured JSON log output (one line per call, all current tags merged in).
8
+ - `Logsy::ControllerHooks` for Rails request_id capture and wide-event emission.
9
+ - `Logsy::SidekiqMiddleware::Client` / `Server` for Sidekiq context propagation. Sets `Logsy[:job_id]` automatically. No Sidekiq dependency at runtime.
10
+ - Default `ignored_caller_paths` covers Logger, ActiveSupport, lograge, sprockets, quiet_assets, and Logsy itself.
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ruby_is_love
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 all
13
+ 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 THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,199 @@
1
+ # Logsy
2
+
3
+ Tagged structured logging for Rails apps. JSON output, one line per log call.
4
+
5
+ Logsy gives you **one wide JSON event per request** plus **correlated breadcrumb logs**, all tagged with whatever per-request context you set via `Logsy[:key] = value`. Tags propagate across background jobs (Sidekiq middleware bundled). Where you ship the JSON is your call — Logsy just writes structured lines to stdout.
6
+
7
+ ## Installation
8
+
9
+ ```ruby
10
+ # Gemfile
11
+ gem 'logsy'
12
+ ```
13
+
14
+ ```bash
15
+ bundle install
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ ### 1. Wire up the JSON formatter
21
+
22
+ ```ruby
23
+ # config/environments/production.rb
24
+ config.logger = ActiveSupport::Logger.new($stdout)
25
+ .tap { |l| l.formatter = Logsy::JsonFormatter.new }
26
+ ```
27
+
28
+ ### 2. Include the controller hooks
29
+
30
+ ```ruby
31
+ # app/controllers/application_controller.rb
32
+ class ApplicationController < ActionController::API
33
+ include Logsy::ControllerHooks
34
+ end
35
+ ```
36
+
37
+ This gives you:
38
+ - `Logsy[:request_id]` automatically populated from `request.request_id` (or `X-Request-Id` header) on every request
39
+ - One "wide event" log line at end of each request with `event: "request"`, method, path, controller, action, status, duration_ms, and any error class/message — plus every tag you set during the request
40
+
41
+ ### 3. Set tags wherever they become known
42
+
43
+ ```ruby
44
+ # In a controller, model, service, anywhere:
45
+ def create
46
+ order = Order.create!(order_params)
47
+ Logsy[:order_id] = order.id # every log from here on includes order_id
48
+ ...
49
+ end
50
+ ```
51
+
52
+ That's it. No `Current` model to define, no attribute declarations — just key/value.
53
+
54
+ ### 4. (Optional) Background job propagation
55
+
56
+ If you use Sidekiq:
57
+
58
+ ```ruby
59
+ # config/initializers/sidekiq.rb
60
+ require 'logsy/sidekiq_middleware'
61
+
62
+ Sidekiq.configure_client do |config|
63
+ config.client_middleware { |chain| chain.add(Logsy::SidekiqMiddleware::Client) }
64
+ end
65
+
66
+ Sidekiq.configure_server do |config|
67
+ config.client_middleware { |chain| chain.add(Logsy::SidekiqMiddleware::Client) }
68
+ config.server_middleware { |chain| chain.add(Logsy::SidekiqMiddleware::Server) }
69
+ end
70
+ ```
71
+
72
+ ```ruby
73
+ # config/initializers/logsy.rb (optional — only if you want to propagate more than request_id)
74
+ Logsy.configure do |c|
75
+ c.job_propagated_keys = %i[request_id user_id tenant_id]
76
+ end
77
+ ```
78
+
79
+ The bundled middleware:
80
+ - **Client side**: when a job is enqueued, copies the configured tags from `Logsy[]` into the job payload
81
+ - **Server side**: when the job runs, sets those tags back in `Logsy[]` AND sets `Logsy[:job_id] = job['jid']` automatically
82
+
83
+ Anything the job code writes via `Logsy[:foo] = bar` while running shows up on every log line emitted during the job. Tags reset automatically after each job (no leak between jobs sharing a worker thread).
84
+
85
+ For other job systems (Resque, GoodJob, custom), write your own middleware using the same pattern — Logsy's `[]=` / `[]` / `tags` / `reset` API is generic.
86
+
87
+ ## What it looks like in your logs
88
+
89
+ A regular log line:
90
+
91
+ ```json
92
+ {
93
+ "ts":"2026-05-02T10:00:00.123Z",
94
+ "level":"INFO",
95
+ "msg":"Calling SPG gateway",
96
+ "file":"app/lib/gateways/spg_gateway.rb",
97
+ "line":437,
98
+ "request_id":"abc-123",
99
+ "user_id":"u-1",
100
+ "message_id":"m-42",
101
+ "order_id":"o-9"
102
+ }
103
+ ```
104
+
105
+ The wide event at end of request:
106
+
107
+ ```json
108
+ {
109
+ "ts":"2026-05-02T10:00:00.250Z",
110
+ "level":"INFO",
111
+ "event":"request",
112
+ "method":"POST",
113
+ "path":"/v1/orders",
114
+ "controller":"orders",
115
+ "action":"create",
116
+ "status":201,
117
+ "duration_ms":127.34,
118
+ "request_id":"abc-123",
119
+ "user_id":"u-1",
120
+ "message_id":"m-42",
121
+ "order_id":"o-9",
122
+ }
123
+ ```
124
+
125
+ Search your log store by `message_id` to find every request from that message. Pivot from a request's `request_id` to see every breadcrumb log line emitted while it ran.
126
+
127
+ ## API reference
128
+
129
+ ```ruby
130
+ Logsy[:user_id] = 'u-1' # set a tag
131
+ Logsy[:user_id] # read it
132
+ Logsy.tags # all tags as a hash
133
+ Logsy.reset # clear all tags (the controller/middleware do this for you)
134
+ ```
135
+
136
+ Symbol and string keys are equivalent — `Logsy['user_id']` and `Logsy[:user_id]` access the same slot.
137
+
138
+ ## Customizing the wide event
139
+
140
+ Override `logsy_request_summary_extras` in your controller to add fields:
141
+
142
+ ```ruby
143
+ class ApplicationController < ActionController::API
144
+ include Logsy::ControllerHooks
145
+
146
+ private
147
+
148
+ def logsy_request_summary_extras
149
+ {
150
+ ip: request.remote_ip,
151
+ user_agent: request.user_agent,
152
+ idempotency_key: request.headers['Idempotency-Key']
153
+ }
154
+ end
155
+ end
156
+ ```
157
+
158
+ ## Configuration reference
159
+
160
+ ```ruby
161
+ Logsy.configure do |c|
162
+ # Tag keys to copy into job payloads at enqueue and read back at execution.
163
+ # Default: [:request_id]
164
+ c.job_propagated_keys = %i[request_id user_id tenant_id]
165
+
166
+ # Capture caller file:line via Kernel#caller_locations on every log line.
167
+ # Disable if you measure overhead. Default: true
168
+ c.include_caller_location = true
169
+
170
+ # Regex patterns for caller frames to skip. Defaults already cover Logger,
171
+ # ActiveSupport, lograge, sprockets, quiet_assets, and Logsy itself.
172
+ c.ignored_caller_paths += [%r{/my_internal_logger/}]
173
+
174
+ # The "event" name on the request summary. Default: "request"
175
+ c.request_summary_event_name = 'http_request'
176
+ end
177
+ ```
178
+
179
+ ## Why Logsy?
180
+
181
+ Existing options each cover one piece:
182
+
183
+ - **`tagged_logging`** (ActiveSupport) — push/pop tag stack, doesn't compose with mutable per-request context
184
+ - **`lograge`** — one wide event per request, but doesn't help your in-action `Rails.logger.info(...)` calls
185
+ - **`semantic_logger`** — heavy, opinionated, replaces the whole logger framework
186
+
187
+ Logsy fills the gap: a small, dictionary-style API (`Logsy[:foo] = bar`) plus a JSON formatter that reads from it on every log line. No subclasses to define, no DSL to learn — just set tags as you discover them.
188
+
189
+ ## Development
190
+
191
+ ```bash
192
+ bundle install
193
+ bundle exec rspec # 34 specs
194
+ bundle exec rubocop # lint
195
+ ```
196
+
197
+ ## License
198
+
199
+ MIT. See [LICENSE.txt](LICENSE.txt).
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Logsy
4
+ class Configuration
5
+ DEFAULT_IGNORED_CALLER_PATHS = [
6
+ %r{/logger\.rb\z},
7
+ %r{/active_support/(tagged_logging|logger|broadcast_logger|log_subscriber)},
8
+ %r{/lograge/},
9
+ %r{/sprockets/},
10
+ %r{/quiet_assets/},
11
+ %r{/lib/logsy/}
12
+ ].freeze
13
+
14
+ # Tag keys that should be carried across job boundaries (e.g. when a
15
+ # background job is enqueued from a web request, the job inherits
16
+ # these). The middleware that does the actual carrying is job-runner
17
+ # specific (Logsy ships one for Sidekiq).
18
+ attr_accessor :job_propagated_keys, :ignored_caller_paths,
19
+ :include_caller_location, :request_summary_event_name
20
+
21
+ def initialize
22
+ @job_propagated_keys = [:request_id]
23
+ @ignored_caller_paths = DEFAULT_IGNORED_CALLER_PATHS.dup
24
+ @include_caller_location = true
25
+ @request_summary_event_name = 'request'
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/concern'
4
+
5
+ module Logsy
6
+ # Mix into your ApplicationController (or specific controllers) to:
7
+ #
8
+ # 1. Capture the request's UUID into Logsy.context.request_id, so every
9
+ # log line emitted during the request carries it.
10
+ # 2. Emit a single "wide event" log line at end of request with the
11
+ # method, path, status, duration_ms, controller, action, error, and
12
+ # every Current.* attribute that was set during the request.
13
+ #
14
+ # Usage:
15
+ #
16
+ # class ApplicationController < ActionController::API
17
+ # include Logsy::ControllerHooks
18
+ # end
19
+ #
20
+ # Override `logsy_request_summary_extras` in your controller to add
21
+ # custom fields to the wide event:
22
+ #
23
+ # def logsy_request_summary_extras
24
+ # { ip: request.remote_ip, user_agent: request.user_agent }
25
+ # end
26
+ module ControllerHooks
27
+ extend ActiveSupport::Concern
28
+
29
+ included do
30
+ before_action :_logsy_capture_request_id
31
+ around_action :_logsy_emit_request_summary
32
+ end
33
+
34
+ private
35
+
36
+ def _logsy_capture_request_id
37
+ Logsy[:request_id] = request.request_id || request.headers['X-Request-Id']
38
+ end
39
+
40
+ def _logsy_emit_request_summary
41
+ started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
42
+ error = nil
43
+ yield
44
+ rescue StandardError => e
45
+ error = e
46
+ raise
47
+ ensure
48
+ duration_ms = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - started_at) * 1000).round(2)
49
+ payload = {
50
+ event: Logsy.configuration.request_summary_event_name,
51
+ method: request.request_method,
52
+ path: request.path,
53
+ controller: controller_name,
54
+ action: action_name,
55
+ status: response&.status,
56
+ duration_ms:,
57
+ error: error && "#{error.class}: #{error.message}"
58
+ }.merge(logsy_request_summary_extras).compact
59
+
60
+ Rails.logger.info(payload)
61
+ end
62
+
63
+ # Override in subclasses to add fields to the request summary wide event.
64
+ def logsy_request_summary_extras
65
+ {}
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'logger'
5
+ require 'time'
6
+
7
+ module Logsy
8
+ # Logger formatter that emits one JSON object per log call.
9
+ #
10
+ # Each line includes:
11
+ # - ts: UTC ISO-8601 timestamp with millis
12
+ # - level: severity ('INFO', 'ERROR', ...)
13
+ # - msg: the log message (when message is a String or Exception)
14
+ # - file/line: source location of the call (when include_caller_location)
15
+ # - any non-nil attributes from the configured Logsy.context
16
+ #
17
+ # When the message is a Hash, its keys are merged directly into the
18
+ # top-level payload (useful for "wide event" emissions like a request
19
+ # summary). Symbols and strings are accepted as keys.
20
+ #
21
+ # Example output:
22
+ # {"ts":"2026-05-02T10:00:00.123Z","level":"INFO","msg":"hello",
23
+ # "file":"app/controllers/orders_controller.rb","line":34,
24
+ # "request_id":"abc-123","user_id":"u-1"}
25
+ class JsonFormatter < ::Logger::Formatter
26
+ def call(severity, time, _progname, message)
27
+ payload = {
28
+ ts: time.utc.iso8601(3),
29
+ level: severity
30
+ }
31
+
32
+ add_caller_location!(payload) if Logsy.configuration.include_caller_location
33
+ merge_context!(payload)
34
+ merge_message!(payload, message)
35
+
36
+ "#{JSON.dump(payload)}\n"
37
+ rescue StandardError => e
38
+ fallback_line(severity, time, e)
39
+ end
40
+
41
+ private
42
+
43
+ def add_caller_location!(payload)
44
+ location = caller_locations.find { |loc| user_frame?(loc.path) }
45
+ return unless location
46
+
47
+ payload[:file] = relative_path(location.path)
48
+ payload[:line] = location.lineno
49
+ end
50
+
51
+ def user_frame?(path)
52
+ Logsy.configuration.ignored_caller_paths.none? { |pattern| path.match?(pattern) }
53
+ end
54
+
55
+ def relative_path(path)
56
+ root = defined?(Rails) && Rails.respond_to?(:root) && Rails.root ? "#{Rails.root}/" : nil
57
+ return path unless root && path.start_with?(root)
58
+
59
+ path[root.length..]
60
+ end
61
+
62
+ def merge_context!(payload)
63
+ Logsy.tags.each do |key, value|
64
+ next if value.nil?
65
+
66
+ payload[key.to_sym] = value
67
+ end
68
+ end
69
+
70
+ def merge_message!(payload, message)
71
+ case message
72
+ when ::String
73
+ payload[:msg] = message
74
+ when ::Hash
75
+ message.each { |k, v| payload[k.to_sym] = v }
76
+ when ::Exception
77
+ payload[:msg] = "#{message.class}: #{message.message}"
78
+ else
79
+ payload[:msg] = message.inspect
80
+ end
81
+ end
82
+
83
+ def fallback_line(severity, time, error)
84
+ safe = {
85
+ ts: time.utc.iso8601(3),
86
+ level: severity,
87
+ msg: 'logsy_formatter_error',
88
+ error: "#{error.class}: #{error.message}"
89
+ }
90
+ "#{JSON.dump(safe)}\n"
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Logsy
4
+ module SidekiqMiddleware
5
+ # Client-side middleware: captures configured tags from the enqueueing
6
+ # process (a web request, a console session, another job) and writes
7
+ # them into the job payload.
8
+ class Client
9
+ def call(_worker_class, job, _queue, _redis_pool)
10
+ Logsy.configuration.job_propagated_keys.each do |key|
11
+ value = Logsy[key]
12
+ job[key.to_s] ||= value if value
13
+ end
14
+ yield
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Logsy
4
+ module SidekiqMiddleware
5
+ # Server-side middleware: reads propagated tags back from the job
6
+ # payload, sets them on the per-job Logsy store, sets job_id from
7
+ # Sidekiq's `jid`, and resets the store after the job runs (even on
8
+ # error) so tags don't leak between jobs sharing a worker thread.
9
+ #
10
+ # Anything the job code writes via `Logsy[:foo] = bar` while running
11
+ # will appear on every log line emitted during the job.
12
+ class Server
13
+ def call(_worker_instance, job, _queue)
14
+ Logsy[:job_id] = job['jid']
15
+ Logsy.configuration.job_propagated_keys.each do |key|
16
+ value = job[key.to_s]
17
+ Logsy[key] = value if value
18
+ end
19
+ yield
20
+ ensure
21
+ Logsy.reset
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logsy/sidekiq_middleware/client'
4
+ require 'logsy/sidekiq_middleware/server'
5
+
6
+ module Logsy
7
+ # Sidekiq middleware namespace. Require this file (or rely on a Rails
8
+ # railtie) to make {Client} and {Server} available, then register them
9
+ # with Sidekiq:
10
+ #
11
+ # Sidekiq.configure_client do |config|
12
+ # config.client_middleware { |chain| chain.add(Logsy::SidekiqMiddleware::Client) }
13
+ # end
14
+ #
15
+ # Sidekiq.configure_server do |config|
16
+ # config.client_middleware { |chain| chain.add(Logsy::SidekiqMiddleware::Client) }
17
+ # config.server_middleware { |chain| chain.add(Logsy::SidekiqMiddleware::Server) }
18
+ # end
19
+ module SidekiqMiddleware
20
+ end
21
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+ require 'active_support/current_attributes'
5
+
6
+ module Logsy
7
+ # Internal per-request / per-job key-value store. Backed by
8
+ # ActiveSupport::CurrentAttributes so it is fiber-local and gets reset
9
+ # automatically by the Rails executor between requests.
10
+ #
11
+ # Consumers should not access this class directly — use `Logsy[:key]`,
12
+ # `Logsy[:key] = value`, `Logsy.tags`, and `Logsy.reset` instead.
13
+ class Store < ActiveSupport::CurrentAttributes
14
+ attribute :tags
15
+
16
+ def tags
17
+ super || (self.tags = {})
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Logsy
4
+ VERSION = '0.1.0'
5
+ end
data/lib/logsy.rb ADDED
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logsy/version'
4
+ require 'logsy/store'
5
+ require 'logsy/configuration'
6
+ require 'logsy/json_formatter'
7
+ require 'logsy/controller_hooks'
8
+
9
+ module Logsy
10
+ class Error < StandardError; end
11
+
12
+ class << self
13
+ # Read a tag from the per-request store.
14
+ #
15
+ # Logsy[:user_id] # => 'u-1' or nil
16
+ def [](key)
17
+ Store.tags[key.to_sym]
18
+ end
19
+
20
+ # Write a tag into the per-request store. From this point on, every
21
+ # log line emitted during the current request (or job) carries it.
22
+ # Non-nil values are coerced to String so log fields have a consistent
23
+ # type when ingested by structured log stores (which often infer field
24
+ # type from the first value they see).
25
+ #
26
+ # Logsy[:user_id] = user.id # 42 -> "42"
27
+ # Logsy[:order_id] = nil # nil stays nil
28
+ def []=(key, value)
29
+ Store.tags[key.to_sym] = value&.to_s
30
+ end
31
+
32
+ # All tags currently set, as a hash. Used by the formatter on every
33
+ # log line; consumers can call it for debugging.
34
+ def tags
35
+ Store.tags
36
+ end
37
+
38
+ # Clear all tags. The Rails executor calls this between requests, and
39
+ # the Sidekiq middleware calls it between jobs. You usually don't need
40
+ # to call it yourself.
41
+ def reset
42
+ Store.reset
43
+ end
44
+
45
+ # Configure the gem. Yields the {Configuration} instance.
46
+ #
47
+ # Logsy.configure do |c|
48
+ # c.job_propagated_keys = %i[request_id user_id]
49
+ # end
50
+ def configure
51
+ yield(configuration)
52
+ configuration
53
+ end
54
+
55
+ def configuration
56
+ @configuration ||= Configuration.new
57
+ end
58
+
59
+ # Test helper.
60
+ def reset_configuration!
61
+ @configuration = nil
62
+ end
63
+ end
64
+ end
metadata ADDED
@@ -0,0 +1,126 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: logsy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - ruby_is_love
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: activesupport
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '7.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '7.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: actionpack
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '7.0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '7.0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: rake
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '13.0'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '13.0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: rspec
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '3.13'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '3.13'
68
+ - !ruby/object:Gem::Dependency
69
+ name: rubocop
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '1.60'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '1.60'
82
+ description: |
83
+ Logsy gives Rails apps one wide JSON event per request plus correlated
84
+ breadcrumb logs, all tagged with whatever per-request context you set
85
+ via Logsy[:key] = value. Tags propagate across background jobs.
86
+ Generic JSON output to stdout — bring your own log shipper.
87
+ executables: []
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - CHANGELOG.md
92
+ - LICENSE.txt
93
+ - README.md
94
+ - lib/logsy.rb
95
+ - lib/logsy/configuration.rb
96
+ - lib/logsy/controller_hooks.rb
97
+ - lib/logsy/json_formatter.rb
98
+ - lib/logsy/sidekiq_middleware.rb
99
+ - lib/logsy/sidekiq_middleware/client.rb
100
+ - lib/logsy/sidekiq_middleware/server.rb
101
+ - lib/logsy/store.rb
102
+ - lib/logsy/version.rb
103
+ homepage: https://rubygems.org/profiles/ruby_is_love
104
+ licenses:
105
+ - MIT
106
+ metadata:
107
+ rubygems_mfa_required: 'true'
108
+ homepage_uri: https://rubygems.org/profiles/ruby_is_love
109
+ rdoc_options: []
110
+ require_paths:
111
+ - lib
112
+ required_ruby_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: 3.2.0
117
+ required_rubygems_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ requirements: []
123
+ rubygems_version: 3.6.9
124
+ specification_version: 4
125
+ summary: Tagged structured logging for Rails apps with cross-process context propagation
126
+ test_files: []