flydata 0.2.17 → 0.2.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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