lib/service_skeleton.rb in service_skeleton-0.0.0.44.g75d07d7 vs lib/service_skeleton.rb in service_skeleton-0.0.0.48.g4a40599
- old
+ new
@@ -1,207 +1,41 @@
# frozen_string_literal: true
-require_relative "service_skeleton/config"
+require_relative "service_skeleton/config_class"
require_relative "service_skeleton/config_variables"
+require_relative "service_skeleton/generator"
require_relative "service_skeleton/logging_helpers"
require_relative "service_skeleton/metrics_methods"
-require_relative "service_skeleton/signal_handler"
+require_relative "service_skeleton/service_name"
+require_relative "service_skeleton/signals_methods"
+require_relative "service_skeleton/ultravisor_children"
require "frankenstein/ruby_gc_metrics"
require "frankenstein/ruby_vm_metrics"
require "frankenstein/process_metrics"
require "frankenstein/server"
require "prometheus/client/registry"
require "sigdump"
-class ServiceSkeleton
- extend ServiceSkeleton::ConfigVariables
-
+module ServiceSkeleton
include ServiceSkeleton::LoggingHelpers
+ extend ServiceSkeleton::Generator
- class Terminate < Exception; end
-
- def self.config_class(klass)
- @config_class = klass
+ def self.included(mod)
+ mod.extend ServiceSkeleton::ServiceName
+ mod.extend ServiceSkeleton::ConfigVariables
+ mod.extend ServiceSkeleton::ConfigClass
+ mod.extend ServiceSkeleton::MetricsMethods
+ mod.extend ServiceSkeleton::SignalsMethods
+ mod.extend ServiceSkeleton::UltravisorChildren
end
- def self.service_name
- service_name_from_class(self)
- end
-
attr_reader :config, :metrics, :logger
- def initialize(env)
- @env = env
- @config = (self.class.instance_variable_get(:@config_class) || ServiceSkeleton::Config).new(env, self)
- @logger = @config.logger
- @op_mutex = Mutex.new
-
- initialize_metrics
- initialize_signals
+ def initialize(*_, metrics:, config:)
+ @metrics = metrics
+ @config = config
+ @logger = @config.logger
end
-
- def start
- @op_mutex.synchronize { @thread = Thread.current }
-
- begin
- logger.info(logloc) { "Starting service #{service_name}" }
- logger.info(logloc) { (["Environment:"] + config.env.map { |k, v| "#{k}=#{v.inspect}" }).join("\n ") }
-
- start_metrics_server
- start_signal_handler
- run
- rescue ServiceSkeleton::Terminate
- # This one is OK
- rescue ServiceSkeleton::Error::InheritanceContractError
- # We want this one to be fatal
- raise
- rescue StandardError => ex
- log_exception(ex)
- end
-
- @thread = nil
- end
-
- def stop(force = false)
- if force
- #:nocov:
- @op_mutex.synchronize do
- if @thread
- @thread.raise(ServiceSkeleton::Terminate)
- end
- end
- #:nocov:
- else
- shutdown
- end
-
- if @metrics_server
- @metrics_server.shutdown
- @metrics_server = nil
- end
-
- @signal_handler.stop!
- end
-
- def service_name
- self.class.service_name
- end
-
- def registered_variables
- self.class.registered_variables
- end
-
- def hook_signal(spec, &blk)
- @signal_handler.hook_signal(spec, &blk)
- end
-
- private
-
- def self.service_name_from_class(klass)
- klass.to_s
- .gsub("::", "_")
- .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
- .gsub(/([a-z\d])([A-Z])/, '\1_\2')
- .downcase
- .gsub(/[^a-zA-Z0-9_]/, "_")
- end
-
- def run
- raise ServiceSkeleton::Error::InheritanceContractError, "ServiceSkeleton#run method not overridden"
- end
-
- def shutdown
- #:nocov:
- @op_mutex.synchronize do
- if @thread
- @thread.raise(ServiceSkeleton::Terminate)
- @thread.join
- @thread = nil
- end
- end
- #:nocov:
- end
-
- def initialize_metrics
- @metrics = Prometheus::Client::Registry.new
-
- Frankenstein::RubyGCMetrics.register(@metrics)
- Frankenstein::RubyVMMetrics.register(@metrics)
- Frankenstein::ProcessMetrics.register(@metrics)
-
- @metrics.singleton_class.prepend(ServiceSkeleton::MetricsMethods)
- @metrics.service = self
- end
-
- def start_metrics_server
- if config.metrics_port
- logger.info(logloc) { "Starting metrics server on port #{config.metrics_port}" }
-
- @metrics_server = Frankenstein::Server.new(
- port: config.metrics_port,
- logger: logger,
- metrics_prefix: :metrics_server,
- registry: @metrics,
- )
- @metrics_server.run
- end
- end
-
- def initialize_signals
- metrics.counter(:"#{self.service_name}_signals_handled_total", "How many of each type of signal have been handled")
- @signal_handler = ServiceSkeleton::SignalHandler.new(logger: logger, service: self, signal_counter: metrics.signals_handled_total)
-
- @signal_handler.hook_signal("USR1") do
- logger.level -= 1 unless logger.level == Logger::DEBUG
- logger.info($0) { "Received SIGUSR1; log level is now #{Logger::SEV_LABEL[logger.level]}." }
- end
-
- @signal_handler.hook_signal("USR2") do
- logger.level += 1 unless logger.level == Logger::ERROR
- logger.info($0) { "Received SIGUSR2; log level is now #{Logger::SEV_LABEL[logger.level]}." }
- end
-
- @signal_handler.hook_signal("HUP") do
- logger.reopen
- logger.info($0) { "Received SIGHUP; log file handle reopened" }
- end
-
- @signal_handler.hook_signal("QUIT") do
- Sigdump.dump("+")
- end
-
- @signal_handler.hook_signal("INT") do
- self.stop(!!@terminating)
- @terminating = true
- end
-
- @signal_handler.hook_signal("TERM") do
- self.stop(!!@terminating)
- @terminating = true
- end
- end
-
- def start_signal_handler
- @signal_handler.start!
- end
-
- @registered_variables = [
- { name: :SERVICE_SKELETON_LOG_LEVEL, class: ConfigVariable::String, opts: { default: "INFO" } },
- { name: :SERVICE_SKELETON_LOG_ENABLE_TIMESTAMPS, class: ConfigVariable::Boolean, opts: { default: false } },
- { name: :SERVICE_SKELETON_LOG_FILE, class: ConfigVariable::String, opts: { default: nil } },
- { name: :SERVICE_SKELETON_LOG_MAX_FILE_SIZE, class: ConfigVariable::Integer, opts: { default: 1048576, range: 0..Float::INFINITY } },
- { name: :SERVICE_SKELETON_LOG_MAX_FILES, class: ConfigVariable::Integer, opts: { default: 3, range: 0..Float::INFINITY } },
- { name: :SERVICE_SKELETON_LOGSTASH_SERVER, class: ConfigVariable::String, opts: { default: nil } },
- { name: :SERVICE_SKELETON_METRICS_PORT, class: ConfigVariable::Integer, opts: { default: nil, range: 1..65535 } },
- ]
-
- def self.inherited(subclass)
- subclass.string(:"#{subclass.service_name.upcase}_LOG_LEVEL", default: "INFO")
- subclass.boolean(:"#{subclass.service_name.upcase}_LOG_ENABLE_TIMESTAMPS", default: false)
- subclass.string(:"#{subclass.service_name.upcase}_LOG_FILE", default: nil)
- subclass.integer(:"#{subclass.service_name.upcase}_LOG_MAX_FILE_SIZE", default: 1048576, range: 0..Float::INFINITY)
- subclass.integer(:"#{subclass.service_name.upcase}_LOG_MAX_FILES", default: 3, range: 1..Float::INFINITY)
- subclass.string(:"#{subclass.service_name.upcase}_LOGSTASH_SERVER", default: "")
- subclass.integer(:"#{subclass.service_name.upcase}_METRICS_PORT", default: nil, range: 1..65535)
- end
end
+
+require_relative "service_skeleton/runner"