activesupport-json_logging 1.0.0 → 1.2.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: b542fc320d19a1789f47805250847291ca671676834f66f1b99257d0311706ef
4
- data.tar.gz: 8dac946f3ee55c6b22ff4d7996e12c6b6ea220434c7ba4514a7495ce251c7504
3
+ metadata.gz: c99bef73cc6bf2627075656ee38a3e3b756ac61ef54f2edcc628814b317ac7bf
4
+ data.tar.gz: c4a0e1ecfd0ae8862db0167cc5507b8da4572e6a58ed4035ec7aa606c3154442
5
5
  SHA512:
6
- metadata.gz: 82f446cdb9ddc23c91ce99df51f90fd4d6cc2f8eecdaa45276bb8ff8aa074cb1ef07bde42b1ce3e2fc907e34e2e235a544cd54d36a1da5016a0fcf621b8b397b
7
- data.tar.gz: 21cc1fe28c94ae215a2293fe5f934bc9f139bba4affd9431b0aa41f0c0620e30ab456c0c43bd4b834e61d4e89a6d2a5876c3fdc47d2ca21b2d4163f4a3327578
6
+ metadata.gz: 3abb724226828b28519f25dc8edb138c3b4d7dc1a07bf2946b952e46bee4bef5d71149a44df4a52cb12e41f892ca4d2f7147bfdeb9dbd7ad0ca3c4ee3873dddc
7
+ data.tar.gz: 25b2c9f5ae0c8107e4f5c7385ff96fbb379cbfba851234be9800909978b14d1d81b5fab961a5d72bd1d4ede78c5d1d16eb5ab1534e6ed54d2074e1c21456395c
data/CHANGELOG.md CHANGED
@@ -1,44 +1,19 @@
1
- # Changelog
1
+ # CHANGELOG
2
2
 
3
- ## 1.0.0 (2024-10-31)
3
+ ## 1.2.0 (2025-11-07)
4
4
 
5
- First stable release.
5
+ - Add support for service-specific tagged loggers: create loggers with permanent tags using `logger.tagged("service")` without a block
6
+ - Improve BroadcastLogger compatibility: service-specific loggers work seamlessly with `ActiveSupport::BroadcastLogger`
7
+ - Fix LocalTagStorage implementation to match Rails' TaggedLogging behavior: use `tag_stack` attribute accessor pattern for proper tag isolation
8
+ - Add comprehensive examples in README for service-specific loggers and BroadcastLogger integration
6
9
 
7
- - feat: single-line JSON format compatible with cloud logging services (GCP, AWS, Azure)
8
- - feat: native tagged logging with `logger.tagged("TAG")` API compatible with Rails
9
- - feat: thread-safe context via `JsonLogging.with_context` for per-thread fields
10
- - feat: smart message parsing for hashes, JSON strings, and plain strings
11
- - feat: inherit all ActiveSupport::Logger features (silence, local_level, etc.)
12
- - feat: BroadcastLogger compatibility for Rails 7.1+ automatic wrapping
13
- - feat: timestamp precision in microseconds (iso8601 with 6 decimals)
14
- - feat: Rails ParameterFilter integration for automatic sensitive data filtering
15
- - feat: input sanitization removing control characters and truncating long strings
16
- - feat: sensitive key pattern matching fallback when ParameterFilter unavailable
17
- - feat: depth and size limits for nested structures to prevent log bloat
18
- - feat: single-line JSON output to prevent log injection via newlines
19
- - feat: graceful error handling with fallback entries on serialization errors
20
- - feat: Rails 6.0, 6.1, 7.0, 7.1, 7.2, 8.0 support
21
- - feat: IsolatedExecutionState for thread/Fiber isolation (Rails 7.1+)
22
- - feat: backward compatible fallback to Thread.current for Rails 6-7.0
23
- - feat: kwargs support in logger initialization for Rails 7+
24
- - perf: ~0.006ms per log entry overhead (250-400% vs plain text, typical for JSON)
25
- - perf: memory efficient with ~3KB per entry and zero retained memory
26
- - feat: performance benchmarks with memory profiling included
27
- - test: 93.78% code coverage with comprehensive RSpec suite
28
- - test: BroadcastLogger integration tests
29
- - test: IsolatedExecutionState thread safety tests
30
- - test: Appraisals configured for multi-version testing (Rails 6-8)
31
- - test: GitHub Actions CI workflow
32
- - docs: complete README with installation, usage, and API docs
33
- - docs: Rails environment configuration examples (development, production, test)
34
- - docs: Lograge integration with third-party logger configurations
35
- - docs: Puma integration example
36
- - docs: security best practices and ParameterFilter guide
37
- - docs: inherited Rails logger features documentation
10
+ ## 1.1.0 (2025-11-04)
38
11
 
