service_skeleton 0.0.0.1.ENOTAG → 0.0.0.2.g46c1e0e

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +0 -2
  3. data/.rubocop.yml +114 -9
  4. data/.travis.yml +11 -0
  5. data/README.md +153 -279
  6. data/lib/service_skeleton/background_worker.rb +80 -0
  7. data/lib/service_skeleton/config.rb +18 -78
  8. data/lib/service_skeleton/config_variable.rb +8 -29
  9. data/lib/service_skeleton/config_variables.rb +68 -54
  10. data/lib/service_skeleton/error.rb +3 -5
  11. data/lib/service_skeleton/filtering_logger.rb +0 -2
  12. data/lib/service_skeleton/logging_helpers.rb +3 -10
  13. data/lib/service_skeleton/metrics_methods.rb +13 -28
  14. data/lib/service_skeleton/signal_handler.rb +183 -0
  15. data/lib/service_skeleton.rb +145 -22
  16. data/service_skeleton.gemspec +9 -10
  17. metadata +19 -102
  18. data/.editorconfig +0 -7
  19. data/.git-blame-ignore-revs +0 -2
  20. data/.github/workflows/ci.yml +0 -50
  21. data/lib/service_skeleton/config_class.rb +0 -16
  22. data/lib/service_skeleton/config_variable/boolean.rb +0 -21
  23. data/lib/service_skeleton/config_variable/enum.rb +0 -27
  24. data/lib/service_skeleton/config_variable/float.rb +0 -25
  25. data/lib/service_skeleton/config_variable/integer.rb +0 -25
  26. data/lib/service_skeleton/config_variable/kv_list.rb +0 -26
  27. data/lib/service_skeleton/config_variable/path_list.rb +0 -13
  28. data/lib/service_skeleton/config_variable/string.rb +0 -18
  29. data/lib/service_skeleton/config_variable/url.rb +0 -36
  30. data/lib/service_skeleton/config_variable/yaml_file.rb +0 -42
  31. data/lib/service_skeleton/generator.rb +0 -165
  32. data/lib/service_skeleton/metric_method_name.rb +0 -9
  33. data/lib/service_skeleton/runner.rb +0 -46
  34. data/lib/service_skeleton/service_name.rb +0 -20
  35. data/lib/service_skeleton/signal_manager.rb +0 -202
  36. data/lib/service_skeleton/signals_methods.rb +0 -15
  37. data/lib/service_skeleton/ultravisor_children.rb +0 -20
  38. data/lib/service_skeleton/ultravisor_loggerstash.rb +0 -11
  39. data/ultravisor/.yardopts +0 -1
  40. data/ultravisor/Guardfile +0 -9
  41. data/ultravisor/README.md +0 -404
  42. data/ultravisor/lib/ultravisor/child/call.rb +0 -21
  43. data/ultravisor/lib/ultravisor/child/call_receiver.rb +0 -14
  44. data/ultravisor/lib/ultravisor/child/cast.rb +0 -16
  45. data/ultravisor/lib/ultravisor/child/cast_receiver.rb +0 -11
  46. data/ultravisor/lib/ultravisor/child/process_cast_call.rb +0 -39
  47. data/ultravisor/lib/ultravisor/child.rb +0 -481
  48. data/ultravisor/lib/ultravisor/error.rb +0 -25
  49. data/ultravisor/lib/ultravisor/logging_helpers.rb +0 -32
  50. data/ultravisor/lib/ultravisor.rb +0 -216
  51. data/ultravisor/spec/example_group_methods.rb +0 -19
  52. data/ultravisor/spec/example_methods.rb +0 -8
  53. data/ultravisor/spec/spec_helper.rb +0 -52
  54. data/ultravisor/spec/ultravisor/add_child_spec.rb +0 -79
  55. data/ultravisor/spec/ultravisor/child/call_spec.rb +0 -121
  56. data/ultravisor/spec/ultravisor/child/cast_spec.rb +0 -111
  57. data/ultravisor/spec/ultravisor/child/id_spec.rb +0 -21
  58. data/ultravisor/spec/ultravisor/child/new_spec.rb +0 -152
  59. data/ultravisor/spec/ultravisor/child/restart_delay_spec.rb +0 -40
  60. data/ultravisor/spec/ultravisor/child/restart_spec.rb +0 -70
  61. data/ultravisor/spec/ultravisor/child/run_spec.rb +0 -95
  62. data/ultravisor/spec/ultravisor/child/shutdown_spec.rb +0 -124
  63. data/ultravisor/spec/ultravisor/child/spawn_spec.rb +0 -107
  64. data/ultravisor/spec/ultravisor/child/unsafe_instance_spec.rb +0 -55
  65. data/ultravisor/spec/ultravisor/child/wait_spec.rb +0 -32
  66. data/ultravisor/spec/ultravisor/new_spec.rb +0 -71
  67. data/ultravisor/spec/ultravisor/remove_child_spec.rb +0 -49
  68. data/ultravisor/spec/ultravisor/run_spec.rb +0 -334
  69. data/ultravisor/spec/ultravisor/shutdown_spec.rb +0 -106
