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.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/flydata-core/.gitignore +16 -0
- data/flydata-core/.rspec +1 -0
- data/flydata-core/.travis.yml +6 -0
- data/flydata-core/Gemfile +11 -0
- data/flydata-core/Gemfile.lock +51 -0
- data/flydata-core/lib/flydata-core/core_ext/module/include.rb +5 -0
- data/flydata-core/lib/flydata-core/core_ext/module.rb +1 -0
- data/flydata-core/lib/flydata-core/core_ext/object/prepend.rb +17 -0
- data/flydata-core/lib/flydata-core/core_ext/object.rb +1 -0
- data/flydata-core/lib/flydata-core/core_ext.rb +3 -0
- data/flydata-core/lib/flydata-core/errors.rb +334 -0
- data/flydata-core/lib/flydata-core/logger.rb +205 -0
- data/{lib/flydata → flydata-core/lib/flydata-core}/table_def/mysql_table_def.rb +6 -8
- data/{lib/flydata → flydata-core/lib/flydata-core}/table_def/redshift_table_def.rb +17 -2
- data/flydata-core/lib/flydata-core/table_def.rb +2 -0
- data/flydata-core/lib/flydata-core/thread_context.rb +31 -0
- data/flydata-core/lib/flydata-core.rb +1 -0
- data/flydata-core/spec/spec_helper.rb +2 -0
- data/{spec/flydata → flydata-core/spec}/table_def/mysql_table_def_spec.rb +22 -8
- data/flydata-core/spec/table_def/mysql_to_redshift_table_def_spec.rb +428 -0
- data/flydata-core/spec/table_def/mysqldump_test_bit_table.dump +51 -0
- data/{spec/flydata → flydata-core/spec}/table_def/mysqldump_test_foreign_key.dump +0 -0
- data/{spec/flydata → flydata-core/spec}/table_def/mysqldump_test_table_all.dump +0 -0
- data/{spec/flydata → flydata-core/spec}/table_def/mysqldump_test_table_column_comment.dump +0 -0
- data/{spec/flydata → flydata-core/spec}/table_def/mysqldump_test_table_enum.dump +0 -0
- data/{spec/flydata → flydata-core/spec}/table_def/mysqldump_test_table_multi_pk.dump +0 -0
- data/{spec/flydata → flydata-core/spec}/table_def/mysqldump_test_table_no_pk.dump +0 -0
- data/{spec/flydata → flydata-core/spec}/table_def/mysqldump_test_unique_key.dump +0 -0
- data/{spec/flydata → flydata-core/spec}/table_def/mysqldump_test_unique_key2.dump +0 -0
- data/{spec/flydata → flydata-core/spec}/table_def/mysqldump_test_unique_key3.dump +0 -0
- data/{spec/flydata → flydata-core/spec}/table_def/mysqldump_test_unsigned.dump +0 -0
- data/{spec/flydata → flydata-core/spec}/table_def/redshift_table_def_spec.rb +63 -16
- data/flydata.gemspec +34 -18
- data/lib/flydata/command/sync.rb +11 -8
- data/lib/flydata/parser/mysql/mysql_alter_table.treetop +128 -18
- data/lib/flydata/parser_provider.rb +1 -1
- data/lib/flydata.rb +11 -1
- data/spec/flydata/parser/mysql/alter_table_parser_spec.rb +173 -2
- data/spec/spec_helper.rb +3 -1
- metadata +34 -18
- data/.gitignore +0 -49
- data/lib/flydata/table_def.rb +0 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e60423bcd2e535f420b6cffae38bb02d4c4183b8
|
4
|
+
data.tar.gz: eff1e3250db8d8e036c977f7c5c0fb50232d4fe2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e30e9303ae3bfa61ccd55eda8837c3606b6502e3523cd4d476068f4ac34b7814ed4fd4858e3af0327705d6f26d2a17d3ec205277acfd92844c919f15ecce15be
|
7
|
+
data.tar.gz: d26ad0a84abdeaac414f75c357a5a10f5b6c918659f7be55cd17603940c8d9bf76bbe67535f2ed75ce891723c01554a2c6d2bbcc167282cc0380b6ace81fd748
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2.
|
1
|
+
0.2.18
|
data/flydata-core/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
@@ -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 @@
|
|
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,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
|