tcp-server 1.2.0-java → 1.4.1-java
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/README.md +28 -28
- data/Rakefile +12 -2
- data/exe/tcp_server +3 -3
- data/lib/{server/argument_parser.rb → tcp_server/arguments_parser.rb} +9 -9
- data/lib/{server/channel_initializer.rb → tcp_server/channelizer.rb} +29 -25
- data/lib/{client.rb → tcp_server/client.rb} +56 -48
- data/lib/{server → tcp_server}/config.rb +8 -4
- data/lib/{server → tcp_server}/default_handler.rb +7 -6
- data/lib/{demo_listener.rb → tcp_server/demo_listener.rb} +5 -5
- data/lib/{server → tcp_server}/instance_methods.rb +70 -31
- data/lib/{server → tcp_server}/listenable.rb +4 -4
- data/lib/tcp_server/logging.rb +310 -0
- data/lib/{server → tcp_server}/message_handler.rb +8 -8
- data/lib/{server → tcp_server}/modular_handler.rb +33 -28
- data/lib/tcp_server/server.rb +58 -0
- data/lib/tcp_server/version.rb +16 -0
- data/lib/tcp_server.rb +3 -2
- metadata +38 -33
- data/lib/logging.rb +0 -253
- data/lib/server/server.rb +0 -39
- data/lib/server/shutdown_hook.rb +0 -35
- data/lib/server/version.rb +0 -16
- data/lib/server.rb +0 -36
- data/lib/tcp-server.rb +0 -13
|
@@ -6,17 +6,19 @@
|
|
|
6
6
|
|
|
7
7
|
# =begin
|
|
8
8
|
#
|
|
9
|
-
# Copyright Nels Nelson 2016-
|
|
9
|
+
# Copyright Nels Nelson 2016-2026 but freely usable (see license)
|
|
10
10
|
#
|
|
11
11
|
# =end
|
|
12
12
|
|
|
13
13
|
require 'java'
|
|
14
14
|
require 'netty'
|
|
15
15
|
|
|
16
|
+
java_import 'io.netty.handler.codec.Delimiters'
|
|
17
|
+
|
|
16
18
|
require 'logger'
|
|
17
19
|
|
|
18
|
-
# The
|
|
19
|
-
module
|
|
20
|
+
# The TcpServer module
|
|
21
|
+
module TcpServer
|
|
20
22
|
DEFAULT_LOG_LEVEL = Logger::INFO
|
|
21
23
|
DEFAULT_HOST = '0.0.0.0'.freeze
|
|
22
24
|
DEFAULT_PORT = 8080
|
|
@@ -26,7 +28,7 @@ module Server
|
|
|
26
28
|
DEFAULT_KEEP_ALIVE = false
|
|
27
29
|
DEFAULT_MAX_QUEUED_INCOMING_CONNECTIONS = 100
|
|
28
30
|
DEFAULT_MAX_FRAME_LENGTH = 8192
|
|
29
|
-
DEFAULT_DELIMITER =
|
|
31
|
+
DEFAULT_DELIMITER = Delimiters.lineDelimiter
|
|
30
32
|
DEFAULT_LOG_REQUESTS = false
|
|
31
33
|
DEFAULT_QUIT_COMMANDS = %i[
|
|
32
34
|
bye cease desist exit leave quit stop terminate
|
|
@@ -35,6 +37,7 @@ module Server
|
|
|
35
37
|
# rubocop: disable Metrics/MethodLength
|
|
36
38
|
def server_config
|
|
37
39
|
@server_config ||= {
|
|
40
|
+
app_name: 'tcp_server',
|
|
38
41
|
host: DEFAULT_HOST,
|
|
39
42
|
port: DEFAULT_PORT,
|
|
40
43
|
ssl: DEFAULT_SSL_ENABLED,
|
|
@@ -52,3 +55,4 @@ module Server
|
|
|
52
55
|
module_function :server_config
|
|
53
56
|
# rubocop: enable Metrics/MethodLength
|
|
54
57
|
end
|
|
58
|
+
# end TcpServer
|
|
@@ -6,16 +6,16 @@
|
|
|
6
6
|
|
|
7
7
|
# =begin
|
|
8
8
|
#
|
|
9
|
-
# Copyright Nels Nelson 2016-
|
|
9
|
+
# Copyright Nels Nelson 2016-2026 but freely usable (see license)
|
|
10
10
|
#
|
|
11
11
|
# =end
|
|
12
12
|
|
|
13
|
-
# The
|
|
14
|
-
module
|
|
13
|
+
# The TcpServer module
|
|
14
|
+
module TcpServer
|
|
15
15
|
# The DefaultHandler module
|
|
16
16
|
module DefaultHandler
|
|
17
17
|
def channel_active(ctx)
|
|
18
|
-
|
|
18
|
+
TcpServer.log.info "Channel active: #{ctx.channel}"
|
|
19
19
|
response = 'Hello, world!'
|
|
20
20
|
log.trace "Sending response: #{response.inspect}"
|
|
21
21
|
ctx.channel.writeAndFlush("#{response}\n")
|
|
@@ -24,7 +24,7 @@ module Server
|
|
|
24
24
|
# rubocop: disable Metrics/AbcSize
|
|
25
25
|
def message_received(ctx, msg)
|
|
26
26
|
return if msg.nil?
|
|
27
|
-
msg.chomp
|
|
27
|
+
msg = msg.chomp if msg.respond_to?(:chomp)
|
|
28
28
|
return if msg.respond_to?(:empty?) && msg.empty?
|
|
29
29
|
log.trace "##{__method__} channel: #{ctx.channel}, message: #{msg.inspect}"
|
|
30
30
|
return ctx.close() if @options[:quit_commands].include?(msg.downcase.to_sym)
|
|
@@ -35,9 +35,10 @@ module Server
|
|
|
35
35
|
# rubocop: enable Metrics/AbcSize
|
|
36
36
|
|
|
37
37
|
def exception_caught(_ctx, cause)
|
|
38
|
-
|
|
38
|
+
TcpServer.log.error "Exception caught: #{cause}"
|
|
39
39
|
cause.backtrace.each { |t| ::Server.log.error t }
|
|
40
40
|
ctx.close()
|
|
41
41
|
end
|
|
42
42
|
end
|
|
43
43
|
end
|
|
44
|
+
# end TcpServer
|
|
@@ -6,17 +6,17 @@
|
|
|
6
6
|
|
|
7
7
|
# =begin
|
|
8
8
|
#
|
|
9
|
-
# Copyright Nels Nelson 2016-
|
|
9
|
+
# Copyright Nels Nelson 2016-2026 but freely usable (see license)
|
|
10
10
|
#
|
|
11
11
|
# =end
|
|
12
12
|
|
|
13
|
-
require_relative '
|
|
13
|
+
require_relative 'default_handler'
|
|
14
14
|
|
|
15
|
-
# The
|
|
16
|
-
module
|
|
15
|
+
# The TcpServer module
|
|
16
|
+
module TcpServer
|
|
17
17
|
# The Demo class
|
|
18
18
|
class Demo
|
|
19
|
-
include
|
|
19
|
+
include TcpServer::DefaultHandler
|
|
20
20
|
|
|
21
21
|
def initialize(options = {})
|
|
22
22
|
@options = options
|
|
@@ -6,54 +6,61 @@
|
|
|
6
6
|
|
|
7
7
|
# =begin
|
|
8
8
|
#
|
|
9
|
-
# Copyright Nels Nelson 2016-
|
|
9
|
+
# Copyright Nels Nelson 2016-2026 but freely usable (see license)
|
|
10
10
|
#
|
|
11
11
|
# =end
|
|
12
12
|
|
|
13
13
|
require 'java'
|
|
14
14
|
require 'netty'
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
java_import 'io.netty.bootstrap.ServerBootstrap'
|
|
17
|
+
java_import 'io.netty.channel.ChannelOption'
|
|
18
|
+
java_import 'io.netty.channel.group.DefaultChannelGroup'
|
|
19
|
+
java_import 'io.netty.channel.nio.NioEventLoopGroup'
|
|
20
|
+
java_import 'io.netty.util.concurrent.GlobalEventExecutor'
|
|
21
|
+
java_import 'io.netty.handler.logging.LoggingHandler'
|
|
22
|
+
java_import 'io.netty.handler.logging.LogLevel'
|
|
23
|
+
|
|
24
|
+
require_relative 'channelizer'
|
|
17
25
|
require_relative 'default_handler'
|
|
18
|
-
require_relative 'shutdown_hook'
|
|
19
|
-
|
|
20
|
-
# The Server module
|
|
21
|
-
module Server
|
|
22
|
-
java_import Java::io.netty.bootstrap.ServerBootstrap
|
|
23
|
-
java_import Java::io.netty.channel.ChannelOption
|
|
24
|
-
java_import Java::io.netty.channel.group.DefaultChannelGroup
|
|
25
|
-
java_import Java::io.netty.channel.nio.NioEventLoopGroup
|
|
26
|
-
java_import Java::io.netty.handler.logging.LogLevel
|
|
27
|
-
java_import Java::io.netty.handler.logging.LoggingHandler
|
|
28
|
-
java_import Java::io.netty.util.concurrent.GlobalEventExecutor
|
|
29
26
|
|
|
27
|
+
# The TcpServer module
|
|
28
|
+
module TcpServer
|
|
30
29
|
# The InstanceMethods module
|
|
31
30
|
module InstanceMethods
|
|
32
|
-
include ::
|
|
31
|
+
include TcpServer::DefaultHandler
|
|
33
32
|
|
|
34
33
|
# rubocop: disable Metrics/AbcSize
|
|
35
34
|
def bootstrap
|
|
36
35
|
@bootstrap = ServerBootstrap.new
|
|
37
36
|
@bootstrap.group(boss_group, worker_group)
|
|
38
37
|
@bootstrap.channel(channel_type)
|
|
39
|
-
@bootstrap.option(
|
|
40
|
-
@bootstrap.childOption(
|
|
38
|
+
@bootstrap.option(backlog_option, max_queued_incoming_connections)
|
|
39
|
+
@bootstrap.childOption(keep_alive_option, keep_alive) if keep_alive
|
|
41
40
|
@bootstrap.handler(logging_handler) if options.fetch(:log_requests, false)
|
|
42
|
-
@bootstrap.childHandler(
|
|
41
|
+
@bootstrap.childHandler(channelizer)
|
|
43
42
|
end
|
|
44
43
|
# rubocop: enable Metrics/AbcSize
|
|
45
44
|
|
|
45
|
+
def backlog_option
|
|
46
|
+
ChannelOption::SO_BACKLOG
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def keep_alive_option
|
|
50
|
+
ChannelOption::SO_KEEPALIVE
|
|
51
|
+
end
|
|
52
|
+
|
|
46
53
|
def configure_handlers(*handlers, &block)
|
|
47
|
-
|
|
54
|
+
channelizer << block if block_given?
|
|
48
55
|
add_listener(*handlers)
|
|
49
56
|
end
|
|
50
57
|
|
|
51
58
|
def channel_type
|
|
52
|
-
@channel_type ||= Server::CHANNEL_TYPE
|
|
59
|
+
@channel_type ||= TcpServer::Server::CHANNEL_TYPE
|
|
53
60
|
end
|
|
54
61
|
|
|
55
|
-
def
|
|
56
|
-
@
|
|
62
|
+
def channelizer
|
|
63
|
+
@channelizer ||= TcpServer::Channelizer.new(channel_group, @options)
|
|
57
64
|
end
|
|
58
65
|
|
|
59
66
|
def boss_group
|
|
@@ -65,21 +72,30 @@ module Server
|
|
|
65
72
|
end
|
|
66
73
|
|
|
67
74
|
def channel_group
|
|
68
|
-
@channel_group ||= DefaultChannelGroup.new(
|
|
75
|
+
@channel_group ||= DefaultChannelGroup.new(
|
|
76
|
+
'server_channels', GlobalEventExecutor::INSTANCE)
|
|
69
77
|
end
|
|
70
78
|
|
|
71
79
|
def logging_handler
|
|
72
80
|
@logging_handler ||= LoggingHandler.new(LogLevel::INFO)
|
|
73
81
|
end
|
|
74
82
|
|
|
83
|
+
def server_channel(port = self.port)
|
|
84
|
+
@server_channel ||= bootstrap.bind(port).sync().channel()
|
|
85
|
+
end
|
|
86
|
+
|
|
75
87
|
# rubocop: disable Metrics/AbcSize
|
|
76
88
|
# rubocop: disable Metrics/MethodLength
|
|
77
89
|
def run(port = self.port)
|
|
78
|
-
channel =
|
|
90
|
+
channel = server_channel(port)
|
|
79
91
|
channel_group.add(channel)
|
|
80
|
-
|
|
92
|
+
Signal.trap('INT') { handle_interrupt }
|
|
93
|
+
Signal.trap('TERM') { handle_interrupt }
|
|
81
94
|
log.info "Listening on #{channel.local_address}"
|
|
82
95
|
channel.closeFuture().sync()
|
|
96
|
+
rescue Interrupt => _e
|
|
97
|
+
$stdout.write "\r\e[0K"
|
|
98
|
+
$stdout.flush
|
|
83
99
|
rescue java.lang.IllegalArgumentException => e
|
|
84
100
|
raise "Invalid argument: #{e.message}"
|
|
85
101
|
rescue java.net.BindException => e
|
|
@@ -96,26 +112,49 @@ module Server
|
|
|
96
112
|
@port ||= (@options[:port] || DEFAULT_PORT).to_i
|
|
97
113
|
end
|
|
98
114
|
|
|
115
|
+
def handle_interrupt
|
|
116
|
+
$stdout.write "\r\e[0K"
|
|
117
|
+
$stdout.flush
|
|
118
|
+
shutdown
|
|
119
|
+
end
|
|
120
|
+
|
|
99
121
|
def shutdown
|
|
100
|
-
|
|
101
|
-
|
|
122
|
+
logger.info 'Shutting down'
|
|
123
|
+
quietly do
|
|
124
|
+
server_channel&.close()
|
|
125
|
+
channel_group.disconnect().awaitUninterruptibly()
|
|
126
|
+
channel_group.close().awaitUninterruptibly()
|
|
127
|
+
end
|
|
128
|
+
rescue StandardError => e
|
|
129
|
+
warn e.message
|
|
130
|
+
$stderr.flush
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def quietly(preserved_verbose = $VERBOSE)
|
|
134
|
+
$VERBOSE = nil
|
|
135
|
+
yield
|
|
136
|
+
ensure
|
|
137
|
+
$VERBOSE = preserved_verbose
|
|
102
138
|
end
|
|
103
139
|
|
|
104
140
|
def stop
|
|
105
141
|
boss_group&.shutdownGracefully()
|
|
106
142
|
worker_group&.shutdownGracefully()
|
|
143
|
+
rescue StandardError => e
|
|
144
|
+
warn e.message
|
|
145
|
+
$stderr.flush
|
|
107
146
|
end
|
|
108
147
|
|
|
109
148
|
def <<(handler)
|
|
110
|
-
|
|
149
|
+
channelizer << handler
|
|
111
150
|
end
|
|
112
151
|
|
|
113
152
|
def add_listener(*listener)
|
|
114
|
-
|
|
153
|
+
channelizer.add_listener(*listener)
|
|
115
154
|
end
|
|
116
155
|
|
|
117
156
|
def replace_listeners(*listener)
|
|
118
|
-
|
|
157
|
+
channelizer.replace_listeners(*listener)
|
|
119
158
|
end
|
|
120
159
|
|
|
121
160
|
def keep_alive
|
|
@@ -133,6 +172,6 @@ module Server
|
|
|
133
172
|
end
|
|
134
173
|
end
|
|
135
174
|
end
|
|
136
|
-
# module
|
|
175
|
+
# module InstanceMethods
|
|
137
176
|
end
|
|
138
|
-
# module
|
|
177
|
+
# module TcpServer
|
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
|
|
7
7
|
# =begin
|
|
8
8
|
#
|
|
9
|
-
# Copyright Nels Nelson 2016-
|
|
9
|
+
# Copyright Nels Nelson 2016-2026 but freely usable (see license)
|
|
10
10
|
#
|
|
11
11
|
# =end
|
|
12
12
|
|
|
13
13
|
require 'java'
|
|
14
14
|
|
|
15
|
-
# The
|
|
16
|
-
module
|
|
15
|
+
# The TcpServer module
|
|
16
|
+
module TcpServer
|
|
17
17
|
# The Listenable module
|
|
18
18
|
module Listenable
|
|
19
19
|
def listeners
|
|
@@ -45,4 +45,4 @@ module Server
|
|
|
45
45
|
end
|
|
46
46
|
# module Listenable
|
|
47
47
|
end
|
|
48
|
-
# module
|
|
48
|
+
# module TcpServer
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
# frozen_string_literal: false
|
|
3
|
+
|
|
4
|
+
def jruby?
|
|
5
|
+
defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby' && defined?(Java)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
if jruby?
|
|
9
|
+
require 'java'
|
|
10
|
+
require 'apache-log4j-2'
|
|
11
|
+
|
|
12
|
+
java_import 'org.apache.logging.log4j.Level'
|
|
13
|
+
java_import 'org.apache.logging.log4j.core.appender.ConsoleAppender'
|
|
14
|
+
java_import 'org.apache.logging.log4j.core.config.builder.api.' \
|
|
15
|
+
'ConfigurationBuilderFactory'
|
|
16
|
+
java_import 'org.apache.logging.log4j.core.config.Configurator'
|
|
17
|
+
java_import 'org.apache.logging.log4j.core.config.LoggerConfig'
|
|
18
|
+
java_import 'org.apache.logging.log4j.core.LoggerContext'
|
|
19
|
+
java_import 'org.apache.logging.log4j.LogManager'
|
|
20
|
+
java_import 'org.apache.logging.log4j.ThreadContext'
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
require 'fileutils'
|
|
24
|
+
require 'logger'
|
|
25
|
+
|
|
26
|
+
# The Logging module
|
|
27
|
+
module Logging
|
|
28
|
+
DEFAULT_TIME_FORMAT = '%Y-%m-%d %H:%M:%S'.freeze
|
|
29
|
+
DEFAULT_APP_NAME = 'app'.freeze
|
|
30
|
+
DEFAULT_LOG_PATTERN = '%d{ABSOLUTE} %-5p [%X{category}] %m%n'.freeze
|
|
31
|
+
DEFAULT_RUBY_LOG_FORMAT =
|
|
32
|
+
"%<timestamp>s %-5<severity>s %<category>s] %<msg>s\n".freeze
|
|
33
|
+
EMPTY_STRING = ''.freeze
|
|
34
|
+
|
|
35
|
+
module_function
|
|
36
|
+
|
|
37
|
+
def backend
|
|
38
|
+
@backend ||= init_backend
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def init_backend
|
|
42
|
+
return init_log4j_backend if jruby?
|
|
43
|
+
init_logger_backend
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def init_logger_backend
|
|
47
|
+
log = Logger.new($stdout)
|
|
48
|
+
log.formatter = method(:ruby_formatter)
|
|
49
|
+
@backend = log
|
|
50
|
+
apply_backend_level(log_level)
|
|
51
|
+
log
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def init_log4j_backend
|
|
55
|
+
init_log4j2!
|
|
56
|
+
apply_backend_level(log_level)
|
|
57
|
+
LogManager.getLogger(app_logger_name)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def proxy
|
|
61
|
+
@proxy ||= Proxy.new
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def app_name
|
|
65
|
+
DEFAULT_APP_NAME
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def app_logger_name
|
|
69
|
+
DEFAULT_APP_NAME
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def time_format
|
|
73
|
+
DEFAULT_TIME_FORMAT
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def ruby_log_format
|
|
77
|
+
DEFAULT_RUBY_LOG_FORMAT
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def java_log_pattern
|
|
81
|
+
DEFAULT_LOG_PATTERN
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def ruby_formatter(severity, datetime, _progname, msg)
|
|
85
|
+
category = (Thread.current[:logging_category] || app_name).to_s
|
|
86
|
+
timestamp = datetime.strftime(time_format)
|
|
87
|
+
format(ruby_log_format, timestamp: timestamp, severity: severity,
|
|
88
|
+
category: category, msg: msg)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def init_log4j2!
|
|
92
|
+
return if @log4j2_initialized
|
|
93
|
+
|
|
94
|
+
Java::java.lang.System.setProperty(
|
|
95
|
+
'log4j.shutdownHookEnabled', Java::java.lang.Boolean.toString(false))
|
|
96
|
+
|
|
97
|
+
configure_log4j
|
|
98
|
+
|
|
99
|
+
@log4j2_initialized = true
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def configure_log4j
|
|
103
|
+
@log4j2_context = Configurator.initialize(log4j_configuration.build)
|
|
104
|
+
@log4j2_context.updateLoggers
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# rubocop: disable Metrics/AbcSize
|
|
108
|
+
# rubocop: disable Metrics/MethodLength
|
|
109
|
+
def log4j_configuration(
|
|
110
|
+
config = ConfigurationBuilderFactory.newConfigurationBuilder)
|
|
111
|
+
layout = config.newLayout('PatternLayout')
|
|
112
|
+
.addAttribute('pattern', java_log_pattern)
|
|
113
|
+
console = config.newAppender('stdout', 'CONSOLE')
|
|
114
|
+
.addAttribute('target', ConsoleAppender::Target::SYSTEM_OUT).add(layout)
|
|
115
|
+
config.add(console)
|
|
116
|
+
root = config.newRootLogger(Level::INFO)
|
|
117
|
+
.add(config.newAppenderRef('stdout'))
|
|
118
|
+
config.add(root)
|
|
119
|
+
app_logger = config.newLogger(app_logger_name, Level::INFO)
|
|
120
|
+
.add(config.newAppenderRef('stdout'))
|
|
121
|
+
.addAttribute('additivity', false)
|
|
122
|
+
config.add(app_logger)
|
|
123
|
+
config
|
|
124
|
+
end
|
|
125
|
+
# rubocop: enable Metrics/AbcSize
|
|
126
|
+
# rubocop: enable Metrics/MethodLength
|
|
127
|
+
|
|
128
|
+
def derive_category(receiver, callsite)
|
|
129
|
+
if receiver.is_a?(Module)
|
|
130
|
+
receiver.name.to_s
|
|
131
|
+
elsif receiver.respond_to?(:class) && receiver.class.respond_to?(:name)
|
|
132
|
+
receiver.class.name.to_s
|
|
133
|
+
else
|
|
134
|
+
EMPTY_STRING
|
|
135
|
+
end.tap do |cat|
|
|
136
|
+
return cat unless cat.empty?
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
File.basename(callsite.path.to_s)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def dispatch(level, *args, &block)
|
|
143
|
+
if jruby?
|
|
144
|
+
backend.public_send(level, *args, &block)
|
|
145
|
+
else
|
|
146
|
+
case level
|
|
147
|
+
when :fatal then backend.fatal(*args, &block)
|
|
148
|
+
when :warn then backend.warn(*args, &block)
|
|
149
|
+
else
|
|
150
|
+
backend.public_send(level, *args, &block)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# rubocop: disable Metrics/MethodLength
|
|
156
|
+
def with_category(category)
|
|
157
|
+
if jruby?
|
|
158
|
+
prev = ThreadContext.get('category')
|
|
159
|
+
|
|
160
|
+
begin
|
|
161
|
+
ThreadContext.put('category', category)
|
|
162
|
+
yield
|
|
163
|
+
ensure
|
|
164
|
+
if prev.nil?
|
|
165
|
+
ThreadContext.remove('category')
|
|
166
|
+
else
|
|
167
|
+
ThreadContext.put('category', prev)
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
else
|
|
171
|
+
prev = Thread.current[:logging_category]
|
|
172
|
+
|
|
173
|
+
begin
|
|
174
|
+
Thread.current[:logging_category] = category
|
|
175
|
+
yield
|
|
176
|
+
ensure
|
|
177
|
+
Thread.current[:logging_category] = prev
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
# rubocop: enable Metrics/MethodLength
|
|
182
|
+
|
|
183
|
+
NUMERIC_TO_SYMBOL = {
|
|
184
|
+
5 => :off,
|
|
185
|
+
4 => :fatal,
|
|
186
|
+
3 => :error,
|
|
187
|
+
2 => :warn,
|
|
188
|
+
1 => :info,
|
|
189
|
+
0 => :debug,
|
|
190
|
+
-1 => :trace
|
|
191
|
+
}.freeze
|
|
192
|
+
|
|
193
|
+
SYMBOL_TO_RUBY = {
|
|
194
|
+
off: Logger::FATAL,
|
|
195
|
+
fatal: Logger::FATAL,
|
|
196
|
+
error: Logger::ERROR,
|
|
197
|
+
warn: Logger::WARN,
|
|
198
|
+
info: Logger::INFO,
|
|
199
|
+
debug: Logger::DEBUG,
|
|
200
|
+
trace: Logger::DEBUG,
|
|
201
|
+
all: Logger::DEBUG
|
|
202
|
+
}.freeze
|
|
203
|
+
|
|
204
|
+
def log_level
|
|
205
|
+
@log_level ||= :info
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def log_level=(level)
|
|
209
|
+
@log_level = case level
|
|
210
|
+
when Integer
|
|
211
|
+
int_to_symbol_level(level)
|
|
212
|
+
when Symbol, String
|
|
213
|
+
level.to_s.downcase.to_sym
|
|
214
|
+
else
|
|
215
|
+
:info
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
apply_backend_level(@log_level)
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def apply_backend_level(sym)
|
|
222
|
+
if jruby?
|
|
223
|
+
ctx = @log4j2_context || LoggerContext.getContext(false)
|
|
224
|
+
app_cfg = app_config(ctx.getConfiguration, sym)
|
|
225
|
+
app_cfg.setLevel(symbol_to_java_level(sym))
|
|
226
|
+
ctx.updateLoggers
|
|
227
|
+
else
|
|
228
|
+
backend.level = SYMBOL_TO_RUBY.fetch(sym, Logger::INFO)
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
sym
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def app_config(context_configuration, sym)
|
|
235
|
+
app_cfg = context_configuration.getLoggerConfig(app_logger_name)
|
|
236
|
+
return app_cfg if !app_cfg.nil? && app_cfg.getName == app_logger_name
|
|
237
|
+
|
|
238
|
+
init_app_logging_config(context_configuration, app_logger_name, sym)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def init_app_logging_config(context_configuration, app_logger_name, sym)
|
|
242
|
+
app_cfg = LoggerConfig.newBuilder.withConfig(context_configuration)
|
|
243
|
+
.withLoggerName(app_logger_name).withLevel(symbol_to_java_level(sym))
|
|
244
|
+
.withAdditivity(false).build
|
|
245
|
+
if (appender = context_configuration.getAppender('stdout')).nil?
|
|
246
|
+
app_cfg.setAdditive(true)
|
|
247
|
+
else
|
|
248
|
+
app_cfg.addAppender(appender, Level::INFO, nil)
|
|
249
|
+
end
|
|
250
|
+
context_configuration.addLogger(app_logger_name, app_cfg)
|
|
251
|
+
app_cfg
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def int_to_symbol_level(level)
|
|
255
|
+
NUMERIC_TO_SYMBOL.fetch(level) do
|
|
256
|
+
level > 5 ? :off : :all
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# rubocop: disable Metrics/CyclomaticComplexity
|
|
261
|
+
def symbol_to_java_level(sym)
|
|
262
|
+
case sym
|
|
263
|
+
when :off then Level::OFF
|
|
264
|
+
when :fatal then Level::FATAL
|
|
265
|
+
when :error then Level::ERROR
|
|
266
|
+
when :warn then Level::WARN
|
|
267
|
+
when :debug then Level::DEBUG
|
|
268
|
+
when :trace then Level::TRACE
|
|
269
|
+
when :all then Level::ALL
|
|
270
|
+
else Level::INFO
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
# rubocop: enable Metrics/CyclomaticComplexity
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
# The Logging class
|
|
277
|
+
module Logging
|
|
278
|
+
# The Proxy class
|
|
279
|
+
class Proxy
|
|
280
|
+
SEVERITIES = %i[trace debug info warn error fatal].freeze
|
|
281
|
+
|
|
282
|
+
def method_missing(method_name, *args, &block)
|
|
283
|
+
return super unless SEVERITIES.include?(method_name)
|
|
284
|
+
|
|
285
|
+
receiver = Thread.current[:logging_receiver]
|
|
286
|
+
callsite = Thread.current[:logging_callsite] || caller_locations(2, 1).first
|
|
287
|
+
category = Logging.derive_category(receiver, callsite)
|
|
288
|
+
|
|
289
|
+
Logging.with_category(category) do
|
|
290
|
+
Logging.dispatch(method_name, *args, &block)
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
def respond_to_missing?(method_name, include_private = false)
|
|
295
|
+
SEVERITIES.include?(method_name) || super
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
# Make `log` and `logger` available everywhere, capturing receiver + callsite.
|
|
301
|
+
module LoggingInjection
|
|
302
|
+
def logger
|
|
303
|
+
Thread.current[:logging_receiver] = self
|
|
304
|
+
Thread.current[:logging_callsite] = caller_locations(1, 1).first
|
|
305
|
+
Logging.proxy
|
|
306
|
+
end
|
|
307
|
+
alias log logger
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
Object.include(LoggingInjection)
|
|
@@ -6,19 +6,19 @@
|
|
|
6
6
|
|
|
7
7
|
# =begin
|
|
8
8
|
#
|
|
9
|
-
# Copyright Nels Nelson 2016-
|
|
9
|
+
# Copyright Nels Nelson 2016-2026 but freely usable (see license)
|
|
10
10
|
#
|
|
11
11
|
# =end
|
|
12
12
|
|
|
13
13
|
require 'java'
|
|
14
14
|
require 'netty'
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
java_import Java::io.netty.channel.ChannelHandler
|
|
20
|
-
java_import Java::io.netty.channel.SimpleChannelInboundHandler
|
|
16
|
+
java_import 'io.netty.channel.ChannelHandler'
|
|
17
|
+
java_import 'io.netty.channel.ChannelFutureListener'
|
|
18
|
+
java_import 'io.netty.channel.SimpleChannelInboundHandler'
|
|
21
19
|
|
|
20
|
+
# The TcpServer module
|
|
21
|
+
module TcpServer
|
|
22
22
|
# The MessageHandler class
|
|
23
23
|
class MessageHandler < SimpleChannelInboundHandler
|
|
24
24
|
include ChannelHandler
|
|
@@ -40,9 +40,9 @@ module Server
|
|
|
40
40
|
|
|
41
41
|
def messageReceived(ctx, msg)
|
|
42
42
|
log.trace "##{__method__} channel: #{ctx.channel}, message: #{msg.inspect}"
|
|
43
|
-
msg&.chomp
|
|
43
|
+
msg = msg&.chomp
|
|
44
44
|
log.info "Received message: #{msg}"
|
|
45
|
-
return super
|
|
45
|
+
return super unless respond_to?(:handle_message) && @handler&.arity == 2
|
|
46
46
|
handle_message(ctx, msg)
|
|
47
47
|
end
|
|
48
48
|
|