stack-service-base 0.0.3 → 0.0.5
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/lib/stack-service-base/logging.rb +141 -0
- data/lib/stack-service-base/open_telemetry.rb +182 -0
- data/lib/stack-service-base/rack_helpers.rb +185 -0
- data/lib/stack-service-base.rb +10 -0
- data/lib/version.rb +1 -1
- metadata +47 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fa21c7280d1fea0d4989a72123bb927756c126d8ab0dda9d155392e70a78d113
|
4
|
+
data.tar.gz: 8bdb53ae89c3bf2480bfb89ac3c056d744678a25f04fef31a50ef524d4c40cfe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 79cf73453d710651ad3c8f9da19b1e29af8edd9b5af0097bf68b77fcd3a755ca4116c0a01d980783e9023c71b8c3d20b1836a792074985cc44e3ee487540f65e
|
7
|
+
data.tar.gz: 4a14e9d3b7ca39ebeb02e8aaef4d5d3a83988ba4b5d46471dd6484640bd8970da5f487c1ca5e121591a23d407b716a3205e80405634b4679c81298a12e7224da
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'console'
|
2
|
+
require 'fiber'
|
3
|
+
|
4
|
+
QUIET = ENV.fetch('CONSOLE_LEVEL', 'false') == 'true'
|
5
|
+
PERFORMANCE = ENV.fetch('PERFORMANCE', 'false') == 'true'
|
6
|
+
|
7
|
+
ENV['CONSOLE_LEVEL'] ||= 'all' unless QUIET || PERFORMANCE
|
8
|
+
|
9
|
+
CONSOLE_LOGGER = Class.new {
|
10
|
+
def <<(...) = Console.logger.info(...)
|
11
|
+
def info(...) = Console.logger.info(...)
|
12
|
+
def debug(...) = Console.logger.debug(...)
|
13
|
+
def debug1(...) = Console.logger.debug(...)
|
14
|
+
def debug2(...) = Console.logger.debug(...)
|
15
|
+
def debug3(...) = Console.logger.debug(...)
|
16
|
+
def warn(...) = Console.logger.warn(...)
|
17
|
+
def error(...) = Console.logger.error(...)
|
18
|
+
def fatal(...) = Console.logger.fatal(...)
|
19
|
+
}.new
|
20
|
+
|
21
|
+
if QUIET
|
22
|
+
ENV['CONSOLE_LEVEL'] = 'error'
|
23
|
+
LOGGER = CONSOLE_LOGGER
|
24
|
+
|
25
|
+
# LOGGER = Class.new {
|
26
|
+
# def <<(...) = nil
|
27
|
+
# def info(...) = nil
|
28
|
+
# def debug(...) = nil
|
29
|
+
# def debug1(...) = nil
|
30
|
+
# def debug2(...) = nil
|
31
|
+
# def debug3(...) = nil
|
32
|
+
# def warn(...) = nil
|
33
|
+
# def error(...) = nil
|
34
|
+
# def fatal(...) = nil
|
35
|
+
# }.new
|
36
|
+
else
|
37
|
+
$stdout.sync = true
|
38
|
+
$stderr.sync = true
|
39
|
+
# class Fiber
|
40
|
+
# alias_method :old_init, :initialize
|
41
|
+
# attr_reader :parent
|
42
|
+
#
|
43
|
+
# def initialize(&)
|
44
|
+
# @parent = Fiber.current
|
45
|
+
# old_init(&)
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# def parents
|
49
|
+
# list = [@parent]
|
50
|
+
# list << list.last.parent while list.last.respond_to?(:parent) && !list.last.parent.nil?
|
51
|
+
# list
|
52
|
+
# end
|
53
|
+
# end
|
54
|
+
|
55
|
+
# https://socketry.github.io/traces/guides/getting-started/index.html
|
56
|
+
# OpenTelemetry / Datadog
|
57
|
+
# https://socketry.github.io/console/guides/getting-started/index.html
|
58
|
+
# ENV['TRACES_BACKEND'] = 'traces/backend/console'
|
59
|
+
# ENV['CONSOLE_LEVEL'] = 'all'
|
60
|
+
# ENV['CONSOLE_OUTPUT'] = 'XTerm' # JSON,Text,XTerm,Default
|
61
|
+
# TRACE_METHODS = true
|
62
|
+
TRACE_METHODS ||= !PERFORMANCE unless defined? TRACE_METHODS
|
63
|
+
if TRACE_METHODS
|
64
|
+
trace = TracePoint.new(:call, :return, :b_call, :b_return) { |tp| # :thread_begin, :thread_end
|
65
|
+
call_stack = Thread.current[:call_stack] ||= {}
|
66
|
+
call_stack_fiber = call_stack[Fiber.current.__id__] ||= []
|
67
|
+
call_stack_fiber << [tp.defined_class, tp.method_id] if [:call, :b_call].include? tp.event
|
68
|
+
call_stack_fiber.pop if [:return, :b_return].include? tp.event
|
69
|
+
}
|
70
|
+
trace.enable
|
71
|
+
end
|
72
|
+
# the_method
|
73
|
+
# trace.disable
|
74
|
+
LOG_DEPTH ||= 10 unless defined? LOG_DEPTH
|
75
|
+
LOGGER = Class.new {
|
76
|
+
def initialize = @context_list ||= {}
|
77
|
+
|
78
|
+
def add_context(name)
|
79
|
+
task = Async::Task.current?
|
80
|
+
@context_list[task.__id__] = name
|
81
|
+
end
|
82
|
+
|
83
|
+
def find_context
|
84
|
+
return '' unless Thread.current[:async_task]
|
85
|
+
|
86
|
+
t_stack = [Async::Task.current]
|
87
|
+
t_stack << t_stack.last.parent while t_stack.last.parent
|
88
|
+
task = t_stack.find { @context_list[_1.__id__] }
|
89
|
+
|
90
|
+
task ? " [#{@context_list[task.__id__]}] " : ''
|
91
|
+
end
|
92
|
+
|
93
|
+
def <<(...) = do_log(:info, ...)
|
94
|
+
|
95
|
+
def info(...) = do_log(:info, ...)
|
96
|
+
|
97
|
+
def debug(...) = do_log(:debug, ...)
|
98
|
+
|
99
|
+
def debug1(...) = do_log(:debug1, ...)
|
100
|
+
|
101
|
+
def debug2(...) = do_log(:debug2, ...)
|
102
|
+
|
103
|
+
def debug3(...) = do_log(:debug3, ...)
|
104
|
+
|
105
|
+
def warn(...) = do_log(:warn, ...)
|
106
|
+
|
107
|
+
def error(...) = do_log(:error, ...)
|
108
|
+
|
109
|
+
def fatal(...) = do_log(:fatal, ...)
|
110
|
+
|
111
|
+
def do_log(name, *args)
|
112
|
+
debug_level = name[/(\d+)/, 1].to_i
|
113
|
+
unless debug_level > LOG_DEPTH
|
114
|
+
if TRACE_METHODS
|
115
|
+
call_stack = Thread.current[:call_stack] ||= {}
|
116
|
+
call_stack_fiber = call_stack[Fiber.current.__id__] ||= []
|
117
|
+
last = call_stack_fiber[-3] ? call_stack_fiber[-3].join('.').gsub('Class:', '').gsub(/[#<>]/, '') : ''
|
118
|
+
last += find_context
|
119
|
+
msg = "\e[33m#{last}:\e[0m \e[38;5;254m" + args.flatten.map(&:inspect).join(', ')
|
120
|
+
else
|
121
|
+
msg = args.flatten.map(&:inspect).join(', ')
|
122
|
+
end
|
123
|
+
_name = name.to_s.gsub(/\d/, '')
|
124
|
+
_name = 'info' if _name == '<<'
|
125
|
+
Console.logger.send _name.to_s.gsub(/\d/, ''), msg
|
126
|
+
end
|
127
|
+
end
|
128
|
+
}.new
|
129
|
+
|
130
|
+
end
|
131
|
+
|
132
|
+
LOGGER_GRAPE = Class.new {
|
133
|
+
def method_missing(name, d)
|
134
|
+
Console.logger.send name, "REST_API: #{d[:method]} #{d[:path]} #{d[:params]} - #{d[:status]} host:#{d[:host]} time:#{d[:time]}"
|
135
|
+
end
|
136
|
+
}.new
|
137
|
+
|
138
|
+
$stdout.puts "QUIET: #{QUIET}"
|
139
|
+
$stdout.puts "PERFORMANCE: #{PERFORMANCE}"
|
140
|
+
|
141
|
+
ENV.select{ |k,v| k =~ /CONSOLE_/}.each { |k,v| $stdout.puts "#{k}: #{v}"}
|
@@ -0,0 +1,182 @@
|
|
1
|
+
ENV['OTEL_LOG_LEVEL'] ||= 'debug'
|
2
|
+
ENV['OTEL_TRACES_EXPORTER'] ||= 'console,otlp'
|
3
|
+
|
4
|
+
unless defined? OTEL_ENABLED
|
5
|
+
OTEL_ENABLED = !ENV['OTEL_EXPORTER_OTLP_ENDPOINT'].to_s.empty?
|
6
|
+
end
|
7
|
+
$stdout.puts "OTEL_ENABLED: #{OTEL_ENABLED}"
|
8
|
+
|
9
|
+
# require 'async'
|
10
|
+
# require 'grape'
|
11
|
+
|
12
|
+
if OTEL_ENABLED
|
13
|
+
STACK_NAME = ENV['STACK_NAME'] || 'undefined_stack'
|
14
|
+
SERVICE_NAME = ENV['STACK_SERVICE_NAME'] || 'undefined_service'
|
15
|
+
ENV['OTEL_RESOURCE_ATTRIBUTES'] ||= "deployment.environment=#{STACK_NAME},service.name=#{SERVICE_NAME}"
|
16
|
+
ENV.select{ |k,v| k =~ /OTEL/}.each { |k,v| $stdout.puts "#{k}: #{v}"}
|
17
|
+
|
18
|
+
require 'opentelemetry/sdk'
|
19
|
+
require 'opentelemetry/exporter/otlp'
|
20
|
+
require 'opentelemetry/instrumentation/all'
|
21
|
+
require 'opentelemetry-api'
|
22
|
+
end
|
23
|
+
|
24
|
+
if defined? Async::Task and OTEL_ENABLED
|
25
|
+
module AsyncTaskOTELPatch
|
26
|
+
def initialize(parent = Task.current?, finished: nil, **options, &block)
|
27
|
+
ctx_ = OpenTelemetry::Context.current
|
28
|
+
|
29
|
+
block_otl = ->(t, *arguments){
|
30
|
+
OpenTelemetry::Context.with_current(ctx_) do
|
31
|
+
block.call t, *arguments
|
32
|
+
end
|
33
|
+
}
|
34
|
+
super parent, finished: , **options, &block_otl
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
Async::Task.prepend AsyncTaskOTELPatch
|
39
|
+
end
|
40
|
+
|
41
|
+
def flatten_hash(hash, path = [], result = {})
|
42
|
+
hash.each do |k, v|
|
43
|
+
path += [k]
|
44
|
+
result[path.join('.')] = v.to_s if v.is_a?(String) || v.is_a?(Numeric)
|
45
|
+
flatten_hash(v, path, result) if v.is_a?(Hash) || v.is_a?(Array)
|
46
|
+
path.pop
|
47
|
+
end
|
48
|
+
result
|
49
|
+
end
|
50
|
+
|
51
|
+
if defined? LOGGER and OTEL_ENABLED
|
52
|
+
OpenTelemetry.logger = LOGGER
|
53
|
+
end
|
54
|
+
|
55
|
+
def otel_initialize(app)
|
56
|
+
$stdout.puts "otl_configure: OTEL_ENABLED: #{OTEL_ENABLED}"
|
57
|
+
return unless OTEL_ENABLED
|
58
|
+
|
59
|
+
OpenTelemetry::SDK.configure do |c|
|
60
|
+
# c.service_name = SERVICE_NAME
|
61
|
+
c.use_all # enables all instrumentation!
|
62
|
+
end
|
63
|
+
|
64
|
+
at_exit do
|
65
|
+
OpenTelemetry.tracer_provider.force_flush
|
66
|
+
OpenTelemetry.tracer_provider.shutdown
|
67
|
+
end
|
68
|
+
|
69
|
+
$tracer_ = OpenTelemetry.tracer_provider.tracer(SERVICE_NAME)
|
70
|
+
|
71
|
+
otl_span "#{SERVICE_NAME} start", {
|
72
|
+
'stack.name': ENV['STACK_NAME'],
|
73
|
+
'stack.service.name': ENV['STACK_SERVICE_NAME'],
|
74
|
+
'org.opencontainers.image.title': ENV['ORG_OPENCONTAINERS_IMAGE_TITLE'],
|
75
|
+
'org.opencontainers.image.url': ENV['ORG_OPENCONTAINERS_IMAGE_URL'],
|
76
|
+
'org.opencontainers.image.source': ENV['ORG_OPENCONTAINERS_IMAGE_SOURCE'],
|
77
|
+
'org.opencontainers.image.created': ENV['ORG_OPENCONTAINERS_IMAGE_CREATED'],
|
78
|
+
'com.gitlab.ci.commt.timestamp': ENV['COM_GITLAB_CI_COMMIT_TIMESTAMP'],
|
79
|
+
'com.gitlab.ci.tag': ENV['COM_GITLAB_CI_TAG'],
|
80
|
+
RACK_ENV: ENV['RACK_ENV'],
|
81
|
+
NODE_ENV: ENV['NODE_ENV'],
|
82
|
+
SERVER_ENV: ENV['SERVER_ENV'],
|
83
|
+
} do |span|
|
84
|
+
|
85
|
+
span.add_event("not-working in kibana APM", attributes:{
|
86
|
+
event: 'Success',
|
87
|
+
message: 'Get data from elastic Success'
|
88
|
+
}.transform_keys(&:to_s) )
|
89
|
+
# span.status = OpenTelemetry::Trace::Status.error("error message here!")
|
90
|
+
end
|
91
|
+
|
92
|
+
app.use OTELTraceInfo
|
93
|
+
end
|
94
|
+
|
95
|
+
if defined? Sequel and OTEL_ENABLED
|
96
|
+
class Sequel::Database
|
97
|
+
alias old_test_connection test_connection
|
98
|
+
|
99
|
+
def test_connection(...)
|
100
|
+
OpenTelemetry::Common::Utilities.untraced do
|
101
|
+
old_test_connection(...)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
alias old_valid_connection? valid_connection?
|
106
|
+
def valid_connection?(...)
|
107
|
+
OpenTelemetry::Common::Utilities.untraced do
|
108
|
+
old_valid_connection?(...)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def otl_span(name, attributes = {})
|
115
|
+
# span_ = OpenTelemetry::Trace.current_span
|
116
|
+
return yield(nil) unless OTEL_ENABLED
|
117
|
+
|
118
|
+
return yield(nil) unless $tracer_
|
119
|
+
$tracer_&.in_span(name, attributes: flatten_hash(attributes.transform_keys(&:to_s).transform_values{_1 || 'n/a'}) ) do |span|
|
120
|
+
yield span
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def otl_current_span
|
125
|
+
return unless OTEL_ENABLED
|
126
|
+
yield OpenTelemetry::Trace.current_span
|
127
|
+
end
|
128
|
+
|
129
|
+
def otl_def(name)
|
130
|
+
original_method = self.respond_to?(:instance_method) ? instance_method(name) : method(name)
|
131
|
+
self.respond_to?(:remove_method) ? remove_method(name) : Object.send(:remove_method, name)
|
132
|
+
original_method = original_method.respond_to?(:unbind) ? original_method.unbind : original_method
|
133
|
+
|
134
|
+
define_method(name) do |*args, **kwargs, &block|
|
135
|
+
klass = self.respond_to?(:class_name) ? self.class_name : (self.respond_to?(:name) ? self.name : 'main')
|
136
|
+
otl_span("method: #{klass}.#{name}", {args: args.to_s, kwargs: kwargs.to_s}) do |span|
|
137
|
+
original_method.bind(self).call(*args, **kwargs, &block)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
if defined? OpenTelemetry::Instrumentation::Rack::Middlewares
|
143
|
+
OpenTelemetry::Instrumentation::Rack::Middlewares::TracerMiddleware.config[:url_quantization] = ->(path, env) {
|
144
|
+
"HTTP #{env['REQUEST_METHOD']} #{path}"
|
145
|
+
}
|
146
|
+
end
|
147
|
+
|
148
|
+
if OTEL_ENABLED
|
149
|
+
class OpenTelemetry::SDK::Trace::Span
|
150
|
+
alias add_attributes_old add_attributes
|
151
|
+
|
152
|
+
def add_attributes(attributes)
|
153
|
+
add_attributes_old flatten_hash attributes
|
154
|
+
.transform_keys(&:to_s)
|
155
|
+
.transform_values{ _1 || 'n/a' }
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
if defined? Safrano::Request and OTEL_ENABLED
|
161
|
+
require 'odata/error'
|
162
|
+
|
163
|
+
class Safrano::Request
|
164
|
+
def process
|
165
|
+
begin
|
166
|
+
@response = Safrano::Response.new
|
167
|
+
before.tap_error { |err| dispatch_error(err) }
|
168
|
+
.tap_valid { |_res| dispatch }
|
169
|
+
|
170
|
+
rescue Sequel::Error => e
|
171
|
+
OpenTelemetry::Trace.current_span.tap do |span|
|
172
|
+
span.record_exception(e)
|
173
|
+
span.status = OpenTelemetry::Trace::Status.error("Unhandled exception of type: #{e.class}")
|
174
|
+
end
|
175
|
+
dispatch_error(Safrano::SequelExceptionError.new(e))
|
176
|
+
end
|
177
|
+
@response.finish
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
|
@@ -0,0 +1,185 @@
|
|
1
|
+
module RackHelpers
|
2
|
+
def Rack.middleware_klass(&block)
|
3
|
+
Class.new do
|
4
|
+
define_method(:initialize) do |app, *opts, &block2|; @app = app; @opts = opts; @block = block2 || block end
|
5
|
+
def call(env) = @block.call env, @app, @opts
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def Rack.define_middleware(name, &block)
|
10
|
+
Object.const_set name, Rack.middleware_klass(&block)
|
11
|
+
end
|
12
|
+
|
13
|
+
Rack.define_middleware :Authentication do |env, app|
|
14
|
+
token = env['HTTP_AUTHORIZATION'][/Bearer (.*)/, 1] rescue nil # Authorization: Bearer token
|
15
|
+
token ||= env['HTTP_AUTH'][/Bearer (.*)/, 1] rescue nil # AUTH: Bearer token
|
16
|
+
token ||= Rack::Request.new(env).cookies['token']
|
17
|
+
token_h = JWT.decode token, '', false, algorithm: 'RS256' rescue nil
|
18
|
+
token_h ||= [{}]
|
19
|
+
|
20
|
+
if defined? OpenTelemetry::Trace
|
21
|
+
OpenTelemetry::Trace.current_span.add_attributes'TOKEN' => token_h[0].to_json,
|
22
|
+
'username' => (token_h[0]['username'] || '')
|
23
|
+
end
|
24
|
+
Async::Task.current.define_singleton_method :token, &-> { token_h[0] }
|
25
|
+
|
26
|
+
app.call env
|
27
|
+
end
|
28
|
+
|
29
|
+
# TODO:
|
30
|
+
# - Only required for cross-thread context transfer
|
31
|
+
#
|
32
|
+
# Rack.define_middleware :AsyncDBConnectionOT do |env, app|
|
33
|
+
# context_ = OpenTelemetry::Context.current
|
34
|
+
# OpenTelemetry::Context.with_current(context_) do
|
35
|
+
# OpenTelemetry::Trace.with_span(OpenTelemetry::Trace.current_span) do
|
36
|
+
# app.call env
|
37
|
+
# end
|
38
|
+
# end
|
39
|
+
# end
|
40
|
+
|
41
|
+
# Rack.define_middleware :SwaggerUI do |env, app, opts|
|
42
|
+
# # url = opts[:swagger]
|
43
|
+
# @static ||= Rack::Static.new app, urls: [''], root: "#{__dir__}/../swagger_ui/", index: 'index.html', cascade: true
|
44
|
+
# env['REQUEST_METHOD'] == 'GET' ? @static.call(env) : app.call(env)
|
45
|
+
# end
|
46
|
+
|
47
|
+
#
|
48
|
+
|
49
|
+
Rack.define_middleware :OTELTraceInfo do |env, app, opts|
|
50
|
+
status, headers, body = app.call env
|
51
|
+
if status.to_i >= 500
|
52
|
+
otl_current_span{
|
53
|
+
span_context = OpenTelemetry::Trace.current_span.context
|
54
|
+
trace_id = span_context.trace_id.unpack1('H*')
|
55
|
+
|
56
|
+
begin
|
57
|
+
bj = JSON.parse(body.join)
|
58
|
+
bj[:trace_id] = trace_id
|
59
|
+
body = [bj.to_json]
|
60
|
+
rescue =>e
|
61
|
+
body = [body.join + "\ntrace_id: #{trace_id}"]
|
62
|
+
end
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
[status, headers, body]
|
67
|
+
end
|
68
|
+
|
69
|
+
Rack.define_middleware :NoCache do |env, app, opts|
|
70
|
+
status, headers, body = app.call env
|
71
|
+
headers['Cache-Control'] = 'private,max-age=0,must-revalidate,no-store'
|
72
|
+
[status, headers, body]
|
73
|
+
end
|
74
|
+
|
75
|
+
# PATCH: for the Grape
|
76
|
+
class Rack::Lint::Wrapper::InputWrapper
|
77
|
+
def rewind = @input.rewind
|
78
|
+
end if defined? Rack::Lint::Wrapper::InputWrapper
|
79
|
+
|
80
|
+
class Rack::Lint
|
81
|
+
def call(env = nil) = @app.call(env)
|
82
|
+
end if defined? Rack::Lint
|
83
|
+
|
84
|
+
class Rack::CommonLogger
|
85
|
+
def log(env, status, header, began_at) = ()
|
86
|
+
end
|
87
|
+
# require 'rack/request'
|
88
|
+
# # sinatra was resolved to 3.0.2, which depends on
|
89
|
+
# # rack (~> 2.2, >= 2.2.4)
|
90
|
+
# # /home/user/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/rack-2.2.4/lib/rack/request.rb
|
91
|
+
# module Rack::Request::Helpers
|
92
|
+
# alias old_post POST
|
93
|
+
# def POST = get_header(Rack::RACK_INPUT).empty? ? {} : old_post
|
94
|
+
# end
|
95
|
+
|
96
|
+
Rack.define_middleware :HeadersLogger do |env, app, opts|
|
97
|
+
LOGGER.info env.select { _1 =~ /HTTP/ }.transform_keys { _1.gsub 'HTTP_', '' }
|
98
|
+
status, headers, body =app.call env
|
99
|
+
|
100
|
+
if status.to_i / 100 == 5
|
101
|
+
_body = body.to_s # each(&:to_s).join
|
102
|
+
LOGGER.error [status, headers, _body]
|
103
|
+
OpenTelemetry::Trace.current_span.tap do |span|
|
104
|
+
event_attributes = { 'exception.type' => "HTTP #{status.to_i}", 'exception.message' => _body, 'exception.stacktrace' => '' }
|
105
|
+
span.add_event('exception', attributes: event_attributes)
|
106
|
+
span.status = OpenTelemetry::Trace::Status.error("Request error: #{status.to_i}")
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
LOGGER.info headers
|
111
|
+
[status, headers, body]
|
112
|
+
end
|
113
|
+
|
114
|
+
Rack.define_middleware :RequestProfile do |env, app, opts|
|
115
|
+
start = Time.now
|
116
|
+
LOGGER.debug "start:#{start}"
|
117
|
+
status, headers, body = app.call env
|
118
|
+
LOGGER.debug "#{Time.now - start} sec - end (fiber id: #{Fiber.current.__id__}, async task: #{Async::Task.current})"
|
119
|
+
headers['x-runtime'] = "#{Time.now - start}"
|
120
|
+
# if headers['Content-Type'] =~ /html/
|
121
|
+
# headers['Set-Cookie'] = "Runtime=#{Time.now - start} sec;expires=Sat, 01-Jan-3000 00:00:00 GMT;path=/;"
|
122
|
+
# end
|
123
|
+
[status, headers, body]
|
124
|
+
end
|
125
|
+
|
126
|
+
class << self
|
127
|
+
def rack_setup(app)
|
128
|
+
# PATCH: for the Grape Swagger
|
129
|
+
# https://github.com/ruby-grape/grape-swagger/pull/905
|
130
|
+
# https://github.com/ruby-grape/grape-swagger/issues/904
|
131
|
+
GrapeSwagger::DocMethods::ParseParams.instance_eval do
|
132
|
+
def parse_enum_or_range_values(values)
|
133
|
+
case values
|
134
|
+
when Proc
|
135
|
+
parse_enum_or_range_values(values.call) if values.parameters.empty?
|
136
|
+
when Range
|
137
|
+
parse_range_values(values) # if values.first.is_a?(Integer) TODO
|
138
|
+
when Array
|
139
|
+
{ enum: values }
|
140
|
+
else
|
141
|
+
{ enum: [values] } if values
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end if defined? GrapeSwagger::DocMethods::ParseParams
|
145
|
+
|
146
|
+
app.use Rack.middleware_klass do |env, app|
|
147
|
+
# env['REQUEST_PATH'] == '/healthcheck' ? [200, {}, ['Healthy']] : app.call(env)
|
148
|
+
env['PATH_INFO'] == '/healthcheck' ? [200, {'Content-Type' =>'application/json'}, [{ Status: 'Healthy' }.to_json ]] : app.call(env)
|
149
|
+
end
|
150
|
+
|
151
|
+
if defined? OpenTelemetry::Instrumentation::Rack::Middlewares::TracerMiddleware
|
152
|
+
use OpenTelemetry::Instrumentation::Rack::Middlewares::TracerMiddleware
|
153
|
+
end
|
154
|
+
app.use Rack::Deflater
|
155
|
+
app.use OTELTraceInfo
|
156
|
+
|
157
|
+
unless defined?(PERFORMANCE) && PERFORMANCE
|
158
|
+
app.use RequestProfile
|
159
|
+
app.use HeadersLogger
|
160
|
+
|
161
|
+
if defined? Rack::ODataCommonLogger
|
162
|
+
app.use Rack::ODataCommonLogger, LOGGER # or use Rack::CommonLogger, LOGGER
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
app.use Rack.middleware_klass do |env, app|
|
167
|
+
code, headers, body = env['REQUEST_METHOD'] == 'OPTIONS' ? [200, {}, []] : app.call(env)
|
168
|
+
|
169
|
+
# scheme = env['rack.url_scheme']
|
170
|
+
# referer = URI.parse env['HTTP_REFERER']
|
171
|
+
headers.merge!(
|
172
|
+
# 'Access-Control-Allow-Origin' => "#{referer.scheme}://#{referer.host}",
|
173
|
+
'Access-Control-Allow-Origin' => '*',
|
174
|
+
'Access-Control-Allow-Methods' => 'GET, PUT, POST, DELETE, HEAD, OPTIONS',
|
175
|
+
'Access-Control-Allow-Headers' => '*',
|
176
|
+
'Access-Control-Allow-Credentials' => 'true')
|
177
|
+
[code, headers, body]
|
178
|
+
end
|
179
|
+
|
180
|
+
# unless @run
|
181
|
+
# run ->(_env) { [200, {'Content-Type' => 'text/plain'}, ['OK!?']] }
|
182
|
+
# end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
data/lib/stack-service-base.rb
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
require 'stack-service-base/logging'
|
2
|
+
require 'stack-service-base/rack_helpers'
|
3
|
+
require 'stack-service-base/open_telemetry'
|
4
|
+
|
1
5
|
module StackServiceBase
|
2
6
|
class << self
|
3
7
|
def rack_setup app
|
@@ -6,6 +10,7 @@ module StackServiceBase
|
|
6
10
|
return unless app.respond_to? :use
|
7
11
|
|
8
12
|
app.instance_eval do
|
13
|
+
RackHelpers.rack_setup app
|
9
14
|
if ENV.fetch('PROMETHEUS_METRICS_EXPORT', 'true') == 'true'
|
10
15
|
require 'stack-service-base/prometheus'
|
11
16
|
|
@@ -13,6 +18,11 @@ module StackServiceBase
|
|
13
18
|
use Prometheus::Middleware::Collector
|
14
19
|
use Prometheus::Middleware::Exporter
|
15
20
|
end
|
21
|
+
|
22
|
+
if OTEL_ENABLED
|
23
|
+
otel_initialize app
|
24
|
+
end
|
25
|
+
|
16
26
|
end
|
17
27
|
end
|
18
28
|
end
|
data/lib/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stack-service-base
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ''
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-05-
|
11
|
+
date: 2025-05-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: prometheus-client
|
@@ -24,6 +24,48 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: opentelemetry-sdk
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: opentelemetry-exporter-otlp
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: opentelemetry-instrumentation-all
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
27
69
|
- !ruby/object:Gem::Dependency
|
28
70
|
name: rake
|
29
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -115,8 +157,11 @@ extensions: []
|
|
115
157
|
extra_rdoc_files: []
|
116
158
|
files:
|
117
159
|
- lib/stack-service-base.rb
|
160
|
+
- lib/stack-service-base/logging.rb
|
161
|
+
- lib/stack-service-base/open_telemetry.rb
|
118
162
|
- lib/stack-service-base/prometheus.rb
|
119
163
|
- lib/stack-service-base/prometheus_parser.rb
|
164
|
+
- lib/stack-service-base/rack_helpers.rb
|
120
165
|
- lib/version.rb
|
121
166
|
homepage:
|
122
167
|
licenses: []
|