omq-cli 0.12.3 → 0.13.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
- data/CHANGELOG.md +37 -0
- data/README.md +10 -10
- data/lib/omq/cli/base_runner.rb +27 -6
- data/lib/omq/cli/cli_parser.rb +30 -3
- data/lib/omq/cli/config.rb +1 -0
- data/lib/omq/cli/expression_evaluator.rb +7 -4
- data/lib/omq/cli/formatter.rb +9 -5
- data/lib/omq/cli/parallel_worker.rb +30 -11
- data/lib/omq/cli/pipe.rb +3 -3
- data/lib/omq/cli/pipe_worker.rb +52 -32
- data/lib/omq/cli/socket_setup.rb +8 -8
- data/lib/omq/cli/term.rb +22 -19
- data/lib/omq/cli/version.rb +1 -1
- metadata +3 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0135d1ab507a4e82186cae115a477ccbd4b231e12a7450cb8989a5c0c1479e50
|
|
4
|
+
data.tar.gz: efc832159dc103ec4b18ba5f65acb5976a09c7530b246e5e433795357ca21181
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a8093420e5209f9e4672dd15a343c60167959532d90fda0fce7f108042807a65d27f033eb39a3abfee80059e8832d2e90680a624f36575c8b608f2da5095865c
|
|
7
|
+
data.tar.gz: 30dc24c50221b726130e638764d891d2736044b358b364f2bd987a21eceac85bea44ed0821ab601d1070d2e60bc10492a61ffd8baa8129588a0dce06b72ec192
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,42 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.13.0 — 2026-04-12
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **`--timestamps[=PRECISION]` flag.** Prefix log lines with UTC
|
|
8
|
+
timestamps. Accepts `s`, `ms` (default), or `us`. Replaces the
|
|
9
|
+
former `-vvvv` special meaning, which has been removed.
|
|
10
|
+
- **`-M` / `--marshal` preserves arbitrary objects on the wire.**
|
|
11
|
+
Eval results in `--format marshal` mode (e.g. `Time.now`, hashes,
|
|
12
|
+
UTF-16LE strings) are now passed through unchanged instead of
|
|
13
|
+
being coerced via `#to_s`. Affects both the main runner and
|
|
14
|
+
Ractor workers (`-P`).
|
|
15
|
+
- **`-P0` ⇒ `nproc`.** `-P0` (or bare `-P`) spawns one Ractor worker
|
|
16
|
+
per CPU, capped at 16. Short-option clustering works: `-P0zvv`
|
|
17
|
+
expands to `-P 0 -z -v -v`.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- **`-vvv` shows decompressed message previews.** When `--compress`
|
|
22
|
+
is active, message traces now log the decompressed parts and
|
|
23
|
+
include the on-wire size: `(5B wire=21B) hello`. Engine-level
|
|
24
|
+
message events are suppressed in compressed mode to avoid
|
|
25
|
+
double-logging compressed bytes.
|
|
26
|
+
- **Truncation marker uses `…` (U+2026).** `Formatter.preview`
|
|
27
|
+
truncation now uses the real horizontal ellipsis character
|
|
28
|
+
instead of three ASCII dots.
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
|
|
32
|
+
- **`FrozenError` in eval handlers returning received parts.**
|
|
33
|
+
`ExpressionEvaluator` no longer mutates the result array with
|
|
34
|
+
`#map!`, which crashed when a script handler returned the frozen
|
|
35
|
+
parts array received from the socket.
|
|
36
|
+
- **README:** fixed broken `if /regex/` examples (no implicit `$_`
|
|
37
|
+
match in `instance_exec`), broken `OMQ.incoming`/`OMQ.outgoing`
|
|
38
|
+
handler table, and `-P` examples that no longer parsed.
|
|
39
|
+
|
|
3
40
|
## 0.12.3 — 2026-04-10
|
|
4
41
|
|
|
5
42
|
### Fixed
|
data/README.md
CHANGED
|
@@ -127,8 +127,8 @@ Pipe creates an internal PULL → eval → PUSH pipeline:
|
|
|
127
127
|
```sh
|
|
128
128
|
omq pipe -c@work -c@sink -e 'it.map(&:upcase)'
|
|
129
129
|
|
|
130
|
-
# with Ractor workers for CPU parallelism
|
|
131
|
-
omq pipe -c@work -c@sink -
|
|
130
|
+
# with Ractor workers for CPU parallelism (-P0 = nproc)
|
|
131
|
+
omq pipe -c@work -c@sink -P0 -r./fib.rb -e 'fib(Integer(it.first)).to_s'
|
|
132
132
|
```
|
|
133
133
|
|
|
134
134
|
The first endpoint is the pull-side (input), the second is the push-side (output).
|
|
@@ -171,10 +171,10 @@ omq pull -b tcp://:5557 -e '|(key, value)| "#{key}=#{value}"'
|
|
|
171
171
|
|
|
172
172
|
```sh
|
|
173
173
|
# skip messages matching a pattern
|
|
174
|
-
omq pull -b tcp://:5557 -e 'next if
|
|
174
|
+
omq pull -b tcp://:5557 -e 'next if it.first.start_with?("#"); it'
|
|
175
175
|
|
|
176
176
|
# stop on "quit"
|
|
177
|
-
omq pull -b tcp://:5557 -e 'break if
|
|
177
|
+
omq pull -b tcp://:5557 -e 'break if it.first == "quit"; it'
|
|
178
178
|
```
|
|
179
179
|
|
|
180
180
|
### BEGIN/END blocks
|
|
@@ -244,8 +244,8 @@ omq req -c tcp://localhost:5555 -r./handler.rb
|
|
|
244
244
|
|
|
245
245
|
| Method | Effect |
|
|
246
246
|
|--------|--------|
|
|
247
|
-
| `OMQ.outgoing {
|
|
248
|
-
| `OMQ.incoming {
|
|
247
|
+
| `OMQ.outgoing { \|msg\| ... }` | Register outgoing message transform |
|
|
248
|
+
| `OMQ.incoming { \|msg\| ... }` | Register incoming message transform |
|
|
249
249
|
|
|
250
250
|
- use explicit block variable (like `msg`) or `it`
|
|
251
251
|
- Setup: use local variables and closures at the top of the script
|
|
@@ -441,11 +441,11 @@ Pipe creates an in-process PULL → eval → PUSH pipeline:
|
|
|
441
441
|
# basic pipe (positional: first = input, second = output)
|
|
442
442
|
omq pipe -c@work -c@sink -e 'it.map(&:upcase)'
|
|
443
443
|
|
|
444
|
-
# parallel Ractor workers (
|
|
445
|
-
omq pipe -c@work -c@sink -
|
|
444
|
+
# parallel Ractor workers (-P0 = nproc, also combinable: -P0zvv)
|
|
445
|
+
omq pipe -c@work -c@sink -P0 -r./fib.rb -e 'fib(Integer(it.first)).to_s'
|
|
446
446
|
|
|
447
447
|
# fixed number of workers
|
|
448
|
-
omq pipe -c@work -c@sink -
|
|
448
|
+
omq pipe -c@work -c@sink -P4 -e 'it.map(&:upcase)'
|
|
449
449
|
|
|
450
450
|
# exit when producer disconnects
|
|
451
451
|
omq pipe -c@work -c@sink --transient -e 'it.map(&:upcase)'
|
|
@@ -467,7 +467,7 @@ omq pipe --in -b tcp://:5555 --out -c@sink1 -c@sink2 -e 'it'
|
|
|
467
467
|
omq pipe --in -b tcp://:5555 -b tcp://:5556 --out -c tcp://sink:5557 -e 'it'
|
|
468
468
|
|
|
469
469
|
# parallel workers with fan-in (all must be -c)
|
|
470
|
-
omq pipe --in -c@a -c@b --out -c@sink -
|
|
470
|
+
omq pipe --in -c@a -c@b --out -c@sink -P4 -e 'it'
|
|
471
471
|
```
|
|
472
472
|
|
|
473
473
|
`-P`/`--parallel` requires all endpoints to be `--connect`. In parallel mode, each Ractor worker
|
data/lib/omq/cli/base_runner.rb
CHANGED
|
@@ -99,7 +99,7 @@ module OMQ
|
|
|
99
99
|
|
|
100
100
|
|
|
101
101
|
def attach_endpoints
|
|
102
|
-
SocketSetup.attach(@sock, config, verbose: config.verbose)
|
|
102
|
+
SocketSetup.attach(@sock, config, verbose: config.verbose, timestamps: config.timestamps)
|
|
103
103
|
end
|
|
104
104
|
|
|
105
105
|
|
|
@@ -313,9 +313,15 @@ module OMQ
|
|
|
313
313
|
|
|
314
314
|
def send_msg(parts)
|
|
315
315
|
return if parts.empty?
|
|
316
|
+
log_parts = parts
|
|
316
317
|
parts = [Marshal.dump(parts)] if config.format == :marshal
|
|
317
|
-
|
|
318
|
-
|
|
318
|
+
if config.compress
|
|
319
|
+
wire = @fmt.compress(parts)
|
|
320
|
+
trace_msg(">>", log_parts, wire)
|
|
321
|
+
@sock.send(wire)
|
|
322
|
+
else
|
|
323
|
+
@sock.send(parts)
|
|
324
|
+
end
|
|
319
325
|
transient_ready!
|
|
320
326
|
end
|
|
321
327
|
|
|
@@ -324,12 +330,24 @@ module OMQ
|
|
|
324
330
|
raw = @sock.receive
|
|
325
331
|
return nil if raw.nil?
|
|
326
332
|
parts = @fmt.decompress(raw)
|
|
333
|
+
trace_msg("<<", parts, raw) if config.compress
|
|
327
334
|
parts = Marshal.load(parts.first) if config.format == :marshal
|
|
328
335
|
transient_ready!
|
|
329
336
|
parts
|
|
330
337
|
end
|
|
331
338
|
|
|
332
339
|
|
|
340
|
+
# Writes an explicit -vvv message trace line. Used when compression
|
|
341
|
+
# is active: the engine-level monitor would see compressed bytes,
|
|
342
|
+
# so we log post-decompression (recv) / pre-compression (send)
|
|
343
|
+
# and annotate the compressed wire size.
|
|
344
|
+
def trace_msg(marker, parts, wire)
|
|
345
|
+
return unless config.verbose >= 3
|
|
346
|
+
wire_size = wire.sum(&:bytesize)
|
|
347
|
+
$stderr.write("#{Term.log_prefix(config.timestamps)}omq: #{marker} #{Formatter.preview(parts, wire_size: wire_size)}\n")
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
|
|
333
351
|
def recv_msg_raw
|
|
334
352
|
msg = @sock.receive
|
|
335
353
|
msg&.dup
|
|
@@ -464,10 +482,13 @@ module OMQ
|
|
|
464
482
|
|
|
465
483
|
# -vv: log connect/disconnect/retry/timeout events via Socket#monitor
|
|
466
484
|
# -vvv: also log message sent/received traces
|
|
467
|
-
#
|
|
485
|
+
# --timestamps[=s|ms|us]: prepend UTC timestamps to log lines
|
|
468
486
|
def start_event_monitor
|
|
469
|
-
|
|
470
|
-
|
|
487
|
+
# When compress is on, BaseRunner#trace_msg logs post-
|
|
488
|
+
# decompression, so we suppress the engine-level message trace.
|
|
489
|
+
trace = config.verbose >= 3 && !config.compress
|
|
490
|
+
@sock.monitor(verbose: trace) do |event|
|
|
491
|
+
Term.write_event(event, config.timestamps)
|
|
471
492
|
end
|
|
472
493
|
end
|
|
473
494
|
end
|
data/lib/omq/cli/cli_parser.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "socket"
|
|
4
|
+
require "etc"
|
|
4
5
|
|
|
5
6
|
module OMQ
|
|
6
7
|
module CLI
|
|
@@ -231,6 +232,7 @@ module OMQ
|
|
|
231
232
|
parallel: nil,
|
|
232
233
|
transient: false,
|
|
233
234
|
verbose: 0,
|
|
235
|
+
timestamps: nil,
|
|
234
236
|
quiet: false,
|
|
235
237
|
echo: false,
|
|
236
238
|
scripts: [],
|
|
@@ -249,6 +251,25 @@ module OMQ
|
|
|
249
251
|
end
|
|
250
252
|
|
|
251
253
|
|
|
254
|
+
# Splits short-option clusters of the form `-P[digits][letters]`
|
|
255
|
+
# so OptionParser sees `-P[digits]` followed by `-[letters]`.
|
|
256
|
+
# Lets `-P0zvv` mean `-P0 -z -v -v` (portable & combinable).
|
|
257
|
+
# Also rewrites bare `--timestamps` to `--timestamps=ms` so
|
|
258
|
+
# OptionParser doesn't consume the next positional token as its
|
|
259
|
+
# argument.
|
|
260
|
+
#
|
|
261
|
+
def split_parallel_cluster(argv)
|
|
262
|
+
argv.flat_map { |a|
|
|
263
|
+
if a =~ /\A-P(\d*)([a-zA-Z].*)\z/
|
|
264
|
+
n, rest = $1, $2
|
|
265
|
+
n.empty? ? ["-P", "-#{rest}"] : ["-P#{n}", "-#{rest}"]
|
|
266
|
+
else
|
|
267
|
+
a
|
|
268
|
+
end
|
|
269
|
+
}.map { |a| a == "--timestamps" ? "--timestamps=ms" : a }
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
|
|
252
273
|
# Validates option combinations, aborting on bad combos.
|
|
253
274
|
#
|
|
254
275
|
def self.validate!(opts)
|
|
@@ -384,8 +405,9 @@ module OMQ
|
|
|
384
405
|
require "omq" unless defined?(OMQ::VERSION)
|
|
385
406
|
opts[:scripts] << (v == "-" ? :stdin : (v.start_with?("./", "../") ? File.expand_path(v) : v))
|
|
386
407
|
}
|
|
387
|
-
o.on("-P", "--parallel N", Integer, "Parallel Ractor workers (max 16)") { |v|
|
|
388
|
-
|
|
408
|
+
o.on("-P", "--parallel [N]", Integer, "Parallel Ractor workers (0 = nproc, max 16)") { |v|
|
|
409
|
+
n = v.nil? || v.zero? ? Etc.nprocessors : v
|
|
410
|
+
opts[:parallel] = [n, 16].min
|
|
389
411
|
}
|
|
390
412
|
|
|
391
413
|
o.separator "\nCURVE encryption (requires system libsodium):"
|
|
@@ -397,7 +419,10 @@ module OMQ
|
|
|
397
419
|
o.separator " OMQ_CRYPTO (backend: rbnacl or nuckle)"
|
|
398
420
|
|
|
399
421
|
o.separator "\nOther:"
|
|
400
|
-
o.on("-v", "--verbose", "Verbosity: -v endpoints, -vv events, -vvv messages
|
|
422
|
+
o.on("-v", "--verbose", "Verbosity: -v endpoints, -vv events, -vvv messages") { opts[:verbose] += 1 }
|
|
423
|
+
o.on( "--timestamps PRECISION", %w[s ms us], "Prefix log lines with UTC timestamp (s/ms/us, default ms)") { |v|
|
|
424
|
+
opts[:timestamps] = v.to_sym
|
|
425
|
+
}
|
|
401
426
|
o.on("-q", "--quiet", "Suppress message output") { opts[:quiet] = true }
|
|
402
427
|
o.on( "--transient", "Exit when all peers disconnect") { opts[:transient] = true }
|
|
403
428
|
o.on( "--ffi", "Use libzmq FFI backend (requires omq-ffi gem + system libzmq 4.x)") do
|
|
@@ -427,6 +452,8 @@ module OMQ
|
|
|
427
452
|
o.separator "\nExit codes: 0 = success, 1 = error, 2 = timeout"
|
|
428
453
|
end
|
|
429
454
|
|
|
455
|
+
argv = split_parallel_cluster(argv)
|
|
456
|
+
|
|
430
457
|
begin
|
|
431
458
|
parser.parse!(argv)
|
|
432
459
|
rescue OptionParser::ParseError => e
|
data/lib/omq/cli/config.rb
CHANGED
|
@@ -49,20 +49,23 @@ module OMQ
|
|
|
49
49
|
return [result] if @format == :marshal
|
|
50
50
|
|
|
51
51
|
result = result.is_a?(Array) ? result : [result]
|
|
52
|
-
result.map
|
|
52
|
+
result.map(&:to_s)
|
|
53
53
|
rescue => e
|
|
54
54
|
$stderr.puts "omq: eval error: #{e.message} (#{e.class})"
|
|
55
55
|
exit 3
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
|
|
59
|
-
# Normalises an eval result to nil (skip) or an Array
|
|
59
|
+
# Normalises an eval result to nil (skip) or an Array.
|
|
60
60
|
# Used inside Ractor worker blocks where instance methods are unavailable.
|
|
61
|
+
# When +format+ is :marshal, arbitrary objects are preserved (wrapped
|
|
62
|
+
# in a one-element Array so the wire path can Marshal.dump them).
|
|
61
63
|
#
|
|
62
|
-
def self.normalize_result(result)
|
|
64
|
+
def self.normalize_result(result, format: nil)
|
|
63
65
|
return nil if result.nil?
|
|
66
|
+
return [result] if format == :marshal
|
|
64
67
|
result = result.is_a?(Array) ? result : [result]
|
|
65
|
-
result.map
|
|
68
|
+
result.map(&:to_s)
|
|
66
69
|
end
|
|
67
70
|
|
|
68
71
|
|
data/lib/omq/cli/formatter.rb
CHANGED
|
@@ -105,15 +105,19 @@ module OMQ
|
|
|
105
105
|
|
|
106
106
|
|
|
107
107
|
# Formats message parts for human-readable preview (logging).
|
|
108
|
+
# When +wire_size+ is given, the header also shows the
|
|
109
|
+
# compressed on-the-wire size: "(29B wire=12B)".
|
|
108
110
|
#
|
|
109
|
-
# @param parts [Array<String>] message frames
|
|
111
|
+
# @param parts [Array<String>] message frames (decompressed)
|
|
112
|
+
# @param wire_size [Integer, nil] compressed bytes on the wire
|
|
110
113
|
# @return [String] truncated preview of each frame joined by |
|
|
111
|
-
def self.preview(parts)
|
|
114
|
+
def self.preview(parts, wire_size: nil)
|
|
112
115
|
total = parts.sum(&:bytesize)
|
|
113
116
|
nparts = parts.size
|
|
114
117
|
shown = parts.first(3).map { |p| preview_frame(p) }
|
|
115
|
-
tail = nparts > 3 ? "
|
|
116
|
-
|
|
118
|
+
tail = nparts > 3 ? "|…" : ""
|
|
119
|
+
size = wire_size ? "#{total}B wire=#{wire_size}B" : "#{total}B"
|
|
120
|
+
header = nparts > 1 ? "(#{size} #{nparts}F)" : "(#{size})"
|
|
117
121
|
|
|
118
122
|
"#{header} #{shown.join("|")}#{tail}"
|
|
119
123
|
end
|
|
@@ -133,7 +137,7 @@ module OMQ
|
|
|
133
137
|
if printable < sample.bytesize / 2
|
|
134
138
|
"[#{bytes.bytesize}B]"
|
|
135
139
|
elsif bytes.bytesize > 12
|
|
136
|
-
"#{sample.gsub(/[^[:print:]]/, ".")}
|
|
140
|
+
"#{sample.gsub(/[^[:print:]]/, ".")}…"
|
|
137
141
|
else
|
|
138
142
|
sample.gsub(/[^[:print:]]/, ".")
|
|
139
143
|
end
|
|
@@ -51,20 +51,35 @@ module OMQ
|
|
|
51
51
|
def log_endpoints
|
|
52
52
|
return unless @config.verbose >= 1
|
|
53
53
|
@endpoints.each do |ep|
|
|
54
|
-
@log_port.send(OMQ::CLI::Term.format_attach(ep.bind? ? :bind : :connect, ep.url, @config.
|
|
54
|
+
@log_port.send(OMQ::CLI::Term.format_attach(ep.bind? ? :bind : :connect, ep.url, @config.timestamps))
|
|
55
55
|
end
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
|
|
59
59
|
def start_monitors
|
|
60
60
|
return unless @config.verbose >= 2
|
|
61
|
-
|
|
61
|
+
# When compress is on, messages are traced explicitly post-
|
|
62
|
+
# decompression; suppress engine-level message trace to avoid
|
|
63
|
+
# showing compressed bytes.
|
|
64
|
+
trace = @config.verbose >= 3 && !@config.compress
|
|
62
65
|
@sock.monitor(verbose: trace) do |event|
|
|
63
|
-
@log_port.send(OMQ::CLI::Term.format_event(event, @config.
|
|
66
|
+
@log_port.send(OMQ::CLI::Term.format_event(event, @config.timestamps))
|
|
64
67
|
end
|
|
65
68
|
end
|
|
66
69
|
|
|
67
70
|
|
|
71
|
+
def trace_in(parts, wire)
|
|
72
|
+
return unless @config.verbose >= 3 && @config.compress
|
|
73
|
+
@log_port.send("#{OMQ::CLI::Term.log_prefix(@config.timestamps)}omq: << #{OMQ::CLI::Formatter.preview(parts, wire_size: wire.sum(&:bytesize))}")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def trace_out(parts, wire)
|
|
78
|
+
return unless @config.verbose >= 3 && @config.compress
|
|
79
|
+
@log_port.send("#{OMQ::CLI::Term.log_prefix(@config.timestamps)}omq: >> #{OMQ::CLI::Formatter.preview(parts, wire_size: wire.sum(&:bytesize))}")
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
|
|
68
83
|
def wait_for_peer
|
|
69
84
|
if @config.timeout
|
|
70
85
|
Fiber.scheduler.with_timeout(@config.timeout) do
|
|
@@ -104,9 +119,10 @@ module OMQ
|
|
|
104
119
|
n = @config.count
|
|
105
120
|
i = 0
|
|
106
121
|
loop do
|
|
107
|
-
|
|
108
|
-
break if
|
|
109
|
-
parts = @fmt.decompress(
|
|
122
|
+
wire = @sock.receive
|
|
123
|
+
break if wire.nil?
|
|
124
|
+
parts = @fmt.decompress(wire)
|
|
125
|
+
trace_in(parts, wire)
|
|
110
126
|
if @eval_proc
|
|
111
127
|
parts = normalize(
|
|
112
128
|
@ctx.instance_exec(parts, &@eval_proc)
|
|
@@ -129,12 +145,15 @@ module OMQ
|
|
|
129
145
|
n = @config.count
|
|
130
146
|
i = 0
|
|
131
147
|
loop do
|
|
132
|
-
|
|
133
|
-
break if
|
|
134
|
-
parts = @fmt.decompress(
|
|
148
|
+
wire = @sock.receive
|
|
149
|
+
break if wire.nil?
|
|
150
|
+
parts = @fmt.decompress(wire)
|
|
151
|
+
trace_in(parts, wire)
|
|
135
152
|
reply = compute_reply(parts)
|
|
136
153
|
output(reply)
|
|
137
|
-
@
|
|
154
|
+
reply_wire = @fmt.compress(reply)
|
|
155
|
+
trace_out(reply, reply_wire)
|
|
156
|
+
@sock.send(reply_wire)
|
|
138
157
|
i += 1
|
|
139
158
|
break if n && n > 0 && i >= n
|
|
140
159
|
end
|
|
@@ -166,7 +185,7 @@ module OMQ
|
|
|
166
185
|
|
|
167
186
|
|
|
168
187
|
def normalize(result)
|
|
169
|
-
OMQ::CLI::ExpressionEvaluator.normalize_result(result)
|
|
188
|
+
OMQ::CLI::ExpressionEvaluator.normalize_result(result, format: @config.format)
|
|
170
189
|
end
|
|
171
190
|
|
|
172
191
|
|
data/lib/omq/cli/pipe.rb
CHANGED
|
@@ -85,8 +85,8 @@ module OMQ
|
|
|
85
85
|
push = OMQ::PUSH.new(**kwargs)
|
|
86
86
|
SocketSetup.apply_options(pull, config)
|
|
87
87
|
SocketSetup.apply_options(push, config)
|
|
88
|
-
SocketSetup.attach_endpoints(pull, in_eps, verbose: config.verbose)
|
|
89
|
-
SocketSetup.attach_endpoints(push, out_eps, verbose: config.verbose)
|
|
88
|
+
SocketSetup.attach_endpoints(pull, in_eps, verbose: config.verbose, timestamps: config.timestamps)
|
|
89
|
+
SocketSetup.attach_endpoints(push, out_eps, verbose: config.verbose, timestamps: config.timestamps)
|
|
90
90
|
[pull, push]
|
|
91
91
|
end
|
|
92
92
|
|
|
@@ -196,7 +196,7 @@ module OMQ
|
|
|
196
196
|
trace = config.verbose >= 3
|
|
197
197
|
[@pull, @push].each do |sock|
|
|
198
198
|
sock.monitor(verbose: trace) do |event|
|
|
199
|
-
Term.write_event(event, config.
|
|
199
|
+
Term.write_event(event, config.timestamps)
|
|
200
200
|
end
|
|
201
201
|
end
|
|
202
202
|
end
|
data/lib/omq/cli/pipe_worker.rb
CHANGED
|
@@ -49,21 +49,37 @@ module OMQ
|
|
|
49
49
|
|
|
50
50
|
def log_endpoints
|
|
51
51
|
(@in_eps + @out_eps).each do |ep|
|
|
52
|
-
@log_port.send(OMQ::CLI::Term.format_attach(ep.bind? ? :bind : :connect, ep.url, @config.
|
|
52
|
+
@log_port.send(OMQ::CLI::Term.format_attach(ep.bind? ? :bind : :connect, ep.url, @config.timestamps))
|
|
53
53
|
end
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
|
|
57
57
|
def start_monitors
|
|
58
|
-
|
|
58
|
+
# When compress is on, messages are traced explicitly in
|
|
59
|
+
# run_message_loop post-decompression; keep engine-level
|
|
60
|
+
# traces off to avoid showing compressed bytes.
|
|
61
|
+
compress = @config.compress || @config.compress_in || @config.compress_out
|
|
62
|
+
trace = @config.verbose >= 3 && !compress
|
|
59
63
|
[@pull, @push].each do |sock|
|
|
60
64
|
sock.monitor(verbose: trace) do |event|
|
|
61
|
-
@log_port.send(OMQ::CLI::Term.format_event(event, @config.
|
|
65
|
+
@log_port.send(OMQ::CLI::Term.format_event(event, @config.timestamps))
|
|
62
66
|
end
|
|
63
67
|
end
|
|
64
68
|
end
|
|
65
69
|
|
|
66
70
|
|
|
71
|
+
def trace_in(parts, wire)
|
|
72
|
+
return unless @config.verbose >= 3 && (@config.compress || @config.compress_in)
|
|
73
|
+
@log_port.send("#{OMQ::CLI::Term.log_prefix(@config.timestamps)}omq: << #{OMQ::CLI::Formatter.preview(parts, wire_size: wire.sum(&:bytesize))}")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def trace_out(parts, wire)
|
|
78
|
+
return unless @config.verbose >= 3 && (@config.compress || @config.compress_out)
|
|
79
|
+
@log_port.send("#{OMQ::CLI::Term.log_prefix(@config.timestamps)}omq: >> #{OMQ::CLI::Formatter.preview(parts, wire_size: wire.sum(&:bytesize))}")
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
|
|
67
83
|
# With --timeout set, fail fast if peers never show up. Without
|
|
68
84
|
# it, there's no point waiting: PULL#receive blocks naturally
|
|
69
85
|
# and PUSH buffers up to send_hwm when no peer is present.
|
|
@@ -91,39 +107,15 @@ module OMQ
|
|
|
91
107
|
n = @config.count
|
|
92
108
|
if @eval_proc
|
|
93
109
|
if n && n > 0
|
|
94
|
-
n.times
|
|
95
|
-
parts = @pull.receive
|
|
96
|
-
break if parts.nil?
|
|
97
|
-
parts = OMQ::CLI::ExpressionEvaluator.normalize_result(
|
|
98
|
-
@ctx.instance_exec(@fmt_in.decompress(parts), &@eval_proc)
|
|
99
|
-
)
|
|
100
|
-
next if parts.nil?
|
|
101
|
-
@push << @fmt_out.compress(parts) unless parts.empty?
|
|
102
|
-
end
|
|
110
|
+
n.times { break unless process_one_eval }
|
|
103
111
|
else
|
|
104
|
-
loop
|
|
105
|
-
parts = @pull.receive
|
|
106
|
-
break if parts.nil?
|
|
107
|
-
parts = OMQ::CLI::ExpressionEvaluator.normalize_result(
|
|
108
|
-
@ctx.instance_exec(@fmt_in.decompress(parts), &@eval_proc)
|
|
109
|
-
)
|
|
110
|
-
next if parts.nil?
|
|
111
|
-
@push << @fmt_out.compress(parts) unless parts.empty?
|
|
112
|
-
end
|
|
112
|
+
loop { break unless process_one_eval }
|
|
113
113
|
end
|
|
114
114
|
else
|
|
115
115
|
if n && n > 0
|
|
116
|
-
n.times
|
|
117
|
-
parts = @pull.receive
|
|
118
|
-
break if parts.nil?
|
|
119
|
-
@push << @fmt_out.compress(@fmt_in.decompress(parts))
|
|
120
|
-
end
|
|
116
|
+
n.times { break unless process_one_passthrough }
|
|
121
117
|
else
|
|
122
|
-
loop
|
|
123
|
-
parts = @pull.receive
|
|
124
|
-
break if parts.nil?
|
|
125
|
-
@push << @fmt_out.compress(@fmt_in.decompress(parts))
|
|
126
|
-
end
|
|
118
|
+
loop { break unless process_one_passthrough }
|
|
127
119
|
end
|
|
128
120
|
end
|
|
129
121
|
rescue IO::TimeoutError, Async::TimeoutError
|
|
@@ -131,10 +123,38 @@ module OMQ
|
|
|
131
123
|
end
|
|
132
124
|
|
|
133
125
|
|
|
126
|
+
def process_one_eval
|
|
127
|
+
wire_in = @pull.receive
|
|
128
|
+
return false if wire_in.nil?
|
|
129
|
+
parts_in = @fmt_in.decompress(wire_in)
|
|
130
|
+
trace_in(parts_in, wire_in)
|
|
131
|
+
parts_out = OMQ::CLI::ExpressionEvaluator.normalize_result(
|
|
132
|
+
@ctx.instance_exec(parts_in, &@eval_proc), format: @config.format
|
|
133
|
+
)
|
|
134
|
+
return true if parts_out.nil? || parts_out.empty?
|
|
135
|
+
wire_out = @fmt_out.compress(parts_out)
|
|
136
|
+
trace_out(parts_out, wire_out)
|
|
137
|
+
@push << wire_out
|
|
138
|
+
true
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def process_one_passthrough
|
|
143
|
+
wire_in = @pull.receive
|
|
144
|
+
return false if wire_in.nil?
|
|
145
|
+
parts_in = @fmt_in.decompress(wire_in)
|
|
146
|
+
trace_in(parts_in, wire_in)
|
|
147
|
+
wire_out = @fmt_out.compress(parts_in)
|
|
148
|
+
trace_out(parts_in, wire_out)
|
|
149
|
+
@push << wire_out
|
|
150
|
+
true
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
|
|
134
154
|
def run_end_block
|
|
135
155
|
return unless @end_proc
|
|
136
156
|
out = OMQ::CLI::ExpressionEvaluator.normalize_result(
|
|
137
|
-
@ctx.instance_exec(&@end_proc)
|
|
157
|
+
@ctx.instance_exec(&@end_proc), format: @config.format
|
|
138
158
|
)
|
|
139
159
|
@push << @fmt_out.compress(out) if out && !out.empty?
|
|
140
160
|
end
|
data/lib/omq/cli/socket_setup.rb
CHANGED
|
@@ -58,32 +58,32 @@ module OMQ
|
|
|
58
58
|
|
|
59
59
|
|
|
60
60
|
# Bind/connect +sock+ using URL strings from +config.binds+ / +config.connects+.
|
|
61
|
-
# +verbose+
|
|
61
|
+
# +verbose+ gates logging (>= 1), +timestamps+ controls prefix.
|
|
62
62
|
#
|
|
63
|
-
def self.attach(sock, config, verbose: 0)
|
|
63
|
+
def self.attach(sock, config, verbose: 0, timestamps: nil)
|
|
64
64
|
config.binds.each do |url|
|
|
65
65
|
sock.bind(url)
|
|
66
|
-
CLI::Term.write_attach(:bind, sock.last_endpoint,
|
|
66
|
+
CLI::Term.write_attach(:bind, sock.last_endpoint, timestamps) if verbose >= 1
|
|
67
67
|
end
|
|
68
68
|
config.connects.each do |url|
|
|
69
69
|
sock.connect(url)
|
|
70
|
-
CLI::Term.write_attach(:connect, url,
|
|
70
|
+
CLI::Term.write_attach(:connect, url, timestamps) if verbose >= 1
|
|
71
71
|
end
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
|
|
75
75
|
# Bind/connect +sock+ from an Array of Endpoint objects.
|
|
76
76
|
# Used by PipeRunner, which works with structured endpoint lists.
|
|
77
|
-
# +verbose+
|
|
77
|
+
# +verbose+ gates logging (>= 1), +timestamps+ controls prefix.
|
|
78
78
|
#
|
|
79
|
-
def self.attach_endpoints(sock, endpoints, verbose: 0)
|
|
79
|
+
def self.attach_endpoints(sock, endpoints, verbose: 0, timestamps: nil)
|
|
80
80
|
endpoints.each do |ep|
|
|
81
81
|
if ep.bind?
|
|
82
82
|
sock.bind(ep.url)
|
|
83
|
-
CLI::Term.write_attach(:bind, sock.last_endpoint,
|
|
83
|
+
CLI::Term.write_attach(:bind, sock.last_endpoint, timestamps) if verbose >= 1
|
|
84
84
|
else
|
|
85
85
|
sock.connect(ep.url)
|
|
86
|
-
CLI::Term.write_attach(:connect, ep.url,
|
|
86
|
+
CLI::Term.write_attach(:connect, ep.url, timestamps) if verbose >= 1
|
|
87
87
|
end
|
|
88
88
|
end
|
|
89
89
|
end
|
data/lib/omq/cli/term.rb
CHANGED
|
@@ -15,15 +15,18 @@ module OMQ
|
|
|
15
15
|
module_function
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
# Returns a stderr log line prefix
|
|
19
|
-
#
|
|
20
|
-
# time-correlatable. Otherwise returns the empty string.
|
|
18
|
+
# Returns a stderr log line prefix with a UTC ISO8601 timestamp
|
|
19
|
+
# at the requested precision (:s/:ms/:us), or "" when nil.
|
|
21
20
|
#
|
|
22
|
-
# @param
|
|
21
|
+
# @param timestamps [Symbol, nil] :s, :ms, :us, or nil (disabled)
|
|
23
22
|
# @return [String]
|
|
24
|
-
def log_prefix(
|
|
25
|
-
|
|
26
|
-
"
|
|
23
|
+
def log_prefix(timestamps)
|
|
24
|
+
case timestamps
|
|
25
|
+
when nil then ""
|
|
26
|
+
when :s then "#{Time.now.utc.strftime("%FT%T")}Z "
|
|
27
|
+
when :ms then "#{Time.now.utc.strftime("%FT%T.%3N")}Z "
|
|
28
|
+
when :us then "#{Time.now.utc.strftime("%FT%T.%6N")}Z "
|
|
29
|
+
end
|
|
27
30
|
end
|
|
28
31
|
|
|
29
32
|
|
|
@@ -31,10 +34,10 @@ module OMQ
|
|
|
31
34
|
# trailing newline).
|
|
32
35
|
#
|
|
33
36
|
# @param event [OMQ::MonitorEvent]
|
|
34
|
-
# @param
|
|
37
|
+
# @param timestamps [Symbol, nil]
|
|
35
38
|
# @return [String]
|
|
36
|
-
def format_event(event,
|
|
37
|
-
prefix = log_prefix(
|
|
39
|
+
def format_event(event, timestamps)
|
|
40
|
+
prefix = log_prefix(timestamps)
|
|
38
41
|
case event.type
|
|
39
42
|
when :message_sent
|
|
40
43
|
"#{prefix}omq: >> #{Formatter.preview(event.detail[:parts])}"
|
|
@@ -52,22 +55,22 @@ module OMQ
|
|
|
52
55
|
#
|
|
53
56
|
# @param kind [:bind, :connect]
|
|
54
57
|
# @param url [String]
|
|
55
|
-
# @param
|
|
58
|
+
# @param timestamps [Symbol, nil]
|
|
56
59
|
# @return [String]
|
|
57
|
-
def format_attach(kind, url,
|
|
60
|
+
def format_attach(kind, url, timestamps)
|
|
58
61
|
verb = kind == :bind ? "Bound to" : "Connecting to"
|
|
59
|
-
"#{log_prefix(
|
|
62
|
+
"#{log_prefix(timestamps)}omq: #{verb} #{url}"
|
|
60
63
|
end
|
|
61
64
|
|
|
62
65
|
|
|
63
66
|
# Writes one formatted event line to +io+ (default $stderr).
|
|
64
67
|
#
|
|
65
68
|
# @param event [OMQ::MonitorEvent]
|
|
66
|
-
# @param
|
|
69
|
+
# @param timestamps [Symbol, nil]
|
|
67
70
|
# @param io [#write] writable sink, default $stderr
|
|
68
71
|
# @return [void]
|
|
69
|
-
def write_event(event,
|
|
70
|
-
io.write("#{format_event(event,
|
|
72
|
+
def write_event(event, timestamps, io: $stderr)
|
|
73
|
+
io.write("#{format_event(event, timestamps)}\n")
|
|
71
74
|
end
|
|
72
75
|
|
|
73
76
|
|
|
@@ -76,11 +79,11 @@ module OMQ
|
|
|
76
79
|
#
|
|
77
80
|
# @param kind [:bind, :connect]
|
|
78
81
|
# @param url [String]
|
|
79
|
-
# @param
|
|
82
|
+
# @param timestamps [Symbol, nil]
|
|
80
83
|
# @param io [#write]
|
|
81
84
|
# @return [void]
|
|
82
|
-
def write_attach(kind, url,
|
|
83
|
-
io.write("#{format_attach(kind, url,
|
|
85
|
+
def write_attach(kind, url, timestamps, io: $stderr)
|
|
86
|
+
io.write("#{format_attach(kind, url, timestamps)}\n")
|
|
84
87
|
end
|
|
85
88
|
end
|
|
86
89
|
end
|
data/lib/omq/cli/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: omq-cli
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.13.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Patrik Wenger
|
|
@@ -15,20 +15,14 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - "~>"
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: '0.
|
|
19
|
-
- - ">="
|
|
20
|
-
- !ruby/object:Gem::Version
|
|
21
|
-
version: 0.17.8
|
|
18
|
+
version: '0.18'
|
|
22
19
|
type: :runtime
|
|
23
20
|
prerelease: false
|
|
24
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
25
22
|
requirements:
|
|
26
23
|
- - "~>"
|
|
27
24
|
- !ruby/object:Gem::Version
|
|
28
|
-
version: '0.
|
|
29
|
-
- - ">="
|
|
30
|
-
- !ruby/object:Gem::Version
|
|
31
|
-
version: 0.17.8
|
|
25
|
+
version: '0.18'
|
|
32
26
|
- !ruby/object:Gem::Dependency
|
|
33
27
|
name: omq-ffi
|
|
34
28
|
requirement: !ruby/object:Gem::Requirement
|