railway-ipc 1.1.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f2a31b80fc9d4f13598bdc2fbd14c84c78360bd4adeb588bb63b3bb20c33a2de
4
- data.tar.gz: 2bfef85b0fbe56b0d66df365f8ffa5b7913b188658edbb63484aaa0138dfa6ba
3
+ metadata.gz: 11e49c11ff3e5815f7c8050ad5fcdfde1510cf7646d10cfa5d1104a5c1868422
4
+ data.tar.gz: 3fbdeab2e555f9c4b3402cf27e3b072d33c833f85659eaccc09f31d11fddc219
5
5
  SHA512:
6
- metadata.gz: a27c830370df223add103179c9e8b60d5259abbb5b21b664f0b2642d96e5736926325c07e271b08822401c312ef532ec8a6f3c03dba30ce5a637edcbbba1ed12
7
- data.tar.gz: 555cb81425239fba2c5752e238bd541db554f48eb7d46d1336122d730781e40fdd0b339a8266a49a42ace8b73909d7a26f145b9e3ea5d9602fbfcefb9db1a022
6
+ metadata.gz: 70d3834ac2a0db7b1a9a53c52edb3718b34a821d3b0d42e49ebe01f1c76c00188b928734a5969ef621e9609a3852117fd8190706566777c870b2b8b3d9592ccd
7
+ data.tar.gz: 297bbf2e92f9a77d85661849f3bbada5b18eff08b213ec6a48089dea112ab65b70a47b196e8ddc59b475e61cb9e6ecfc8648bb5dcbcd2836e341079d81b2fd92
@@ -10,6 +10,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
10
10
  ### Removed
11
11
  ### Fixed
12
12
 
13
+ ## [2.0.0] - 2020-08-20
14
+ ### Added
15
+ * Several additions to internal logging:
16
+ - Log messages now include a `feature` key. This can be used by logging aggregator tools to group log messages across different systems that use the gem. If one isn't provided a default value of `railway_ipc` is used.
17
+ - Protobufs are logged as a sub-hash which contains both the protobuf type and payload.
18
+ - Exchange and queue names are logged where applicable.
19
+ - The internal Bunny connection now uses the `RailwayIpc::Logger` instead of a generic `Logger`.
20
+
21
+ ### Changed
22
+ * *Breaking Change* `RailwayIpc.configure` now takes `device`, `level`, and `formatter` instead of a complete `Logger` instance. The instance is now managed internally by Railway. This is a breaking change to the `RailwayIpc.configure` API, clients will need to be updated to use the new syntax.
23
+
13
24
  ## [1.1.0] - 2020-08-07
14
25
  ### Changed
15
26
  * allow multiple consumers to handle the same message
data/README.md CHANGED
@@ -19,7 +19,7 @@ And then execute:
19
19
  ```ruby
20
20
  # config/initializers/railway_ipc.rb
21
21
 
22
- RailwayIpc.configure(logger: Rails.logger)
22
+ RailwayIpc.configure(STDOUT, Logger::INFO, MyFormatter)
23
23
  ```
24
24
 
25
25
  * Load the rake tasks in your Rakefile
@@ -29,16 +29,12 @@ module RailwayIpc
29
29
  Rake::Task['sneakers:run'].invoke
30
30
  end
31
31
 
32
- def self.configure(logger: ::Logger.new(STDOUT))
33
- @logger = RailwayIpc::Logger.new(logger)
32
+ def self.configure(log_device=STDOUT, level=::Logger::INFO, log_formatter=nil)
33
+ @logger = RailwayIpc::Logger.new(log_device, level, log_formatter)
34
34
  end
35
35
 
36
36
  def self.logger
37
- @logger || RailwayIpc::Logger.new(::Logger.new(STDOUT))
38
- end
39
-
40
- def self.bunny_logger
41
- logger.logger
37
+ @logger || RailwayIpc::Logger.new(STDOUT)
42
38
  end
43
39
 
44
40
  def self.bunny_connection
@@ -44,10 +44,12 @@ module RailwayIpc
44
44
  RailwayIpc::ProcessIncomingMessage.call(self, message)
45
45
  ack!
46
46
  rescue StandardError => e