@@ -0,0 +1,80 @@
1
+ class ServiceSkeleton
2
+ module BackgroundWorker
3
+ include ServiceSkeleton::LoggingHelpers
4
+
5
+ # async code is a shit to test, and rarely finds bugs anyway, so let's
6
+ # just
7
+ #:nocov:
8
+ # this whole thing.
9
+
10
+ # This signal is raised internally if needed to shut down a worker thread.
11
+ class TerminateBackgroundThread < Exception; end
12
+ private_constant :TerminateBackgroundThread
13
+
14
+ def initialize(*_)
15
+ @bg_worker_op_mutex = Mutex.new
16
+ @bg_worker_op_cv = ConditionVariable.new
17
+
18
+ begin
19
+ super
20
+ rescue ArgumentError => ex
21
+ if ex.message =~ /wrong number of arguments.*expected 0/
22
+ super()
23
+ else
24
+ raise
25
+ end
26
+ end
27
+ end
28
+
29
+ def start!
30
+ @bg_worker_op_mutex.synchronize do
31
+ return if @bg_worker_thread
32
+
33
+ @bg_worker_thread = Thread.new do
34
+ Thread.current.name = self.class.to_s
35
+
36
+ Thread.handle_interrupt(Exception => :never) do
37
+ logger.debug("BackgroundWorker(#{self.class})#start!") { "Background worker thread #{Thread.current.object_id} starting" }
38
+ begin
39
+ Thread.handle_interrupt(Exception => :immediate) do
40
+ @bg_worker_op_mutex.synchronize { @bg_worker_op_cv.signal }
41
+ self.start
42
+ end
43
+ rescue TerminateBackgroundThread
44
+ logger.debug("BackgroundWorker(#{self.class})#start!") { "Background worker thread #{Thread.current.object_id} received magical termination exception" }
45
+ rescue Exception => ex
46
+ log_exception(ex) { "Background worker thread #{Thread.current.object_id} received fatal exception" }
47
+ else
48
+ logger.debug("BackgroundWorker(#{self.class})#start!") { "Background worker thread #{Thread.current.object_id} terminating" }
49
+ end
50
+ end
51
+ end
52
+
53
+ @bg_worker_op_cv.wait(@bg_worker_op_mutex) until @bg_worker_thread
54
+ end
55
+ end
56
+
57
+ def stop!
58
+ @bg_worker_op_mutex.synchronize do
59
+ return if @bg_worker_thread.nil?
60
+
61
+ logger.debug("BackgroundWorker(#{self.class})#stop!") { "Terminating worker thread #{Thread.current.object_id} as requested" }
62
+
63
+ shutdown
64
+
65
+ @bg_worker_thread.join
66
+ @bg_worker_thread = nil
67
+
68
+ logger.debug("BackgroundWorker(#{self.class})#stop!") { "Worker thread terminated" }
69
+ end
70
+ end
71
+
72
+ private
73
+
74
+ attr_reader :logger
75
+
76
+ def shutdown
77
+ @bg_worker_thread.raise(TerminateBackgroundThread)
78
+ end
79
+ end
80
+ end
@@ -1,58 +1,38 @@
1
- # frozen_string_literal: true
2
-
3
1
  require "to_regexp"
4
2
 
5
3
  require_relative "./filtering_logger"
6
4
 
7
- require "loggerstash"
8
-
9
- module ServiceSkeleton
5
+ class ServiceSkeleton
10
6
  class Config
11
- attr_reader :logger, :pre_run_logger, :env, :service_name
12
-
13
- def initialize(env, service_name, variables)
14
- @service_name = service_name
15
-
16
- # Parsing variables will redact the environment, so we want to take a
17
- # private unredacted copy before that happens for #[] lookup in the
18
- # future.
19
- @env = env.to_hash.dup.freeze
7
+ attr_reader :logger
20
8
 
