flydata 0.2.17 → 0.2.18

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/flydata-core/.gitignore +16 -0
  4. data/flydata-core/.rspec +1 -0
  5. data/flydata-core/.travis.yml +6 -0
  6. data/flydata-core/Gemfile +11 -0
  7. data/flydata-core/Gemfile.lock +51 -0
  8. data/flydata-core/lib/flydata-core/core_ext/module/include.rb +5 -0
  9. data/flydata-core/lib/flydata-core/core_ext/module.rb +1 -0
  10. data/flydata-core/lib/flydata-core/core_ext/object/prepend.rb +17 -0
  11. data/flydata-core/lib/flydata-core/core_ext/object.rb +1 -0
  12. data/flydata-core/lib/flydata-core/core_ext.rb +3 -0
  13. data/flydata-core/lib/flydata-core/errors.rb +334 -0
  14. data/flydata-core/lib/flydata-core/logger.rb +205 -0
  15. data/{lib/flydata → flydata-core/lib/flydata-core}/table_def/mysql_table_def.rb +6 -8
  16. data/{lib/flydata → flydata-core/lib/flydata-core}/table_def/redshift_table_def.rb +17 -2
  17. data/flydata-core/lib/flydata-core/table_def.rb +2 -0
  18. data/flydata-core/lib/flydata-core/thread_context.rb +31 -0
  19. data/flydata-core/lib/flydata-core.rb +1 -0
  20. data/flydata-core/spec/spec_helper.rb +2 -0
  21. data/{spec/flydata → flydata-core/spec}/table_def/mysql_table_def_spec.rb +22 -8
  22. data/flydata-core/spec/table_def/mysql_to_redshift_table_def_spec.rb +428 -0
  23. data/flydata-core/spec/table_def/mysqldump_test_bit_table.dump +51 -0
  24. data/{spec/flydata → flydata-core/spec}/table_def/mysqldump_test_foreign_key.dump +0 -0
  25. data/{spec/flydata → flydata-core/spec}/table_def/mysqldump_test_table_all.dump +0 -0
  26. data/{spec/flydata → flydata-core/spec}/table_def/mysqldump_test_table_column_comment.dump +0 -0
  27. data/{spec/flydata → flydata-core/spec}/table_def/mysqldump_test_table_enum.dump +0 -0
  28. data/{spec/flydata → flydata-core/spec}/table_def/mysqldump_test_table_multi_pk.dump +0 -0
  29. data/{spec/flydata → flydata-core/spec}/table_def/mysqldump_test_table_no_pk.dump +0 -0
  30. data/{spec/flydata → flydata-core/spec}/table_def/mysqldump_test_unique_key.dump +0 -0
  31. data/{spec/flydata → flydata-core/spec}/table_def/mysqldump_test_unique_key2.dump +0 -0
  32. data/{spec/flydata → flydata-core/spec}/table_def/mysqldump_test_unique_key3.dump +0 -0
  33. data/{spec/flydata → flydata-core/spec}/table_def/mysqldump_test_unsigned.dump +0 -0
  34. data/{spec/flydata → flydata-core/spec}/table_def/redshift_table_def_spec.rb +63 -16
  35. data/flydata.gemspec +34 -18
  36. data/lib/flydata/command/sync.rb +11 -8
  37. data/lib/flydata/parser/mysql/mysql_alter_table.treetop +128 -18
  38. data/lib/flydata/parser_provider.rb +1 -1
  39. data/lib/flydata.rb +11 -1
  40. data/spec/flydata/parser/mysql/alter_table_parser_spec.rb +173 -2
  41. data/spec/spec_helper.rb +3 -1
  42. metadata +34 -18
  43. data/.gitignore +0 -49
  44. data/lib/flydata/table_def.rb +0 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a3b273dc439a9c6c58398ed75794f67ab7fcd785
