console 1.34.3 → 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 +3 -2
- data/context/command-line.md +47 -0
- data/context/configuration.md +23 -0
- data/context/events.md +71 -0
- data/context/getting-started.md +170 -0
- data/context/index.yaml +28 -0
- data/context/integration.md +37 -0
- data/lib/console/clock.rb +2 -1
- data/lib/console/filter.rb +1 -0
- data/lib/console/format/safe.rb +147 -20
- data/lib/console/format.rb +1 -0
- data/lib/console/terminal/xterm.rb +1 -1
- data/lib/console/version.rb +2 -2
- data/license.md +3 -2
- data/readme.md +26 -10
- data/releases.md +10 -0
- data.tar.gz.sig +2 -1
- metadata +14 -5
- 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
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
��
|
|
2
|
-
|
|
1
|
+
2�S*��F#4/�Pb �(�d��"�Xa�È9G�'��^�j����Պ��hm�ե2Vg�W�ųӮ�Dy�.��Hr]%�X'6��'�e���3��m��r;pִ��F�IոR�b
|
|
2
|
+
�O��م-� ����f�!zh����;���|=dl�̲*�d��E�ŽrIו`���s'X��r���SQ ��[���@���m����p6iL�u��]�_U�/����a�=8�*������GkBbC���¹v8>�Ƴ��n�Ӭ���{�'��`�m��7
|
|
3
|
+
��v[Z���
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Command Line
|
|
2
|
+
|
|
3
|
+
This guide explains how the `console` gem can be controlled using environment variables.
|
|
4
|
+
|
|
5
|
+
## Environment Variables
|
|
6
|
+
|
|
7
|
+
The following program, `app.rb` will be used to explain the different environmet variables:
|
|
8
|
+
|
|
9
|
+
~~~ ruby
|
|
10
|
+
#!/usr/bin/env ruby
|
|
11
|
+
|
|
12
|
+
require 'console'
|
|
13
|
+
|
|
14
|
+
class MyApplication
|
|
15
|
+
def do_work
|
|
16
|
+
Console.logger.debug(self, "Doing some work.")
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
Console.logger.debug(self, "Creating the application.")
|
|
21
|
+
application = MyApplication.new
|
|
22
|
+
application.do_work
|
|
23
|
+
~~~
|
|
24
|
+
|
|
25
|
+
### `CONSOLE_LEVEL=debug`
|
|
26
|
+
|
|
27
|
+
Control the default logger level. You can set it to any of the supported log levels: `debug`, `info`, `warn`, `error`, `fatal`.
|
|
28
|
+
|
|
29
|
+
By default, the debug level is not enabled, but you can enable it using `CONSOLE_LEVEL=debug`:
|
|
30
|
+
|
|
31
|
+
<pre>> <font color="#00AFFF">CONSOLE_LEVEL</font><font color="#00A6B2">=</font><font color="#00AFFF">debug</font> <font color="#005FD7">./app.rb</font>
|
|
32
|
+
<font color="#00AAAA"> 0.0s debug:</font> <b>Object</b> <font color="#717171">[oid=0x3c] [ec=0x50] [pid=990900] [2022-10-12 17:28:15 +1300]</font>
|
|
33
|
+
| Creating the application.
|
|
34
|
+
<font color="#00AAAA"> 0.0s debug:</font> <b>MyApplication</b> <font color="#717171">[oid=0x64] [ec=0x50] [pid=990900] [2022-10-12 17:28:15 +1300]</font>
|
|
35
|
+
| Doing some work.
|
|
36
|
+
</pre>
|
|
37
|
+
|
|
38
|
+
### `CONSOLE_DEBUG=MyClass,MyModule::MyClass`
|
|
39
|
+
|
|
40
|
+
Enable debug logging for the specified class names. You can specify one or more class names which will be resolved at runtime. This is specifically related to subject logging.
|
|
41
|
+
|
|
42
|
+
By default, the debug level is not enabled, but you can enable it for the specific `MyApplication` class using `CONSOLE_DEBUG=MyApplication`:
|
|
43
|
+
|
|
44
|
+
<pre>> <font color="#00AFFF">CONSOLE_DEBUG</font><font color="#00A6B2">=</font><font color="#00AFFF">MyApplication</font> <font color="#005FD7">./app.rb</font>
|
|
45
|
+
<font color="#00AAAA"> 0.0s debug:</font> <b>MyApplication</b> <font color="#717171">[oid=0x64] [ec=0x78] [pid=991855] [2022-10-12 17:30:56 +1300]</font>
|
|
46
|
+
| Doing some work.
|
|
47
|
+
</pre>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
This guide explains how to implement per-project configuration for the `console` gem.
|
|
4
|
+
|
|
5
|
+
## Configuration File
|
|
6
|
+
|
|
7
|
+
The `console` gem can load a configuration file, by default `config/console.rb`. This file is evaluated in an instance of {ruby Console::Config} which allows you to override methods that implement the default behaviour for a given project.
|
|
8
|
+
|
|
9
|
+
Here is an example configuration file:
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
# config/console.rb
|
|
13
|
+
|
|
14
|
+
# Override the default log level
|
|
15
|
+
def log_level
|
|
16
|
+
:debug
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Override the default output
|
|
20
|
+
def make_output
|
|
21
|
+
Console::Output::Datadog.new(Console::Output::Default.new)
|
|
22
|
+
end
|
|
23
|
+
```
|
data/context/events.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Events
|
|
2
|
+
|
|
3
|
+
This guide explains how to log structured events with a well-defined schema.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Logs often fall into two categories:
|
|
8
|
+
|
|
9
|
+
- Free-form logs which include some structured data, but are primarily text-based and used for debugging or troubleshooting.
|
|
10
|
+
- Structured logs which are designed to be machine-readable and can be used for monitoring, alerting, auditing and analytics.
|
|
11
|
+
|
|
12
|
+
Events are a type of structured log which are designed to be machine-readable and have a well-defined schema. They are used to represent a specific occurrence within a system, such as a request, a response, an error, or a warning. You can create custom events to represent any structured data you like.
|
|
13
|
+
|
|
14
|
+
## Core Concepts
|
|
15
|
+
|
|
16
|
+
- {ruby Console::Event::Generic} is the base class for all events.
|
|
17
|
+
- {ruby Console::Terminal::Formatter} includes a collection of formatters for rendering specific events in a human-readable format.
|
|
18
|
+
|
|
19
|
+
## Emitting Events
|
|
20
|
+
|
|
21
|
+
To emit an event, you can create an instance of a specific event class and call the `#emit` method. For example, to emit a failure event:
|
|
22
|
+
|
|
23
|
+
```ruby
|
|
24
|
+
def handle_request
|
|
25
|
+
begin
|
|
26
|
+
# ... user code ...
|
|
27
|
+
rescue => error
|
|
28
|
+
Console::Event::Failure.for(error).emit(self, "Failed to handle request!")
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
This will emit a failure event with the error message and backtrace.
|
|
34
|
+
|
|
35
|
+
### Emitting Events with Different Severity Levels
|
|
36
|
+
|
|
37
|
+
Events can have different severity levels, such as `:info`, `:warn`, `:error`, and `:fatal`. You can specify the severity level when emitting an event:
|
|
38
|
+
|
|
39
|
+
```ruby
|
|
40
|
+
Console::Event::Failure.for(error).emit(self, "Failed to handle request!", severity: :debug)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Custom Events
|
|
44
|
+
|
|
45
|
+
You can create custom events by subclassing {ruby Console::Event::Generic} and defining the schema for the event. For example, to create a custom event for tracking user logins:
|
|
46
|
+
|
|
47
|
+
```ruby
|
|
48
|
+
class UserLoginEvent < Console::Event::Generic
|
|
49
|
+
def self.for(request, user)
|
|
50
|
+
self.new(user.id, request.ip)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def new(id, ip)
|
|
54
|
+
@id = id
|
|
55
|
+
@ip = ip
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def to_hash
|
|
59
|
+
{
|
|
60
|
+
# Specifying a type field is recommended:
|
|
61
|
+
type: :login,
|
|
62
|
+
|
|
63
|
+
# Custom fields:
|
|
64
|
+
id: @id,
|
|
65
|
+
ip: @ip
|
|
66
|
+
}
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
UserLoginEvent.for(request, user).emit(self, "User logged in.")
|
|
71
|
+
```
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# Getting Started
|
|
2
|
+
|
|
3
|
+
This guide explains how to use `console` for logging.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Add the gem to your project:
|
|
8
|
+
|
|
9
|
+
~~~ bash
|
|
10
|
+
$ bundle add console
|
|
11
|
+
~~~
|
|
12
|
+
|
|
13
|
+
## Core Concepts
|
|
14
|
+
|
|
15
|
+
`console` has several core concepts:
|
|
16
|
+
|
|
17
|
+
- A log message which consists of a set of arguments and options, which includes a timestamp, severity, and other structured data.
|
|
18
|
+
- The {ruby Console} module which provides an abstract interface for logging.
|
|
19
|
+
- A {ruby Console::Logger} instance which is the main entry point for logging for a specific system and writes data to a given output formatter.
|
|
20
|
+
- An output instance such as {ruby Console::XTerm}, {ruby Console::Serialized::Logger} which formats these log messages and writes them to a specific output device.
|
|
21
|
+
- An event instance, such as {ruby Console::Event::Progress} or {ruby Console::Event::Spawn} which represents a structured event within a system, which can be formatted in a specific way.
|
|
22
|
+
|
|
23
|
+
## Basic Logging
|
|
24
|
+
|
|
25
|
+
Out of the box, {ruby Console} provides a logger interface that outputs to the current terminal via `stderr`.
|
|
26
|
+
|
|
27
|
+
~~~ ruby
|
|
28
|
+
require 'console'
|
|
29
|
+
|
|
30
|
+
Console.info("Hello World")
|
|
31
|
+
~~~
|
|
32
|
+
|
|
33
|
+
<pre>
|
|
34
|
+
<font color="#00AA00"> 0.0s info:</font> <b>Hello World</b> <font color="#717171">[pid=219113] [2020-08-08 12:21:26 +1200]</font>
|
|
35
|
+
</pre>
|
|
36
|
+
|
|
37
|
+
The method name `info` indicates the severity level of the log message. You can filter out severity levels, and by default, `debug` messages are filtered out. Here are some examples of the different log levels:
|
|
38
|
+
|
|
39
|
+
~~~ ruby
|
|
40
|
+
require 'console'
|
|
41
|
+
|
|
42
|
+
Console.debug("The input voltage has stabilized.")
|
|
43
|
+
Console.info("Making a request to the machine.")
|
|
44
|
+
Console.warn("The machine has detected a temperature anomaly.")
|
|
45
|
+
Console.error("The machine was unable to complete the request!")
|
|
46
|
+
Console.fatal("Depressurisation detected, evacuate the area!")
|
|
47
|
+
~~~
|
|
48
|
+
|
|
49
|
+
From the terminal, you can control the log level using the `CONSOLE_LEVEL` environment variable. To log all messages including `debug`:
|
|
50
|
+
|
|
51
|
+
~~~ bash
|
|
52
|
+
$ CONSOLE_LEVEL=debug ./machine
|
|
53
|
+
~~~
|
|
54
|
+
|
|
55
|
+
Alternatively to restrict log messages to warnings and above:
|
|
56
|
+
|
|
57
|
+
~~~ bash
|
|
58
|
+
$ CONSOLE_LEVEL=warn ./machine
|
|
59
|
+
~~~
|
|
60
|
+
|
|
61
|
+
If otherwise unspecified, Ruby's standard `$DEBUG` and `$VERBOSE` global variables will be checked and adjust the log level appropriately.
|
|
62
|
+
|
|
63
|
+
## Metadata
|
|
64
|
+
|
|
65
|
+
You can add any options you like to a log message and they will be included as part of the log output:
|
|
66
|
+
|
|
67
|
+
~~~ ruby
|
|
68
|
+
duration = measure{...}
|
|
69
|
+
Console.info("Execution completed!", duration: duration)
|
|
70
|
+
~~~
|
|
71
|
+
|
|
72
|
+
## Subject Logging
|
|
73
|
+
|
|
74
|
+
The first argument to the log statement becomes the implicit subject of the log message.
|
|
75
|
+
|
|
76
|
+
~~~ ruby
|
|
77
|
+
require 'console'
|
|
78
|
+
|
|
79
|
+
class Machine
|
|
80
|
+
def initialize(voltage)
|
|
81
|
+
@voltage = voltage.floor
|
|
82
|
+
Console.info(self, "The input voltage has stabilized.")
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
Machine.new(5.5)
|
|
87
|
+
~~~
|
|
88
|
+
|
|
89
|
+
The given subject, in this case `self`, is used on the first line, along with associated metadata, while the message itself appears on subsequent lines:
|
|
90
|
+
|
|
91
|
+
<pre>
|
|
92
|
+
<font color="#00AA00"> 0.0s info:</font> <b>Machine</b> <font color="#717171">[oid=0x3c] [pid=219041] [2020-08-08 12:17:33 +1200]</font>
|
|
93
|
+
| The input voltage has stabilized.
|
|
94
|
+
</pre>
|
|
95
|
+
|
|
96
|
+
If you want to disable log messages which come from this particular class, you can execute your command:
|
|
97
|
+
|
|
98
|
+
~~~ bash
|
|
99
|
+
$ CONSOLE_FATAL=Machine ./machine
|
|
100
|
+
~~~
|
|
101
|
+
|
|
102
|
+
This will prevent any log message which has a subject of class `Machine` from logging messages of a severity less than `fatal`.
|
|
103
|
+
|
|
104
|
+
## Exception Logging
|
|
105
|
+
|
|
106
|
+
If your code has an unhandled exception, you may wish to log it. In order to log a full backtrace, you must log the subject followed by the exception.
|
|
107
|
+
|
|
108
|
+
~~~ ruby
|
|
109
|
+
require 'console'
|
|
110
|
+
|
|
111
|
+
class Cache
|
|
112
|
+
def initialize
|
|
113
|
+
@entries = {}
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def fetch(key)
|
|
117
|
+
@entries.fetch(key)
|
|
118
|
+
rescue => error
|
|
119
|
+
Console.warn(self, "Cache fetch failure!", error)
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
Cache.new.fetch(:foo)
|
|
124
|
+
~~~
|
|
125
|
+
|
|
126
|
+
This will produce the following output:
|
|
127
|
+
|
|
128
|
+
<pre><font color="#C01C28"> 0.0s error:</font> <b>Cache</b> <font color="#8B8A88">[oid=0x848] [ec=0x85c] [pid=571936] [2024-05-03 10:55:11 +1200]</font>
|
|
129
|
+
| Cache fetch failure!
|
|
130
|
+
| <font color="#C01C28"><b>KeyError</b></font>: <b>key not found: :foo (</b><u style="text-decoration-style:solid"><b>KeyError</b></u><b>)</b>
|
|
131
|
+
| → <font color="#C01C28">test.rb:15</font> in `fetch'
|
|
132
|
+
| <font color="#C01C28">test.rb:15</font> in `fetch'
|
|
133
|
+
| <font color="#C01C28">test.rb:21</font> in `<top (required)>'
|
|
134
|
+
</pre>
|
|
135
|
+
|
|
136
|
+
## Program Structure
|
|
137
|
+
|
|
138
|
+
Generally, programs should use the global `Console` interface to log messages.
|
|
139
|
+
|
|
140
|
+
Individual classes should not be catching and logging exceptions. It makes for simpler code if this is handled in a few places near the top of your program. Exceptions should collect enough information such that logging them produces a detailed backtrace leading to the failure.
|
|
141
|
+
|
|
142
|
+
### Multiple Outputs
|
|
143
|
+
|
|
144
|
+
Use `Console::Split` to log to multiple destinations.
|
|
145
|
+
|
|
146
|
+
``` ruby
|
|
147
|
+
require "console"
|
|
148
|
+
require "console/output/split"
|
|
149
|
+
|
|
150
|
+
terminal = Console::Output::Terminal.new($stderr)
|
|
151
|
+
serialized = Console::Output::Serialized.new(File.open("log.json", "a"))
|
|
152
|
+
Console.logger = Console::Logger.new(Console::Output::Split[terminal, serialized])
|
|
153
|
+
|
|
154
|
+
Console.info "I can go everywhere!"
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Custom Log Levels
|
|
158
|
+
|
|
159
|
+
`Console::Filter` implements support for multiple log levels.
|
|
160
|
+
|
|
161
|
+
``` ruby
|
|
162
|
+
require "console"
|
|
163
|
+
|
|
164
|
+
MyLogger = Console::Filter[noise: 0, stuff: 1, broken: 2]
|
|
165
|
+
|
|
166
|
+
# verbose: true - log severity/name/pid etc.
|
|
167
|
+
logger = MyLogger.new(Console.logger, name: "Java", verbose: true)
|
|
168
|
+
|
|
169
|
+
logger.broken("It's so janky.")
|
|
170
|
+
```
|
data/context/index.yaml
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Automatically generated context index for Utopia::Project guides.
|
|
2
|
+
# Do not edit then files in this directory directly, instead edit the guides and then run `bake utopia:project:agent:context:update`.
|
|
3
|
+
---
|
|
4
|
+
description: Beautiful logging for Ruby.
|
|
5
|
+
metadata:
|
|
6
|
+
documentation_uri: https://socketry.github.io/console/
|
|
7
|
+
funding_uri: https://github.com/sponsors/ioquatix/
|
|
8
|
+
source_code_uri: https://github.com/socketry/console.git
|
|
9
|
+
files:
|
|
10
|
+
- path: getting-started.md
|
|
11
|
+
title: Getting Started
|
|
12
|
+
description: This guide explains how to use `console` for logging.
|
|
13
|
+
- path: command-line.md
|
|
14
|
+
title: Command Line
|
|
15
|
+
description: This guide explains how the `console` gem can be controlled using environment
|
|
16
|
+
variables.
|
|
17
|
+
- path: configuration.md
|
|
18
|
+
title: Configuration
|
|
19
|
+
description: This guide explains how to implement per-project configuration for
|
|
20
|
+
the `console` gem.
|
|
21
|
+
- path: integration.md
|
|
22
|
+
title: Integration
|
|
23
|
+
description: This guide explains how to integrate the `console` output into different
|
|
24
|
+
systems.
|
|
25
|
+
- path: events.md
|
|
26
|
+
title: Events
|
|
27
|
+
description: This guide explains how to log structured events with a well-defined
|
|
28
|
+
schema.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Integration
|
|
2
|
+
|
|
3
|
+
This guide explains how to integrate the `console` output into different systems.
|
|
4
|
+
|
|
5
|
+
## Output Redirection
|
|
6
|
+
|
|
7
|
+
The `console` is primarily an interface for logging, with a general purpose implementation which can log to a terminal (human readable) or a file (JSON by default). Output can also be augmented, redirected or manipulated by adding output wrappers. The output wrappers are generally configured using environment variables.
|
|
8
|
+
|
|
9
|
+
### `Console::Output::Datadog`
|
|
10
|
+
|
|
11
|
+
This output wrapper augments log messages with trace metadata so that the log messages can be correlated with spans. Note that the default output is still used `Console::Output::Default`.
|
|
12
|
+
|
|
13
|
+
~~~ bash
|
|
14
|
+
$ CONSOLE_OUTPUT='Console::Output::Datadog,Console::Output::Default' ./app.rb
|
|
15
|
+
~~~
|
|
16
|
+
|
|
17
|
+
The `Console::Output::Datadog` is a wrapper that augments the metadata of the log message with the `trace_id` and `span_id`.
|
|
18
|
+
|
|
19
|
+
### Custom Output Wrapper
|
|
20
|
+
|
|
21
|
+
It's straight forward to define your own output wrapper:
|
|
22
|
+
|
|
23
|
+
~~~ ruby
|
|
24
|
+
# Add a happy face to all log messages.
|
|
25
|
+
class HappyLogger
|
|
26
|
+
def call(subject = nil, *arguments, **options, &block)
|
|
27
|
+
arguments << "😊"
|
|
28
|
+
|
|
29
|
+
@output.call(subject, *arguments, **options, &block)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
~~~
|
|
33
|
+
|
|
34
|
+
## Adapters
|
|
35
|
+
|
|
36
|
+
- [Console::Adapter::Rails](https://github.com/socketry/console-adapter-rails)
|
|
37
|
+
- [Console::Adapter::Sidekiq](https://github.com/socketry/console-adapter-sidekiq)
|
data/lib/console/clock.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
4
|
# Copyright, 2021-2025, by Samuel Williams.
|
|
5
|
+
# Copyright, 2026, by William T. Nelson.
|
|
5
6
|
|
|
6
7
|
module Console
|
|
7
8
|
# A simple clock utility for tracking and formatting time.
|
|
@@ -12,7 +13,7 @@ module Console
|
|
|
12
13
|
# @returns [String] The formatted duration.
|
|
13
14
|
def self.formatted_duration(duration)
|
|
14
15
|
if duration < 60.0
|
|
15
|
-
return "
|
|
16
|
+
return format("%.2fs", duration)
|
|
16
17
|
end
|
|
17
18
|
|
|
18
19
|
duration /= 60.0
|
data/lib/console/filter.rb
CHANGED
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/license.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# MIT License
|
|
2
2
|
|
|
3
|
-
Copyright, 2019-
|
|
3
|
+
Copyright, 2019-2026, by Samuel Williams.
|
|
4
4
|
Copyright, 2019-2021, by Bryan Powell.
|
|
5
5
|
Copyright, 2019, by Cyril Roelandt.
|
|
6
6
|
Copyright, 2020, by Olle Jonsson.
|
|
@@ -8,11 +8,12 @@ Copyright, 2020, by Michael Adams.
|
|
|
8
8
|
Copyright, 2021, by Cédric Boutillier.
|
|
9
9
|
Copyright, 2021, by Robert Schulze.
|
|
10
10
|
Copyright, 2022, by Anton Sozontov.
|
|
11
|
-
Copyright, 2022, by William T. Nelson.
|
|
11
|
+
Copyright, 2022-2026, by William T. Nelson.
|
|
12
12
|
Copyright, 2023, by Felix Yan.
|
|
13
13
|
Copyright, 2024-2025, by Patrik Wenger.
|
|
14
14
|
Copyright, 2025, by Shigeru Nakajima.
|
|
15
15
|
Copyright, 2025, by Yasha Krasnou.
|
|
16
|
+
Copyright, 2026, by Copilot.
|
|
16
17
|
|
|
17
18
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
18
19
|
of this software and associated documentation files (the "Software"), to deal
|
data/readme.md
CHANGED
|
@@ -34,6 +34,16 @@ 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
|
+
|
|
43
|
+
### v1.35.0
|
|
44
|
+
|
|
45
|
+
- Fix handling of `Errno::ENODEV` errors when calculating the width of a terminal that was been re-opened to `File::NULL`.
|
|
46
|
+
|
|
37
47
|
### v1.34.1
|
|
38
48
|
|
|
39
49
|
- Add `process_id` to serialized output records for clarity (`pid` is still included for backwards compatibility).
|
|
@@ -70,16 +80,6 @@ Please see the [project releases](https://socketry.github.io/console/releases/in
|
|
|
70
80
|
|
|
71
81
|
- Fix logging `exception:` keyword argument when the value was not an exception.
|
|
72
82
|
|
|
73
|
-
### v1.29.0
|
|
74
|
-
|
|
75
|
-
- Don't make `Kernel#warn` redirection to `Console.warn` the default behavior, you must `require 'console/warn'` to enable it.
|
|
76
|
-
- Remove deprecated `Console::Logger#failure`.
|
|
77
|
-
- [Consistent handling of exceptions.](https://socketry.github.io/console/releases/index#consistent-handling-of-exceptions.)
|
|
78
|
-
|
|
79
|
-
### v1.28.0
|
|
80
|
-
|
|
81
|
-
- Add support for `Kernel#warn` redirection to `Console.warn`.
|
|
82
|
-
|
|
83
83
|
## Contributing
|
|
84
84
|
|
|
85
85
|
We welcome contributions to this project.
|
|
@@ -90,6 +90,22 @@ We welcome contributions to this project.
|
|
|
90
90
|
4. Push to the branch (`git push origin my-new-feature`).
|
|
91
91
|
5. Create new Pull Request.
|
|
92
92
|
|
|
93
|
+
### Running Tests
|
|
94
|
+
|
|
95
|
+
To run the test suite:
|
|
96
|
+
|
|
97
|
+
``` shell
|
|
98
|
+
bundle exec sus
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Making Releases
|
|
102
|
+
|
|
103
|
+
To make a new release:
|
|
104
|
+
|
|
105
|
+
``` shell
|
|
106
|
+
bundle exec bake gem:release:patch # or minor or major
|
|
107
|
+
```
|
|
108
|
+
|
|
93
109
|
### Developer Certificate of Origin
|
|
94
110
|
|
|
95
111
|
In order to protect users of this project, we require all contributors to comply with the [Developer Certificate of Origin](https://developercertificate.org/). This ensures that all contributions are properly licensed and attributed.
|
data/releases.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
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
|
+
|
|
9
|
+
## v1.35.0
|
|
10
|
+
|
|
11
|
+
- Fix handling of `Errno::ENODEV` errors when calculating the width of a terminal that was been re-opened to `File::NULL`.
|
|
12
|
+
|
|
3
13
|
## v1.34.1
|
|
4
14
|
|
|
5
15
|
- Add `process_id` to serialized output records for clarity (`pid` is still included for backwards compatibility).
|
data.tar.gz.sig
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
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
|
|
@@ -9,13 +9,14 @@ authors:
|
|
|
9
9
|
- Bryan Powell
|
|
10
10
|
- Michael Adams
|
|
11
11
|
- Patrik Wenger
|
|
12
|
+
- William T. Nelson
|
|
12
13
|
- Anton Sozontov
|
|
14
|
+
- Copilot
|
|
13
15
|
- Cyril Roelandt
|
|
14
16
|
- Cédric Boutillier
|
|
15
17
|
- Felix Yan
|
|
16
18
|
- Olle Jonsson
|
|
17
19
|
- Shigeru Nakajima
|
|
18
|
-
- William T. Nelson
|
|
19
20
|
- Yasha Krasnou
|
|
20
21
|
bindir: bin
|
|
21
22
|
cert_chain:
|
|
@@ -97,6 +98,12 @@ extensions: []
|
|
|
97
98
|
extra_rdoc_files: []
|
|
98
99
|
files:
|
|
99
100
|
- bake/console.rb
|
|
101
|
+
- context/command-line.md
|
|
102
|
+
- context/configuration.md
|
|
103
|
+
- context/events.md
|
|
104
|
+
- context/getting-started.md
|
|
105
|
+
- context/index.yaml
|
|
106
|
+
- context/integration.md
|
|
100
107
|
- lib/console.rb
|
|
101
108
|
- lib/console/adapter.rb
|
|
102
109
|
- lib/console/capture.rb
|
|
@@ -135,11 +142,13 @@ files:
|
|
|
135
142
|
- license.md
|
|
136
143
|
- readme.md
|
|
137
144
|
- releases.md
|
|
138
|
-
homepage: https://
|
|
145
|
+
homepage: https://github.com/socketry/console
|
|
139
146
|
licenses:
|
|
140
147
|
- MIT
|
|
141
148
|
metadata:
|
|
142
149
|
documentation_uri: https://socketry.github.io/console/
|
|
150
|
+
funding_uri: https://github.com/sponsors/ioquatix/
|
|
151
|
+
source_code_uri: https://github.com/socketry/console.git
|
|
143
152
|
rdoc_options: []
|
|
144
153
|
require_paths:
|
|
145
154
|
- lib
|
|
@@ -147,14 +156,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
147
156
|
requirements:
|
|
148
157
|
- - ">="
|
|
149
158
|
- !ruby/object:Gem::Version
|
|
150
|
-
version: '3.
|
|
159
|
+
version: '3.3'
|
|
151
160
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
152
161
|
requirements:
|
|
153
162
|
- - ">="
|
|
154
163
|
- !ruby/object:Gem::Version
|
|
155
164
|
version: '0'
|
|
156
165
|
requirements: []
|
|
157
|
-
rubygems_version: 4.0.
|
|
166
|
+
rubygems_version: 4.0.10
|
|
158
167
|
specification_version: 4
|
|
159
168
|
summary: Beautiful logging for Ruby.
|
|
160
169
|
test_files: []
|
metadata.gz.sig
CHANGED
|
Binary file
|