grpc_interceptors 0.1.2 → 0.1.3
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 +5 -1
- data/lib/grpc_interceptors/client/logging.rb +67 -0
- data/lib/grpc_interceptors/client/{open_telemetry_tracing_instrument.rb → opentelemetry_tracing_instrument.rb} +5 -16
- data/lib/grpc_interceptors/common/grpc_helper.rb +47 -6
- data/lib/grpc_interceptors/common/log_payload.rb +29 -0
- data/lib/grpc_interceptors/common/opentelemetry_helper.rb +22 -0
- data/lib/grpc_interceptors/server/logging.rb +63 -0
- data/lib/grpc_interceptors/server/opentelemetry_tracing_instrument.rb +39 -0
- data/lib/grpc_interceptors/server/statsd_metrics.rb +4 -2
- metadata +8 -5
- data/lib/grpc_interceptors/common/logger.rb +0 -11
- data/lib/grpc_interceptors/server/open_telemetry_tracing_instrument.rb +0 -55
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2d7842d003c6ac5d3ed32a99b351e1c4637b2a9f2cc492ae4e53d7035c36e3e5
|
4
|
+
data.tar.gz: 67486ab61fb8f18703281e86233ec99fd501567aeb0887475e5cf5e07573e49a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dc309aaa264a867dc0e20bc6f657de562fd15e75f9ca1da2a9f05f955b64a62e0432b695b57c21584094ce8ad8ee9b2fd63a69d3327674f64670b811553706d3
|
7
|
+
data.tar.gz: b797809c323717c89c4ef73f61151748e0b2d0a26697684328bae47d9d263a9c5ea9f9d98b9aa9a32cdf381d7bed4af55f8e2960730b7cddfe1f3b4fd503d8b4
|
data/README.md
CHANGED
@@ -29,7 +29,11 @@ _WIP_
|
|
29
29
|
|
30
30
|
### Logging
|
31
31
|
|
32
|
-
When the `LOG_LEVEL` env variable is set to `INFO` then the server logs out
|
32
|
+
When the `LOG_LEVEL` env variable is set to `INFO` then the server logs out.
|
33
|
+
|
34
|
+
When the `LOG_LEVEL` env variable is set to `DEBUG` then the server additionally adds the request to the log message. (Note, adding the response is currently blocked by [this gRPC issue](https://github.com/grpc/grpc/pull/26547).)
|
35
|
+
|
36
|
+
|
33
37
|
|
34
38
|
### StatsD metrics
|
35
39
|
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../common/grpc_helper'
|
4
|
+
require_relative '../common/log_payload'
|
5
|
+
|
6
|
+
module GrpcInterceptors
|
7
|
+
module Client
|
8
|
+
class Logging < ::GRPC::ServerInterceptor
|
9
|
+
def initialize(logger)
|
10
|
+
@logger = logger
|
11
|
+
|
12
|
+
super()
|
13
|
+
end
|
14
|
+
|
15
|
+
def request_response(
|
16
|
+
request: nil, call: nil, method: nil, metadata: nil, &block
|
17
|
+
)
|
18
|
+
log(request, method, 'unary', &block)
|
19
|
+
end
|
20
|
+
|
21
|
+
# def client_streamer(_requests: nil, call: nil, method: nil, metadata: nil)
|
22
|
+
# yield
|
23
|
+
# end
|
24
|
+
|
25
|
+
# def server_streamer(_request: nil, call: nil, method: nil, metadata: nil)
|
26
|
+
# yield
|
27
|
+
# end
|
28
|
+
|
29
|
+
# def bidi_streamer(_requests: nil, call: nil, method: nil, metadata: nil)
|
30
|
+
# yield
|
31
|
+
# end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
# if the server responds with error, then the error is attached to the log
|
36
|
+
# if the current level is INFO, then it logs out basic facts
|
37
|
+
# if the current level is DEBUG, then it additionally includes the request
|
38
|
+
def log(request, method, method_type)
|
39
|
+
grpc_code = ::GRPC::Core::StatusCodes::OK
|
40
|
+
|
41
|
+
response = yield
|
42
|
+
rescue StandardError => e
|
43
|
+
grpc_code = e.is_a?(::GRPC::BadStatus) ? e.code : ::GRPC::Core::StatusCodes::UNKNOWN
|
44
|
+
|
45
|
+
raise
|
46
|
+
ensure
|
47
|
+
payload = Common::LogPayload.build(
|
48
|
+
method, method_type, grpc_code, 'client'
|
49
|
+
)
|
50
|
+
|
51
|
+
if e
|
52
|
+
payload['error'] = e.class.to_s
|
53
|
+
payload['error_message'] = e.message
|
54
|
+
payload['backtrace'] = e.backtrace
|
55
|
+
end
|
56
|
+
|
57
|
+
if @logger.level == Logger::Severity::INFO
|
58
|
+
@logger.info(payload)
|
59
|
+
elsif @logger.level == Logger::Severity::DEBUG
|
60
|
+
payload['request'] = Common::GrpcHelper.proto_to_json(request)
|
61
|
+
payload['response'] = Common::GrpcHelper.proto_to_json(response)
|
62
|
+
@logger.debug(payload)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -1,15 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative '../common/grpc_helper'
|
4
|
+
require_relative '../common/opentelemetry_helper'
|
4
5
|
|
5
6
|
module GrpcInterceptors
|
6
7
|
module Client
|
7
8
|
class OpenTelemetryTracingInstrument < ::GRPC::ClientInterceptor
|
8
9
|
def request_response(request: nil, call: nil, method: nil, metadata: nil)
|
9
10
|
kind = OpenTelemetry::Trace::SpanKind::CLIENT
|
10
|
-
attributes = tracing_attributes(method)
|
11
|
+
attributes = Common::OpenTelemetryHelper.tracing_attributes(method)
|
11
12
|
|
12
|
-
|
13
|
+
Common::OpenTelemetryHelper.tracer.in_span(
|
14
|
+
method, kind: kind, attributes: attributes
|
15
|
+
) do
|
13
16
|
OpenTelemetry.propagation.inject(metadata)
|
14
17
|
yield
|
15
18
|
end
|
@@ -26,20 +29,6 @@ module GrpcInterceptors
|
|
26
29
|
# def bidi_streamer(_requests: nil, call: nil, method: nil, metadata: nil)
|
27
30
|
# yield
|
28
31
|
# end
|
29
|
-
|
30
|
-
private
|
31
|
-
|
32
|
-
def tracing_attributes(method)
|
33
|
-
method_parts = method.to_s.sub(%r{^/}, '').split('/')
|
34
|
-
service_name = method_parts.shift
|
35
|
-
method_name = method_parts.join('/')
|
36
|
-
|
37
|
-
{
|
38
|
-
OpenTelemetry::SemanticConventions::Trace::RPC_SYSTEM => 'grpc',
|
39
|
-
OpenTelemetry::SemanticConventions::Trace::RPC_SERVICE => service_name,
|
40
|
-
OpenTelemetry::SemanticConventions::Trace::RPC_METHOD => method_name,
|
41
|
-
}
|
42
|
-
end
|
43
32
|
end
|
44
33
|
end
|
45
34
|
end
|
@@ -1,13 +1,54 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module GrpcInterceptors
|
4
|
-
module
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
module Common
|
5
|
+
module GrpcHelper
|
6
|
+
def self.route_name(method)
|
7
|
+
return method unless method.is_a?(Method) # client case
|
8
|
+
|
9
|
+
# server case
|
10
|
+
service_name = service_name_from_server(method)
|
11
|
+
method_name = method_name_from_server(method)
|
12
|
+
|
13
|
+
"/#{service_name}/#{method_name}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.service_name(method)
|
17
|
+
return service_name_from_server(method) if method.is_a?(Method) # server case
|
18
|
+
|
19
|
+
service_name_from_client(method) # client case
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.method_name(method)
|
23
|
+
return method_name_from_server(method) if method.is_a?(Method) # server case
|
24
|
+
|
25
|
+
method_name_from_client(method) # client case
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.service_name_from_server(method)
|
29
|
+
method.owner.service_name.to_s
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.method_name_from_server(method)
|
33
|
+
method.original_name.to_s
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.service_name_from_client(method)
|
37
|
+
method_parts = method.to_s.sub(%r{^/}, '').split('/')
|
38
|
+
method_parts.first
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.method_name_from_client(method)
|
42
|
+
method_parts = method.to_s.sub(%r{^/}, '').split('/')
|
43
|
+
method_parts[1..].join('/')
|
44
|
+
end
|
8
45
|
|
9
|
-
|
10
|
-
|
46
|
+
def self.proto_to_json(proto)
|
47
|
+
proto.to_json(
|
48
|
+
emit_defaults: true,
|
49
|
+
preserve_proto_fieldnames: true
|
50
|
+
)
|
51
|
+
end
|
11
52
|
end
|
12
53
|
end
|
13
54
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GrpcInterceptors
|
4
|
+
module Common
|
5
|
+
module LogPayload
|
6
|
+
def self.build(method, method_type, grpc_code, kind)
|
7
|
+
service = GrpcHelper.service_name(method)
|
8
|
+
method = GrpcHelper.method_name(method)
|
9
|
+
|
10
|
+
payload = {
|
11
|
+
'pid' => Process.pid,
|
12
|
+
'grpc.component' => kind, # the caller, server or client
|
13
|
+
'grpc.service' => service,
|
14
|
+
'grpc.method' => method,
|
15
|
+
'grpc.method_type' => method_type,
|
16
|
+
'grpc.code' => grpc_code
|
17
|
+
}
|
18
|
+
|
19
|
+
if defined?(OpenTelemetry) && OpenTelemetry::Trace.current_span.recording?
|
20
|
+
tracing_context = OpenTelemetry::Trace.current_span.context
|
21
|
+
payload['span_id'] = tracing_context.hex_span_id
|
22
|
+
payload['trace_id'] = tracing_context.hex_trace_id
|
23
|
+
end
|
24
|
+
|
25
|
+
payload
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GrpcInterceptors
|
4
|
+
module Common
|
5
|
+
module OpenTelemetryHelper
|
6
|
+
def self.tracer
|
7
|
+
OpenTelemetry.tracer_provider.tracer('grpc')
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.tracing_attributes(method)
|
11
|
+
service_name = Common::GrpcHelper.service_name(method)
|
12
|
+
method_name = Common::GrpcHelper.method_name(method)
|
13
|
+
|
14
|
+
{
|
15
|
+
OpenTelemetry::SemanticConventions::Trace::RPC_SYSTEM => 'grpc',
|
16
|
+
OpenTelemetry::SemanticConventions::Trace::RPC_SERVICE => service_name,
|
17
|
+
OpenTelemetry::SemanticConventions::Trace::RPC_METHOD => method_name
|
18
|
+
}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../common/grpc_helper'
|
4
|
+
require_relative '../common/log_payload'
|
5
|
+
|
6
|
+
module GrpcInterceptors
|
7
|
+
module Server
|
8
|
+
class Logging < ::GRPC::ServerInterceptor
|
9
|
+
def initialize(logger)
|
10
|
+
@logger = logger
|
11
|
+
|
12
|
+
super()
|
13
|
+
end
|
14
|
+
|
15
|
+
def request_response(request: nil, call: nil, method: nil, &block)
|
16
|
+
log(request, method, 'unary', &block)
|
17
|
+
end
|
18
|
+
|
19
|
+
# def client_streamer(call: nil, method: nil)
|
20
|
+
# yield
|
21
|
+
# end
|
22
|
+
|
23
|
+
# def server_streamer(_request: nil, call: nil, method: nil)
|
24
|
+
# yield
|
25
|
+
# end
|
26
|
+
|
27
|
+
# def bidi_streamer(_requests: nil, call: nil, method: nil)
|
28
|
+
# yield
|
29
|
+
# end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# in case of an exception, it logs out on the ERROR level including error details
|
34
|
+
# if the current level is INFO, then it logs out basic facts
|
35
|
+
# if the current level is DEBUG, then it additionally includes the request
|
36
|
+
def log(request, method, method_type)
|
37
|
+
grpc_code = ::GRPC::Core::StatusCodes::OK
|
38
|
+
|
39
|
+
yield
|
40
|
+
rescue StandardError => e
|
41
|
+
grpc_code = e.is_a?(::GRPC::BadStatus) ? e.code : ::GRPC::Core::StatusCodes::UNKNOWN
|
42
|
+
|
43
|
+
raise
|
44
|
+
ensure
|
45
|
+
payload = Common::LogPayload.build(
|
46
|
+
method, method_type, grpc_code, 'server'
|
47
|
+
)
|
48
|
+
|
49
|
+
if e
|
50
|
+
payload['error'] = e.class.to_s
|
51
|
+
payload['error_message'] = e.message
|
52
|
+
payload['backtrace'] = e.backtrace
|
53
|
+
@logger.error(payload)
|
54
|
+
elsif @logger.level == Logger::Severity::INFO
|
55
|
+
@logger.info(payload)
|
56
|
+
elsif @logger.level == Logger::Severity::DEBUG
|
57
|
+
payload['request'] = Common::GrpcHelper.proto_to_json(request)
|
58
|
+
@logger.debug(payload)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../common/grpc_helper'
|
4
|
+
require_relative '../common/opentelemetry_helper'
|
5
|
+
|
6
|
+
module GrpcInterceptors
|
7
|
+
module Server
|
8
|
+
# https://github.com/grpc/grpc/blob/master/src/ruby/lib/grpc/generic/interceptors.rb
|
9
|
+
class OpenTelemetryTracingInstrument < ::GRPC::ServerInterceptor
|
10
|
+
def request_response(request: nil, call: nil, method: nil, &block)
|
11
|
+
context = OpenTelemetry.propagation.extract(call.metadata)
|
12
|
+
route_name = Common::GrpcHelper.route_name(method)
|
13
|
+
attributes = Common::OpenTelemetryHelper.tracing_attributes(method)
|
14
|
+
kind = OpenTelemetry::Trace::SpanKind::SERVER
|
15
|
+
|
16
|
+
OpenTelemetry::Context.with_current(context) do
|
17
|
+
Common::OpenTelemetryHelper.tracer.in_span(
|
18
|
+
route_name,
|
19
|
+
attributes: attributes,
|
20
|
+
kind: kind,
|
21
|
+
&block
|
22
|
+
)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# def client_streamer(call: nil, method: nil)
|
27
|
+
# yield
|
28
|
+
# end
|
29
|
+
|
30
|
+
# def server_streamer(_request: nil, call: nil, method: nil)
|
31
|
+
# yield
|
32
|
+
# end
|
33
|
+
|
34
|
+
# def bidi_streamer(_requests: nil, call: nil, method: nil)
|
35
|
+
# yield
|
36
|
+
# end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative '../common/grpc_helper'
|
4
|
+
|
3
5
|
module GrpcInterceptors
|
4
6
|
module Server
|
5
7
|
class StatsDMetrics < ::GRPC::ServerInterceptor
|
@@ -32,8 +34,8 @@ module GrpcInterceptors
|
|
32
34
|
|
33
35
|
def common_labels(method)
|
34
36
|
{
|
35
|
-
grpc_method: method
|
36
|
-
grpc_service:
|
37
|
+
grpc_method: Common::GrpcHelper.method_name(method),
|
38
|
+
grpc_service: Common::GrpcHelper.service_name(method)
|
37
39
|
}
|
38
40
|
end
|
39
41
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: grpc_interceptors
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- michal-kazmierczak
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-03-
|
11
|
+
date: 2024-03-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: opentelemetry-api
|
@@ -272,10 +272,13 @@ files:
|
|
272
272
|
- LICENSE
|
273
273
|
- README.md
|
274
274
|
- lib/grpc_interceptors.rb
|
275
|
-
- lib/grpc_interceptors/client/
|
275
|
+
- lib/grpc_interceptors/client/logging.rb
|
276
|
+
- lib/grpc_interceptors/client/opentelemetry_tracing_instrument.rb
|
276
277
|
- lib/grpc_interceptors/common/grpc_helper.rb
|
277
|
-
- lib/grpc_interceptors/common/
|
278
|
-
- lib/grpc_interceptors/
|
278
|
+
- lib/grpc_interceptors/common/log_payload.rb
|
279
|
+
- lib/grpc_interceptors/common/opentelemetry_helper.rb
|
280
|
+
- lib/grpc_interceptors/server/logging.rb
|
281
|
+
- lib/grpc_interceptors/server/opentelemetry_tracing_instrument.rb
|
279
282
|
- lib/grpc_interceptors/server/statsd_metrics.rb
|
280
283
|
homepage: https://github.com/michal-kazmierczak/ruby-grpc-interceptors
|
281
284
|
licenses:
|
@@ -1,55 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative '../common/grpc_helper'
|
4
|
-
|
5
|
-
module GrpcInterceptors
|
6
|
-
module Server
|
7
|
-
# https://github.com/grpc/grpc/blob/master/src/ruby/lib/grpc/generic/interceptors.rb
|
8
|
-
class OpenTelemetryTracingInstrument < ::GRPC::ServerInterceptor
|
9
|
-
def request_response(request: nil, call: nil, method: nil)
|
10
|
-
parent_context = OpenTelemetry.propagation.extract(call.metadata)
|
11
|
-
route_name = GrpcHelper.route_name(method)
|
12
|
-
attributes = tracing_attributes(method)
|
13
|
-
kind = OpenTelemetry::Trace::SpanKind::SERVER
|
14
|
-
span = GrpcHelper.tracer.start_span(
|
15
|
-
route_name,
|
16
|
-
with_parent: parent_context,
|
17
|
-
attributes: attributes,
|
18
|
-
kind: kind
|
19
|
-
)
|
20
|
-
|
21
|
-
yield
|
22
|
-
|
23
|
-
span.finish
|
24
|
-
rescue StandardError => e
|
25
|
-
OpenTelemetry.handle_error(exception: e)
|
26
|
-
|
27
|
-
raise e
|
28
|
-
ensure
|
29
|
-
span.finish if span.recording?
|
30
|
-
end
|
31
|
-
|
32
|
-
# def client_streamer(call: nil, method: nil)
|
33
|
-
# yield
|
34
|
-
# end
|
35
|
-
|
36
|
-
# def server_streamer(_request: nil, call: nil, method: nil)
|
37
|
-
# yield
|
38
|
-
# end
|
39
|
-
|
40
|
-
# def bidi_streamer(_requests: nil, call: nil, method: nil)
|
41
|
-
# yield
|
42
|
-
# end
|
43
|
-
|
44
|
-
private
|
45
|
-
|
46
|
-
def tracing_attributes(method)
|
47
|
-
{
|
48
|
-
OpenTelemetry::SemanticConventions::Trace::RPC_SYSTEM => 'grpc',
|
49
|
-
OpenTelemetry::SemanticConventions::Trace::RPC_SERVICE => method.owner.service_name,
|
50
|
-
OpenTelemetry::SemanticConventions::Trace::RPC_METHOD => method.original_name.to_s,
|
51
|
-
}
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|