4
- data.tar.gz: 20a9d323a93ebb65865e5dbee86750d3c71b8dbe
3
+ metadata.gz: e60423bcd2e535f420b6cffae38bb02d4c4183b8
4
+ data.tar.gz: eff1e3250db8d8e036c977f7c5c0fb50232d4fe2
5
5
  SHA512:
6
- metadata.gz: 1596629dd6c036e157db63f20ffef8fb84f9201f1677b201892e8b59b2f81c5178535a58a3b2f6a4e35fca908ac87a6b5cc3d8af10ecdbd8408734532a9f4a57
7
- data.tar.gz: c113a7bdce48f2d683769b50c134efdc6925e6b40897ce14e90ac3c51cf8e54d6c018d1c9ef967ced2e7f957bb78584d7e0fdf10eac182ab3853757d3c6ec369
6
+ metadata.gz: e30e9303ae3bfa61ccd55eda8837c3606b6502e3523cd4d476068f4ac34b7814ed4fd4858e3af0327705d6f26d2a17d3ec205277acfd92844c919f15ecce15be
7
+ data.tar.gz: d26ad0a84abdeaac414f75c357a5a10f5b6c918659f7be55cd17603940c8d9bf76bbe67535f2ed75ce891723c01554a2c6d2bbcc167282cc0380b6ace81fd748
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.17
1
+ 0.2.18
@@ -0,0 +1,16 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ InstalledFiles
7
+ _yardoc
8
+ coverage
9
+ doc/
10
+ lib/bundler/man
11
+ pkg
12
+ rdoc
13
+ spec/reports
14
+ test/tmp
15
+ test/version_tmp
16
+ tmp
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 2.0.0
5
+ - 2.1.0
6
+ - 2.1.1
@@ -0,0 +1,11 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # for dependencies
4
+ group :development, :test do
5
+ gem 'timecop'
6
+ gem 'ruby-prof'
7
+ gem 'rubocop'
8
+ gem 'rspec', '~> 3.1'
9
+ gem 'simplecov', :require => false
10
+ end
11
+
@@ -0,0 +1,51 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ ast (2.0.0)
5
+ astrolabe (1.3.0)
6
+ parser (>= 2.2.0.pre.3, < 3.0)
7
+ diff-lcs (1.2.5)
8
+ docile (1.1.5)
9
+ multi_json (1.10.1)
10
+ parser (2.2.0.pre.8)
11
+ ast (>= 1.1, < 3.0)
12
+ slop (~> 3.4, >= 3.4.5)
13
+ powerpack (0.0.9)
14
+ rainbow (2.0.0)
15
+ rspec (3.1.0)
16
+ rspec-core (~> 3.1.0)
17
+ rspec-expectations (~> 3.1.0)
18
+ rspec-mocks (~> 3.1.0)
19
+ rspec-core (3.1.7)
20
+ rspec-support (~> 3.1.0)
21
+ rspec-expectations (3.1.2)
22
+ diff-lcs (>= 1.2.0, < 2.0)
23
+ rspec-support (~> 3.1.0)
24
+ rspec-mocks (3.1.3)
25
+ rspec-support (~> 3.1.0)
26
+ rspec-support (3.1.2)
27
+ rubocop (0.27.1)
28
+ astrolabe (~> 1.3)
29
+ parser (>= 2.2.0.pre.7, < 3.0)
30
+ powerpack (~> 0.0.6)
31
+ rainbow (>= 1.99.1, < 3.0)
32
+ ruby-progressbar (~> 1.4)
33
+ ruby-prof (0.15.2)
34
+ ruby-progressbar (1.7.0)
35
+ simplecov (0.9.1)
36
+ docile (~> 1.1.0)
37
+ multi_json (~> 1.0)
38
+ simplecov-html (~> 0.8.0)
39
+ simplecov-html (0.8.0)
40
+ slop (3.6.0)
41
+ timecop (0.7.1)
42
+
43
+ PLATFORMS
44
+ ruby
45
+
46
+ DEPENDENCIES
47
+ rspec (~> 3.1)
48
+ rubocop
49
+ ruby-prof
50
+ simplecov
51
+ timecop
@@ -0,0 +1,5 @@
1
+ class Module
2
+ def include_unless_included(m)
3
+ include m unless self.include?(m)
4
+ end
5
+ end
@@ -0,0 +1 @@
1
+ require_relative 'module/include'
@@ -0,0 +1,17 @@
1
+ module PrependDependenciesCheckable
2
+ def check_prepend_dependencies(target, dependencies)
3
+ order = Array(dependencies[:before]) + [target] + Array(dependencies[:after])
4
+ self.class.ancestors.reverse.each { |cls| order.shift if cls == order.first }
5
+ order.empty?
6
+ end
7
+
8
+ def check_prepend_dependencies!(target, dependencies)
9
+ unless check_prepend_dependencies(target, dependencies)
10
+ raise "Invalid prepend dependencies. target:#{target} ancestors:#{self.class.ancestors} dependencies:#{dependencies}"
11
+ end
12
+ end
13
+ end
14
+
15
+ class Object
16
+ include PrependDependenciesCheckable
17
+ end
@@ -0,0 +1 @@
1
+ require_relative 'object/prepend'
@@ -0,0 +1,3 @@
1
+ Dir["#{File.dirname(__FILE__)}/core_ext/*.rb"].each do |path|
2
+ require path
3
+ end
@@ -0,0 +1,334 @@
1
+ require 'flydata-core/thread_context'
2
+
3
+ module FlydataCore
4
+
5
+
6
+ # Error 12xx are derived from AWS.
7
+ # Error 100xx are FlyData errros.
8
+ module ErrorCode
9
+ VALUE_OUT_OF_RANGE_ERROR = 1204
10
+ INVALID_DATE_FORMAT_ERROR = 1205
11
+ INVALID_TIMESTAMP_FORMAT_ERROR = 1206
12
+ INVALID_NUMBER_ERROR = 1207
13
+ INVALID_FLOAT_ERROR = 1208
14
+ INVALID_DECIMAL_ERROR = 1209
15
+ INVALID_BOOLEAN_ERROR = 1210
16
+ NOT_NULL_VIOLATION_ERROR = 1213
17
+ INVALID_VARCHAR_ERROR = 1214
18
+ INVALID_CHAR_ERROR = 1215
19
+ INVALID_UTF8_CHARACTER_ERROR = 1220
20
+ INVALID_DATA_FORMAT_ERROR = 1201
21
+ MORE_COLUMNS_ERROR = 1202
22
+ LESS_COLUMNS_ERROR = 1203
23
+ NO_DATA_ERROR = 1211
24
+ RESERVED_TABLE_NAME_ERROR = 10001
25
+ RESERVED_COLUMN_NAME_ERROR = 10002
26
+ TABLE_MISSING_ERROR = 10003
27
+ AUTHENTICATION_ERROR = 10004
28
+ RETRY_LIMIT_EXCEED_ERROR = 10005
29
+ INFORMATION_SCHEMA_ERROR = 10006
30
+ TABLE_REVISION_ERROR = 10007
31
+ S3_ACCESS_ERROR = 10008
32
+ REDSHIFT_ACCESS_ERROR = 10009
33
+ BREAKING_ALTER_TABLE_ERROR = 10010
34
+ INTERNAL_ERROR = 1200
35
+ end
36
+
37
+ # Error Level
38
+ # 0 Emergency: system is unusable
39
+ # 1 Alert: action must be taken immediately
40
+ # 2 Critical: critical conditions
41
+ # 3 Error: error conditions
42
+ # 4 Warning: warning conditions
43
+ # 5 Notice: normal but significant condition
44
+ # 6 Informational: informational messages
45
+ # 7 Debug: debug-level messages
46
+ module ErrorLevel
47
+ ERROR = 3 # failed to load a record
48
+ WARN = 4 # loaded a record but a record is modified
49
+ end
50
+
51
+ # RetryableError
52
+
53
+ class RetryableError < StandardError
54
+ def initialize(original_exception, retry_alert_limit = nil)
55
+ @original_exception = original_exception
56
+ @retry_alert_limit = retry_alert_limit
57
+ end
58
+ attr_reader :original_exception
59
+ attr_reader :retry_alert_limit
60
+ end
61
+
62
+ # DataDeliveryError
63
+ # data_node_id, data_entry_id, chunk_identifier, table_name, err_code, err_reason, err_level
64
+
65
+ class DataDeliveryError < StandardError
66
+ def initialize(error_reason, error_content = {})
67
+ error_content = if error_reason.kind_of?(String)
68
+ { err_reason: error_reason}.merge(error_content)
69
+ elsif error_reason.kind_of?(Hash)
70
+ error_reason.merge(error_content)
71
+ else
72
+ raise ArgumentError.new(
73
+ "Invalid argument. First parameter must be string or hash object.")
74
+ end
75
+ @error_content = default_params.
76
+ merge(context_params).
77
+ merge(error_content).
78
+ merge(overwrite_params)
79
+ filter_content(@error_content)
80
+ @error_content[:err_code] = err_code
81
+ end
82
+ attr_reader :error_content
83
+ def default_params
84
+ if h = FlydataCore::ThreadContext.parameters
85
+ {data_node_id: h[:node_id],
86
+ data_entry_id: h[:data_entry_id],
87
+ table_name: h[:table_name],
88
+ chunk_identifier: h[:chunk_identifier],
89
+ err_level: ErrorLevel::ERROR}
90
+ else
91
+ {}
92
+ end
93
+ end
94
+ def context_params
95
+ DataDeliveryErrorThreadContext.context_params || {}
96
+ end
97
+ def overwrite_params
98
+ {}
99
+ end
100
+ def err_code
101
+ raise NoMethodError, "default_parameters is not implemented"
102
+ end
103
+ def warn?
104
+ err_level == ErrorLevel::WARN
105
+ end
106
+ def err_level
107
+ @error_content[:err_level]
108
+ end
109
+ def to_s
110
+ if r = @error_content[:err_reason]
111
+ r
112
+ else
113
+ super
114
+ end
115
+ end
116
+
117
+ MAX_CONTENT_SIZE = 1024 # 1kb
118
+
119
+ def filter_content(error_content = @error_content)
120
+ error_content.each do |k, value|
121
+ if value.kind_of?(String) && value.length > MAX_CONTENT_SIZE
122
+ error_content[k] = value[0..MAX_CONTENT_SIZE-1]
123
+ end
124
+ end
125
+ end
126
+ end
127
+
128
+ # + RecordDeliveryError
129
+ # data_node_id, data_entry_id, chunk_identifier, table_name, err_code, err_reason, err_level, record_no, raw_record
130
+
131
+ class RecordDeliveryError < DataDeliveryError
132
+ def to_s
133
+ "#{super} raw_record=(#{@error_content[:raw_record]})"
134
+ end
135
+ end
136
+ class InvalidDataFormatError < RecordDeliveryError
137
+ def err_code; ErrorCode::INVALID_DATA_FORMAT_ERROR; end
138
+ end
139
+ class MoreColumnsError < RecordDeliveryError
140
+ def err_code; ErrorCode::MORE_COLUMNS_ERROR; end
141
+ end
142
+ class LessColumnsError < RecordDeliveryError
143
+ def err_code; ErrorCode::LESS_COLUMNS_ERROR; end
144
+ end
145
+ class NoDataError < RecordDeliveryError
146
+ def err_code; ErrorCode::NO_DATA_ERROR; end
147
+ end
148
+ class ReservedTableNameError < RecordDeliveryError
149
+ def err_code; ErrorCode::RESERVED_TABLE_NAME_ERROR; end
150
+ end
151
+ class ReservedColumnNameError < RecordDeliveryError
152
+ def err_code; ErrorCode::RESERVED_COLUMN_NAME_ERROR; end
153
+ end
154
+ class TableMissingError < RecordDeliveryError
155
+ def err_code; ErrorCode::TABLE_MISSING_ERROR; end
156
+ end
157
+ class RecordDeliveryInternalError < RecordDeliveryError
158
+ def err_code; ErrorCode::INTERNAL_ERROR; end
159
+ end
160
+
161
+ # information schema, especially flydata_ctl_columns table, is broken
162
+ class InformationSchemaError < RecordDeliveryError
163
+ def err_code; ErrorCode::INFORMATION_SCHEMA_ERROR; end
164
+ end
165
+
166
+ # No table information found in the database
167
+ class MissingTableInformationError < InformationSchemaError
168
+ def err_code; ErrorCode::INFORMATION_SCHEMA_ERROR; end
169
+ end
170
+
171
+ # Table's revision does not match the record's revision.
172
+ class TableRevisionError < RecordDeliveryError
173
+ def err_code; ErrorCode::TABLE_REVISION_ERROR; end
174
+ end
175
+
176
+ # Unsupported ALTER TABLE which breaks the tables sync consistency.
177
+ class BreakingAlterTableError < RecordDeliveryError
178
+ def err_code; ErrorCode::BREAKING_ALTER_TABLE_ERROR; end
179
+ end
180
+
181
+ # + BadValueError
182
+ # data_node_id, data_entry_id, chunk_identifier, table_name, err_code, err_reason, err_level, record_no, raw_record, colname, type, raw_value, modified_value
183
+
184
+ class BadValueError < RecordDeliveryError
185
+ def modified_value
186
+ @error_content[:modified_value]
187
+ end
188
+
189
+ def has_modified_value?
190
+ @error_content.has_key?(:modified_value)
191
+ end
192
+
193
+ def to_s
194
+ "#{super} column=(#{@error_content[:col_name]}) value=(#{@error_content[:raw_value]})"
195
+ end
196
+ end
197
+ class ValueOutOfRangeError < BadValueError
198
+ def err_code; ErrorCode::VALUE_OUT_OF_RANGE_ERROR; end
199
+ end
200
+ class InvalidDateFormatError < BadValueError
201
+ def err_code; ErrorCode::INVALID_DATE_FORMAT_ERROR; end
202
+ end
203
+ class InvalidTimestampFormatError < BadValueError
204
+ def err_code; ErrorCode::INVALID_TIMESTAMP_FORMAT_ERROR; end
205
+ end
206
+ class InvalidNumberError < BadValueError
207
+ def err_code; ErrorCode::INVALID_NUMBER_ERROR; end
208
+ end
209
+ class InvalidFloatError < BadValueError
210
+ def err_code; ErrorCode::INVALID_FLOAT_ERROR; end
211
+ end
212
+ class InvalidDecimalError < BadValueError
213
+ def err_code; ErrorCode::INVALID_DECIMAL_ERROR; end
214
+ end
215
+ class InvalidBooleanError < BadValueError
216
+ def err_code; ErrorCode::INVALID_BOOLEAN_ERROR; end
217
+ end
218
+ class NotNullViolationError < BadValueError
219
+ def err_code; ErrorCode::NOT_NULL_VIOLATION_ERROR; end
220
+ end
221
+ class InvalidVarcharError < BadValueError
222
+ def err_code; ErrorCode::INVALID_VARCHAR_ERROR; end
223
+ end
224
+ class InvalidCharError < BadValueError
225
+ def err_code; ErrorCode::INVALID_CHAR_ERROR; end
226
+ end
227
+ class InvalidUtf8CharacterError < BadValueError
228
+ def err_code; ErrorCode::INVALID_UTF8_CHARACTER_ERROR; end
229
+ end
230
+
231
+
232
+ # + ChunkDeliveryError
233
+ # data_node_id, data_entry_id, chunk_identifier, table_name, err_code, err_reason, err_level
234
+
235
+ class ChunkDeliveryError < DataDeliveryError
236
+ end
237
+
238
+ class AuthenticationError < ChunkDeliveryError
239
+ # end_point_url
240
+ def err_code; ErrorCode::AUTHENTICATION_ERROR; end
241
+ end
242
+
243
+ class RetryLimitExceedError < ChunkDeliveryError
244
+ def err_code; ErrorCode::RETRY_LIMIT_EXCEED_ERROR; end
245
+ end
246
+
247
+ class ChunkDeliveryInternalError < ChunkDeliveryError
248
+ def err_code; ErrorCode::INTERNAL_ERROR; end
249
+ end
250
+
251
+ class UserResourceError < ChunkDeliveryError
252
+ def default_params
253
+ super.merge(timestamp: Time.now.utc)
254
+ end
255
+ def overwrite_params
256
+ super.merge(table_name: nil)
257
+ end
258
+ end
259
+
260
+ class S3AccessError < UserResourceError
261
+ def err_code; ErrorCode::S3_ACCESS_ERROR; end
262
+ end
263
+
264
+ class RedshiftAccessError < UserResourceError
265
+ def err_code; ErrorCode::REDSHIFT_ACCESS_ERROR; end
266
+ end
267
+
268
+
269
+ ## Depricated errors
270
+
271
+ # The record is valid but not supported.
272
+ class UnsupportedRecordFormat < StandardError; end
273
+
274
+ # Table Def Error
275
+ class TableDefError < StandardError
276
+ attr_reader :err_hash
277
+ def initialize(err_hash)
278
+ @err_hash = err_hash
279
+ end
280
+ end
281
+
282
+ ## Error container
283
+
284
+ class DataDeliveryErrorThreadContext
285
+ THREAD_LOCAL_KEY_ERROR_LIST = :flydata_data_delivery_error_list
286
+ THREAD_LOCAL_KEY_RECORD_LIMIT = "#{THREAD_LOCAL_KEY_ERROR_LIST}_limit".to_sym
287
+ DEFAULT_RECORD_LIMIT = 10
288
+ THREAD_LOCAL_KEY_CONTEXT_PARAMS = "#{THREAD_LOCAL_KEY_ERROR_LIST}_context_params".to_sym
289
+
290
+ def self.initialize(record_limit = DEFAULT_RECORD_LIMIT)
291
+ reset
292
+ Thread.current[THREAD_LOCAL_KEY_RECORD_LIMIT] = record_limit
293
+ end
294
+
295
+ def self.add_error(error)
296
+ return if error_list.include?(error)
297
+ if error_list.count < record_limit
298
+ error_list << error
299
+ end
300
+ #TODO: write warning log if record limit
301
+ end
302
+
303
+ def self.reset
304
+ Thread.current[THREAD_LOCAL_KEY_ERROR_LIST] = nil
305
+ Thread.current[THREAD_LOCAL_KEY_RECORD_LIMIT] = nil
306
+ Thread.current[THREAD_LOCAL_KEY_CONTEXT_PARAMS] = nil
307
+ end
308
+
309
+ def self.record_limit
310
+ Thread.current[THREAD_LOCAL_KEY_RECORD_LIMIT] || DEFAULT_RECORD_LIMIT
311
+ end
312
+
313
+ def self.error_list
314
+ unless list = Thread.current[THREAD_LOCAL_KEY_ERROR_LIST]
315
+ list = Thread.current[THREAD_LOCAL_KEY_ERROR_LIST] = []
316
+ end
317
+ list
318
+ end
319
+
320
+ def self.set_context_params(params)
321
+ Thread.current[THREAD_LOCAL_KEY_CONTEXT_PARAMS] = params
322
+ end
323
+
324
+ def self.add_context_params(params)
325
+ cur_param = Thread.current[THREAD_LOCAL_KEY_CONTEXT_PARAMS] || {}
326
+ Thread.current[THREAD_LOCAL_KEY_CONTEXT_PARAMS] = cur_param.merge(params)
327
+ end
328
+
329
+ def self.context_params
330
+ Thread.current[THREAD_LOCAL_KEY_CONTEXT_PARAMS]
331
+ end
332
+ end
333
+
334
+ end
@@ -0,0 +1,205 @@
1
+ require 'forwardable'
2
+
3
+ require 'flydata-core/errors'
4
+ require 'flydata-core/thread_context'
5
+
6
+ module FlydataCore
7
+ class LogContext
8
+ THREAD_LOCAL_KEY_LOG_CONTEXT = 'flydata_log_context'
9
+ THREAD_LOCAL_KEY_LOGGER = 'flydata_logger'
10
+
11
+ class << self
12
+ extend Forwardable
13
+ def_delegators :log_context, :[], :[]=, :each, :merge, :merge!, :delete
14
+
15
+ def log_context
16
+ if h = Thread.current[THREAD_LOCAL_KEY_LOG_CONTEXT]
17
+ h
18
+ else
19
+ Thread.current[THREAD_LOCAL_KEY_LOG_CONTEXT] = {}
20
+ end
21
+ end
22
+
23
+ def reset_log_context
24
+ Thread.current[THREAD_LOCAL_KEY_LOG_CONTEXT] = nil
25
+ end
26
+
27
+ def logger
28
+ Thread.current[THREAD_LOCAL_KEY_LOGGER]
29
+ end
30
+
31
+ def set_logger(new_logger)
32
+ Thread.current[THREAD_LOCAL_KEY_LOGGER] = new_logger
33
+ end
34
+
35
+ def reset
36
+ reset_log_context
37
+ Thread.current[THREAD_LOCAL_KEY_LOGGER] = nil
38
+ end
39
+ end
40
+ end
41
+
42
+ module Logger
43
+ attr_accessor :logger
44
+
45
+ def initialize_log_context(new_logger, log_items)
46
+ reset_log_context
47
+ set_log_context_logger(new_logger)
48
+ set_log_context_items(log_items)
49
+ end
50
+
51
+ def log_context_items
52
+ LogContext.log_context
53
+ end
54
+
55
+ def log_context_item(k)
56
+ LogContext[k]
57
+ end
58
+
59
+ def set_log_context_item(k, v)
60
+ LogContext[k] = v
61
+ end
62
+
63
+ def delete_log_context_item(*keys)
64
+ keys.each do |k|
65
+ LogContext.delete(k)
66
+ end
67
+ end
68
+
69
+ def set_log_context_items(items)
70
+ reset_log_context_items
71
+ add_log_context_items(items)
72
+ end
73
+
74
+ def add_log_context_items(items)
75
+ LogContext.merge!(items)
76
+ end
77
+
78
+ def reset_log_context_items
79
+ LogContext.reset_log_context
80
+ end
81
+
82
+ def log_context_logger
83
+ LogContext.logger
84
+ end
85
+
86
+ def set_log_context_logger(new_logger)
87
+ LogContext.set_logger(new_logger)
88
+ end
89
+
90
+ def reset_log_context
91
+ LogContext.reset
92
+ end
93
+
94
+ # for override
95
+ def custom_log_items
96
+ {}
97
+ end
98
+
99
+ def filter_log_params(log_params)
100
+ log_params
101
+ end
102
+
103
+ # log methods
104
+ def log_common(level, message, log_params = {}, options = {})
105
+ # check keys
106
+ if e = log_params[:error]
107
+ message += " error_class=#{e.class.to_s} error=\"#{e.to_s.strip.gsub(/\n/, ' ')}\""
108
+ log_params.delete(:error)
109
+ end
110
+
111
+ # get prefix text
112
+ prefix = if log_params[:prefix]
113
+ log_params[:prefix]
114
+ elsif log_context_items[:prefix]
115
+ log_context_items[:prefix]
116
+ elsif custom_log_items[:prefix]
117
+ custom_log_items[:prefix]
118
+ else
119
+ ''
120
+ end
121
+ prefix = "#{prefix} " unless prefix.to_s == ''
122
+
123
+ # build suffix text
124
+ suffix = [build_log_text(filter_log_params(log_params)),
125
+ build_log_text,
126
+ build_log_text(custom_log_items)].compact.join(' ')
127
+ suffix = " #{suffix}" unless suffix.empty?
128
+
129
+ msg = "#{prefix}#{message}#{suffix}"
130
+
131
+ # add backtrace of error
132
+ if e and options[:backtrace]
133
+ backtrace = e.backtrace ? e.backtrace.join("\n") : 'backtrace is empty.'
134
+ msg += " backtrace:\n#{backtrace}"
135
+ end
136
+
137
+ # get logger
138
+ lg = options[:logger] || logger || log_context_logger || $log
139
+ lg.send(level, msg)
140
+ end
141
+
142
+ def log_debug(message, log_params = {}, options = {})
143
+ log_common(:debug, message, log_params, options)
144
+ end
145
+
146
+ def log_info(message, log_params = {}, options = {})
147
+ log_common(:info, message, log_params, options)
148
+ end
149
+
150
+ def log_warn(message, log_params = {}, options = {})
151
+ log_common(:warn, message, log_params, options)
152
+ end
153
+
154
+ def log_error(message, log_params = {}, options = {})
155
+ log_common(:error, message, log_params, options)
156
+ end
157
+
158
+ def log_error_with_backtrace(message, log_params = {}, options = {})
159
+ log_common(:error, message, log_params, options.merge(backtrace: true))
160
+ end
161
+
162
+ def build_log_text(items = log_context_items)
163
+ str_list = []
164
+ items.each do |k, v|
165
+ next if k == :prefix
166
+ case v
167
+ when Array
168
+ str_list << "#{k}:[#{v.join(',')}]"
169
+ when Hash
170
+ str_list << build_log_text(v)
171
+ else
172
+ str_list << if k.to_s.start_with?('__')
173
+ v.to_s.empty? ? nil : v.to_s
174
+ elsif %w|' " ( [|.include?(v.to_s[0]) or (v.kind_of?(Numeric))
175
+ "#{k}:#{v}"
176
+ else
177
+ "#{k}:\"#{v}\""
178
+ end
179
+ end
180
+ end
181
+ ret = str_list.compact.join(' ')
182
+ ret.empty? ? nil : ret
183
+ end
184
+
185
+ # Log an error for the given exception. It does not log a RetryableError if
186
+ # retry_count is less than retry_alert_limit.
187
+ # It returns the original exception of a RetryableError it has one.
188
+ # This method requires method 'logger' which returns a Ruby Logger instance or
189
+ # a compatible one.
190
+ def log_retryable_error(exception, message = "unknown error occured.", retry_count = -1,
191
+ retry_alert_limit = 13)
192
+ is_retryable_error = false
193
+ if (exception.kind_of?(FlydataCore::RetryableError))
194
+ is_retryable_error = true
195
+ retry_alert_limit = exception.retry_alert_limit unless exception.retry_alert_limit.nil?
196
+ exception = exception.original_exception unless exception.original_exception.nil?
197
+ end
198
+
199
+ unless is_retryable_error && retry_count < retry_alert_limit
200
+ log_error_with_backtrace(message, retry_count: retry_count, error: exception)
201
+ end
202
+ exception
203
+ end
204
+ end
205
+ end