47
- RailwayIpc.logger.log_exception(
48
- feature: 'railway_consumer',
47
+ RailwayIpc.logger.error(
48
+ e.message,
49
+ feature: 'railway_ipc_consumer',
50
+ exchange: exchange_name,
51
+ queue: queue_name,
49
52
  error: e.class,
50
- error_message: e.message,
51
53
  payload: payload
52
54
  )
53
55
  raise e
@@ -16,8 +16,9 @@ module RailwayIpc
16
16
 
17
17
  def run
18
18
  logger.warn(
19
- incoming_message.decoded,
20
- "Ignoring unknown message of type '#{incoming_message.type}'"
19
+ "Ignoring unknown message of type '#{incoming_message.type}'",
20
+ feature: 'railway_ipc_consumer',
21
+ protobuf: { type: incoming_message.type, data: incoming_message.decoded }
21
22
  )
22
23
  end
23
24
  end
@@ -36,8 +37,9 @@ module RailwayIpc
36
37
 
37
38
  def run
38
39
  logger.warn(
39
- incoming_message.decoded,
40
- "Ignoring message, no registered handler for '#{incoming_message.type}'"
40
+ "Ignoring message, no registered handler for '#{incoming_message.type}'",
41
+ feature: 'railway_ipc_consumer',
42
+ protobuf: { type: incoming_message.type, data: incoming_message.decoded }
41
43
  )
42
44
  end
43
45
  end
@@ -81,7 +83,13 @@ module RailwayIpc
81
83
 
82
84
  def raise_message_invalid_error
83
85
  error = "Message is invalid: #{incoming_message.stringify_errors}."
84
- logger.error(incoming_message.decoded, error)
86
+ logger.error(
87
+ error,
88
+ feature: 'railway_ipc_consumer',
89
+ exchange: consumer.exchange_name,
90
+ queue: consumer.queue_name,
91
+ protobuf: { type: incoming_message.class, data: incoming_message.decoded }
92
+ )
85
93
  raise RailwayIpc::IncomingMessage::InvalidMessage.new(error)
86
94
  end
87
95
 
@@ -11,15 +11,27 @@ module RailwayIpc
11
11
  end
12
12
 
13
13
  def handle(message)
14
- RailwayIpc.logger.info(message, 'Handling message')
14
+ RailwayIpc.logger.info('Handling message', log_message_options(message))
15
15
  response = self.class.block.call(message)
16
16
  if response.success?
17
- RailwayIpc.logger.info(message, 'Successfully handled message')
17
+ RailwayIpc.logger.info('Successfully handled message', log_message_options(message))
18
18
  else
19
- RailwayIpc.logger.error(message, 'Failed to handle message')
19
+ RailwayIpc.logger.error('Failed to handle message', log_message_options(message))
20
20
  end
21
21
 
22
22
  response
23
23
  end
24
+
25
+ private
26
+
27
+ def log_message_options(message)
28
+ {
29
+ feature: 'railway_ipc_consumer',
30
+ protobuf: {
31
+ type: message.class.name,
32
+ data: message
33
+ }
34
+ }
35
+ end
24
36
  end
25
37
  end
@@ -1,36 +1,41 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailwayIpc
4
+ # Custom logger that accepts a `device`, `level`, and `formatter`.
5
+ # `formatter` can be any object that responds to `call`; a
6
+ # `Logger::Formatter` is used if the argument is not provided.
7
+ #
8
+ # Here is an example formatter that uses `Oj` to format structured log
9
+ # messages:
10
+ #
11
+ # require 'oj'
12
+ # OjFormatter = proc do |severity, datetime, progname, data|
13
+ # data.merge!(
14
+ # name: progname,
15
+ # timestamp: datetime,
16
+ # severity: severity
17
+ # )
18
+ # Oj.dump(data, { mode: :compat, time_format: :xmlschema })
19
+ # end
20
+ #
21
+ # logger = RailwayIpc::Logger.new(STDOUT, Logger::INFO, OjFormatter)
22
+ #
4
23
  class Logger
5
- attr_reader :logger
6
-
7
- def initialize(logger)
8
- @logger = logger
9
- end
10
-
11
- def info(message, statement)
12
- logger.info("[#{message_header(message)}] #{statement}")
24
+ def initialize(device=STDOUT, level=::Logger::INFO, formatter=nil)
25
+ @logger = ::Logger.new(device)
26
+ @logger.level = level
27
+ @logger.formatter = formatter if formatter
13
28
  end
14
29
 
15
- def warn(message, statement)
16
- logger.warn("[#{message_header(message)}] #{statement}")
30
+ %w[fatal error warn info debug].each do |level|
31
+ define_method(level) do |message, data={}|
32
+ data.merge!(feature: 'railway_ipc') unless data.key?(:feature)
33
+ logger.send(level, data.merge(message: message))
34
+ end
17
35
  end
18
36
 
19
- def debug(message, statement)
20
- logger.debug("[#{message_header(message)}] #{statement}")
21
- end
37
+ private
22
38
 
23
- def error(message, statement)
24
- logger.error("[#{message_header(message)}] #{statement}")
25
- end
26
-
27
- def log_exception(exception)
28
- logger.error(exception)
29
- end
30
-
31
- def message_header(message)
32
- log_statement = "message type: #{message.class}, uuid: #{message.uuid}, correlation_id: #{message.correlation_id}"
33
- message.respond_to?(:user_uuid) ? "#{log_statement}, user_uuid: #{message.user_uuid}" : log_statement
34
- end
39
+ attr_reader :logger
35
40
  end
36
41
  end
@@ -21,16 +21,15 @@ module RailwayIpc
21
21
  end
22
22
 
23
23
  def publish(message, published_message_store=RailwayIpc::PublishedMessage)
24
- RailwayIpc.logger.logger.warn('DEPRECATED: Use new PublisherInstance class')
24
+ RailwayIpc.logger.warn('DEPRECATED: Use new PublisherInstance class', log_message_options)
25
25
  ensure_message_uuid(message)
26
26
  ensure_correlation_id(message)
27
- RailwayIpc.logger.info(message, 'Publishing message')
28
-
27
+ RailwayIpc.logger.info('Publishing message', log_message_options(message))
29
28
  result = super(RailwayIpc::Rabbitmq::Payload.encode(message))
30
29
  published_message_store.store_message(self.class.exchange_name, message)
31
30
  result
32
31
  rescue RailwayIpc::InvalidProtobuf => e
33
- RailwayIpc.logger.error(message, 'Invalid protobuf')
32
+ RailwayIpc.logger.error('Invalid protobuf', log_message_options(message))
34
33
  raise e
35
34
  end
36
35
 
@@ -45,6 +44,11 @@ module RailwayIpc
45
44
  message.correlation_id = SecureRandom.uuid if message.correlation_id.blank?
46
45
  message
47
46
  end
47
+
48
+ def log_message_options(message=nil)
49
+ options = { feature: 'railway_ipc_publisher', exchange: self.class.exchange_name }
50
+ message.nil? ? options : options.merge(protobuf: { type: message.class, data: message })
51
+ end
48
52
  end
49
53
  end
50
54
 
@@ -68,20 +72,33 @@ module RailwayIpc
68
72
  def publish(message)
69
73
  message.uuid = SecureRandom.uuid if message.uuid.blank?
70
74
  message.correlation_id = SecureRandom.uuid if message.correlation_id.blank?
71
- RailwayIpc.logger.info(message, 'Publishing message')
75
+ RailwayIpc.logger.info('Publishing message', log_message_options(message))
72
76
 
73
77
  stored_message = message_store.store_message(exchange_name, message)
74
78
  super(RailwayIpc::Rabbitmq::Payload.encode(message))
75
79
  rescue RailwayIpc::InvalidProtobuf => e
76
- RailwayIpc.logger.error(message, 'Invalid protobuf')
80
+ RailwayIpc.logger.error('Invalid protobuf', log_message_options(message))
77
81
  raise e
78
82
  rescue ActiveRecord::RecordInvalid => e
79
- RailwayIpc.logger.error(message, 'Failed to store outgoing message')
83
+ RailwayIpc.logger.error('Failed to store outgoing message', log_message_options(message))
80
84
  raise RailwayIpc::FailedToStoreOutgoingMessage.new(e)
81
85
  rescue StandardError => e
82
86
  stored_message&.destroy
83
87
  raise e
84
88
  end
85
89
  # rubocop:enable Metrics/AbcSize
90
+
91
+ private
92
+
93
+ def log_message_options(message)
94
+ {
95
+ feature: 'railway_ipc_publisher',
96
+ exchange: exchange_name,
97
+ protobuf: {
98
+ type: message.class,
99
+ data: message
100
+ }
101
+ }
102
+ end
86
103
  end
87
104
  end
@@ -28,7 +28,7 @@ module RailwayIpc
28
28
  port: settings[:port],
29
29
  vhost: vhost,
30
30
  automatic_recovery: false,
31
- logger: RailwayIpc.bunny_logger
31
+ logger: RailwayIpc.logger
32
32
  }.merge(options))