21
- parse_variables(internal_variables + variables, env)
9
+ def initialize(env, svc)
10
+ @env = env.to_hash.freeze
11
+ @svc = svc
22
12
 
23
- # Sadly, we can't setup the logger until we know *how* to setup the
24
- # logger, which requires parsing config variables
13
+ parse_registered_variables(env)
25
14
  setup_logger
26
15
  end
27
16
 
28
17
  def [](k)
29
- @env[k].dup
18
+ @env[k]
30
19
  end
31
20
 
32
21
  private
33
22
 
34
- def parse_variables(variables, env)
35
- variables.map do |var|
36
- var[:class].new(var[:name], env, **var[:opts])
37
- end.each do |var|
38
- val = var.value
39
- method_name = var.method_name(@service_name).to_sym
40
-
41
- define_singleton_method(method_name) do
23
+ def parse_registered_variables(env)
24
+ @svc.registered_variables.each do |var|
25
+ val = var.value(env[var.name.to_s])
26
+ define_singleton_method(var.method_name(@svc.service_name)) do
42
27
  val
43
28
  end
44
29
 
45
- define_singleton_method(:"#{method_name}=") do |new_value|
46
- val = new_value
47
- end
48
- end.each do |var|
49
- if var.redact?(env)
30
+ if var.sensitive?
50
31
  if env.object_id != ENV.object_id
51
32
  raise ServiceSkeleton::Error::CannotSanitizeEnvironmentError,
52
- "Attempted to sanitize sensitive variable #{var.name}, but we're not operating on the process' environment"
53
- else
54
- var.redact!(env)
33
+ "Attempted to sanitize sensitive variable #{var.name}, but was not passed the ENV object"
55
34
  end
35
+ env.delete(var.name.to_s)
56
36
  end
57
37
  end
58
38
  end
@@ -66,42 +46,14 @@ module ServiceSkeleton
66
46
 
67
47
  @logger = Logger.new(log_file || $stderr, shift_age, shift_size)
68
48
 
69
- # Can be used prior to a call to ultravisor#run. This prevents a race condition
70
- # when a logstash server is configured but the logstash writer is not yet
71
- # initialised. This should never be updated after it is configured.
72
- @pre_run_logger = Logger.new(log_file || $stderr, shift_age, shift_size)
73
-
74
- if Thread.main
75
- Thread.main[:thread_map_number] = 0
49
+ if log_enable_timestamps
50
+ @logger.formatter = ->(s, t, p, m) { "#{t.utc.strftime("%FT%T.%NZ")} #{s[0]} [#{p}] #{m}\n" }
76
51
  else
77
- #:nocov:
78
- Thread.current[:thread_map_number] = 0
79
- #:nocov:
80
- end
81
-
82
- thread_map_mutex = Mutex.new
83
-
84
- [@logger, @pre_run_logger].each do |logger|
85
- logger.formatter = ->(s, t, p, m) do
86
- th_n = if Thread.current.name
87
- #:nocov:
88
- Thread.current.name
89
- #:nocov:
90
- else
91
- thread_map_mutex.synchronize do
92
- Thread.current[:thread_map_number] ||= begin
93
- Thread.list.select { |th| th[:thread_map_number] }.length
94
- end
95
- end
96
- end
97
-
98
- ts = log_enable_timestamps ? "#{t.utc.strftime("%FT%T.%NZ")} " : ""
99
- "#{ts}#{$$}##{th_n} #{s[0]} [#{p}] #{m}\n"
100
- end
52
+ @logger.formatter = ->(s, t, p, m) { "#{s[0]} [#{p}] #{m}\n" }
101
53
  end
102
54
 
103
55
  @logger.filters = []
104
- @env.fetch("#{@service_name.upcase}_LOG_LEVEL", "INFO").split(/\s*,\s*/).each do |spec|
56
+ @env.fetch("#{@svc.service_name.upcase}_LOG_LEVEL", "INFO").split(/\s*,\s*/).each do |spec|
105
57
  if spec.index("=")
106
58
  # "Your developers were so preoccupied with whether or not they
107
59
  # could, they didn't stop to think if they should."
@@ -124,17 +76,5 @@ module ServiceSkeleton
124
76
  end
125
77
  end
126
78
  end
