Add plain text and coloured logging to the Client
Logs websocket events (connect, message, error and close events)
This commit is contained in:
parent
a0cf41bfb4
commit
90ab1a6149
@ -8,6 +8,8 @@ AllCops:
|
|||||||
TargetRubyVersion: 3.3
|
TargetRubyVersion: 3.3
|
||||||
DisplayCopNames: true
|
DisplayCopNames: true
|
||||||
NewCops: enable
|
NewCops: enable
|
||||||
|
Exclude:
|
||||||
|
- docs/**/*
|
||||||
|
|
||||||
# ----------------------- Gemspec -----------------------
|
# ----------------------- Gemspec -----------------------
|
||||||
|
|
||||||
|
122
adr/0003-logging-methods-vs-logger-class.md
Normal file
122
adr/0003-logging-methods-vs-logger-class.md
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
# 3. Logging methods vs logger class
|
||||||
|
|
||||||
|
Date: 2024-03-19
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Accepted
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
I'm deciding between integrating logging directly into the main class or creating a dedicated logger class.
|
||||||
|
|
||||||
|
### Option 1: Logging methods
|
||||||
|
|
||||||
|
The first approach weaves logging actions into the operational code, resulting in a tight coupling of functionality and
|
||||||
|
logging. Classes should be open for extension but closed for modification, and this strategy violates that principle.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
class Client
|
||||||
|
def connect(relay)
|
||||||
|
execute_within_an_em_thread do
|
||||||
|
client = build_websocket_client(relay.url)
|
||||||
|
parent_to_child_channel.subscribe do |msg|
|
||||||
|
client.send(msg)
|
||||||
|
emit(:send, msg)
|
||||||
|
log_send(msg) # <------ new code
|
||||||
|
end
|
||||||
|
|
||||||
|
client.on :open do
|
||||||
|
child_to_parent_channel.push(type: :open, relay:)
|
||||||
|
log_connection_opened(relay) # <------ new code
|
||||||
|
end
|
||||||
|
|
||||||
|
client.on :message do |event|
|
||||||
|
child_to_parent_channel.push(type: :message, data: event.data)
|
||||||
|
log_message_received(event.data) # <------ new code
|
||||||
|
end
|
||||||
|
|
||||||
|
client.on :error do |event|
|
||||||
|
child_to_parent_channel.push(type: :error, message: event.message)
|
||||||
|
log_error(event.message) # <------ new code
|
||||||
|
end
|
||||||
|
|
||||||
|
client.on :close do |event|
|
||||||
|
child_to_parent_channel.push(type: :close, code: event.code, reason: event.reason)
|
||||||
|
log_connection_closed(event.code, event.reason) # <------ new code
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# ------ new code below ------
|
||||||
|
|
||||||
|
def log_send(msg)
|
||||||
|
logger.info("Message sent: #{msg}")
|
||||||
|
end
|
||||||
|
|
||||||
|
def log_connection_opened(relay)
|
||||||
|
logger.info("Connection opened to #{relay.url}")
|
||||||
|
end
|
||||||
|
|
||||||
|
def log_message_received(data)
|
||||||
|
logger.info("Message received: #{data}")
|
||||||
|
end
|
||||||
|
|
||||||
|
def log_error(message)
|
||||||
|
logger.error("Error: #{message}")
|
||||||
|
end
|
||||||
|
|
||||||
|
def log_connection_closed(code, reason)
|
||||||
|
logger.info("Connection closed with code: #{code}, reason: #{reason}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 2: Logger class
|
||||||
|
|
||||||
|
The second strategy separates logging into its own class, promoting cleaner code and adherence to the Single
|
||||||
|
Responsibility Principle. Client already exposes events that can be tapped into, so the logger class can listen to these
|
||||||
|
events and log accordingly.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
class ClientLogger
|
||||||
|
def attach_to(client)
|
||||||
|
logger_instance = self
|
||||||
|
|
||||||
|
client.on(:connect) { |relay| logger_instance.on_connect(relay) }
|
||||||
|
client.on(:message) { |message| logger_instance.on_message(message) }
|
||||||
|
client.on(:send) { |message| logger_instance.on_send(message) }
|
||||||
|
client.on(:error) { |message| logger_instance.on_error(message) }
|
||||||
|
client.on(:close) { |code, reason| logger_instance.on_close(code, reason) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def on_connect(relay); end
|
||||||
|
def on_message(message); end
|
||||||
|
def on_send(message); end
|
||||||
|
def on_error(message); end
|
||||||
|
def on_close(code, reason); end
|
||||||
|
end
|
||||||
|
|
||||||
|
client = Nostr::Client.new
|
||||||
|
logger = Nostr::ClientLogger.new
|
||||||
|
logger.attach_to(client)
|
||||||
|
```
|
||||||
|
|
||||||
|
This approach decouples logging from the main class, making it easier to maintain and extend the logging system without
|
||||||
|
affecting the core logic.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
I've chosen the dedicated logger class route. This choice is driven by a desire for extensibility in the logging system.
|
||||||
|
With a separate logger, I can easily modify logging behavior—like changing formats, adjusting verbosity levels,
|
||||||
|
switching colors, or altering output destinations (files, networks, etc.) — without needing to rewrite any of the main
|
||||||
|
operational code.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
Adopting a dedicated logger class offers greater flexibility and simplifies maintenance, making it straightforward to
|
||||||
|
adjust how and what I log independently of the core logic. This separation of concerns means that any future changes
|
||||||
|
to logging preferences or requirements can be implemented quickly and without risk to the main class's functionality.
|
||||||
|
However, it's important to manage the integration carefully to avoid introducing complexity, such as handling
|
||||||
|
dependencies and ensuring seamless communication between the main operations and the logging system.
|
||||||
|
|
66
adr/0004-default-logging-activation.md
Normal file
66
adr/0004-default-logging-activation.md
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
# 3. Default Logging Activation
|
||||||
|
|
||||||
|
Date: 2024-03-19
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Accepted
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
Logging provides visibility into the gem's behavior and helps to diagnose issues. The decision centered on whether
|
||||||
|
to enable logging by default or require manual activation.
|
||||||
|
|
||||||
|
### Option 1: On-demand Logging
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
client = Nostr::Client.new
|
||||||
|
logger = Nostr::ClientLogger.new
|
||||||
|
logger.attach_to(client)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Advantages:
|
||||||
|
|
||||||
|
- Offers users flexibility and control over logging.
|
||||||
|
- Conserves resources by logging only when needed.
|
||||||
|
|
||||||
|
#### Disadvantages:
|
||||||
|
|
||||||
|
- Potential to miss critical logs if not enabled.
|
||||||
|
- Requires users to read and understand documentation to enable logging.
|
||||||
|
- Requires users to manually activate and configure logging.
|
||||||
|
|
||||||
|
### Option 2: Automatic Logging
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
class Client
|
||||||
|
def initialize(logger: ClientLogger.new)
|
||||||
|
@logger = logger
|
||||||
|
logger&.attach_to(self)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
client = Nostr::Client.new
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Advantages:
|
||||||
|
|
||||||
|
- Ensures comprehensive logging without user intervention.
|
||||||
|
- Simplifies debugging and monitoring.
|
||||||
|
- Balances logging detail with performance impact.
|
||||||
|
|
||||||
|
#### Disadvantages:
|
||||||
|
|
||||||
|
- Needs additional steps to disable logging.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
Logging will be enabled by default. Users can disable logging by passing a `nil` logger to the client:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
client = Nostr::Client.new(logger: nil)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
Enabling logging by default favors ease of use and simplifies the developer's experience.
|
32
adr/0005-logger-types.md
Normal file
32
adr/0005-logger-types.md
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
# 3. Logger Types
|
||||||
|
|
||||||
|
Date: 2024-04-01
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Accepted
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
In developing the `Nostr::Client` logging mechanism, I identified a need to cater to multiple development environments
|
||||||
|
and developer preferences. The consideration was whether to implement a singular logger type or to introduce
|
||||||
|
multiple, specialized loggers. The alternatives were:
|
||||||
|
|
||||||
|
- A single `ClientLogger` providing a one-size-fits-all solution.
|
||||||
|
- Multiple logger types:
|
||||||
|
- `Nostr::Client::Logger`: The base class for logging, providing core functionalities.
|
||||||
|
- `Nostr::Client::ColorLogger`: An extension of the base logger, introducing color-coded outputs for enhanced readability in environments that support ANSI colors.
|
||||||
|
- `Nostr::Client::PlainLogger`: A variation that produces logs without color coding, suitable for environments lacking color support or for users preferring plain text.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
I decided to implement the latter option: three specific kinds of loggers (`Nostr::Client::Logger`,
|
||||||
|
`Nostr::Client::ColorLogger`, and `Nostr::Client::PlainLogger`). This approach is intended to offer flexibility and
|
||||||
|
cater to the varied preferences and requirements of our users, recognizing the diverse environments in which our
|
||||||
|
library might be used.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- `Developer Choice`: Developers gain the ability to select the one that best matches their needs and environmental constraints, thereby enhancing the library's usability.
|
||||||
|
- `Code Complexity`: While introducing multiple logger types increases the library's code complexity, this is offset by the significant gain in flexibility and user satisfaction.
|
||||||
|
- `Broad Compatibility`: This decision ensures that the logging mechanism is adaptable to a wide range of operational environments, enhancing the library's overall robustness and accessibility.
|
@ -78,6 +78,7 @@ export default defineConfig(withMermaid({
|
|||||||
text: 'Common use cases',
|
text: 'Common use cases',
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
items: [
|
items: [
|
||||||
|
{ text: 'Logging and debugging', link: '/common-use-cases/logging-and-debugging' },
|
||||||
{ text: 'Bech32 enc/decoding (NIP-19)', link: '/common-use-cases/bech32-encoding-and-decoding-(NIP-19)' },
|
{ text: 'Bech32 enc/decoding (NIP-19)', link: '/common-use-cases/bech32-encoding-and-decoding-(NIP-19)' },
|
||||||
{ text: 'Signing/verifying messages', link: '/common-use-cases/signing-and-verifying-messages' },
|
{ text: 'Signing/verifying messages', link: '/common-use-cases/signing-and-verifying-messages' },
|
||||||
{ text: 'Signing/verifying events', link: '/common-use-cases/signing-and-verifying-events' },
|
{ text: 'Signing/verifying events', link: '/common-use-cases/signing-and-verifying-events' },
|
||||||
|
60
docs/common-use-cases/logging-and-debugging.md
Normal file
60
docs/common-use-cases/logging-and-debugging.md
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
# Logging and debugging
|
||||||
|
|
||||||
|
The `Nostr::Client` class provides built-in logging functionality to help you debug and monitor client interactions with
|
||||||
|
relays. By default, the client uses the `ColorLogger`, which logs events in color. However, you can customize the
|
||||||
|
logging behavior or disable it entirely.
|
||||||
|
|
||||||
|
## Disabling logging
|
||||||
|
|
||||||
|
To instantiate a client without any logging, simply pass `logger: nil` when creating the client instance:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
client = Nostr::Client.new(logger: nil)
|
||||||
|
```
|
||||||
|
|
||||||
|
This will disable all logging for the client.
|
||||||
|
|
||||||
|
## Formatting the logging
|
||||||
|
|
||||||
|
The `Nostr::Client::Logger` class is the base class for logging functionality. It defines the following methods for
|
||||||
|
logging different events:
|
||||||
|
|
||||||
|
- `on_connect(relay)`: Logs when the client connects to a relay.
|
||||||
|
- `on_message(message)`: Logs a message received from the relay.
|
||||||
|
- `on_send(message)`: Logs a message sent to the relay.
|
||||||
|
- `on_error(message)`: Logs an error message.
|
||||||
|
- `on_close(code, reason)`: Logs when the connection with a relay is closed.
|
||||||
|
|
||||||
|
You can create your own logger by subclassing `Nostr::Client::Logger` and overriding these methods to customize the
|
||||||
|
logging format.
|
||||||
|
|
||||||
|
The `Nostr::Client::ColorLogger` is a built-in logger that logs events in color. It uses ANSI escape codes to add color
|
||||||
|
to the log output. Here's an example of how the ColorLogger formats the log messages:
|
||||||
|
|
||||||
|
- Connection: `"\u001b[32m\u001b[1mConnected to the relay\u001b[22m #{relay.name} (#{relay.url})\u001b[0m"`
|
||||||
|
- Message received: `"\u001b[32m\u001b[1m◄-\u001b[0m #{message}"`
|
||||||
|
- Message sent: `"\u001b[32m\u001b[1m-►\u001b[0m #{message}"`
|
||||||
|
- Error: `"\u001b[31m\u001b[1mError: \u001b[22m#{message}\u001b[0m"`
|
||||||
|
- Connection closed: `"\u001b[31m\u001b[1mConnection closed: \u001b[22m#{reason} (##{code})\u001b[0m"`
|
||||||
|
|
||||||
|
## Plain text logging
|
||||||
|
|
||||||
|
If you prefer plain text logging without colors, you can use the `Nostr::Client::PlainLogger`. This logger formats the
|
||||||
|
log messages in a simple, readable format without any ANSI escape codes.
|
||||||
|
|
||||||
|
To use the `PlainLogger`, pass it as the `logger` option when creating the client instance:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
client = Nostr::Client.new(logger: Nostr::Client::PlainLogger.new)
|
||||||
|
```
|
||||||
|
|
||||||
|
The `PlainLogger` formats the log messages as follows:
|
||||||
|
|
||||||
|
- Connection: `"Connected to the relay #{relay.name} (#{relay.url})"`
|
||||||
|
- Message received: `"◄- #{message}"`
|
||||||
|
- Message sent: `"-► #{message}"`
|
||||||
|
- Error: `"Error: #{message}"`
|
||||||
|
- Connection closed: `"Connection closed: #{reason} (##{code})"`
|
||||||
|
|
||||||
|
By using the appropriate logger or creating your own custom logger, you can effectively debug and monitor your Nostr
|
||||||
|
client's interactions with relays.
|
@ -16,6 +16,8 @@ require_relative 'nostr/signature'
|
|||||||
require_relative 'nostr/event'
|
require_relative 'nostr/event'
|
||||||
require_relative 'nostr/events/encrypted_direct_message'
|
require_relative 'nostr/events/encrypted_direct_message'
|
||||||
require_relative 'nostr/client'
|
require_relative 'nostr/client'
|
||||||
|
require_relative 'nostr/client/logger'
|
||||||
|
require_relative 'nostr/client/color_logger'
|
||||||
require_relative 'nostr/user'
|
require_relative 'nostr/user'
|
||||||
require_relative 'nostr/key'
|
require_relative 'nostr/key'
|
||||||
require_relative 'nostr/private_key'
|
require_relative 'nostr/private_key'
|
||||||
|
@ -18,11 +18,19 @@ module Nostr
|
|||||||
# @api public
|
# @api public
|
||||||
#
|
#
|
||||||
# @example Instantiating a client that logs all the events it sends and receives
|
# @example Instantiating a client that logs all the events it sends and receives
|
||||||
# client = Nostr::Client.new(debug: true)
|
# client = Nostr::Client.new
|
||||||
#
|
#
|
||||||
def initialize
|
# @example Instantiating a client with no logging
|
||||||
|
# client = Nostr::Client.new(logger: nil)
|
||||||
|
#
|
||||||
|
# @example Instantiating a client with your own logger
|
||||||
|
# client = Nostr::Client.new(logger: YourLogger.new)
|
||||||
|
#
|
||||||
|
def initialize(logger: ColorLogger.new)
|
||||||
@subscriptions = {}
|
@subscriptions = {}
|
||||||
|
@logger = logger
|
||||||
|
|
||||||
|
logger&.attach_to(self)
|
||||||
initialize_channels
|
initialize_channels
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -122,6 +130,14 @@ module Nostr
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
# The logger that prints all the events that the client sends and receives
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
# @return [ClientLogger]
|
||||||
|
#
|
||||||
|
attr_reader :logger
|
||||||
|
|
||||||
# The subscriptions that the client has created
|
# The subscriptions that the client has created
|
||||||
#
|
#
|
||||||
# @api private
|
# @api private
|
||||||
|
69
lib/nostr/client/color_logger.rb
Normal file
69
lib/nostr/client/color_logger.rb
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Nostr
|
||||||
|
class Client
|
||||||
|
# Logs connection events, messages sent and received, errors, and connection closures in color.
|
||||||
|
class ColorLogger < Logger
|
||||||
|
# Logs connection to a relay
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
# @param [Nostr::Relay] relay The relay the client connected to.
|
||||||
|
#
|
||||||
|
# @return [void]
|
||||||
|
#
|
||||||
|
def on_connect(relay)
|
||||||
|
puts "\u001b[32m\u001b[1mConnected to the relay\u001b[22m #{relay.name} (#{relay.url})\u001b[0m"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Logs a message received from the relay
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
# @param [String] message The message received.
|
||||||
|
#
|
||||||
|
# @return [void]
|
||||||
|
#
|
||||||
|
def on_message(message)
|
||||||
|
puts "\u001b[32m\u001b[1m◄-\u001b[0m #{message}"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Logs a message sent to the relay
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
# @param [String] message The message sent.
|
||||||
|
#
|
||||||
|
# @return [void]
|
||||||
|
#
|
||||||
|
def on_send(message)
|
||||||
|
puts "\u001b[32m\u001b[1m-►\u001b[0m #{message}"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Logs an error message
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
# @param [String] message The error message.
|
||||||
|
#
|
||||||
|
# @return [void]
|
||||||
|
#
|
||||||
|
def on_error(message)
|
||||||
|
puts "\u001b[31m\u001b[1mError: \u001b[22m#{message}\u001b[0m"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Logs a closure of connection with a relay
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
# @param [String] code The closure code.
|
||||||
|
# @param [String] reason The reason for the closure.
|
||||||
|
#
|
||||||
|
# @return [void]
|
||||||
|
#
|
||||||
|
def on_close(code, reason)
|
||||||
|
puts "\u001b[31m\u001b[1mConnection closed: \u001b[22m#{reason} (##{code})\u001b[0m"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
85
lib/nostr/client/logger.rb
Normal file
85
lib/nostr/client/logger.rb
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Nostr
|
||||||
|
class Client
|
||||||
|
# Logs connection events, messages sent and received, errors, and connection closures.
|
||||||
|
class Logger
|
||||||
|
# Attaches event handlers to the specified Nostr client for logging purposes
|
||||||
|
#
|
||||||
|
# @api public
|
||||||
|
#
|
||||||
|
# @example Attaching the logger to a client
|
||||||
|
# client = Nostr::Client.new
|
||||||
|
# logger = Nostr::Client::Logger.new
|
||||||
|
# logger.attach_to(client)
|
||||||
|
#
|
||||||
|
# # Now, actions like connecting, sending messages, receiving messages,
|
||||||
|
# # errors, and closing the connection will be logged to the console.
|
||||||
|
#
|
||||||
|
# @param [Nostr::Client] client The client to attach logging functionality to.
|
||||||
|
#
|
||||||
|
# @return [void]
|
||||||
|
#
|
||||||
|
def attach_to(client)
|
||||||
|
logger_instance = self
|
||||||
|
|
||||||
|
client.on(:connect) { |relay| logger_instance.on_connect(relay) }
|
||||||
|
client.on(:message) { |message| logger_instance.on_message(message) }
|
||||||
|
client.on(:send) { |message| logger_instance.on_send(message) }
|
||||||
|
client.on(:error) { |message| logger_instance.on_error(message) }
|
||||||
|
client.on(:close) { |code, reason| logger_instance.on_close(code, reason) }
|
||||||
|
end
|
||||||
|
|
||||||
|
# Logs connection to a relay
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
# @param [Nostr::Relay] relay The relay the client connected to.
|
||||||
|
#
|
||||||
|
# @return [void]
|
||||||
|
#
|
||||||
|
def on_connect(relay); end
|
||||||
|
|
||||||
|
# Logs a message received from the relay
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
# @param [String] message The message received.
|
||||||
|
#
|
||||||
|
# @return [void]
|
||||||
|
#
|
||||||
|
def on_message(message); end
|
||||||
|
|
||||||
|
# Logs a message sent to the relay
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
# @param [String] message The message sent.
|
||||||
|
#
|
||||||
|
# @return [void]
|
||||||
|
#
|
||||||
|
def on_send(message); end
|
||||||
|
|
||||||
|
# Logs an error message
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
# @param [String] message The error message.
|
||||||
|
#
|
||||||
|
# @return [void]
|
||||||
|
#
|
||||||
|
def on_error(message); end
|
||||||
|
|
||||||
|
# Logs a closure of connection with a relay
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
# @param [String] code The closure code.
|
||||||
|
# @param [String] reason The reason for the closure.
|
||||||
|
#
|
||||||
|
# @return [void]
|
||||||
|
#
|
||||||
|
def on_close(code, reason); end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
69
lib/nostr/client/plain_logger.rb
Normal file
69
lib/nostr/client/plain_logger.rb
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Nostr
|
||||||
|
class Client
|
||||||
|
# Logs connection events, messages sent and received, errors, and connection closures.
|
||||||
|
class PlainLogger < Logger
|
||||||
|
# Logs connection to a relay
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
# @param [Nostr::Relay] relay The relay the client connected to.
|
||||||
|
#
|
||||||
|
# @return [void]
|
||||||
|
#
|
||||||
|
def on_connect(relay)
|
||||||
|
puts "Connected to the relay #{relay.name} (#{relay.url})"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Logs a message received from the relay
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
# @param [String] message The message received.
|
||||||
|
#
|
||||||
|
# @return [void]
|
||||||
|
#
|
||||||
|
def on_message(message)
|
||||||
|
puts "◄- #{message}"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Logs a message sent to the relay
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
# @param [String] message The message sent.
|
||||||
|
#
|
||||||
|
# @return [void]
|
||||||
|
#
|
||||||
|
def on_send(message)
|
||||||
|
puts "-► #{message}"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Logs an error message
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
# @param [String] message The error message.
|
||||||
|
#
|
||||||
|
# @return [void]
|
||||||
|
#
|
||||||
|
def on_error(message)
|
||||||
|
puts "Error: #{message}"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Logs a closure of connection with a relay
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
# @param [String] code The closure code.
|
||||||
|
# @param [String] reason The reason for the closure.
|
||||||
|
#
|
||||||
|
# @return [void]
|
||||||
|
#
|
||||||
|
def on_close(code, reason)
|
||||||
|
puts "Connection closed: #{reason} (##{code})"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -10,6 +10,7 @@ module Nostr
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
attr_reader logger: Logger
|
||||||
attr_reader subscriptions: Hash[String, Subscription]
|
attr_reader subscriptions: Hash[String, Subscription]
|
||||||
attr_reader parent_to_child_channel: EventMachine::Channel
|
attr_reader parent_to_child_channel: EventMachine::Channel
|
||||||
attr_reader child_to_parent_channel: EventMachine::Channel
|
attr_reader child_to_parent_channel: EventMachine::Channel
|
||||||
|
6
sig/nostr/client/color_logger.rbs
Normal file
6
sig/nostr/client/color_logger.rbs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
module Nostr
|
||||||
|
class Client
|
||||||
|
class ColorLogger < Logger
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
12
sig/nostr/client/logger.rbs
Normal file
12
sig/nostr/client/logger.rbs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
module Nostr
|
||||||
|
class Client
|
||||||
|
class Logger
|
||||||
|
def attach_to: (Client client) -> void
|
||||||
|
def on_connect: (Relay relay) -> void
|
||||||
|
def on_message: (String message) -> void
|
||||||
|
def on_send: (String message) -> void
|
||||||
|
def on_error: (String message) -> void
|
||||||
|
def on_close: (String code, String reason) -> void
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
6
sig/nostr/client/plain_logger.rbs
Normal file
6
sig/nostr/client/plain_logger.rbs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
module Nostr
|
||||||
|
class Client
|
||||||
|
class PlainLogger < Logger
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
62
spec/nostr/client/color_logger_spec.rb
Normal file
62
spec/nostr/client/color_logger_spec.rb
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
RSpec.describe Nostr::Client::ColorLogger do
|
||||||
|
let(:client) { instance_spy(Nostr::Client) }
|
||||||
|
let(:relay) { Nostr::Relay.new(url: 'ws://0.0.0.0:4180/', name: 'localhost') }
|
||||||
|
let(:logger) { described_class.new }
|
||||||
|
|
||||||
|
describe '#attach_to' do
|
||||||
|
it 'attaches event handlers to the client' do
|
||||||
|
logger.attach_to(client)
|
||||||
|
|
||||||
|
aggregate_failures do
|
||||||
|
expect(client).to have_received(:on).with(:connect)
|
||||||
|
expect(client).to have_received(:on).with(:message)
|
||||||
|
expect(client).to have_received(:on).with(:send)
|
||||||
|
expect(client).to have_received(:on).with(:error)
|
||||||
|
expect(client).to have_received(:on).with(:close)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#on_connect' do
|
||||||
|
it 'logs connection to a relay' do
|
||||||
|
expect do
|
||||||
|
logger.on_connect(relay)
|
||||||
|
end.to output("\e[32m\e[1mConnected to the relay\e[22m localhost (ws://0.0.0.0:4180/)\e[0m\n").to_stdout
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#on_message' do
|
||||||
|
it 'logs a message received from the relay' do
|
||||||
|
message = 'Received message'
|
||||||
|
expect { logger.on_message(message) }.to output("\e[32m\e[1m◄-\e[0m #{message}\n").to_stdout
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#on_send' do
|
||||||
|
it 'logs a message sent to the relay' do
|
||||||
|
message = 'Sent message'
|
||||||
|
expect { logger.on_send(message) }.to output("\e[32m\e[1m-►\e[0m #{message}\n").to_stdout
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#on_error' do
|
||||||
|
it 'logs an error message' do
|
||||||
|
message = 'Error message'
|
||||||
|
expect { logger.on_error(message) }.to output("\e[31m\e[1mError: \e[22m#{message}\e[0m\n").to_stdout
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#on_close' do
|
||||||
|
it 'logs a closure of connection with a relay' do
|
||||||
|
code = '1000'
|
||||||
|
reason = 'Connection closed'
|
||||||
|
expect do
|
||||||
|
logger.on_close(code, reason)
|
||||||
|
end.to output("\e[31m\e[1mConnection closed: \e[22m#{reason} (##{code})\e[0m\n").to_stdout
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
58
spec/nostr/client/logger_spec.rb
Normal file
58
spec/nostr/client/logger_spec.rb
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
RSpec.describe Nostr::Client::Logger do
|
||||||
|
let(:client) { instance_spy(Nostr::Client) }
|
||||||
|
let(:relay) { Nostr::Relay.new(url: 'ws://0.0.0.0:4180/', name: 'localhost') }
|
||||||
|
let(:logger) { described_class.new }
|
||||||
|
|
||||||
|
describe '#attach_to' do
|
||||||
|
it 'attaches event handlers to the client' do
|
||||||
|
logger.attach_to(client)
|
||||||
|
|
||||||
|
aggregate_failures do
|
||||||
|
expect(client).to have_received(:on).with(:connect)
|
||||||
|
expect(client).to have_received(:on).with(:message)
|
||||||
|
expect(client).to have_received(:on).with(:send)
|
||||||
|
expect(client).to have_received(:on).with(:error)
|
||||||
|
expect(client).to have_received(:on).with(:close)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#on_connect' do
|
||||||
|
it 'returns nil' do
|
||||||
|
expect(logger.on_connect(relay)).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#on_message' do
|
||||||
|
it 'returns nil' do
|
||||||
|
message = 'Received message'
|
||||||
|
expect(logger.on_message(message)).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#on_send' do
|
||||||
|
it 'returns nil' do
|
||||||
|
message = 'Sent message'
|
||||||
|
expect(logger.on_send(message)).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#on_error' do
|
||||||
|
it 'returns nil' do
|
||||||
|
message = 'Error message'
|
||||||
|
expect(logger.on_error(message)).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#on_close' do
|
||||||
|
it 'returns nil' do
|
||||||
|
code = 1000
|
||||||
|
reason = 'Normal closure'
|
||||||
|
expect(logger.on_close(code, reason)).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
63
spec/nostr/client/plain_logger_spec.rb
Normal file
63
spec/nostr/client/plain_logger_spec.rb
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
require 'nostr/client/plain_logger'
|
||||||
|
|
||||||
|
RSpec.describe Nostr::Client::PlainLogger do
|
||||||
|
let(:client) { instance_spy(Nostr::Client) }
|
||||||
|
let(:relay) { Nostr::Relay.new(url: 'ws://0.0.0.0:4180/', name: 'localhost') }
|
||||||
|
let(:logger) { described_class.new }
|
||||||
|
|
||||||
|
describe '#attach_to' do
|
||||||
|
it 'attaches event handlers to the client' do
|
||||||
|
logger.attach_to(client)
|
||||||
|
|
||||||
|
aggregate_failures do
|
||||||
|
expect(client).to have_received(:on).with(:connect)
|
||||||
|
expect(client).to have_received(:on).with(:message)
|
||||||
|
expect(client).to have_received(:on).with(:send)
|
||||||
|
expect(client).to have_received(:on).with(:error)
|
||||||
|
expect(client).to have_received(:on).with(:close)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#on_connect' do
|
||||||
|
it 'logs connection to a relay' do
|
||||||
|
expect do
|
||||||
|
logger.on_connect(relay)
|
||||||
|
end.to output("Connected to the relay localhost (ws://0.0.0.0:4180/)\n").to_stdout
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#on_message' do
|
||||||
|
it 'logs a message received from the relay' do
|
||||||
|
message = 'Received message'
|
||||||
|
expect { logger.on_message(message) }.to output("◄- #{message}\n").to_stdout
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#on_send' do
|
||||||
|
it 'logs a message sent to the relay' do
|
||||||
|
message = 'Sent message'
|
||||||
|
expect { logger.on_send(message) }.to output("-► #{message}\n").to_stdout
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#on_error' do
|
||||||
|
it 'logs an error message' do
|
||||||
|
message = 'Error message'
|
||||||
|
expect { logger.on_error(message) }.to output("Error: #{message}\n").to_stdout
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#on_close' do
|
||||||
|
it 'logs a closure of connection with a relay' do
|
||||||
|
code = '1000'
|
||||||
|
reason = 'Connection closed'
|
||||||
|
expect do
|
||||||
|
logger.on_close(code, reason)
|
||||||
|
end.to output("Connection closed: #{reason} (##{code})\n").to_stdout
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -17,7 +17,7 @@ RSpec.describe Nostr::Client do
|
|||||||
@echo_server.stop
|
@echo_server.stop
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:client) { described_class.new }
|
let(:client) { described_class.new(logger: nil) }
|
||||||
let(:relay) { Nostr::Relay.new(url: plain_text_url, name: 'localhost') }
|
let(:relay) { Nostr::Relay.new(url: plain_text_url, name: 'localhost') }
|
||||||
|
|
||||||
let(:port) { 4180 }
|
let(:port) { 4180 }
|
||||||
@ -27,8 +27,8 @@ RSpec.describe Nostr::Client do
|
|||||||
after { stop }
|
after { stop }
|
||||||
|
|
||||||
describe '.new' do
|
describe '.new' do
|
||||||
client = described_class.new
|
|
||||||
it 'creates an instance of a client' do
|
it 'creates an instance of a client' do
|
||||||
|
client = described_class.new(logger: nil)
|
||||||
|
|
||||||
expect(client).to be_an_instance_of(described_class)
|
expect(client).to be_an_instance_of(described_class)
|
||||||
end
|
end
|
||||||
@ -134,7 +134,7 @@ RSpec.describe Nostr::Client do
|
|||||||
it 'sends a REQ message to the relay, asking for all events and returns a subscription with the same id' do
|
it 'sends a REQ message to the relay, asking for all events and returns a subscription with the same id' do
|
||||||
id = '16605b59b539f6e86762f28fb57db2fd'
|
id = '16605b59b539f6e86762f28fb57db2fd'
|
||||||
|
|
||||||
client = described_class.new
|
client = described_class.new(logger: nil)
|
||||||
|
|
||||||
sent_message = nil
|
sent_message = nil
|
||||||
subscription = nil
|
subscription = nil
|
||||||
@ -164,7 +164,7 @@ RSpec.describe Nostr::Client do
|
|||||||
allow(SecureRandom).to receive(:hex).and_return('16605b59b539f6e86762f28fb57db2fd')
|
allow(SecureRandom).to receive(:hex).and_return('16605b59b539f6e86762f28fb57db2fd')
|
||||||
filter = Nostr::Filter.new(since: 1_230_981_305)
|
filter = Nostr::Filter.new(since: 1_230_981_305)
|
||||||
|
|
||||||
client = described_class.new
|
client = described_class.new(logger: nil)
|
||||||
|
|
||||||
sent_message = nil
|
sent_message = nil
|
||||||
subscription = nil
|
subscription = nil
|
||||||
@ -194,7 +194,7 @@ RSpec.describe Nostr::Client do
|
|||||||
id = '16605b59b539f6e86762f28fb57db2fd'
|
id = '16605b59b539f6e86762f28fb57db2fd'
|
||||||
filter = Nostr::Filter.new(since: 1_230_981_305)
|
filter = Nostr::Filter.new(since: 1_230_981_305)
|
||||||
|
|
||||||
client = described_class.new
|
client = described_class.new(logger: nil)
|
||||||
|
|
||||||
sent_message = nil
|
sent_message = nil
|
||||||
subscription = nil
|
subscription = nil
|
||||||
@ -223,7 +223,7 @@ RSpec.describe Nostr::Client do
|
|||||||
it 'sends a REQ message to the relay, asking for all events and returns a subscription with a random id' do
|
it 'sends a REQ message to the relay, asking for all events and returns a subscription with a random id' do
|
||||||
allow(SecureRandom).to receive(:hex).and_return('16605b59b539f6e86762f28fb57db2fd')
|
allow(SecureRandom).to receive(:hex).and_return('16605b59b539f6e86762f28fb57db2fd')
|
||||||
|
|
||||||
client = described_class.new
|
client = described_class.new(logger: nil)
|
||||||
|
|
||||||
sent_message = nil
|
sent_message = nil
|
||||||
subscription = nil
|
subscription = nil
|
||||||
@ -253,7 +253,7 @@ RSpec.describe Nostr::Client do
|
|||||||
it 'sends a CLOSE message to the relay, asking it to stop a subscription' do
|
it 'sends a CLOSE message to the relay, asking it to stop a subscription' do
|
||||||
subscription_id = '16605b59b539f6e86762f28fb57db2fd'
|
subscription_id = '16605b59b539f6e86762f28fb57db2fd'
|
||||||
|
|
||||||
client = described_class.new
|
client = described_class.new(logger: nil)
|
||||||
|
|
||||||
sent_message = nil
|
sent_message = nil
|
||||||
|
|
||||||
@ -277,7 +277,7 @@ RSpec.describe Nostr::Client do
|
|||||||
describe '#publish' do
|
describe '#publish' do
|
||||||
it 'sends a message to the relay' do
|
it 'sends a message to the relay' do
|
||||||
relay = Nostr::Relay.new(url: plain_text_url, name: 'localhost')
|
relay = Nostr::Relay.new(url: plain_text_url, name: 'localhost')
|
||||||
client = described_class.new
|
client = described_class.new(logger: nil)
|
||||||
event = Nostr::Event.new(
|
event = Nostr::Event.new(
|
||||||
id: '2a3184512d34077601e992ba3c3215354b21a8c76f85c2c7f66093481854e811',
|
id: '2a3184512d34077601e992ba3c3215354b21a8c76f85c2c7f66093481854e811',
|
||||||
pubkey: '2d7661527d573cc8e84f665fa971dd969ba51e2526df00c149ff8e40a58f9558',
|
pubkey: '2d7661527d573cc8e84f665fa971dd969ba51e2526df00c149ff8e40a58f9558',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user