33
33
  end
34
34
 
@@ -11,7 +11,11 @@ module RailwayIpc
11
11
  end
12
12
 
13
13
  def respond(request)
14
- RailwayIpc.logger.info(request, 'Responding to request')
14
+ RailwayIpc.logger.info(
15
+ 'Responding to request',
16
+ protobuf: { type: request.class, data: request },
17
+ feature: 'railway_ipc_request'
18
+ )
15
19
  response = self.class.block.call(request)
16
20
  raise ResponseTypeError.new(response.class) unless response.is_a?(Google::Protobuf::MessageExts)
17
21
 
@@ -39,18 +39,26 @@ module RailwayIpc
39
39
  end
40
40
 
41
41
  # rubocop:disable Metrics/AbcSize
42
+ # rubocop:disable Metrics/MethodLength
42
43
  def process_payload(response)
43
44
  decoded_payload = decode_payload(response)
44
45
  case decoded_payload.type
45
46
  when *registered_handlers
46
47
  @message = get_message_class(decoded_payload).decode(decoded_payload.message)
47
- RailwayIpc.logger.info(message, 'Handling response')
48
+ RailwayIpc.logger.info(
49
+ 'Handling response',
50
+ feature: 'railway_ipc_consumer',
51
+ exchange: self.class.exchange_name,
52
+ queue: self.class.queue_name,
53
+ protobuf: { type: message.class, data: message }
54
+ )
48
55
  RailwayIpc::Response.new(message, success: true)
