hoodoo 1.0.2
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 +7 -0
- data/bin/hoodoo +5 -0
- data/lib/hoodoo.rb +27 -0
- data/lib/hoodoo/active.rb +32 -0
- data/lib/hoodoo/active/active_model/uuid_validator.rb +45 -0
- data/lib/hoodoo/active/active_record/base.rb +81 -0
- data/lib/hoodoo/active/active_record/creator.rb +134 -0
- data/lib/hoodoo/active/active_record/dated.rb +343 -0
- data/lib/hoodoo/active/active_record/error_mapping.rb +351 -0
- data/lib/hoodoo/active/active_record/finder.rb +606 -0
- data/lib/hoodoo/active/active_record/search_helper.rb +189 -0
- data/lib/hoodoo/active/active_record/secure.rb +431 -0
- data/lib/hoodoo/active/active_record/support.rb +106 -0
- data/lib/hoodoo/active/active_record/translated.rb +87 -0
- data/lib/hoodoo/active/active_record/uuid.rb +80 -0
- data/lib/hoodoo/active/active_record/writer.rb +321 -0
- data/lib/hoodoo/client.rb +23 -0
- data/lib/hoodoo/client/augmented_array.rb +29 -0
- data/lib/hoodoo/client/augmented_base.rb +168 -0
- data/lib/hoodoo/client/augmented_hash.rb +23 -0
- data/lib/hoodoo/client/client.rb +354 -0
- data/lib/hoodoo/client/endpoint/endpoint.rb +427 -0
- data/lib/hoodoo/client/endpoint/endpoints/amqp.rb +180 -0
- data/lib/hoodoo/client/endpoint/endpoints/auto_session.rb +194 -0
- data/lib/hoodoo/client/endpoint/endpoints/http.rb +203 -0
- data/lib/hoodoo/client/endpoint/endpoints/http_based.rb +367 -0
- data/lib/hoodoo/client/endpoint/endpoints/not_found.rb +59 -0
- data/lib/hoodoo/client/headers.rb +269 -0
- data/lib/hoodoo/communicators.rb +23 -0
- data/lib/hoodoo/communicators/fast.rb +44 -0
- data/lib/hoodoo/communicators/pool.rb +601 -0
- data/lib/hoodoo/communicators/slow.rb +84 -0
- data/lib/hoodoo/data.rb +51 -0
- data/lib/hoodoo/data/resources/caller.rb +39 -0
- data/lib/hoodoo/data/resources/errors.rb +28 -0
- data/lib/hoodoo/data/resources/log.rb +31 -0
- data/lib/hoodoo/data/resources/session.rb +26 -0
- data/lib/hoodoo/data/types/error_primitive.rb +27 -0
- data/lib/hoodoo/data/types/permissions.rb +40 -0
- data/lib/hoodoo/data/types/permissions_defaults.rb +32 -0
- data/lib/hoodoo/data/types/permissions_full.rb +28 -0
- data/lib/hoodoo/data/types/permissions_resources.rb +31 -0
- data/lib/hoodoo/discovery.rb +20 -0
- data/lib/hoodoo/errors.rb +19 -0
- data/lib/hoodoo/errors/error_descriptions.rb +229 -0
- data/lib/hoodoo/errors/errors.rb +322 -0
- data/lib/hoodoo/generator.rb +139 -0
- data/lib/hoodoo/logger.rb +23 -0
- data/lib/hoodoo/logger/fast_writer.rb +27 -0
- data/lib/hoodoo/logger/flattener_mixin.rb +36 -0
- data/lib/hoodoo/logger/logger.rb +387 -0
- data/lib/hoodoo/logger/slow_writer.rb +49 -0
- data/lib/hoodoo/logger/writer_mixin.rb +52 -0
- data/lib/hoodoo/logger/writers/file_writer.rb +45 -0
- data/lib/hoodoo/logger/writers/log_entries_dot_com_writer.rb +64 -0
- data/lib/hoodoo/logger/writers/stream_writer.rb +43 -0
- data/lib/hoodoo/middleware.rb +33 -0
- data/lib/hoodoo/presenters.rb +45 -0
- data/lib/hoodoo/presenters/base.rb +281 -0
- data/lib/hoodoo/presenters/base_dsl.rb +519 -0
- data/lib/hoodoo/presenters/common_resource_fields.rb +31 -0
- data/lib/hoodoo/presenters/embedding.rb +232 -0
- data/lib/hoodoo/presenters/types/array.rb +118 -0
- data/lib/hoodoo/presenters/types/boolean.rb +26 -0
- data/lib/hoodoo/presenters/types/date.rb +26 -0
- data/lib/hoodoo/presenters/types/date_time.rb +26 -0
- data/lib/hoodoo/presenters/types/decimal.rb +47 -0
- data/lib/hoodoo/presenters/types/enum.rb +55 -0
- data/lib/hoodoo/presenters/types/field.rb +158 -0
- data/lib/hoodoo/presenters/types/float.rb +26 -0
- data/lib/hoodoo/presenters/types/hash.rb +361 -0
- data/lib/hoodoo/presenters/types/integer.rb +26 -0
- data/lib/hoodoo/presenters/types/object.rb +117 -0
- data/lib/hoodoo/presenters/types/string.rb +53 -0
- data/lib/hoodoo/presenters/types/tags.rb +24 -0
- data/lib/hoodoo/presenters/types/text.rb +26 -0
- data/lib/hoodoo/presenters/types/uuid.rb +54 -0
- data/lib/hoodoo/services.rb +34 -0
- data/lib/hoodoo/services/discovery/discoverers/by_consul.rb +66 -0
- data/lib/hoodoo/services/discovery/discoverers/by_convention.rb +173 -0
- data/lib/hoodoo/services/discovery/discoverers/by_drb/by_drb.rb +195 -0
- data/lib/hoodoo/services/discovery/discoverers/by_drb/drb_server.rb +166 -0
- data/lib/hoodoo/services/discovery/discoverers/by_drb/drb_server_start.rb +37 -0
- data/lib/hoodoo/services/discovery/discovery.rb +186 -0
- data/lib/hoodoo/services/discovery/results/for_amqp.rb +58 -0
- data/lib/hoodoo/services/discovery/results/for_http.rb +85 -0
- data/lib/hoodoo/services/discovery/results/for_local.rb +85 -0
- data/lib/hoodoo/services/discovery/results/for_remote.rb +57 -0
- data/lib/hoodoo/services/middleware/amqp_log_message.rb +186 -0
- data/lib/hoodoo/services/middleware/amqp_log_writer.rb +119 -0
- data/lib/hoodoo/services/middleware/endpoints/inter_resource_local.rb +130 -0
- data/lib/hoodoo/services/middleware/endpoints/inter_resource_remote.rb +202 -0
- data/lib/hoodoo/services/middleware/exception_reporting/base_reporter.rb +105 -0
- data/lib/hoodoo/services/middleware/exception_reporting/exception_reporting.rb +115 -0
- data/lib/hoodoo/services/middleware/exception_reporting/reporters/airbrake_reporter.rb +64 -0
- data/lib/hoodoo/services/middleware/exception_reporting/reporters/raygun_reporter.rb +63 -0
- data/lib/hoodoo/services/middleware/interaction.rb +127 -0
- data/lib/hoodoo/services/middleware/middleware.rb +2705 -0
- data/lib/hoodoo/services/middleware/rack_monkey_patch.rb +73 -0
- data/lib/hoodoo/services/services/context.rb +153 -0
- data/lib/hoodoo/services/services/implementation.rb +132 -0
- data/lib/hoodoo/services/services/interface.rb +934 -0
- data/lib/hoodoo/services/services/permissions.rb +250 -0
- data/lib/hoodoo/services/services/request.rb +189 -0
- data/lib/hoodoo/services/services/response.rb +316 -0
- data/lib/hoodoo/services/services/service.rb +141 -0
- data/lib/hoodoo/services/services/session.rb +729 -0
- data/lib/hoodoo/utilities.rb +12 -0
- data/lib/hoodoo/utilities/string_inquirer.rb +54 -0
- data/lib/hoodoo/utilities/utilities.rb +380 -0
- data/lib/hoodoo/utilities/uuid.rb +44 -0
- data/lib/hoodoo/version.rb +17 -0
- data/spec/active/active_record/base_spec.rb +57 -0
- data/spec/active/active_record/creator_spec.rb +88 -0
- data/spec/active/active_record/dated_spec.rb +248 -0
- data/spec/active/active_record/error_mapping_spec.rb +360 -0
- data/spec/active/active_record/finder_spec.rb +744 -0
- data/spec/active/active_record/search_helper_spec.rb +384 -0
- data/spec/active/active_record/secure_spec.rb +435 -0
- data/spec/active/active_record/support_spec.rb +225 -0
- data/spec/active/active_record/translated_spec.rb +19 -0
- data/spec/active/active_record/uuid_spec.rb +72 -0
- data/spec/active/active_record/writer_spec.rb +272 -0
- data/spec/alchemy/alchemy-amq.rb +33 -0
- data/spec/client/augmented_array_spec.rb +15 -0
- data/spec/client/augmented_base_spec.rb +50 -0
- data/spec/client/augmented_hash_spec.rb +15 -0
- data/spec/client/client_spec.rb +955 -0
- data/spec/client/endpoint/endpoint_spec.rb +70 -0
- data/spec/client/endpoint/endpoints/amqp_spec.rb +16 -0
- data/spec/client/endpoint/endpoints/auto_session_spec.rb +9 -0
- data/spec/client/endpoint/endpoints/http_based_spec.rb +9 -0
- data/spec/client/endpoint/endpoints/http_spec.rb +103 -0
- data/spec/client/endpoint/endpoints/not_found_spec.rb +35 -0
- data/spec/client/headers_spec.rb +172 -0
- data/spec/communicators/fast_spec.rb +9 -0
- data/spec/communicators/pool_spec.rb +339 -0
- data/spec/communicators/slow_spec.rb +15 -0
- data/spec/data/resources/caller_spec.rb +156 -0
- data/spec/data/resources/errors_spec.rb +22 -0
- data/spec/data/resources/log_spec.rb +20 -0
- data/spec/data/resources/session_spec.rb +15 -0
- data/spec/data/types/error_primitive_spec.rb +15 -0
- data/spec/data/types/permissions_defaults_spec.rb +25 -0
- data/spec/data/types/permissions_full_spec.rb +44 -0
- data/spec/data/types/permissions_resources_spec.rb +34 -0
- data/spec/data/types/permissions_spec.rb +37 -0
- data/spec/errors/error_descriptions_spec.rb +98 -0
- data/spec/errors/errors_spec.rb +346 -0
- data/spec/integration/service_actions_spec.rb +112 -0
- data/spec/logger/fast_writer_spec.rb +18 -0
- data/spec/logger/logger_spec.rb +259 -0
- data/spec/logger/slow_writer_spec.rb +144 -0
- data/spec/logger/writers/file_writer_spec.rb +37 -0
- data/spec/logger/writers/log_entries_dot_com_writer_spec.rb +29 -0
- data/spec/logger/writers/stream_writer_spec.rb +38 -0
- data/spec/presenters/base_dsl_spec.rb +111 -0
- data/spec/presenters/base_spec.rb +871 -0
- data/spec/presenters/common_resource_fields_spec.rb +30 -0
- data/spec/presenters/embedding_spec.rb +87 -0
- data/spec/presenters/types/array_spec.rb +249 -0
- data/spec/presenters/types/boolean_spec.rb +51 -0
- data/spec/presenters/types/date_spec.rb +57 -0
- data/spec/presenters/types/date_time_spec.rb +59 -0
- data/spec/presenters/types/decimal_spec.rb +58 -0
- data/spec/presenters/types/enum_spec.rb +71 -0
- data/spec/presenters/types/field_spec.rb +77 -0
- data/spec/presenters/types/float_spec.rb +50 -0
- data/spec/presenters/types/hash_spec.rb +1069 -0
- data/spec/presenters/types/integer_spec.rb +50 -0
- data/spec/presenters/types/object_spec.rb +177 -0
- data/spec/presenters/types/string_spec.rb +65 -0
- data/spec/presenters/types/tags_spec.rb +56 -0
- data/spec/presenters/types/text_spec.rb +50 -0
- data/spec/presenters/types/uuid_spec.rb +46 -0
- data/spec/presenters/walk_spec.rb +198 -0
- data/spec/services/discovery/discoverers/by_consul_spec.rb +29 -0
- data/spec/services/discovery/discoverers/by_convention_spec.rb +67 -0
- data/spec/services/discovery/discoverers/by_drb/by_drb_spec.rb +80 -0
- data/spec/services/discovery/discoverers/by_drb/drb_server_spec.rb +205 -0
- data/spec/services/discovery/discovery_spec.rb +73 -0
- data/spec/services/discovery/results/for_amqp_spec.rb +17 -0
- data/spec/services/discovery/results/for_http_spec.rb +37 -0
- data/spec/services/discovery/results/for_local_spec.rb +21 -0
- data/spec/services/discovery/results/for_remote_spec.rb +15 -0
- data/spec/services/middleware/amqp_log_message_spec.rb +60 -0
- data/spec/services/middleware/amqp_log_writer_spec.rb +95 -0
- data/spec/services/middleware/endpoints/inter_resource_local_spec.rb +9 -0
- data/spec/services/middleware/endpoints/inter_resource_remote_spec.rb +9 -0
- data/spec/services/middleware/exception_reporting/base_reporter_spec.rb +16 -0
- data/spec/services/middleware/exception_reporting/exception_reporting_spec.rb +92 -0
- data/spec/services/middleware/exception_reporting/reporters/airbrake_reporter_spec.rb +24 -0
- data/spec/services/middleware/exception_reporting/reporters/raygun_reporter_spec.rb +23 -0
- data/spec/services/middleware/middleware_cors_spec.rb +93 -0
- data/spec/services/middleware/middleware_create_update_spec.rb +489 -0
- data/spec/services/middleware/middleware_dated_at_spec.rb +186 -0
- data/spec/services/middleware/middleware_exotic_communication_spec.rb +560 -0
- data/spec/services/middleware/middleware_logging_spec.rb +356 -0
- data/spec/services/middleware/middleware_multi_local_spec.rb +1094 -0
- data/spec/services/middleware/middleware_multi_remote_spec.rb +1440 -0
- data/spec/services/middleware/middleware_permissions_spec.rb +1014 -0
- data/spec/services/middleware/middleware_public_spec.rb +238 -0
- data/spec/services/middleware/middleware_spec.rb +1569 -0
- data/spec/services/middleware/string_inquirer_spec.rb +30 -0
- data/spec/services/services/application_spec.rb +74 -0
- data/spec/services/services/context_spec.rb +48 -0
- data/spec/services/services/implementation_spec.rb +45 -0
- data/spec/services/services/interface_spec.rb +262 -0
- data/spec/services/services/permissions_spec.rb +249 -0
- data/spec/services/services/request_spec.rb +95 -0
- data/spec/services/services/response_spec.rb +250 -0
- data/spec/services/services/session_spec.rb +432 -0
- data/spec/spec_helper.rb +298 -0
- data/spec/utilities/utilities_spec.rb +537 -0
- data/spec/utilities/uuid_spec.rb +20 -0
- metadata +615 -0
@@ -0,0 +1,139 @@
|
|
1
|
+
########################################################################
|
2
|
+
# File:: generator.rb
|
3
|
+
# (C):: Loyalty New Zealand 2014
|
4
|
+
#
|
5
|
+
# Purpose:: Implement the +hoodoo+ command line interface. See also
|
6
|
+
# +/bin/hoodoo+.
|
7
|
+
# ----------------------------------------------------------------------
|
8
|
+
# 07-Oct-2014 (JDC): Created.
|
9
|
+
########################################################################
|
10
|
+
|
11
|
+
require 'singleton'
|
12
|
+
require 'fileutils'
|
13
|
+
|
14
|
+
module Hoodoo
|
15
|
+
|
16
|
+
# Implement the +hoodoo+ command line interface.
|
17
|
+
#
|
18
|
+
class Generator
|
19
|
+
include Singleton
|
20
|
+
|
21
|
+
# Kernel::exit takes a boolean but defines no constants to describe
|
22
|
+
# what it means; very bad form. This constant equates to the 'success'
|
23
|
+
# boolean value.
|
24
|
+
#
|
25
|
+
KERNEL_EXIT_SUCCESS = true
|
26
|
+
|
27
|
+
# Kernel::exit takes a boolean but defines no constants to describe
|
28
|
+
# what it means; very bad form. This constant equates to the 'failed'
|
29
|
+
# boolean value.
|
30
|
+
#
|
31
|
+
KERNEL_EXIT_FAILURE = false
|
32
|
+
|
33
|
+
# Regular expression describing allowed names of services (A-Z,
|
34
|
+
# a-z, 0-9, underscore or hyphen; between 2 and 30 characters).
|
35
|
+
#
|
36
|
+
NAME_REGEX = /^[a-zA-Z01-9_-]{2,30}$/
|
37
|
+
|
38
|
+
# Run the +hoodoo+ command implementation.
|
39
|
+
#
|
40
|
+
# +args+:: Array of command line arguments, excluding the +hoodoo+
|
41
|
+
# command itself (so, just any extra arguments passed in).
|
42
|
+
#
|
43
|
+
def run!( args )
|
44
|
+
return show_usage if args_empty?(args)
|
45
|
+
|
46
|
+
name = args.first
|
47
|
+
|
48
|
+
return show_usage if name == '-h' || name == '--help'
|
49
|
+
|
50
|
+
return usage_and_warning( "SERVICE_NAME must match #{ NAME_REGEX.inspect }" ) if naughty_name?( name )
|
51
|
+
return usage_and_warning( "'#{ name }' already exists" ) if File.exist?( "./#{ name }" )
|
52
|
+
|
53
|
+
git = args[ 2 ] if args[ 1 ] == '--from'
|
54
|
+
git ||= 'git@github.com:LoyaltyNZ/service_shell.git'
|
55
|
+
|
56
|
+
return create_service( name, git )
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def create_service( name, git )
|
62
|
+
if create_dir( name ) &&
|
63
|
+
clone_service_shell( name, git ) &&
|
64
|
+
remove_dot_git( name, git ) &&
|
65
|
+
replace_strings( name )
|
66
|
+
|
67
|
+
puts "Success! ./#{name} created."
|
68
|
+
Kernel::exit( KERNEL_EXIT_SUCCESS )
|
69
|
+
else
|
70
|
+
Kernel::exit( KERNEL_EXIT_FAILURE )
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def create_dir( name )
|
75
|
+
`mkdir #{ name }`
|
76
|
+
$?.to_i == 0
|
77
|
+
end
|
78
|
+
|
79
|
+
def clone_service_shell( name, git )
|
80
|
+
`git clone #{ git } #{ name }`
|
81
|
+
$?.to_i == 0
|
82
|
+
end
|
83
|
+
|
84
|
+
def remove_dot_git( name, git )
|
85
|
+
git_folder = "./#{ name }/.git"
|
86
|
+
git_config = "#{ git_folder }/config"
|
87
|
+
|
88
|
+
if File.read( git_config ).include?( "url = #{ git }" ) # Paranoid
|
89
|
+
FileUtils.remove_dir( git_folder )
|
90
|
+
return true
|
91
|
+
else
|
92
|
+
raise 'Did not find a .git folder with a service_shell config file in it!'
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def replace_strings( name )
|
97
|
+
human_name = name.split( '_' )
|
98
|
+
human_name = human_name.drop( 1 ) if ( human_name[ 0 ].downcase == 'service' )
|
99
|
+
human_name = human_name.map( &:capitalize ).join( ' ' )
|
100
|
+
|
101
|
+
base_cmd = "LC_CTYPE=C && LANG=C && find #{ name } -type f -print0 | xargs -0 sed -i \"\" \"s/%s/g\""
|
102
|
+
uscore_cmd = base_cmd % "service_shell/#{ Regexp.escape( name ) }"
|
103
|
+
human_cmd = base_cmd % "#{ Regexp.escape( 'Platform Service: Generic' ) }/#{ Regexp.escape( 'Platform Service: ' + human_name ) }"
|
104
|
+
|
105
|
+
puts "Replacing shell names with real service name:"
|
106
|
+
puts uscore_cmd
|
107
|
+
`#{ uscore_cmd }`
|
108
|
+
result = $?.to_i == 0
|
109
|
+
return false unless result == true
|
110
|
+
|
111
|
+
puts human_cmd
|
112
|
+
`#{ human_cmd }`
|
113
|
+
result = $?.to_i == 0
|
114
|
+
return result
|
115
|
+
end
|
116
|
+
|
117
|
+
def args_empty?( args )
|
118
|
+
args.empty? || args.first == ''
|
119
|
+
end
|
120
|
+
|
121
|
+
def naughty_name?( name )
|
122
|
+
!( name =~ NAME_REGEX )
|
123
|
+
end
|
124
|
+
|
125
|
+
def show_usage
|
126
|
+
puts "Usage: hoodoo SERVICE_NAME [--from <git-repository>]"
|
127
|
+
puts " e.g. hoodoo service_cron"
|
128
|
+
puts " hoodoo service_person --from git@github.com:YOURNAME/service_shell_fork.git"
|
129
|
+
|
130
|
+
Kernel::exit( KERNEL_EXIT_FAILURE )
|
131
|
+
end
|
132
|
+
|
133
|
+
def usage_and_warning( warning )
|
134
|
+
puts "WARNING: #{warning}"
|
135
|
+
puts
|
136
|
+
show_usage
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
########################################################################
|
2
|
+
# File:: logger.rb
|
3
|
+
# (C):: Loyalty New Zealand 2014
|
4
|
+
#
|
5
|
+
# Purpose:: Include the schema based data validation and rendering code.
|
6
|
+
# ----------------------------------------------------------------------
|
7
|
+
# 26-Jan-2015 (ADH): Split from top-level inclusion file.
|
8
|
+
########################################################################
|
9
|
+
|
10
|
+
# Dependencies
|
11
|
+
|
12
|
+
require 'hoodoo/communicators'
|
13
|
+
|
14
|
+
# Logger code
|
15
|
+
|
16
|
+
require 'hoodoo/logger/logger'
|
17
|
+
require 'hoodoo/logger/writer_mixin'
|
18
|
+
require 'hoodoo/logger/flattener_mixin'
|
19
|
+
require 'hoodoo/logger/fast_writer'
|
20
|
+
require 'hoodoo/logger/slow_writer'
|
21
|
+
require 'hoodoo/logger/writers/file_writer'
|
22
|
+
require 'hoodoo/logger/writers/stream_writer'
|
23
|
+
require 'hoodoo/logger/writers/log_entries_dot_com_writer'
|
@@ -0,0 +1,27 @@
|
|
1
|
+
########################################################################
|
2
|
+
# File:: fast_writer.rb
|
3
|
+
# (C):: Loyalty New Zealand 2014
|
4
|
+
#
|
5
|
+
# Purpose:: Base class for fast log writers.
|
6
|
+
# ----------------------------------------------------------------------
|
7
|
+
# 16-Dec-2014 (ADH): Created.
|
8
|
+
########################################################################
|
9
|
+
|
10
|
+
module Hoodoo
|
11
|
+
class Logger
|
12
|
+
|
13
|
+
# Log writer classes are used through the Hoodoo::Logger class.
|
14
|
+
#
|
15
|
+
# Subclass FastWriter if you are writing a log data output mechanism which
|
16
|
+
# responds very quickly. File output might fall into this category
|
17
|
+
# depending upon target deployment infrastructure; printing to the console
|
18
|
+
# would certainly qualify.
|
19
|
+
#
|
20
|
+
# The subclass only needs to implement
|
21
|
+
# Hoodoo::Logger::WriterMixin#report.
|
22
|
+
#
|
23
|
+
class FastWriter
|
24
|
+
include Hoodoo::Logger::WriterMixin
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
########################################################################
|
2
|
+
# File:: flattener_mixin.rb
|
3
|
+
# (C):: Loyalty New Zealand 2014
|
4
|
+
#
|
5
|
+
# Purpose:: Common code for fast and slow log writer base classes.
|
6
|
+
# ----------------------------------------------------------------------
|
7
|
+
# 19-Dec-2014 (ADH): Created.
|
8
|
+
########################################################################
|
9
|
+
|
10
|
+
require 'time'
|
11
|
+
|
12
|
+
module Hoodoo
|
13
|
+
class Logger
|
14
|
+
|
15
|
+
# This mixin is used by custom logger subclasses and defines a single
|
16
|
+
# method, Hoodoo::Logger::FlattenerMixin#flatten, which takes
|
17
|
+
# structured log data and turns it into a single line of output
|
18
|
+
# including ISO 8601 time and date in local server time zone (since
|
19
|
+
# that makes log analysis easier for humans working in non-UTC time
|
20
|
+
# zone regions).
|
21
|
+
#
|
22
|
+
# Using this is of course entirely optional, but if you do use it, you
|
23
|
+
# will be ensuring consistent non-structured log output across any
|
24
|
+
# non-structured writers.
|
25
|
+
#
|
26
|
+
module FlattenerMixin
|
27
|
+
|
28
|
+
# Take the parameters from Hoodoo::Logger::WriterMixin#report and
|
29
|
+
# return a single line string representing the "flattened" log data.
|
30
|
+
#
|
31
|
+
def flatten( log_level, component, code, data )
|
32
|
+
"#{ log_level.to_s.upcase } [#{ Time.now.iso8601( 6 ) }] #{ component } - #{ code }: #{ data.inspect }"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,387 @@
|
|
1
|
+
########################################################################
|
2
|
+
# File:: logger.rb
|
3
|
+
# (C):: Loyalty New Zealand 2014
|
4
|
+
#
|
5
|
+
# Purpose:: Multiple output logging via local code or external services.
|
6
|
+
# ----------------------------------------------------------------------
|
7
|
+
# 16-Dec-2014 (ADH): Created.
|
8
|
+
########################################################################
|
9
|
+
|
10
|
+
module Hoodoo
|
11
|
+
|
12
|
+
# Multiple output logging via local code or external services. Instantiate
|
13
|
+
# a new Logger, then use #add to add _instances_ of writer classes to the
|
14
|
+
# collection of log writers. When #report, #debug, #info, #warn or #error
|
15
|
+
# are called, a corresponding log message is sent once to each of the
|
16
|
+
# writers, provided that the configured logging level (#level, #level=)
|
17
|
+
# allows it.
|
18
|
+
#
|
19
|
+
# By default, a new logger instance has no configured writers so logged
|
20
|
+
# messages will not go anywhere. You must use #add to add at least one
|
21
|
+
# writer for the instance to be useful.
|
22
|
+
#
|
23
|
+
# Some writer classes are provided by Hoodoo, including:
|
24
|
+
#
|
25
|
+
# * Hoodoo::Logger::StreamWriter - write to output streams, typically
|
26
|
+
# expected to be fast, e.g. unredirected $stdout or $stderr.
|
27
|
+
#
|
28
|
+
# * Hoodoo::Logger::FileWriter - write to files, typically expected to
|
29
|
+
# be relatively slow.
|
30
|
+
#
|
31
|
+
# Some loggers can preserve structural logged data (see #report) while others
|
32
|
+
# flatten all log messages. For example, Hoodoo::Logger::StreamWriter must
|
33
|
+
# flatten messages but a custom writer that, say, persisted messages in a
|
34
|
+
# database should be able to preserve structure.
|
35
|
+
#
|
36
|
+
# Writers are either considered fast or slow. Fast writers are called inline
|
37
|
+
# as soon as a message gets logged. Slow writers are called asynchronously
|
38
|
+
# via a Thread. A Queue is used to buffer messages for slow writers; if this
|
39
|
+
# gets too large, messages may be dropped. Once the slow writer catches up,
|
40
|
+
# a +warn+ level log message is automatically logged to report the number of
|
41
|
+
# dropped messages in the interim.
|
42
|
+
#
|
43
|
+
# To create a new custom writer class of any name/namespace, just subclass
|
44
|
+
# Hoodoo::Logger::FastWriter or Hoodoo::Logger::SlowWriter - see those
|
45
|
+
# classes for details.
|
46
|
+
#
|
47
|
+
class Logger
|
48
|
+
|
49
|
+
# Create a new logger instance. Once created, use #add to add writers.
|
50
|
+
#
|
51
|
+
# +component+:: Flat logging methods (see #debug, #info, #warn and #error)
|
52
|
+
# are internally logged through the structured logger (see
|
53
|
+
# #report) using the +component+ (again, see #report)
|
54
|
+
# optionally passed here as a Symbol or String. Default is
|
55
|
+
# +:Middleware+.
|
56
|
+
#
|
57
|
+
def initialize( component = :Middleware )
|
58
|
+
@level = :debug
|
59
|
+
@pool = Hoodoo::Communicators::Pool.new
|
60
|
+
@component = component
|
61
|
+
@writers = {}
|
62
|
+
end
|
63
|
+
|
64
|
+
# Add a new writer instance to this logger. Example:
|
65
|
+
#
|
66
|
+
# file_writer = Hoodoo::Logger::FileWriter.new( 'output.log' )
|
67
|
+
# stdout_writer = Hoodoo::Logger::StreamWriter.new
|
68
|
+
#
|
69
|
+
# @logger = Hoodoo::Logger.new
|
70
|
+
#
|
71
|
+
# logger.add( file_writer )
|
72
|
+
# logger.add( stdout_writer )
|
73
|
+
#
|
74
|
+
# # ...then later...
|
75
|
+
#
|
76
|
+
# logger.report( ... ) # -> Sends to "output.log" and $stdout
|
77
|
+
#
|
78
|
+
# +writer_instances+:: One or more _instances_ of a subclass of
|
79
|
+
# Hoodoo::Logger::FastWriter or
|
80
|
+
# Hoodoo::Logger::SlowWriter, passed as one or
|
81
|
+
# more comma-separated parameters.
|
82
|
+
#
|
83
|
+
def add( *writer_instances )
|
84
|
+
writer_instances.each do | writer_instance |
|
85
|
+
communicator = if writer_instance.is_a?( Hoodoo::Logger::FastWriter )
|
86
|
+
FastCommunicator.new( writer_instance, self )
|
87
|
+
elsif writer_instance.is_a?( Hoodoo::Logger::SlowWriter )
|
88
|
+
SlowCommunicator.new( writer_instance, self )
|
89
|
+
else
|
90
|
+
raise "Hoodoo::Logger\#add: Only instances of Hoodoo::Logger::FastWriter or Hoodoo::Logger::SlowWriter can be added - #{ writer_instance.class.name } was given"
|
91
|
+
end
|
92
|
+
|
93
|
+
@pool.add( communicator )
|
94
|
+
@writers[ writer_instance ] = communicator
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Remove a writer instance from this logger. If the instance has not been
|
99
|
+
# previously added, no error is raised.
|
100
|
+
#
|
101
|
+
# Slow writers may take a while to finish processing and shut down in
|
102
|
+
# the background. As a result, this method might take a while to return.
|
103
|
+
# Internal default timeouts may even mean that the writer is still running
|
104
|
+
# (possibly entirely hung).
|
105
|
+
#
|
106
|
+
# +writer_instance+:: An _instance_ of a subclass of
|
107
|
+
# Hoodoo::Logger::FastWriter or
|
108
|
+
# Hoodoo::Logger::SlowWriter.
|
109
|
+
#
|
110
|
+
def remove( writer_instance )
|
111
|
+
communicator = @writers[ writer_instance ]
|
112
|
+
@pool.remove( communicator ) unless communicator.nil?
|
113
|
+
end
|
114
|
+
|
115
|
+
# Remove all writer instances from this logger.
|
116
|
+
#
|
117
|
+
# Slow writers may take a while to finish processing and shut down in
|
118
|
+
# the background. As a result, this method might take a while to return.
|
119
|
+
# Internal default timeouts may even mean that one or more slow writers
|
120
|
+
# are still running (possibly entirely hung).
|
121
|
+
#
|
122
|
+
def remove_all
|
123
|
+
@pool.terminate()
|
124
|
+
@writers = {}
|
125
|
+
end
|
126
|
+
|
127
|
+
# Returns an array of all log writer instances currently in use, in order
|
128
|
+
# of addition. See #add.
|
129
|
+
#
|
130
|
+
def instances
|
131
|
+
|
132
|
+
# Implicit ordering relies on Ruby >= 1.9 documented behaviour of
|
133
|
+
# preserving order of addition to a Hash.
|
134
|
+
#
|
135
|
+
@writers.keys
|
136
|
+
|
137
|
+
end
|
138
|
+
|
139
|
+
# Wait for all writers to finish writing all log messages sent up to the
|
140
|
+
# point of calling. Internal default timeouts for slow writers mean that
|
141
|
+
# hung or extremely slow/backlogged writers may not have finished by the
|
142
|
+
# time the call returns, but it's necessary to enforce a timeout else
|
143
|
+
# this call may never return at all.
|
144
|
+
#
|
145
|
+
def wait
|
146
|
+
@pool.wait()
|
147
|
+
end
|
148
|
+
|
149
|
+
# Return or set the current log level. This is +:debug+ by default.
|
150
|
+
#
|
151
|
+
attr_accessor :level
|
152
|
+
|
153
|
+
# Given the log level configuration of this instance - see #level= and
|
154
|
+
# #level - should a message of the given log level be reported? Returns
|
155
|
+
# +true+ if so else +false+.
|
156
|
+
#
|
157
|
+
# This is mostly for internal use but external callers might find it
|
158
|
+
# useful from time to time, especially in tests.
|
159
|
+
#
|
160
|
+
# +log_level+:: Log level of interest as a Symbol - +debug+, +info+, +warn+
|
161
|
+
# or +error+.
|
162
|
+
#
|
163
|
+
def report?( log_level )
|
164
|
+
return false if log_level == :debug && @level != :debug
|
165
|
+
return false if log_level == :info && @level != :debug && @level != :info
|
166
|
+
return false if log_level == :warn && @level != :debug && @level != :info && @level != :warn
|
167
|
+
return true
|
168
|
+
end
|
169
|
+
|
170
|
+
# Logs a message using the structured logger. Whether or not log data is
|
171
|
+
# written in a stuctured manner depends upon the writer(s) in use (see
|
172
|
+
# #add). Structured writers preserve data structures like hashes or arrays
|
173
|
+
# rather than (say) dumping things out as strings into flat output streams.
|
174
|
+
#
|
175
|
+
# As with flat logging methods #debug, #info, #warn and #error, a
|
176
|
+
# message is only logged if the logging threshold level (see #level=) is
|
177
|
+
# set to an equal or lower level.
|
178
|
+
#
|
179
|
+
# +log_level+:: Log level as a symbol - one of, from most trivial to most
|
180
|
+
# severe, +:debug+, +:info+, +:warn+ or +:error+.
|
181
|
+
#
|
182
|
+
# +component+:: Component; for example, the resource name for a specific
|
183
|
+
# resource endpoint implementation, 'Middleware' for Hoodoo
|
184
|
+
# middleware itself, or some other name you think is useful.
|
185
|
+
# String or Symbol.
|
186
|
+
#
|
187
|
+
# +code+:: Component-defined code. Think of this in a manner similar
|
188
|
+
# to platform error codes, appearing after the "."; messages
|
189
|
+
# related to the same thing should share the same code. The
|
190
|
+
# intent is to produce log data that someone can filter on
|
191
|
+
# code to get useful information about that specific aspect
|
192
|
+
# of a service implementation's behaviour.
|
193
|
+
#
|
194
|
+
# +data+:: A Hash containing the level-, component- and code-dependent
|
195
|
+
# payload data to be logged.
|
196
|
+
#
|
197
|
+
def report( log_level, component, code, data )
|
198
|
+
return unless self.report?( log_level )
|
199
|
+
|
200
|
+
@pool.communicate(
|
201
|
+
Payload.new(
|
202
|
+
log_level: log_level,
|
203
|
+
component: component,
|
204
|
+
code: code,
|
205
|
+
data: data
|
206
|
+
)
|
207
|
+
)
|
208
|
+
end
|
209
|
+
|
210
|
+
# Write a +debug+ log message, provided the log level is +:debug+.
|
211
|
+
#
|
212
|
+
# The logging data is unstructured, but gets passed to #report for
|
213
|
+
# structured logging under the component specified in the constructor
|
214
|
+
# and code 'log'.
|
215
|
+
#
|
216
|
+
# Calling #report is recommended over unstructured direct logging.
|
217
|
+
#
|
218
|
+
# *args:: One or more arguments that will be treated as strings and
|
219
|
+
# written in the presented order to the log, each on its own
|
220
|
+
# line of output ("\\n" terminated).
|
221
|
+
#
|
222
|
+
def debug( *args )
|
223
|
+
self.report( :debug, @component, :log, { '_data' => args } )
|
224
|
+
end
|
225
|
+
|
226
|
+
# Write an +info+ log message, provided the log level is +:debug+ or
|
227
|
+
# +:info+.
|
228
|
+
#
|
229
|
+
# The logging data is unstructured, but gets passed to #report for
|
230
|
+
# structured logging under the component specified in the constructor
|
231
|
+
# and code 'log'.
|
232
|
+
#
|
233
|
+
# Calling #report is recommended over unstructured direct logging.
|
234
|
+
#
|
235
|
+
# *args:: One or more arguments that will be treated as strings and
|
236
|
+
# written in the presented order to the log, each on its own
|
237
|
+
# line of output ("\\n" terminated).
|
238
|
+
#
|
239
|
+
def info( *args )
|
240
|
+
self.report( :info, @component, :log, { '_data' => args } )
|
241
|
+
end
|
242
|
+
|
243
|
+
# Write a +warn+ log message, provided the log level is +:debug+,
|
244
|
+
# +:info+ or +:warn+.
|
245
|
+
#
|
246
|
+
# The logging data is unstructured, but gets passed to #report for
|
247
|
+
# structured logging under the component specified in the constructor
|
248
|
+
# and code 'log'.
|
249
|
+
#
|
250
|
+
# Calling #report is recommended over unstructured direct logging.
|
251
|
+
#
|
252
|
+
# *args:: One or more arguments that will be treated as strings and
|
253
|
+
# written in the presented order to the log, each on its own
|
254
|
+
# line of output ("\\n" terminated).
|
255
|
+
#
|
256
|
+
def warn( *args )
|
257
|
+
self.report( :warn, @component, :log, { '_data' => args } )
|
258
|
+
end
|
259
|
+
|
260
|
+
# Write an +error+ log message, regardless of logging level.
|
261
|
+
#
|
262
|
+
# The logging data is unstructured, but gets passed to #report for
|
263
|
+
# structured logging under the component specified in the constructor
|
264
|
+
# and code 'log'.
|
265
|
+
#
|
266
|
+
# Calling #report is recommended over unstructured direct logging.
|
267
|
+
#
|
268
|
+
# *args:: One or more arguments that will be treated as strings and
|
269
|
+
# written in the presented order to the log, each on its own
|
270
|
+
# line of output ("\\n" terminated).
|
271
|
+
#
|
272
|
+
def error( *args )
|
273
|
+
self.report( :error, @component, :log, { '_data' => args } )
|
274
|
+
end
|
275
|
+
|
276
|
+
# Used internally toommunicate details of a log message across the
|
277
|
+
# Hoodoo::Communicators::Pool mechanism and through to a log writer.
|
278
|
+
# Log writer authors do not need to use this class;
|
279
|
+
# Hoodoo::Logger::WriterMixin unpacks it and calls your subclass's
|
280
|
+
# #report implementation with individual parameters for you.
|
281
|
+
#
|
282
|
+
class Payload
|
283
|
+
|
284
|
+
# Log level - see Hoodoo::Logger#report.
|
285
|
+
#
|
286
|
+
attr_reader :log_level
|
287
|
+
|
288
|
+
# Component - see Hoodoo::Logger#report.
|
289
|
+
#
|
290
|
+
attr_reader :component
|
291
|
+
|
292
|
+
# Code - see Hoodoo::Logger#report.
|
293
|
+
#
|
294
|
+
attr_reader :code
|
295
|
+
|
296
|
+
# Data - see Hoodoo::Logger#report.
|
297
|
+
#
|
298
|
+
attr_reader :data
|
299
|
+
|
300
|
+
# Create an instance. Named parameters are:
|
301
|
+
#
|
302
|
+
# +log_level+:: See Hoodoo::Logger#report.
|
303
|
+
# +component+:: See Hoodoo::Logger#report.
|
304
|
+
# +code+:: See Hoodoo::Logger#report.
|
305
|
+
# +data+:: See Hoodoo::Logger#report.
|
306
|
+
#
|
307
|
+
def initialize( log_level:, component:, code:, data: )
|
308
|
+
@log_level = log_level
|
309
|
+
@component = component
|
310
|
+
@code = code
|
311
|
+
@data = data
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
# Mixin used internally for the FastCommunicator and SlowCommunicator
|
316
|
+
# wrappers that hide implementation complexity from log writer subclasses.
|
317
|
+
#
|
318
|
+
module Communicator
|
319
|
+
|
320
|
+
# Create an instance of a logging communicator, based on the given
|
321
|
+
# log writer and owning logger instance.
|
322
|
+
#
|
323
|
+
# +writer_instance+:: Hoodoo::Logger::FastWriter or
|
324
|
+
# Hoodoo::Logger::SlowWriter subclass instance
|
325
|
+
# that will log things when this Communicator
|
326
|
+
# asks it to do so.
|
327
|
+
#
|
328
|
+
# +owning_logger+:: Hoodoo::Logger instance that will be using
|
329
|
+
# this communicator instance.
|
330
|
+
#
|
331
|
+
def initialize( writer_instance, owning_logger )
|
332
|
+
@writer_instance = writer_instance
|
333
|
+
@owning_logger = owning_logger
|
334
|
+
end
|
335
|
+
|
336
|
+
# Implement Hoodoo::Communicators::Base#communicate for both slow and
|
337
|
+
# fast writers. Assumes it will be passed a Hoodoo::Logger::Payload
|
338
|
+
# class instance which describes the structured log data to report; also
|
339
|
+
# assumes it is only called when the calling logger's configured log
|
340
|
+
# level threshold should allow through the level of the log message in
|
341
|
+
# question. Calls through to the #report implementation.
|
342
|
+
#
|
343
|
+
def communicate( payload )
|
344
|
+
@writer_instance.report(
|
345
|
+
payload.log_level,
|
346
|
+
payload.component,
|
347
|
+
payload.code,
|
348
|
+
payload.data
|
349
|
+
)
|
350
|
+
end
|
351
|
+
|
352
|
+
# Implement optional method Hoodoo::Communicators::Slow#dropped on
|
353
|
+
# behalf of subclasses. The method turns the 'messages dropped'
|
354
|
+
# notification into a log message of +:warn+ level and which it reports
|
355
|
+
# internally immediately for the Writer instance only (since different
|
356
|
+
# writers have different queues and thus different dropped message
|
357
|
+
# warnings), provided that the owning Hoodoo::Logger instance log
|
358
|
+
# level lets warnings through.
|
359
|
+
#
|
360
|
+
def dropped( number )
|
361
|
+
if @owning_logger.report?( :warn )
|
362
|
+
@writer_instance.report(
|
363
|
+
:warn,
|
364
|
+
self.class.name,
|
365
|
+
'dropped.messages',
|
366
|
+
"Logging flooded - #{ number } messages dropped"
|
367
|
+
)
|
368
|
+
end
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
# Used internally as a Hoodoo::Communicator::Pool communicator wrapping
|
373
|
+
# fast log writer instances.
|
374
|
+
#
|
375
|
+
class FastCommunicator < Hoodoo::Communicators::Fast
|
376
|
+
include Communicator
|
377
|
+
end
|
378
|
+
|
379
|
+
# Used internally as a Hoodoo::Communicator::Pool communicator wrapping
|
380
|
+
# slow log writer instances.
|
381
|
+
#
|
382
|
+
class SlowCommunicator < Hoodoo::Communicators::Slow
|
383
|
+
include Communicator
|
384
|
+
end
|
385
|
+
|
386
|
+
end
|
387
|
+
end
|