127
-
128
- def internal_variables
129
- [
130
- { name: "#{@service_name.upcase}_LOG_LEVEL", class: ConfigVariable::String, opts: { default: "INFO" } },
131
- { name: "#{@service_name.upcase}_LOG_ENABLE_TIMESTAMPS", class: ConfigVariable::Boolean, opts: { default: false } },
132
- { name: "#{@service_name.upcase}_LOG_FILE", class: ConfigVariable::String, opts: { default: nil } },
133
- { name: "#{@service_name.upcase}_LOG_MAX_FILE_SIZE", class: ConfigVariable::Integer, opts: { default: 1048576, range: 0..Float::INFINITY } },
134
- { name: "#{@service_name.upcase}_LOG_MAX_FILES", class: ConfigVariable::Integer, opts: { default: 3, range: 1..Float::INFINITY } },
135
- { name: "#{@service_name.upcase}_LOGSTASH_SERVER", class: ConfigVariable::String, opts: { default: "" } },
136
- { name: "#{@service_name.upcase}_METRICS_PORT", class: ConfigVariable::Integer, opts: { default: nil, range: 1..65535 } },
137
- ]
138
- end
139
79
  end
140
80
  end
@@ -1,44 +1,23 @@
1
- # frozen_string_literal: true
2
-
3
- module ServiceSkeleton
1
+ class ServiceSkeleton
4
2
  class ConfigVariable
5
- attr_reader :name, :value
3
+ attr_reader :name
6
4
 
7
- def initialize(name, env, **opts, &blk)
5
+ def initialize(name, **opts, &blk)
8
6
  @name = name
9
7
  @opts = opts
10
8
  @blk = blk
11
-
12
- @value = pluck_value(env)
13
9
  end
14
10
 
15
11
  def method_name(svc_name)