49
56
  else
50
57
  @message = LearnIpc::ErrorMessage.decode(decoded_payload.message)
51
58
  raise RailwayIpc::UnhandledMessageError.new("#{self.class} does not know how to handle #{decoded_payload.type}")
52
59
  end
53
60
  end
61
+ # rubocop:enable Metrics/MethodLength
54
62
  # rubocop:enable Metrics/AbcSize
55
63
 
56
64
  def setup_rabbit_connection
@@ -78,10 +86,12 @@ module RailwayIpc
78
86
  private
79
87
 
80
88
  def log_exception(exception, payload)
81
- RailwayIpc.logger.log_exception(
82
- feature: 'railway_consumer',
89
+ RailwayIpc.logger.error(
90
+ exception.message,
91
+ feature: 'railway_ipc_consumer',
92
+ exchange: self.class.exchange_name,
93
+ queue: self.class.queue_name,
83
94
  error: exception.class,
84
- error_message: exception.message,
85
95
  payload: decode_for_error(exception, payload)
86
96
  )
87
97
  end
@@ -99,7 +109,12 @@ module RailwayIpc
99
109
  end
100
110
 
101
111
  def publish_message
102
- RailwayIpc.logger.info(request_message, 'Sending request')
112
+ RailwayIpc.logger.info(
113
+ 'Sending request',
114
+ feature: 'railway_ipc_publisher',
115
+ exchange: self.class.exchange_name,
116
+ protobuf: { type: request_message.class, data: request_message }
117
+ )
103
118
  rabbit_connection.publish(RailwayIpc::Rabbitmq::Payload.encode(request_message), routing_key: '')
104
119
  end
105
120
 
@@ -45,10 +45,12 @@ module RailwayIpc
45
45
  raise RailwayIpc::UnhandledMessageError.new("#{self.class} does not know how to handle #{decoded_payload.type}")
46
46
  end
47
47
  rescue StandardError => e
48
- RailwayIpc.logger.log_exception(
49
- feature: 'railway_consumer',
48
+ RailwayIpc.logger.error(
49
+ e.message,
50
+ feature: 'railway_ipc_consumer',
51
+ exchange: self.class.exchange_name,
52
+ queue: self.class.queue_name,
50
53
  error: e.class,
51
- error_message: e.message,
52
54
  payload: payload
53
55
  )
54
56
  raise e
@@ -56,10 +58,19 @@ module RailwayIpc
56
58
  # rubocop:enable Metrics/AbcSize
57
59
  # rubocop:enable Metrics/MethodLength
58
60
 
61
+ # rubocop:disable Metrics/AbcSize
62
+ # rubocop:disable Metrics/MethodLength
59
63
  def handle_request(payload)
60
64
  response = work(payload)
61
65
  rescue StandardError => e
62
- RailwayIpc.logger.error(message, "Error responding to message. Error: #{e.class}, #{e.message}")
66
+ RailwayIpc.logger.error(
67
+ 'Error responding to message.',
68
+ exception: e,
69
+ feature: 'railway_ipc_consumer',
70
+ exchange: self.class.exchange_name,
71
+ queue: self.class.queue_name,
72
+ protobuf: { type: message.class, data: message }
73
+ )
63
74
  response = self.class.rpc_error_adapter_class.error_message(e, message)
64
75
  ensure
65
76
  if response
@@ -68,6 +79,8 @@ module RailwayIpc
68
79
  )
69
80
  end
70
81
  end
82
+ # rubocop:enable Metrics/AbcSize
83
+ # rubocop:enable Metrics/MethodLength
71
84
 
72
85
  private
73
86
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailwayIpc
4
- VERSION = '1.1.0'
4
+ VERSION = '2.0.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: railway-ipc
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ''
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-08-07 00:00:00.000000000 Z
11
+ date: 2020-08-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -263,7 +263,7 @@ licenses:
263
263
  - MIT
264
264
  metadata:
265
265
  allowed_push_host: https://rubygems.org/
266
- post_install_message:
266
+ post_install_message:
267
267
  rdoc_options: []
268
268
  require_paths:
269
269
  - lib
@@ -278,9 +278,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
278
278
  - !ruby/object:Gem::Version
279
279
  version: '0'
280
280
  requirements: []
281
- rubyforge_project:
281
+ rubyforge_project:
282
282
  rubygems_version: 2.7.6
283
- signing_key:
283
+ signing_key:
284
284
  specification_version: 4
285
285
  summary: IPC components for Rails
286
286
  test_files: []