console 1.35.1 → 1.36.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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/lib/console/format/safe.rb +147 -20
- data/lib/console/format.rb +1 -0
- data/lib/console/version.rb +1 -1
- data/readme.md +6 -6
- data/releases.md +6 -0
- data.tar.gz.sig +2 -2
- metadata +2 -2
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 749f01df7fec2a4868a2b4958b23ee328ddb2911b0fc2a635bdf298d901cae60
|
|
4
|
+
data.tar.gz: 32daa46920a77f0fa713685d0176dc24ed034fe39fe99cde3d1d789ecfb1c48b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 51796e3366c0778f6e811428ff9e8aeffb0a34fe99ce0528488fd9931c392596c1a654f34e49781f68afd1377898ef16ce6aeaaa3a35c82c201bf4579f423073
|
|
7
|
+
data.tar.gz: 82af50570a7e55a26562cda15f7ccf5622bcc956ef6b6a065f4a1e38ecf663911f84ccf6a47de1443bc85f1b1e79effeec1fcbdccc21480c0b18b53872efdc13
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/lib/console/format/safe.rb
CHANGED
|
@@ -10,31 +10,166 @@ module Console
|
|
|
10
10
|
module Format
|
|
11
11
|
# A safe format for converting objects to strings.
|
|
12
12
|
#
|
|
13
|
-
# Handles issues like circular references and
|
|
13
|
+
# Handles issues like circular references, encoding errors, excessive nesting depth, and excessive output size.
|
|
14
14
|
class Safe
|
|
15
|
+
# The JSON fragment used as the truncation marker when dropped fields cannot be named.
|
|
16
|
+
TRUNCATED = "\"truncated\":true"
|
|
17
|
+
|
|
15
18
|
# Create a new safe format.
|
|
16
19
|
#
|
|
17
20
|
# @parameter format [JSON] The format to use for serialization.
|
|
18
|
-
# @parameter
|
|
21
|
+
# @parameter depth_limit [Integer] The maximum depth to recurse into objects (the JSON `max_nesting`).
|
|
22
|
+
# @parameter size_limit [Integer | Nil] The maximum byte size of the serialized output, or `nil` to disable size limiting. Limits below {TRUNCATED} (the minimal marker) cannot be honoured.
|
|
19
23
|
# @parameter encoding [Encoding] The encoding to use for strings.
|
|
20
|
-
|
|
24
|
+
# @parameter limit [Integer | Nil] Deprecated alias for `depth_limit`.
|
|
25
|
+
def initialize(format: ::JSON, depth_limit: 12, size_limit: 16 * 1024, encoding: ::Encoding::UTF_8, limit: nil)
|
|
26
|
+
if limit
|
|
27
|
+
warn "Console::Format::Safe `limit:` is deprecated, use `depth_limit:` instead.", uplevel: 1, category: :deprecated
|
|
28
|
+
depth_limit = limit
|
|
29
|
+
end
|
|
30
|
+
|
|
21
31
|
@format = format
|
|
22
|
-
@
|
|
32
|
+
@depth_limit = depth_limit
|
|
33
|
+
@size_limit = size_limit
|
|
23
34
|
@encoding = encoding
|
|
24
35
|
end
|
|
25
36
|
|
|
37
|
+
# @attribute [Integer] The maximum depth to recurse into objects.
|
|
38
|
+
attr :depth_limit
|
|
39
|
+
|
|
40
|
+
# @attribute [Integer | Nil] The maximum byte size of the serialized output.
|
|
41
|
+
attr :size_limit
|
|
42
|
+
|
|
26
43
|
# Dump the given object to a string.
|
|
27
44
|
#
|
|
45
|
+
# The common case is a single fast serialization. If that fails (e.g. circular
|
|
46
|
+
# references, excessive nesting, or encoding errors) or its output exceeds
|
|
47
|
+
# {size_limit}, it falls back to {safe_dump}, which rebuilds the record
|
|
48
|
+
# field-by-field within the limit.
|
|
49
|
+
#
|
|
28
50
|
# @parameter object [Object] The object to dump.
|
|
29
51
|
# @returns [String] The dumped object.
|
|
30
52
|
def dump(object)
|
|
31
|
-
@format.dump(object, @
|
|
32
|
-
|
|
33
|
-
@
|
|
53
|
+
buffer = @format.dump(object, @depth_limit)
|
|
54
|
+
|
|
55
|
+
if @size_limit and buffer.bytesize > @size_limit
|
|
56
|
+
return safe_dump(object)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
return buffer
|
|
60
|
+
rescue SystemStackError, StandardError
|
|
61
|
+
return safe_dump(object)
|
|
34
62
|
end
|
|
35
63
|
|
|
36
64
|
private
|
|
37
65
|
|
|
66
|
+
# Produce a safe, size-limited serialization of the given object. This is the
|
|
67
|
+
# fallback path, used both when direct serialization fails (an exception) and
|
|
68
|
+
# when its output exceeds {size_limit}.
|
|
69
|
+
#
|
|
70
|
+
# Each top-level value is serialized independently and defensively, so a single
|
|
71
|
+
# un-serializable or oversized value cannot break or bloat the whole record.
|
|
72
|
+
# Whenever a field is degraded, the reason is recorded in a trailing `"truncated"`
|
|
73
|
+
# object that maps the field name to why it was truncated:
|
|
74
|
+
#
|
|
75
|
+
# - `"key": true` — the value was dropped because it did not fit the size limit.
|
|
76
|
+
# - `"key": {error}` — the value could not be serialized directly; a safe
|
|
77
|
+
# representation was kept in its place and the triggering error is recorded.
|
|
78
|
+
#
|
|
79
|
+
# Fields are kept while they fit, always reserving room for at least a minimal
|
|
80
|
+
# `"truncated":true` marker. The detailed reason map is then emitted only if it
|
|
81
|
+
# fits in the remaining space; otherwise it degrades to `"truncated":true`. This
|
|
82
|
+
# is best-effort — in the worst case the per-field detail is lost — but it keeps
|
|
83
|
+
# the bookkeeping simple and the size guarantee hard.
|
|
84
|
+
#
|
|
85
|
+
# @parameter object [Object] The object to serialize.
|
|
86
|
+
# @returns [String] The safe, size-limited serialized record.
|
|
87
|
+
def safe_dump(object)
|
|
88
|
+
# Serialize hash-like objects field-by-field; anything else falls through to the
|
|
89
|
+
# error handler below, which emits a minimal truncated marker.
|
|
90
|
+
object = object.to_hash
|
|
91
|
+
|
|
92
|
+
# Serialize each field once, capturing the error for any value that could not be
|
|
93
|
+
# serialized directly. Our own "truncated" key is skipped so it is never duplicated.
|
|
94
|
+
errors = {}
|
|
95
|
+
fragments = []
|
|
96
|
+
object.each do |key, value|
|
|
97
|
+
name = key.to_s
|
|
98
|
+
next if name == "truncated"
|
|
99
|
+
|
|
100
|
+
fragment, error = dump_pair(key, value)
|
|
101
|
+
errors[name] = error_info(error) if error
|
|
102
|
+
fragments << [name, fragment]
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Assemble the body, keeping each field while it fits — always reserving room for
|
|
106
|
+
# at least a minimal `"truncated":true` marker. Each truncated field's reason is
|
|
107
|
+
# collected: its error (value recovered) or `true` (dropped for size).
|
|
108
|
+
buffer = +"{"
|
|
109
|
+
first = true
|
|
110
|
+
reasons = {}
|
|
111
|
+
|
|
112
|
+
fragments.each do |name, fragment|
|
|
113
|
+
if buffer.bytesize + (first ? 0 : 1) + fragment.bytesize + TRUNCATED.bytesize + 2 <= @size_limit
|
|
114
|
+
buffer << "," unless first
|
|
115
|
+
buffer << fragment
|
|
116
|
+
first = false
|
|
117
|
+
|
|
118
|
+
# The value was kept; if it had to be recovered, note why.
|
|
119
|
+
reasons[name] = errors[name] if errors[name]
|
|
120
|
+
else
|
|
121
|
+
# The value did not fit and was dropped entirely.
|
|
122
|
+
reasons[name] = true
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
unless reasons.empty?
|
|
127
|
+
# Include the detailed reasons if they fit, otherwise fall back to the minimal
|
|
128
|
+
# marker so the truncation is still signalled.
|
|
129
|
+
detailed = "\"truncated\":#{@format.dump(reasons)}"
|
|
130
|
+
fits = buffer.bytesize + (first ? 0 : 1) + detailed.bytesize + 1 <= @size_limit
|
|
131
|
+
|
|
132
|
+
buffer << "," unless first
|
|
133
|
+
buffer << (fits ? detailed : TRUNCATED)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
buffer << "}"
|
|
137
|
+
|
|
138
|
+
return buffer
|
|
139
|
+
rescue SystemStackError, StandardError
|
|
140
|
+
return "{#{TRUNCATED}}"
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Serialize a single top-level `"key":value` pair, safely handling values that
|
|
144
|
+
# cannot be serialized directly.
|
|
145
|
+
#
|
|
146
|
+
# @parameter key [Object] The field key.
|
|
147
|
+
# @parameter value [Object] The field value.
|
|
148
|
+
# @returns [Array(String, Exception | Nil)] The `"key":value` fragment and the error, if recovery was needed.
|
|
149
|
+
def dump_pair(key, value)
|
|
150
|
+
value_json, error = dump_value(value)
|
|
151
|
+
|
|
152
|
+
return ["#{dump_string(String(key))}:#{value_json}", error]
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Serialize a single value, falling back to a safe representation on failure.
|
|
156
|
+
#
|
|
157
|
+
# @parameter value [Object] The value to serialize.
|
|
158
|
+
# @returns [Array(String, Exception | Nil)] The serialized value and the error, if recovery was needed.
|
|
159
|
+
def dump_value(value)
|
|
160
|
+
[@format.dump(value, @depth_limit), nil]
|
|
161
|
+
rescue SystemStackError, StandardError => error
|
|
162
|
+
[@format.dump(safe_dump_recurse(value)), error]
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Serialize a string as a JSON string, encoding it safely first.
|
|
166
|
+
#
|
|
167
|
+
# @parameter value [String] The string to serialize.
|
|
168
|
+
# @returns [String] The serialized (quoted) string.
|
|
169
|
+
def dump_string(value)
|
|
170
|
+
@format.dump(value.encode(@encoding, invalid: :replace, undef: :replace))
|
|
171
|
+
end
|
|
172
|
+
|
|
38
173
|
# Filter the backtrace to remove duplicate frames and reduce verbosity.
|
|
39
174
|
#
|
|
40
175
|
# @parameter error [Exception] The exception to filter.
|
|
@@ -76,24 +211,16 @@ module Console
|
|
|
76
211
|
return frames
|
|
77
212
|
end
|
|
78
213
|
|
|
79
|
-
#
|
|
80
|
-
#
|
|
81
|
-
# This is a slow path so we try to avoid it.
|
|
214
|
+
# Build a safe, primitive representation of an error for inclusion as an `"error"` field.
|
|
82
215
|
#
|
|
83
|
-
# @parameter object [Object] The object to dump.
|
|
84
216
|
# @parameter error [Exception] The error that occurred while dumping the object.
|
|
85
|
-
# @returns [Hash] The
|
|
86
|
-
def
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
object[:truncated] = true
|
|
90
|
-
object[:error] = {
|
|
217
|
+
# @returns [Hash] The error details (class, message, filtered backtrace).
|
|
218
|
+
def error_info(error)
|
|
219
|
+
{
|
|
91
220
|
class: safe_dump_recurse(error.class.name),
|
|
92
221
|
message: safe_dump_recurse(error.message),
|
|
93
222
|
backtrace: safe_dump_recurse(filter_backtrace(error)),
|
|
94
223
|
}
|
|
95
|
-
|
|
96
|
-
return object
|
|
97
224
|
end
|
|
98
225
|
|
|
99
226
|
# Create a new hash with identity comparison.
|
|
@@ -107,7 +234,7 @@ module Console
|
|
|
107
234
|
# @parameter limit [Integer] The maximum depth to recurse into objects.
|
|
108
235
|
# @parameter objects [Hash] The objects that have already been visited.
|
|
109
236
|
# @returns [Object] The dumped object as a primitive representation.
|
|
110
|
-
def safe_dump_recurse(object, limit = @
|
|
237
|
+
def safe_dump_recurse(object, limit = @depth_limit, objects = default_objects)
|
|
111
238
|
case object
|
|
112
239
|
when Hash
|
|
113
240
|
if limit <= 0 || objects[object]
|
data/lib/console/format.rb
CHANGED
data/lib/console/version.rb
CHANGED
data/readme.md
CHANGED
|
@@ -34,6 +34,12 @@ Please see the [project documentation](https://socketry.github.io/console/) for
|
|
|
34
34
|
|
|
35
35
|
Please see the [project releases](https://socketry.github.io/console/releases/index) for all releases.
|
|
36
36
|
|
|
37
|
+
### v1.36.0
|
|
38
|
+
|
|
39
|
+
- Add a `size_limit` to `Console::Format::Safe` (default 16KiB) which rebuilds oversized records field-by-field, keeping as many top-level fields as fit within the limit.
|
|
40
|
+
- Degraded fields are recorded in a `truncated` object that maps each field name to why it was truncated: `true` (dropped for size) or the error (the value could not be serialized directly and a safe representation was kept in its place).
|
|
41
|
+
- Rename `Console::Format::Safe`'s `limit:` to `depth_limit:` (with a deprecated `limit:` alias) to clarify its purpose alongside the new `size_limit:`.
|
|
42
|
+
|
|
37
43
|
### v1.35.0
|
|
38
44
|
|
|
39
45
|
- Fix handling of `Errno::ENODEV` errors when calculating the width of a terminal that was been re-opened to `File::NULL`.
|
|
@@ -74,12 +80,6 @@ Please see the [project releases](https://socketry.github.io/console/releases/in
|
|
|
74
80
|
|
|
75
81
|
- Fix logging `exception:` keyword argument when the value was not an exception.
|
|
76
82
|
|
|
77
|
-
### v1.29.0
|
|
78
|
-
|
|
79
|
-
- Don't make `Kernel#warn` redirection to `Console.warn` the default behavior, you must `require 'console/warn'` to enable it.
|
|
80
|
-
- Remove deprecated `Console::Logger#failure`.
|
|
81
|
-
- [Consistent handling of exceptions.](https://socketry.github.io/console/releases/index#consistent-handling-of-exceptions.)
|
|
82
|
-
|
|
83
83
|
## Contributing
|
|
84
84
|
|
|
85
85
|
We welcome contributions to this project.
|
data/releases.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Releases
|
|
2
2
|
|
|
3
|
+
## v1.36.0
|
|
4
|
+
|
|
5
|
+
- Add a `size_limit` to `Console::Format::Safe` (default 16KiB) which rebuilds oversized records field-by-field, keeping as many top-level fields as fit within the limit.
|
|
6
|
+
- Degraded fields are recorded in a `truncated` object that maps each field name to why it was truncated: `true` (dropped for size) or the error (the value could not be serialized directly and a safe representation was kept in its place).
|
|
7
|
+
- Rename `Console::Format::Safe`'s `limit:` to `depth_limit:` (with a deprecated `limit:` alias) to clarify its purpose alongside the new `size_limit:`.
|
|
8
|
+
|
|
3
9
|
## v1.35.0
|
|
4
10
|
|
|
5
11
|
- Fix handling of `Errno::ENODEV` errors when calculating the width of a terminal that was been re-opened to `File::NULL`.
|
data.tar.gz.sig
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
:}��CS4��<\i�&��<ք��P��]�ן��:�C��3�l����|Q���'���]G
|
|
2
|
+
�[�����
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: console
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.36.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Samuel Williams
|
|
@@ -163,7 +163,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
163
163
|
- !ruby/object:Gem::Version
|
|
164
164
|
version: '0'
|
|
165
165
|
requirements: []
|
|
166
|
-
rubygems_version: 4.0.
|
|
166
|
+
rubygems_version: 4.0.10
|
|
167
167
|
specification_version: 4
|
|
168
168
|
summary: Beautiful logging for Ruby.
|
|
169
169
|
test_files: []
|
metadata.gz.sig
CHANGED
|
Binary file
|