16
- @name.to_s.gsub(/\A#{Regexp.quote(svc_name)}_/i, '').downcase
12
+ name.to_s.gsub(/\A#{Regexp.quote(svc_name)}_/i, '').downcase
17
13
  end
18
14
 
19
- def redact?(env)
20
- @opts[:sensitive]
15
+ def sensitive?
16
+ !!@opts[:sensitive]
21
17
  end
22
18
 
23
- def redact!(env)
24
- if @opts[:sensitive]
25
- env[@name.to_s] = "*SENSITIVE*" if env.has_key?(@name.to_s)
26
- end
27
- end
28
-
29
- private
30
-
31
- def maybe_default(env)
32
- if env.has_key?(@name.to_s)
33
- yield
34
- else
35
- if @opts.has_key?(:default)
36
- @opts[:default]
37
- else
38
- raise ServiceSkeleton::Error::InvalidEnvironmentError,
39
- "Value for required environment variable #{@name} not specified"
40
- end
41
- end
19
+ def value(raw_val)
20
+ @blk.call(raw_val)
42
21
  end
43
22
  end
44
23
  end
@@ -1,79 +1,93 @@
1
- # frozen_string_literal: true
2
-
3
1
  require_relative "./error"
4
2
 
5
3
  require "service_skeleton/config_variable"
6
4
 
7
- module ServiceSkeleton
5
+ class ServiceSkeleton
8
6
  module ConfigVariables
9
- UNDEFINED = Module.new
7
+ attr_reader :registered_variables
8
+
9
+ UNDEFINED = Class.new
10
10
  private_constant :UNDEFINED
11
11
 
12
- def registered_variables
12
+ def register_variable(name, **opts, &callback)
13
13
  @registered_variables ||= []
14
+
15
+ @registered_variables << ServiceSkeleton::ConfigVariable.new(name, **opts, &callback)
14
16
  end
15
17
 
16
- def register_variable(name, klass, **opts)
17
- if opts[:default] == UNDEFINED
18
- opts.delete(:default)
18
+ def string(var_name, default: UNDEFINED, sensitive: false)
19
+ register_variable(var_name, sensitive: sensitive) do |value|
20
+ maybe_default(value, default, var_name) do
21
+ value
22
+ end
19
23
  end
20
-
21
- registered_variables << {
22
- name: name,
23
- class: klass,
24
- opts: opts,
25
- }
26
24
  end
27
25
 
28
26
  def boolean(var_name, default: UNDEFINED, sensitive: false)
29
- register_variable(var_name, ConfigVariable::Boolean, default: default, sensitive: sensitive)
30
- end
31
-
32
- def enum(var_name, values:, default: UNDEFINED, sensitive: false)
33
- unless values.is_a?(Hash) || values.is_a?(Array)
34
- raise ArgumentError,
35
- "values option to enum must be a hash or array"
27
+ register_variable(var_name, sensitive: sensitive) do |value|
28
+ maybe_default(value, default, var_name) do
29
+ case value
30
+ when /\A(no|n|off|0|false)\z/i
31
+ false
32
+ when /\A(yes|y|on|1|true)\z/i
33
+ true
34
+ else
35
+ raise ServiceSkeleton::Error::InvalidEnvironmentError,
36
+ "Value #{value.inspect} for environment variable #{var_name} is not a valid boolean value"
37
+ end
38
+ end
36
39
  end
37
-
38
- register_variable(var_name, ConfigVariable::Enum, default: default, sensitive: sensitive, values: values)
39
- end
40
-
41
- def float(var_name, default: UNDEFINED, sensitive: false, range: -Float::INFINITY..Float::INFINITY)
42
- register_variable(var_name, ConfigVariable::Float, default: default, sensitive: sensitive, range: range)
43
40
  end
44
41
 
45
42
  def integer(var_name, default: UNDEFINED, sensitive: false, range: -Float::INFINITY..Float::INFINITY)
46
- register_variable(var_name, ConfigVariable::Integer, default: default, sensitive: sensitive, range: range)
47
- end
48
-
49
- def kv_list(var_name, default: UNDEFINED, sensitive: false, key_pattern: /\A#{var_name}_(.*)\z/)
50
- register_variable(var_name, ConfigVariable::KVList, default: default, sensitive: sensitive, key_pattern: key_pattern)
51
- end
52
-
53
- def path_list(var_name, default: UNDEFINED, sensitive: false)
54
- register_variable(var_name, ConfigVariable::PathList, default: default, sensitive: sensitive)
55
- end
56
-
57
- def string(var_name, default: UNDEFINED, sensitive: false, match: nil)
58
- register_variable(var_name, ConfigVariable::String, default: default, sensitive: sensitive, match: match)
43
+ register_variable(var_name, sensitive: sensitive) do |value|
44
+ maybe_default(value, default, var_name) do
45
+ if value =~ /\A-?\d+\z/
46
+ value.to_i.tap do |i|
47
+ unless range.include?(i)
48
+ raise ServiceSkeleton::Error::InvalidEnvironmentError,
49
+ "Value #{i} for environment variable #{var_name} is out of the valid range (must be between #{range.first} and #{range.last} inclusive)"
50
+ end
51
+ end
52
+ else
53
+ raise ServiceSkeleton::Error::InvalidEnvironmentError,
54
+ "Value #{value.inspect} for environment variable #{var_name} is not a valid integer value"
55
+ end
56
+ end
57
+ end
59
58
  end
60
59
 
61
- def url(var_name, default: UNDEFINED, sensitive: false)
62
- register_variable(var_name, ConfigVariable::URL, default: default, sensitive: sensitive)
60
+ def float(var_name, default: UNDEFINED, sensitive: false, range: -Float::INFINITY..Float::INFINITY)
61
+ register_variable(var_name, sensitive: sensitive) do |value|
62
+ maybe_default(value, default, var_name) do
63
+ if value =~ /\A-?\d+.?\d*\z/
64
+ value.to_f.tap do |i|
65
+ unless range.include?(i)
66
+ raise ServiceSkeleton::Error::InvalidEnvironmentError,
67
+ "Value #{i} for environment variable #{var_name} is out of the valid range (must be between #{range.first} and #{range.last} inclusive)"
68
+ end
69
+ end
70
+ else
71
+ raise ServiceSkeleton::Error::InvalidEnvironmentError,
72
+ "Value #{value.inspect} for environment variable #{var_name} is not a valid numeric value"
73
+ end
74
+ end
75
+ end
63
76
  end
64
77
 
65
- def yaml_file(var_name, default: UNDEFINED, sensitive: false, klass: nil)
66
- register_variable(var_name, ConfigVariable::YamlFile, default: default, sensitive: sensitive, klass: klass)
78
+ private
79
+
80
+ def maybe_default(value, default, var_name)
81
+ if value.nil?
82
+ if default == UNDEFINED
83
+ raise ServiceSkeleton::Error::InvalidEnvironmentError,
84
+ "Value for required environment variable #{var_name} not specified"
85
+ else
86
+ default
87
+ end
88
+ else
89
+ yield
90
+ end
67
91
  end
68
92
  end
69
93
  end
70
-
71
- require_relative "config_variable/boolean"
72
- require_relative "config_variable/enum"
73
- require_relative "config_variable/float"
74
- require_relative "config_variable/integer"
75
- require_relative "config_variable/kv_list"
76
- require_relative "config_variable/path_list"
77
- require_relative "config_variable/string"
78
- require_relative "config_variable/url"
79
- require_relative "config_variable/yaml_file"
@@ -1,10 +1,8 @@
1
- # frozen_string_literal: true
2
-
3
- module ServiceSkeleton
1
+ class ServiceSkeleton
4
2
  class Error < StandardError
5
- class CannotSanitizeEnvironmentError < Error; end
6
3
  class InvalidEnvironmentError < Error; end
4
+ class CannotSanitizeEnvironmentError < Error; end
5
+ class InheritanceContractError < Error; end
7
6
  class InvalidMetricNameError < Error; end
8
- class InvalidServiceClassError < Error; end
9
7
  end
10
8
  end
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  require 'logger'
4
2
 
5
3
  module FilteringLogger
@@ -1,28 +1,21 @@
1
- # frozen_string_literal: true
2
-
3
- module ServiceSkeleton
1
+ class ServiceSkeleton
4
2
  module LoggingHelpers
5
3
  private
6
4
 
7
5
  def log_exception(ex, progname = nil)
8
- #:nocov:
9
6
  progname ||= "#{self.class.to_s}##{caller_locations(2, 1).first.label}"
10
7
 
11
8
  logger.error(progname) do
9
+ #:nocov:
12
10
  explanation = if block_given?
13
11
  yield
14
12
  else
15
13
  nil
16
14
  end
15
+ #:nocov:
17
16
 
18
17
  (["#{explanation}#{explanation ? ": " : ""}#{ex.message} (#{ex.class})"] + ex.backtrace).join("\n ")
19
18
  end
20
- #:nocov:
21
- end
22
-
23
- def logloc
24
- loc = caller_locations.first
25
- "#{self.class}##{loc.label}"
26
19
  end
27
20
  end
28
21
  end
@@ -1,37 +1,22 @@
1
- # frozen_string_literal: true
2
-
3
- require "prometheus/client"
4
-
5
- require_relative "metric_method_name"
6
-
7
- Prometheus::Client::Metric.include(ServiceSkeleton::MetricMethodName)
8
-
9
- module ServiceSkeleton
1
+ class ServiceSkeleton
10
2
  module MetricsMethods
11
- def registered_metrics
12
- @registered_metrics || []
13
- end
14
-
15
- def metric(metric)
16
- @registered_metrics ||= []
17
-
18
- @registered_metrics << metric
3
+ def service=(svc)
4
+ @service = svc
19
5
  end
20
6
 
21
- def counter(name, docstring:, labels: [], preset_labels: {})
22
- metric(Prometheus::Client::Counter.new(name, docstring: docstring, labels: labels, preset_labels: preset_labels))
23
- end
7
+ def register(metric)
8
+ method_name = metric.name.to_s.gsub(/\A#{Regexp.quote(@service.service_name)}_/, '').to_sym
24
9
 
25
- def gauge(name, docstring:, labels: [], preset_labels: {})
26
- metric(Prometheus::Client::Gauge.new(name, docstring: docstring, labels: labels, preset_labels: preset_labels))
27
- end
10
+ if self.class.method_defined?(method_name)
11
+ raise ServiceSkeleton::Error::InvalidMetricNameError,
12
+ "There is already a method named #{method_name} on ##metrics, so you can't have a metric named #{metric.name}"
13
+ end
28
14
 
29
- def summary(name, docstring:, labels: [], preset_labels: {})
30
- metric(Prometheus::Client::Summary.new(name, docstring: docstring, labels: labels, preset_labels: preset_labels))
31
- end
15
+ define_singleton_method(method_name) do
16
+ metric
17
+ end
32
18
 
33
- def histogram(name, docstring:, labels: [], preset_labels: {}, buckets: Prometheus::Client::Histogram::DEFAULT_BUCKETS)
34
- metric(Prometheus::Client::Histogram.new(name, docstring: docstring, labels: labels, preset_labels: preset_labels, buckets: buckets))
19
+ super
35
20
  end
36
21
  end
37
22
  end