observed 0.1.1
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 +15 -0
- data/.gitignore +20 -0
- data/.travis.yml +11 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +204 -0
- data/Rakefile +4 -0
- data/examples/00readme/Gemfile +5 -0
- data/examples/00readme/clockwork.rb +16 -0
- data/examples/00readme/observed.rb +17 -0
- data/examples/observed.rb +10 -0
- data/exe/observed-oneshot +11 -0
- data/features/oneshot.feature +24 -0
- data/features/support/env.rb +8 -0
- data/features/test_in_single_ruby_source.feature +32 -0
- data/integrations/observed-clockwork/.gitignore +17 -0
- data/integrations/observed-clockwork/Gemfile +4 -0
- data/integrations/observed-clockwork/LICENSE.txt +22 -0
- data/integrations/observed-clockwork/README.md +53 -0
- data/integrations/observed-clockwork/Rakefile +1 -0
- data/integrations/observed-clockwork/bin/clockwork +16 -0
- data/integrations/observed-clockwork/bin/clockworkd +16 -0
- data/integrations/observed-clockwork/bin/observed-clockwork +16 -0
- data/integrations/observed-clockwork/bin/rake +16 -0
- data/integrations/observed-clockwork/examples/clockwork/clockwork.rb +9 -0
- data/integrations/observed-clockwork/examples/clockwork/foo_plugin.rb +19 -0
- data/integrations/observed-clockwork/examples/clockwork/observed.conf +6 -0
- data/integrations/observed-clockwork/features/run_observed_inside_clockwork.feature +59 -0
- data/integrations/observed-clockwork/features/support/env.rb +8 -0
- data/integrations/observed-clockwork/lib/observed/clockwork/version.rb +5 -0
- data/integrations/observed-clockwork/lib/observed/clockwork.rb +20 -0
- data/integrations/observed-clockwork/observed-clockwork.gemspec +29 -0
- data/lib/observed/application/oneshot.rb +114 -0
- data/lib/observed/application.rb +1 -0
- data/lib/observed/builtin_plugins/file.rb +49 -0
- data/lib/observed/builtin_plugins/stdout.rb +35 -0
- data/lib/observed/builtin_plugins.rb +2 -0
- data/lib/observed/config.rb +27 -0
- data/lib/observed/config_builder.rb +167 -0
- data/lib/observed/config_dsl.rb +77 -0
- data/lib/observed/configurable.rb +56 -0
- data/lib/observed/default/observer.rb +16 -0
- data/lib/observed/default/reporter.rb +17 -0
- data/lib/observed/default.rb +2 -0
- data/lib/observed/hash/builder.rb +24 -0
- data/lib/observed/hash/fetcher.rb +19 -0
- data/lib/observed/hash/key_path_encoding.rb +62 -0
- data/lib/observed/hash.rb +3 -0
- data/lib/observed/observer.rb +18 -0
- data/lib/observed/observer_helpers/timer.rb +37 -0
- data/lib/observed/pluggable.rb +34 -0
- data/lib/observed/reader.rb +14 -0
- data/lib/observed/reporter/regexp_matching.rb +11 -0
- data/lib/observed/reporter.rb +25 -0
- data/lib/observed/system.rb +109 -0
- data/lib/observed/version.rb +3 -0
- data/lib/observed/writer.rb +14 -0
- data/lib/observed.rb +77 -0
- data/observed.gemspec +30 -0
- data/plugins/observed-fluentd/.gitignore +20 -0
- data/plugins/observed-fluentd/Gemfile +12 -0
- data/plugins/observed-fluentd/LICENSE.txt +22 -0
- data/plugins/observed-fluentd/README.md +99 -0
- data/plugins/observed-fluentd/Rakefile +1 -0
- data/plugins/observed-fluentd/features/plugin.feature +61 -0
- data/plugins/observed-fluentd/features/support/env.rb +32 -0
- data/plugins/observed-fluentd/fluent.d/fluent.conf +93 -0
- data/plugins/observed-fluentd/fluentd.pid +1 -0
- data/plugins/observed-fluentd/lib/observed/fluentd/version.rb +5 -0
- data/plugins/observed-fluentd/lib/observed/fluentd.rb +30 -0
- data/plugins/observed-fluentd/observe.d/clockwork.rb +9 -0
- data/plugins/observed-fluentd/observe.d/observed.conf +17 -0
- data/plugins/observed-fluentd/observed-fluentd.gemspec +26 -0
- data/plugins/observed-gauge/.gitignore +18 -0
- data/plugins/observed-gauge/Gemfile +4 -0
- data/plugins/observed-gauge/LICENSE.txt +22 -0
- data/plugins/observed-gauge/README.md +29 -0
- data/plugins/observed-gauge/Rakefile +1 -0
- data/plugins/observed-gauge/lib/observed/gauge/version.rb +5 -0
- data/plugins/observed-gauge/lib/observed/gauge.rb +117 -0
- data/plugins/observed-gauge/observed-gauge.gemspec +30 -0
- data/plugins/observed-gauge/spec/gauge_spec.rb +195 -0
- data/plugins/observed-gauge/spec/spec_helper.rb +25 -0
- data/plugins/observed-http/.gitignore +17 -0
- data/plugins/observed-http/Gemfile +4 -0
- data/plugins/observed-http/LICENSE.txt +22 -0
- data/plugins/observed-http/README.md +37 -0
- data/plugins/observed-http/Rakefile +1 -0
- data/plugins/observed-http/features/observe_web_services_via_http.feature +32 -0
- data/plugins/observed-http/features/support/env.rb +8 -0
- data/plugins/observed-http/lib/observed/http/version.rb +5 -0
- data/plugins/observed-http/lib/observed/http.rb +60 -0
- data/plugins/observed-http/observed-http.gemspec +31 -0
- data/plugins/observed-http/spec/fixtures/observed.conf +10 -0
- data/plugins/observed-http/spec/http_spec.rb +14 -0
- data/plugins/observed-http/spec/spec_helper.rb +25 -0
- data/spec/builtin_plugins/file_spec.rb +145 -0
- data/spec/builtin_plugins/stdout_spec.rb +56 -0
- data/spec/config_builder_spec.rb +146 -0
- data/spec/config_dsl_spec.rb +50 -0
- data/spec/configurable_spec.rb +65 -0
- data/spec/fixtures/configure_by_conf/foo_plugin.rb +12 -0
- data/spec/fixtures/configure_by_conf/observed.conf +6 -0
- data/spec/fixtures/configure_by_conf_dot_d/foo_plugin.rb +18 -0
- data/spec/fixtures/configure_by_conf_dot_d/observed.conf.d/check_foo_1.rb +1 -0
- data/spec/fixtures/configure_by_conf_dot_d/observed.conf.d/plugins.rb +1 -0
- data/spec/fixtures/configure_by_require/observed_conf.rb +6 -0
- data/spec/hash/builder_spec.rb +36 -0
- data/spec/hash/fetcher_spec.rb +29 -0
- data/spec/input_helpers/timer_spec.rb +54 -0
- data/spec/observed_spec.rb +79 -0
- data/spec/observer_spec.rb +123 -0
- data/spec/oneshot_spec.rb +58 -0
- data/spec/reader_spec.rb +15 -0
- data/spec/reporter_spec.rb +19 -0
- data/spec/spec_helper.rb +65 -0
- data/spec/system_spec.rb +75 -0
- data/spec/writer_spec.rb +16 -0
- metadata +299 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
Feature: Running Observed inside Clockwork
|
|
2
|
+
|
|
3
|
+
In order to integrate Observed with Clockwork,
|
|
4
|
+
I want to configure Clockwork to run Observed.
|
|
5
|
+
|
|
6
|
+
Scenario: Create a .rb file containing Observed code and run it with the ruby command
|
|
7
|
+
Given a file named "observed.conf" with:
|
|
8
|
+
"""
|
|
9
|
+
require 'observed/builtin_plugins'
|
|
10
|
+
require_relative 'foo_plugin'
|
|
11
|
+
|
|
12
|
+
observe 'foo_1', via: 'foo'
|
|
13
|
+
|
|
14
|
+
report /foo_1/, via: 'stdout'
|
|
15
|
+
"""
|
|
16
|
+
Given a file named "foo_plugin.rb" with:
|
|
17
|
+
"""
|
|
18
|
+
module OneshotSpec
|
|
19
|
+
class FooPlugin < Observed::Observer
|
|
20
|
+
|
|
21
|
+
plugin_name 'foo'
|
|
22
|
+
|
|
23
|
+
default timeout_in_milliseconds: 5000
|
|
24
|
+
default number_of_trials: 10
|
|
25
|
+
|
|
26
|
+
def observe
|
|
27
|
+
sleep_duration = rand / 20
|
|
28
|
+
sleep sleep_duration
|
|
29
|
+
system.report(tag, "Foo #{sleep_duration}")
|
|
30
|
+
|
|
31
|
+
# For testing purpose,
|
|
32
|
+
# we want the `clockwork` process to exit as soon as we ran Observed to report the test
|
|
33
|
+
# output to the stdout
|
|
34
|
+
exit
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def logger
|
|
38
|
+
@logger ||= Logger.new(STDOUT)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
"""
|
|
43
|
+
Given a file named "clockwork.rb" with:
|
|
44
|
+
"""
|
|
45
|
+
require 'clockwork'
|
|
46
|
+
require 'observed/clockwork'
|
|
47
|
+
|
|
48
|
+
include Clockwork
|
|
49
|
+
include Observed::Clockwork
|
|
50
|
+
|
|
51
|
+
register_observed_handler :config_file => File.dirname(__FILE__) + '/observed.conf'
|
|
52
|
+
|
|
53
|
+
every(10.seconds, 'foo_1')
|
|
54
|
+
"""
|
|
55
|
+
When I run `clockwork clockwork.rb`
|
|
56
|
+
Then the output should contain:
|
|
57
|
+
"""
|
|
58
|
+
foo_1 Foo
|
|
59
|
+
"""
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require 'clockwork'
|
|
2
|
+
require 'observed'
|
|
3
|
+
require "observed/clockwork/version"
|
|
4
|
+
require "observed/application"
|
|
5
|
+
|
|
6
|
+
module Observed
|
|
7
|
+
module Clockwork
|
|
8
|
+
extend self
|
|
9
|
+
|
|
10
|
+
def register_observed_handler(args)
|
|
11
|
+
Observed.init!
|
|
12
|
+
Observed.configure args
|
|
13
|
+
Observed.load! args[:config_file]
|
|
14
|
+
::Clockwork.handler do |job|
|
|
15
|
+
Observed.run job
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'observed/clockwork/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = "observed-clockwork"
|
|
8
|
+
spec.version = Observed::Clockwork::VERSION
|
|
9
|
+
spec.authors = ["KUOKA Yusuke"]
|
|
10
|
+
spec.email = ["yusuke.kuoka@gree.net"]
|
|
11
|
+
spec.description = %q{Observed Clockwork Integration}
|
|
12
|
+
spec.summary = %q{An integration of Observed with Clockwork}
|
|
13
|
+
spec.homepage = ""
|
|
14
|
+
spec.license = "MIT"
|
|
15
|
+
|
|
16
|
+
spec.files = `git ls-files`.split($/)
|
|
17
|
+
spec.bindir = 'exe'
|
|
18
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
19
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
20
|
+
spec.require_paths = ["lib"]
|
|
21
|
+
|
|
22
|
+
spec.add_dependency 'clockwork'
|
|
23
|
+
spec.add_dependency 'daemons'
|
|
24
|
+
spec.add_dependency 'observed', "~> 0.1.0"
|
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
|
26
|
+
spec.add_development_dependency "rake"
|
|
27
|
+
spec.add_development_dependency "cucumber"
|
|
28
|
+
spec.add_development_dependency "aruba"
|
|
29
|
+
end
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
require 'logger'
|
|
2
|
+
require 'optparse'
|
|
3
|
+
require 'pathname'
|
|
4
|
+
|
|
5
|
+
require 'observed/config'
|
|
6
|
+
require 'observed/config_builder'
|
|
7
|
+
require 'observed/config_dsl'
|
|
8
|
+
require 'observed/observer'
|
|
9
|
+
require 'observed/system'
|
|
10
|
+
|
|
11
|
+
module Observed
|
|
12
|
+
|
|
13
|
+
module Application
|
|
14
|
+
# The application which is usually ran from CLI to run health-checks and write the results to a log file, and then exit.
|
|
15
|
+
# An "Oneshot" application is the opposite of a "Daemon" or "Resident" application.
|
|
16
|
+
class Oneshot
|
|
17
|
+
|
|
18
|
+
class InvalidArgumentError < RuntimeError; end
|
|
19
|
+
|
|
20
|
+
# @param [Observed::Config] config
|
|
21
|
+
def initialize(config, sys)
|
|
22
|
+
@config = config
|
|
23
|
+
@system = sys
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def config
|
|
27
|
+
@config || fail('Missing configuration for Application::Oneshot')
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def run(observation_name=nil)
|
|
31
|
+
system.run(observation_name)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def logger
|
|
35
|
+
@logger ||= Logger.new(STDOUT)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
class << self
|
|
39
|
+
def from_argv(argv)
|
|
40
|
+
|
|
41
|
+
command_line_args = argv.dup
|
|
42
|
+
|
|
43
|
+
args = {}
|
|
44
|
+
|
|
45
|
+
opts = OptionParser.new
|
|
46
|
+
opts.accept(Pathname) do |s,|
|
|
47
|
+
Pathname.new(s)
|
|
48
|
+
end
|
|
49
|
+
opts.on('-d', '--debug') do
|
|
50
|
+
args[:debug] = true
|
|
51
|
+
end
|
|
52
|
+
opts.on('-l LOG_FILE', '--l LOG_FILE', Pathname) do |log_file|
|
|
53
|
+
args[:log_file] = log_file
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
opts.parse!(command_line_args)
|
|
57
|
+
|
|
58
|
+
if command_line_args.size != 1
|
|
59
|
+
fail InvalidArgumentError, "Invalid number of arguments #{command_line_args.size} where arguments are #{command_line_args}"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
args[:config_file] = command_line_args.first
|
|
63
|
+
|
|
64
|
+
create(args)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# @param [Hash<Symbol,String>] args
|
|
68
|
+
# @option args [Array<String>] :argv The Ruby's `ARGV` like object which is treated as intialization parameters for Oneshoft application.
|
|
69
|
+
def create(args)
|
|
70
|
+
logger_out = if args[:log_file]
|
|
71
|
+
File.open(args[:log_file], 'a')
|
|
72
|
+
else
|
|
73
|
+
STDOUT
|
|
74
|
+
end
|
|
75
|
+
logger = Logger.new(logger_out)
|
|
76
|
+
logger.level = if args[:debug]
|
|
77
|
+
Logger::DEBUG
|
|
78
|
+
else
|
|
79
|
+
Logger::INFO
|
|
80
|
+
end
|
|
81
|
+
sys = Observed::System.new(logger: logger)
|
|
82
|
+
config = if args[:yaml_file]
|
|
83
|
+
YAML.load_file(args[:yaml_file])
|
|
84
|
+
elsif args[:config_file]
|
|
85
|
+
path = args[:config_file]
|
|
86
|
+
config_builder = Observed::ConfigBuilder.new(system: sys, logger: logger)
|
|
87
|
+
config_dsl = Observed::ConfigDSL.new(builder: config_builder, logger: logger)
|
|
88
|
+
config_dsl.eval_file(path)
|
|
89
|
+
config_dsl.config
|
|
90
|
+
elsif args[:config]
|
|
91
|
+
c = args[:config]
|
|
92
|
+
c
|
|
93
|
+
else
|
|
94
|
+
fail 'No configuration provided'
|
|
95
|
+
end
|
|
96
|
+
config = if config.is_a? Hash
|
|
97
|
+
Observed::Config.create(config)
|
|
98
|
+
else
|
|
99
|
+
config
|
|
100
|
+
end
|
|
101
|
+
sys.config = config
|
|
102
|
+
new(config, sys)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
private
|
|
107
|
+
|
|
108
|
+
def system
|
|
109
|
+
@system
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'observed/application/oneshot'
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require 'observed/hash/fetcher'
|
|
2
|
+
require 'observed/reporter'
|
|
3
|
+
require 'observed/reporter/regexp_matching'
|
|
4
|
+
|
|
5
|
+
module Observed
|
|
6
|
+
module BuiltinPlugins
|
|
7
|
+
class File < Observed::Reporter
|
|
8
|
+
|
|
9
|
+
include Observed::Reporter::RegexpMatching
|
|
10
|
+
|
|
11
|
+
UNDEFINED = Object.new
|
|
12
|
+
|
|
13
|
+
attribute :format, default: -> tag, time, data { "#{Time.at(time)} #{tag} #{data}" }
|
|
14
|
+
attribute :path
|
|
15
|
+
attribute :mode, default: :append
|
|
16
|
+
|
|
17
|
+
# @param [String] tag
|
|
18
|
+
# @param [Time] time
|
|
19
|
+
# @param [Hash] data
|
|
20
|
+
def report(tag, time, data)
|
|
21
|
+
num_params = format.parameters.size
|
|
22
|
+
formatted_data = case num_params
|
|
23
|
+
when 3
|
|
24
|
+
format.call(tag, time, data)
|
|
25
|
+
when 4
|
|
26
|
+
format.call(tag, time, data, Observed::Hash::Fetcher.new(data))
|
|
27
|
+
else
|
|
28
|
+
fail "Number of parameters for the function for the key :format must be 3 or 4, but was #{num_params}(#{format.parameters}"
|
|
29
|
+
end
|
|
30
|
+
mode = case self.mode
|
|
31
|
+
when :append, 'a'
|
|
32
|
+
'a'
|
|
33
|
+
when :overwrite, 'w'
|
|
34
|
+
'w'
|
|
35
|
+
else
|
|
36
|
+
fail "Unsupported value for the parameter `:mode`: Supported values are :append, :overwrite, " +
|
|
37
|
+
"'a', 'w'. The specified value is #{self.mode.inspect}"
|
|
38
|
+
end
|
|
39
|
+
::File.open(path, mode) do |f|
|
|
40
|
+
f.puts formatted_data
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def self.plugin_name
|
|
45
|
+
'file'
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
require 'observed/hash/fetcher'
|
|
2
|
+
require 'observed/observer'
|
|
3
|
+
require 'observed/reporter'
|
|
4
|
+
require 'observed/reporter/regexp_matching'
|
|
5
|
+
|
|
6
|
+
module Observed
|
|
7
|
+
module BuiltinPlugins
|
|
8
|
+
class Stdout < Observed::Reporter
|
|
9
|
+
|
|
10
|
+
include Observed::Reporter::RegexpMatching
|
|
11
|
+
|
|
12
|
+
attribute :format, default: -> tag, time, data { "#{Time.at(time)} #{tag} #{data}" }
|
|
13
|
+
|
|
14
|
+
# @param [String] tag
|
|
15
|
+
# @param [Time] time
|
|
16
|
+
# @param [Hash] data
|
|
17
|
+
def report(tag, time, data)
|
|
18
|
+
num_params = format.parameters.size
|
|
19
|
+
formatted_data = case num_params
|
|
20
|
+
when 3
|
|
21
|
+
format.call(tag, time, data)
|
|
22
|
+
when 4
|
|
23
|
+
format.call(tag, time, data, Observed::Hash::Fetcher.new(data))
|
|
24
|
+
else
|
|
25
|
+
fail "Number of parameters for the function for the key :format must be 3 or 4, but was #{num_params}(#{format.parameters}"
|
|
26
|
+
end
|
|
27
|
+
puts formatted_data
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.plugin_name
|
|
31
|
+
'stdout'
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require 'observed/configurable'
|
|
2
|
+
|
|
3
|
+
module Observed
|
|
4
|
+
# The configuration for Observed which may be built by Observed::Builder,
|
|
5
|
+
# which may contains configured writers, readers, reporters, observers.
|
|
6
|
+
class Config
|
|
7
|
+
|
|
8
|
+
include Observed::Configurable
|
|
9
|
+
|
|
10
|
+
# !@attribute [rw] writers
|
|
11
|
+
# @return [Array<Observed::Writer>]
|
|
12
|
+
attribute :writers
|
|
13
|
+
|
|
14
|
+
# !@attribute [rw] readers
|
|
15
|
+
# @return [Array<Observed::Reader>]
|
|
16
|
+
attribute :readers
|
|
17
|
+
|
|
18
|
+
# !@attribute [rw] reporters
|
|
19
|
+
# @return [Array<Observed::Reporter>]
|
|
20
|
+
attribute :reporters
|
|
21
|
+
|
|
22
|
+
# !@attribute [rw] observers
|
|
23
|
+
# @return [Array<Observed::Observer>]
|
|
24
|
+
attribute :observers
|
|
25
|
+
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
require 'observed/config'
|
|
2
|
+
require 'observed/configurable'
|
|
3
|
+
require 'observed/default'
|
|
4
|
+
require 'observed/hash'
|
|
5
|
+
require 'observed/reader'
|
|
6
|
+
require 'observed/writer'
|
|
7
|
+
|
|
8
|
+
module Observed
|
|
9
|
+
|
|
10
|
+
class ConfigBuilder
|
|
11
|
+
include Observed::Configurable
|
|
12
|
+
|
|
13
|
+
def initialize(args)
|
|
14
|
+
@writer_plugins = args[:writer_plugins] if args[:writer_plugins]
|
|
15
|
+
@reader_plugins = args[:reader_plugins] if args[:reader_plugins]
|
|
16
|
+
@observer_plugins = args[:observer_plugins] if args[:observer_plugins]
|
|
17
|
+
@reporter_plugins = args[:reporter_plugins] if args[:reporter_plugins]
|
|
18
|
+
@system = args[:system] || fail("The key :system must be in #{args}")
|
|
19
|
+
@logger = args[:logger] || Logger.new(STDOUT, Logger::DEBUG)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def system
|
|
23
|
+
@system
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def writer_plugins
|
|
27
|
+
@writer_plugins || select_named_plugins_of(Observed::Writer)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def reader_plugins
|
|
31
|
+
@reader_plugins || select_named_plugins_of(Observed::Reader)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def observer_plugins
|
|
35
|
+
@observer_plugins || select_named_plugins_of(Observed::Observer)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def reporter_plugins
|
|
39
|
+
@reporter_plugins || select_named_plugins_of(Observed::Reporter)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def select_named_plugins_of(klass)
|
|
43
|
+
plugins = {}
|
|
44
|
+
klass.select_named_plugins.each do |plugin|
|
|
45
|
+
plugins[plugin.plugin_name] = plugin
|
|
46
|
+
end
|
|
47
|
+
plugins
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def build
|
|
51
|
+
Observed::Config.new(
|
|
52
|
+
writers: writers,
|
|
53
|
+
readers: readers,
|
|
54
|
+
observers: observers,
|
|
55
|
+
reporters: reporters
|
|
56
|
+
)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# @param [Regexp] tag_pattern The pattern to match tags added to data from observers
|
|
60
|
+
# @param [Hash] args The configuration for each reporter which may or may not contain (1) which reporter plugin to
|
|
61
|
+
# use or which writer plugin to use (in combination with the default reporter plugin) (2) initialization parameters
|
|
62
|
+
# to instantiate the reporter/writer plugin
|
|
63
|
+
def report(tag_pattern, args)
|
|
64
|
+
writer = write(args)
|
|
65
|
+
tag_pattern || fail("Tag pattern missing: #{tag_pattern} where args: #{args}")
|
|
66
|
+
reporter = if writer
|
|
67
|
+
Observed::Default::Reporter.new.configure(tag_pattern: tag_pattern, writer: writer, system: system)
|
|
68
|
+
else
|
|
69
|
+
via = args[:via] || args[:using]
|
|
70
|
+
with = args[:with] || args[:which] || {}
|
|
71
|
+
with = ({logger: @logger}).merge(with).merge({tag_pattern: tag_pattern})
|
|
72
|
+
plugin = reporter_plugins[via] ||
|
|
73
|
+
fail(RuntimeError, %Q|The reporter plugin named "#{via}" is not found in "#{reporter_plugins}"|)
|
|
74
|
+
plugin.new(with)
|
|
75
|
+
end
|
|
76
|
+
begin
|
|
77
|
+
reporter.match('test')
|
|
78
|
+
rescue => e
|
|
79
|
+
fail "A mis-configured reporter plugin found: #{reporter}"
|
|
80
|
+
rescue NotImplementedError => e
|
|
81
|
+
builtin_methods = Object.methods
|
|
82
|
+
info = (reporter.methods - builtin_methods).map {|sym| reporter.method(sym) }.map(&:source_location).compact
|
|
83
|
+
fail "Incomplete reporter plugin found: #{reporter}, defined in: #{info}"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
reporters << reporter
|
|
87
|
+
reporter
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# @param [String] tag The tag which is assigned to data which is generated from this observer, and is sent to
|
|
91
|
+
# reporters later
|
|
92
|
+
# @param [Hash] args The configuration for each observer which may or may not contain (1) which observer plugin to
|
|
93
|
+
# use or which reader plugin to use (in combination with the default observer plugin) (2) initialization parameters
|
|
94
|
+
# to instantiate the observer/reader plugin
|
|
95
|
+
def observe(tag, args)
|
|
96
|
+
reader = read(args)
|
|
97
|
+
observer = if reader
|
|
98
|
+
Observed::Default::Observer.new.configure(tag: tag, reader: reader, system: system)
|
|
99
|
+
else
|
|
100
|
+
via = args[:via] || args[:using] ||
|
|
101
|
+
fail(RuntimeError, %Q|Missing observer plugin name for the tag "#{tag}" in "#{args}"|)
|
|
102
|
+
with = args[:with] || args[:which] || {}
|
|
103
|
+
plugin = observer_plugins[via] ||
|
|
104
|
+
fail(RuntimeError, %Q|The observer plugin named "#{via}" is not found in "#{observer_plugins}"|)
|
|
105
|
+
plugin.new(({logger: @logger}).merge(with).merge(tag: tag, system: system))
|
|
106
|
+
end
|
|
107
|
+
observers << observer
|
|
108
|
+
observer
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def write(args)
|
|
112
|
+
to = args[:to]
|
|
113
|
+
with = args[:with] || args[:which]
|
|
114
|
+
writer = case to
|
|
115
|
+
when String
|
|
116
|
+
plugin = writer_plugins[to] ||
|
|
117
|
+
fail(RuntimeError, %Q|The writer plugin named "#{to}" is not found in "#{writer_plugins}"|)
|
|
118
|
+
with = ({logger: @logger}).merge(with)
|
|
119
|
+
plugin.new(with)
|
|
120
|
+
when Observed::Writer
|
|
121
|
+
to
|
|
122
|
+
when nil
|
|
123
|
+
nil
|
|
124
|
+
else
|
|
125
|
+
fail "Unexpected type of value for the key :to in: #{args}"
|
|
126
|
+
end
|
|
127
|
+
writers << writer if writer
|
|
128
|
+
writer
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def read(args)
|
|
132
|
+
from = args[:from]
|
|
133
|
+
with = args[:with] || [:which]
|
|
134
|
+
reader = case from
|
|
135
|
+
when String
|
|
136
|
+
plugin = reader_plugins[from] || fail(RuntimeError, %Q|The reader plugin named "#{from}" is not found in "#{reader_plugins}"|)
|
|
137
|
+
with = ({logger: @logger}).merge(with)
|
|
138
|
+
plugin.new(with)
|
|
139
|
+
when Observed::Reader
|
|
140
|
+
from
|
|
141
|
+
when nil
|
|
142
|
+
nil
|
|
143
|
+
else
|
|
144
|
+
fail "Unexpected type of value for the key :from in: #{args}"
|
|
145
|
+
end
|
|
146
|
+
readers << reader if reader
|
|
147
|
+
reader
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def writers
|
|
151
|
+
@writers ||= []
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def readers
|
|
155
|
+
@readers ||= []
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def reporters
|
|
159
|
+
@reporters ||= []
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def observers
|
|
163
|
+
@observers ||= []
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
require 'observed/observer'
|
|
2
|
+
require 'observed/configurable'
|
|
3
|
+
require 'forwardable'
|
|
4
|
+
require 'logger'
|
|
5
|
+
|
|
6
|
+
module Observed
|
|
7
|
+
# The DSL to describe Observed's configuration.
|
|
8
|
+
# @example
|
|
9
|
+
# context = ConfigDSL.new(builder: the_builder)
|
|
10
|
+
# context.eval_file observed_conf_file(a.k.a user code describes Observed configuration)
|
|
11
|
+
# context.config #=> can be used to instantiate Observed::System
|
|
12
|
+
class ConfigDSL
|
|
13
|
+
|
|
14
|
+
extend Forwardable
|
|
15
|
+
|
|
16
|
+
include Observed::Configurable
|
|
17
|
+
|
|
18
|
+
def_delegators :@builder, :observe, :report, :read, :write
|
|
19
|
+
|
|
20
|
+
attribute :builder
|
|
21
|
+
|
|
22
|
+
def initialize(args)
|
|
23
|
+
args[:builder] || fail("The key :builder must exist in #{args}")
|
|
24
|
+
@builder = args[:builder]
|
|
25
|
+
|
|
26
|
+
@logger = args[:logger] if args[:logger]
|
|
27
|
+
|
|
28
|
+
configure(args)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def eval_file(file)
|
|
32
|
+
@file = File.expand_path(file)
|
|
33
|
+
working_directory File.dirname(@file)
|
|
34
|
+
logger.debug "Reading the file: #{@file}"
|
|
35
|
+
code = File.read(@file)
|
|
36
|
+
logger.debug "Evaluating: #{code}"
|
|
37
|
+
instance_eval(code, @file)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# The `current directory` in which `require_relative` finds source files
|
|
41
|
+
def working_directory(wd=nil)
|
|
42
|
+
@working_directory = wd if wd
|
|
43
|
+
@working_directory
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# The replacement for Ruby's built-in `require_relative`.
|
|
47
|
+
# Although the built-in one can not be used in `eval` or `instance_eval` or etc because
|
|
48
|
+
# there is no `current file` semantics in `eval`, this replacement takes the file which is going to be evaluated
|
|
49
|
+
# as the `current file`.
|
|
50
|
+
# Thanks to this method, we can use `require_relative` in observed.conf files both when it is evaluated with `eval`
|
|
51
|
+
# and when it is evaluated in result of `require`.
|
|
52
|
+
def require_relative(lib)
|
|
53
|
+
path = File.expand_path("#{working_directory}/#{lib}")
|
|
54
|
+
logger.debug "Require '#{path}'"
|
|
55
|
+
require path
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Build and returns the Observed configuration
|
|
59
|
+
# @return [Observed::Config]
|
|
60
|
+
def config
|
|
61
|
+
@builder.build
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Load the file and evaluate the containing code in context of this object(a.k.a DSL).
|
|
65
|
+
# @param [String|Pathname] file The path to Ruby script containing the code in Observed's configuration DSL,
|
|
66
|
+
# typically `observed.conf`.
|
|
67
|
+
def load!(file)
|
|
68
|
+
eval_file file
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
private
|
|
72
|
+
|
|
73
|
+
def logger
|
|
74
|
+
@logger ||= Logger.new(STDOUT)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
module Observed
|
|
2
|
+
module Configurable
|
|
3
|
+
|
|
4
|
+
def initialize(args={})
|
|
5
|
+
configure(args)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def configure(args={})
|
|
9
|
+
if @attributes
|
|
10
|
+
@attributes.merge! args
|
|
11
|
+
else
|
|
12
|
+
@attributes ||= args.dup
|
|
13
|
+
end
|
|
14
|
+
self
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
module ClassMethods
|
|
18
|
+
# @param [String|Symbol] name
|
|
19
|
+
def attribute(name, options={})
|
|
20
|
+
define_method(name) do
|
|
21
|
+
instance_variable_get("@#{name.to_s}") || @attributes[name] || self.class.defaults[name] || fail_for_not_configured_parameter(name)
|
|
22
|
+
end
|
|
23
|
+
default_value = options && options[:default]
|
|
24
|
+
default name => default_value if default_value
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def default(args)
|
|
28
|
+
@defaults = defaults.merge(args)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def defaults
|
|
32
|
+
@defaults ||= {}
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def create(args)
|
|
36
|
+
self.new(args)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
class NotConfiguredError < RuntimeError; end
|
|
42
|
+
|
|
43
|
+
class << self
|
|
44
|
+
def included(klass)
|
|
45
|
+
klass.extend ClassMethods
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def fail_for_not_configured_parameter(name)
|
|
52
|
+
fail NotConfiguredError.new("The parameter `#{name}` is not configured. attributes=#{@attributes}, defaults=#{self.class.defaults}")
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
end
|
|
56
|
+
end
|