service_skeleton 0.0.0.1.ENOTAG → 0.0.0.2.g46c1e0e

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.
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