39
- ---
12
+ - Move tags to root level of JSON payload instead of nested in context (breaking change: tags now at `payload["tags"]` instead of `payload["context"]["tags"]`)
13
+ - Filter system-controlled keys (severity, timestamp, message, tags, context) from user context to prevent conflicts
14
+ - Prevent nested context objects when user context includes a `context` key
15
+ - Fix pending spec for TimeWithZone objects by requiring ActiveSupport time extensions
40
16
 
41
- ## 0.1.0
17
+ ## 1.0.0 (2025-10-31)
42
18
 
43
- - feat: initial JSON formatter and logger implementation
44
- - test: basic RSpec test coverage
19
+ - Initial stable release
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # activesupport-json_logging
2
2
 
3
- [![Gem Version](https://badge.fury.io/rb/activesupport-json_logging.svg)](https://badge.fury.io/rb/activesupport-json_logging) [![Test Status](https://github.com/amkisko/activesupport-json_logging.rb/actions/workflows/ci.yml/badge.svg)](https://github.com/amkisko/activesupport-json_logging.rb/actions/workflows/ci.yml) [![codecov](https://codecov.io/gh/amkisko/activesupport-json_logging.rb/graph/badge.svg?token=881AFPL643)](https://codecov.io/gh/amkisko/activesupport-json_logging.rb)
3
+ [![Gem Version](https://badge.fury.io/rb/activesupport-json_logging.svg)](https://badge.fury.io/rb/activesupport-json_logging) [![Test Status](https://github.com/amkisko/activesupport-json_logging.rb/actions/workflows/test.yml/badge.svg)](https://github.com/amkisko/activesupport-json_logging.rb/actions/workflows/test.yml) [![codecov](https://codecov.io/gh/amkisko/activesupport-json_logging.rb/graph/badge.svg?token=UX80FTO0Y0)](https://codecov.io/gh/amkisko/activesupport-json_logging.rb)
4
4
 
5
5
  Structured JSON logging for Rails and ActiveSupport with a safe, single-line formatter.
6
6
  No dependencies beyond Rails and Activesupport.
@@ -8,6 +8,11 @@ Supports Rails versions from 6 to 8.
8
8
 
9
9
  Sponsored by [Kisko Labs](https://www.kiskolabs.com).
10
10
 
11
+ <a href="https://www.kiskolabs.com">
12
+ <img src="kisko.svg" width="200" alt="Sponsored by Kisko Labs" />
13
+ </a>
14
+
15
+
11
16
  ## Installation
12
17
 
13
18
  Add to your Gemfile:
@@ -30,22 +35,6 @@ Rails.application.configure do
30
35
  end
31
36
  ```
32
37
 
33
- ### Development: Using from Local Repository
34
-
35
- When developing the gem or testing changes in your application, you can point your Gemfile to a local path:
36
-
37
- ```ruby
38
- # In your application's Gemfile
39
- gem "activesupport-json_logging", path: "../activesupport-json_logging.rb"
40
- ```
41
-
42
- Then run:
43
-
44
- ```bash
45
- bundle install
46
- ```
47
-
48
- **Note:** When using `path:` in your Gemfile, Bundler will use the local gem directly. Changes you make to the gem code will be immediately available in your application without needing to rebuild or reinstall the gem. This is ideal for development and testing.
49
38
 
50
39
  ## What you get
51
40
 
@@ -55,6 +44,8 @@ bundle install
55
44
  - `JsonLogging.with_context` to attach contextual fields per-thread
56
45
  - Smart message parsing (handles hashes, JSON strings, plain strings, and Exception objects)
57
46
  - Native `tagged` method support - use it just like Rails' tagged logger
47
+ - Service-specific tagged loggers - create loggers with permanent tags using `logger.tagged("service")` without a block
48
+ - Full compatibility with `ActiveSupport::BroadcastLogger` (Rails 7.1+)
58
49
  - Automatic Rails integration via Railtie (auto-requires the gem in Rails apps)
59
50
 
60
51
  ## Basic usage
@@ -78,6 +69,17 @@ logger.tagged("BCX").info("Stuff")
78
69
  logger.tagged("BCX", "Jason").info("Stuff")
79
70
  logger.tagged("BCX").tagged("Jason").info("Stuff")
80
71
 
72
+ # Create a service-specific logger with permanent tags
73
+ # All logs from this logger will include the "dotenv" tag
74
+ dotenv_logger = logger.tagged("dotenv")
75
+ dotenv_logger.info("Loading environment variables") # Includes "dotenv" tag
76
+ dotenv_logger.warn("Missing .env file") # Includes "dotenv" tag
77
+
78
+ # You can also create service loggers directly
79
+ base_logger = JsonLogging.logger($stdout)
80
+ service_logger = base_logger.tagged("my-service")
81
+ service_logger.info("Service started") # All logs tagged with "my-service"
82
+
81
83
  # Add context
82
84
  JsonLogging.with_context(user_id: 123) do
83
85
  logger.warn({event: "slow_query", duration_ms: 250})
@@ -100,6 +102,84 @@ This gem does **not** automatically configure your Rails app. You set it up manu
100
102
  - In Rails 7.1+, Rails automatically wraps your logger in `ActiveSupport::BroadcastLogger` to enable writing to multiple destinations (e.g., STDOUT and file simultaneously). This works seamlessly with our logger - your JSON logger will be wrapped and all method calls will delegate correctly. No special handling needed.
101
103
  - In Rails 7.1+, tag storage uses `ActiveSupport::IsolatedExecutionState` for improved thread/Fiber safety.
102
104
 
105
+ ### Service-specific loggers with tags
106
+
107
+ You can create loggers with permanent tags for specific services or components. This is useful when you want all logs from a particular service to be tagged consistently:
108
+
109
+ ```ruby
110
+ # Create a logger for DotEnv service with "dotenv" tag
111
+ base_logger = JsonLogging.logger($stdout)
112
+ dotenv_logger = base_logger.tagged("dotenv")
113
+
114
+ # All logs from this logger will include the "dotenv" tag
115
+ dotenv_logger.info("Loading .env file")
116
+ dotenv_logger.warn("Missing .env.local file")
117
+ dotenv_logger.error("Invalid environment variable format")
118
+
119
+ # Example: Configure Dotenv::Rails to use tagged logger
120
+ if defined?(Dotenv::Rails)
121
+ Dotenv::Rails.logger = base_logger.tagged("dotenv")
122
+ end
123
+
124
+ # Example: Create multiple service loggers
125
+ redis_logger = base_logger.tagged("redis")
126
+ sidekiq_logger = base_logger.tagged("sidekiq")
127
+ api_logger = base_logger.tagged("api")
128
+
129
+ # Each service logger maintains its tag across all log calls
130
+ redis_logger.info("Connected to Redis") # Tagged with "redis"
131
+ sidekiq_logger.info("Job enqueued") # Tagged with "sidekiq"
132
+ api_logger.info("Request received") # Tagged with "api"
133
+ ```
134
+
135
+ ### BroadcastLogger integration
136
+
137
+ `ActiveSupport::BroadcastLogger` (Rails 7.1+) allows writing logs to multiple destinations simultaneously. `JsonLogging` works seamlessly with `BroadcastLogger`:
138
+
139
+ ```ruby
140
+ # Create JSON loggers for different destinations
141
+ stdout_logger = JsonLogging.logger($stdout)
142
+ file_logger = JsonLogging.logger(Rails.root.join("log", "production.log"))
143
+
144
+ # Wrap in BroadcastLogger to write to both destinations
145
+ broadcast_logger = ActiveSupport::BroadcastLogger.new(stdout_logger)
146
+ broadcast_logger.broadcast_to(file_logger)
147
+
148
+ # All logging methods work through BroadcastLogger
149
+ broadcast_logger.info("This goes to both STDOUT and file")
150
+ broadcast_logger.warn({event: "warning", message: "Something happened"})
151
+
152
+ # Tagged logging works through BroadcastLogger
153
+ broadcast_logger.tagged("REQUEST", request_id) do
154
+ broadcast_logger.info("Processing request") # Tagged logs go to both destinations
155
+ end
156
+
157
+ # Service-specific loggers work with BroadcastLogger
158
+ # Note: Create service logger from underlying logger, then wrap in BroadcastLogger
159
+ # (BroadcastLogger.tagged without block returns array due to delegation)
160
+ base_logger = JsonLogging.logger($stdout)
161
+ dotenv_logger = base_logger.tagged("dotenv")
162
+ dotenv_broadcast = ActiveSupport::BroadcastLogger.new(dotenv_logger)
163
+ dotenv_broadcast.broadcast_to(file_logger.tagged("dotenv")) # Tag second destination too
164
+ dotenv_broadcast.info("Environment loaded") # Tagged and broadcast to all destinations
165
+
166
+ # Rails 7.1+ automatically uses BroadcastLogger
167
+ # Your configuration can be simplified:
168
+ Rails.application.configure do
169
+ # Rails will automatically wrap this in BroadcastLogger
170
+ base_logger = ActiveSupport::Logger.new($stdout)
171
+ json_logger = JsonLogging.new(base_logger)
172
+ config.logger = json_logger # Rails wraps this in BroadcastLogger automatically
173
+ end
174
+ ```
175
+
176
+ **Key points:**
177
+ - All logger methods (`info`, `warn`, `error`, etc.) work through `BroadcastLogger`
178
+ - Tagged logging (`tagged`) works correctly through `BroadcastLogger`
179
+ - Service-specific tagged loggers work with `BroadcastLogger`
180
+ - Each destination receives properly formatted JSON logs
181
+ - No special configuration needed - just wrap your `JsonLogging` logger in `BroadcastLogger`
182
+
103
183
  ### Basic setup
104
184
 
105
185
  Create `config/initializers/json_logging.rb`:
@@ -329,10 +409,10 @@ logger.tagged("BCX").tagged("Jason").info("Stuff")
329
409
  # Wrap a TaggedLogging logger - works perfectly
330
410
  tagged_logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
331
411
  json_logger = JsonLogging.new(tagged_logger)
332
- json_logger.tagged("TEST") { json_logger.info("message") } # Tags appear in JSON context
412
+ json_logger.tagged("TEST") { json_logger.info("message") } # Tags appear at root level in JSON
333
413
  ```
334
414
 
335
- **Note:** If you wrap a `JsonLogging` logger with `ActiveSupport::TaggedLogging`, the TaggedLogging's text-based tags will appear as part of the message string in the JSON output, not as structured tags in the context. For best results, wrap loggers with `JsonLogging` last.
415
+ **Note:** If you wrap a `JsonLogging` logger with `ActiveSupport::TaggedLogging`, the TaggedLogging's text-based tags will appear as part of the message string in the JSON output, not as structured tags at the root level. For best results, wrap loggers with `JsonLogging` last.
336
416
 
337
417
  ### JsonLogging::JsonLogger
338
418
 
@@ -359,7 +439,7 @@ formatter.call("INFO", Time.now, nil, "message")
359
439
 
360
440
  # With tags (useful for Puma or other standalone use cases)
361
441
  formatter = JsonLogging::Formatter.new(tags: ["Puma"])
362
- formatter.call("INFO", Time.now, nil, "message") # Output includes "Puma" tag in context
442
+ formatter.call("INFO", Time.now, nil, "message") # Output includes "Puma" tag at root level
363
443
 
364
444
  # Multiple tags
365
445
  formatter = JsonLogging::Formatter.new(tags: ["Puma", "Worker"])
@@ -511,12 +591,13 @@ Rails.application.config.filter_parameters += [
511
591
 
512
592
  The gem will automatically filter these from all log entries, including context data. Encrypted attributes (using Rails 7+ `encrypts`) are automatically filtered as well.
513
593
 
594
+
514
595
  ## Development
515
596
 
516
597
  ```bash
517
598
  # Install dependencies
518
599
  bundle install
519
- bundle exec appraisal install
600
+ bundle exec appraisal generate
520
601
 
521
602
  # Run tests for current Rails version
522
603
  bundle exec rspec
@@ -536,6 +617,47 @@ bundle exec appraisal rails-7.0 rspec
536
617
  bundle exec standardrb --fix
537
618
  ```
538
619
 
620
+ ### Development: Using from Local Repository
621
+
622
+ When developing the gem or testing changes in your application, you can point your Gemfile to a local path:
623
+
624
+ ```ruby
625
+ # In your application's Gemfile
626
+ gem "activesupport-json_logging", path: "../activesupport-json_logging.rb"
627
+ ```
628
+
629
+ Then run:
630
+
631
+ ```bash
632
+ bundle install
633
+ ```
634
+
635
+ **Note:** When using `path:` in your Gemfile, Bundler will use the local gem directly. Changes you make to the gem code will be immediately available in your application without needing to rebuild or reinstall the gem. This is ideal for development and testing.
636
+
637
+ ## Contributing
638
+
639
+ Bug reports and pull requests are welcome on GitHub at https://github.com/amkisko/activesupport-json_logging.rb
640
+
641
+ Contribution policy:
642
+ - New features are not necessarily added to the gem
643
+ - Pull request should have test coverage for affected parts
644
+ - Pull request should have changelog entry
645
+
646
+ Review policy:
647
+ - It might take up to 2 calendar weeks to review and merge critical fixes
648
+ - It might take up to 6 calendar months to review and merge pull request
649
+ - It might take up to 1 calendar year to review an issue
650
+
651
+
652
+ ## Publishing
653
+
654
+ ```sh
655
+ rm activesupport-json_logging-*.gem
656
+ gem build activesupport-json_logging.gemspec
657
+ gem push activesupport-json_logging-*.gem
658
+ ```
659
+
539
660
  ## License
540
661
 
541
- MIT
662
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
663
+
@@ -4,4 +4,3 @@ require "json_logging"
4
4
 
5
5
  # Ensure Railtie is loaded so Rails auto-discovers it
6
6
  require "activesupport/json_logging/railtie" if defined?(Rails)
7
-
@@ -8,7 +8,13 @@ module JsonLogging
8
8
  end
9
9
 
10
10
  def current_tags
11
- @logger.send(:current_tags)
11
+ # If LocalTagStorage is extended on this formatter, use its tag_stack
12
+ # This matches Rails' TaggedLogging behavior where tag_stack attribute shadows the method
13
+ if respond_to?(:tag_stack, true) && instance_variable_defined?(:@tag_stack)
14
+ tag_stack.tags
15
+ else
16
+ @logger.send(:current_tags)
17
+ end
12
18
  end
13
19
 
14
20
  def call(severity, timestamp, progname, msg)
@@ -22,18 +28,42 @@ module JsonLogging
22
28
  build_fallback_output(severity, timestamp, msg, e)
23
29
  end
24
30
 
31
+ def push_tags(*tags)
32
+ # If LocalTagStorage is present, use it; otherwise use logger's thread-local storage
33
+ if respond_to?(:tag_stack, true) && instance_variable_defined?(:@tag_stack)
34
+ tag_stack.push_tags(tags)
35
+ else
36
+ @logger.send(:push_tags, tags)
37
+ end
38
+ end
39
+
25
40
  # Support tagged blocks for formatter
26
41
  def tagged(*tags)
27
42
  if block_given?
28
- previous = @logger.send(:current_tags).dup
29
- @logger.send(:push_tags, tags)
30
- begin
31
- yield @logger
32
- ensure
33
- @logger.send(:set_tags, previous)
43
+ # If LocalTagStorage is present, use it; otherwise use logger's thread-local storage
44
+ if respond_to?(:tag_stack, true) && instance_variable_defined?(:@tag_stack)
45
+ previous_count = tag_stack.tags.size
46
+ tag_stack.push_tags(tags)
47
+ begin
48
+ yield @logger
49
+ ensure
50
+ tag_stack.pop_tags(tag_stack.tags.size - previous_count)
51
+ end
52
+ else
53
+ previous = @logger.send(:current_tags).dup
54
+ @logger.send(:push_tags, tags)
55
+ begin
56
+ yield @logger
57
+ ensure
58
+ @logger.send(:set_tags, previous)
59
+ end
34
60
  end
35
61
  else
36
- @logger.send(:push_tags, tags)
62
+ if respond_to?(:tag_stack, true) && instance_variable_defined?(:@tag_stack)
63
+ tag_stack.push_tags(tags)
64
+ else
65
+ @logger.send(:push_tags, tags)
66
+ end
37
67
  self
38
68
  end
39
69
  end
@@ -75,7 +75,9 @@ module JsonLogging
75
75
  # Return a new wrapped logger with tags applied (similar to TaggedLogging)
76
76
  logger = JsonLogging.new(self)
77
77
  # Extend formatter with LocalTagStorage to preserve current tags when creating nested loggers
78
+ # This matches Rails' TaggedLogging behavior
78
79
  logger.formatter.extend(LocalTagStorage)
80
+ # Push tags through formatter (matches Rails delegation pattern)
79
81
  logger.formatter.push_tags(*formatter.current_tags, *tags)
80
82
  logger
81
83
  end
@@ -132,7 +134,7 @@ module JsonLogging
132
134
  payload = PayloadBuilder.merge_context(
133
135
  payload,
134
136
  additional_context: JsonLogging.additional_context.compact,
135
- tags: current_tags
137
+ tags: formatter.current_tags
136
138
  )
137
139
 
138
140
  payload.compact
@@ -159,19 +161,37 @@ module JsonLogging
159
161
  # Module for preserving current tags when creating nested tagged loggers
160
162
  # Similar to ActiveSupport::TaggedLogging::LocalTagStorage
161
163
  # When extended on a formatter, stores tags locally instead of using thread-local storage
164
+ # Uses tag_stack attribute accessor pattern to match Rails' TaggedLogging behavior
162
165
  module LocalTagStorage
166
+ attr_accessor :tag_stack
167
+
163
168
  def self.extended(base)
164
- base.instance_variable_set(:@local_tags, [])
169
+ base.tag_stack = LocalTagStack.new
165
170
  end
166
171
 
167
- def push_tags(*tags)
168
- flat = tags.flatten.compact.map(&:to_s).reject(&:empty?)
169
- return if flat.empty?
170
- @local_tags = (@local_tags || []) + flat
171
- end
172
+ # Simple tag stack implementation for local tag storage
173
+ # Similar to ActiveSupport::TaggedLogging::TagStack but simplified for JSON logging
174
+ class LocalTagStack
175
+ attr_reader :tags
172
176
 
173
- def current_tags
174
- @local_tags || []
177
+ def initialize
178
+ @tags = []
179
+ end
180
+
181
+ def push_tags(tags)
182
+ flat = Array(tags).flatten.compact.map(&:to_s).reject(&:empty?)
183
+ return [] if flat.empty?
184
+ @tags.concat(flat)
185
+ flat
186
+ end
187
+
188
+ def pop_tags(count = 1)
189
+ @tags.pop(count)
190
+ end
191
+
192
+ def clear
193
+ @tags.clear
194
+ end
175
195
  end
176
196
  end
177
197
  end
@@ -29,14 +29,22 @@ module JsonLogging
29
29
  additional_context || {}
30
30
  end
31
31
 
32
- deduped_additional = sanitized_context.reject { |k, _| payload.key?(k) }
32
+ # Filter out system-controlled keys from user context
33
+ # These keys should never be set by user context as they're controlled by the logger
34
+ system_controlled_keys = [:tags, "tags", :severity, "severity", :timestamp, "timestamp", :message, "message", :context, "context"]
35
+ user_context_filtered = sanitized_context.except(*system_controlled_keys)
36
+
37
+ # Also prevent overriding any existing payload keys (additional safety)
38
+ deduped_additional = user_context_filtered.reject { |k, _| payload.key?(k) }
33
39
  merged_context = existing_context.merge(deduped_additional)
34
40
 
41
+ # Put tags at root level, separate from context
42
+ # Merge with existing tags from payload (e.g., when logging a hash with tags: [...] at root)
35
43
  unless tags.empty?
36
- existing_tags = Array(merged_context[:tags])
44
+ existing_tags = Array(payload[:tags] || payload["tags"])
37
45
  # Sanitize tag strings (remove control chars, truncate)
38
46
  sanitized_tags = tags.map { |tag| Sanitizer.sanitize_string(tag.to_s) }
39
- merged_context[:tags] = (existing_tags + sanitized_tags).uniq
47
+ payload[:tags] = (existing_tags + sanitized_tags).uniq
40
48
  end
41
49
 
42
50
  payload[:context] = merged_context unless merged_context.empty?
@@ -1,3 +1,3 @@
1
1
  module JsonLogging
2
- VERSION = "1.0.0"
2
+ VERSION = "1.2.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activesupport-json_logging
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
- - amkisko
7
+ - Andrei Makarov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-10-31 00:00:00.000000000 Z
11
+ date: 2025-11-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -56,84 +56,140 @@ dependencies:
56
56
  requirements:
57
57
  - - "~>"
58
58
  - !ruby/object:Gem::Version
59
- version: '3.12'
59
+ version: '3'
60
60
  type: :development
61
61
  prerelease: false
62
62
  version_requirements: !ruby/object:Gem::Requirement
63
63
  requirements:
64
64
  - - "~>"
65
65
  - !ruby/object:Gem::Version
66
- version: '3.12'
66
+ version: '3'
67
+ - !ruby/object:Gem::Dependency
68
+ name: webmock
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: '3'
74
+ type: :development
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - "~>"
79
+ - !ruby/object:Gem::Version
80
+ version: '3'
67
81
  - !ruby/object:Gem::Dependency
68
82
  name: rake
69
83
  requirement: !ruby/object:Gem::Requirement
70
84
  requirements:
71
85
  - - "~>"
72
86
  - !ruby/object:Gem::Version
73
- version: '13.0'
87
+ version: '13'
74
88
  type: :development
75
89
  prerelease: false
76
90
  version_requirements: !ruby/object:Gem::Requirement
77
91
  requirements:
78
92
  - - "~>"
79
93
  - !ruby/object:Gem::Version
80
- version: '13.0'
94
+ version: '13'
81
95
  - !ruby/object:Gem::Dependency
82
96
  name: simplecov
83
97
  requirement: !ruby/object:Gem::Requirement
84
98
  requirements:
85
99
  - - "~>"
86
100
  - !ruby/object:Gem::Version
87
- version: '0.21'
101
+ version: '0.22'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - "~>"
107
+ - !ruby/object:Gem::Version
108
+ version: '0.22'
109
+ - !ruby/object:Gem::Dependency
110
+ name: rspec_junit_formatter
111
+ requirement: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - "~>"
114
+ - !ruby/object:Gem::Version
115
+ version: '0.6'
88
116
  type: :development
89
117
  prerelease: false
90
118
  version_requirements: !ruby/object:Gem::Requirement
91
119
  requirements:
92
120
  - - "~>"
93
121
  - !ruby/object:Gem::Version
94
- version: '0.21'
122
+ version: '0.6'
123
+ - !ruby/object:Gem::Dependency
124
+ name: simplecov-cobertura
125
+ requirement: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - "~>"
128
+ - !ruby/object:Gem::Version
129
+ version: '3'
130
+ type: :development
131
+ prerelease: false
132
+ version_requirements: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - "~>"
135
+ - !ruby/object:Gem::Version
136
+ version: '3'
95
137
  - !ruby/object:Gem::Dependency
96
138
  name: standard
97
139
  requirement: !ruby/object:Gem::Requirement
98
140
  requirements:
99
141
  - - "~>"
100
142
  - !ruby/object:Gem::Version
101
- version: '1.0'
143
+ version: '1'
102
144
  type: :development
103
145
  prerelease: false
104
146
  version_requirements: !ruby/object:Gem::Requirement
105
147
  requirements:
106
148
  - - "~>"
107
149
  - !ruby/object:Gem::Version
108
- version: '1.0'
150
+ version: '1'
109
151
  - !ruby/object:Gem::Dependency
110
152
  name: appraisal
111
153
  requirement: !ruby/object:Gem::Requirement
112
154
  requirements:
113
155
  - - "~>"
114
156
  - !ruby/object:Gem::Version
115
- version: '2.4'
157
+ version: '2'
116
158
  type: :development
117
159
  prerelease: false
118
160
  version_requirements: !ruby/object:Gem::Requirement
119
161
  requirements:
120
162
  - - "~>"
121
163
  - !ruby/object:Gem::Version
122
- version: '2.4'
164
+ version: '2'
123
165
  - !ruby/object:Gem::Dependency
124
166
  name: memory_profiler
125
167
  requirement: !ruby/object:Gem::Requirement
126
168
  requirements:
127
169
  - - "~>"
128
170
  - !ruby/object:Gem::Version
129
- version: '1.0'
171
+ version: '1'
172
+ type: :development
173
+ prerelease: false
174
+ version_requirements: !ruby/object:Gem::Requirement
175
+ requirements:
176
+ - - "~>"
177
+ - !ruby/object:Gem::Version
178
+ version: '1'
179
+ - !ruby/object:Gem::Dependency
180
+ name: rbs
181
+ requirement: !ruby/object:Gem::Requirement
182
+ requirements:
183
+ - - "~>"
184
+ - !ruby/object:Gem::Version
185
+ version: '3'
130
186
  type: :development
131
187
  prerelease: false
132
188
  version_requirements: !ruby/object:Gem::Requirement
133
189
  requirements:
134
190
  - - "~>"
135
191
  - !ruby/object:Gem::Version
136
- version: '1.0'
192
+ version: '3'
137
193
  description: Lightweight JSON logger and formatter integrating with Rails/ActiveSupport.
138
194
  No extra deps beyond Rails/Activesupport. Compatible with Rails 6–8.
139
195
  email: