aliyun-log 0.2.0 → 0.2.10
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/lib/aliyun/log/common/logging.rb +1 -1
- data/lib/aliyun/log/config.rb +2 -2
- data/lib/aliyun/log/logstore.rb +1 -7
- data/lib/aliyun/log/logtail.rb +13 -0
- data/lib/aliyun/log/protocol.rb +3 -3
- data/lib/aliyun/log/record.rb +11 -26
- data/lib/aliyun/log/record/exception.rb +1 -0
- data/lib/aliyun/log/record/field.rb +11 -10
- data/lib/aliyun/log/record/persistence.rb +37 -13
- data/lib/aliyun/log/record/relation.rb +177 -10
- data/lib/aliyun/log/record/scope_registry.rb +40 -0
- data/lib/aliyun/log/record/scoping.rb +55 -0
- data/lib/aliyun/log/record/type_casting.rb +191 -0
- data/lib/aliyun/log/request.rb +1 -1
- data/lib/aliyun/version.rb +1 -1
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b0d96cc9d1e5e1a2bee7cdcbca04043f3a80e8b733b1be820aac363b31f8ec34
|
4
|
+
data.tar.gz: 2769ddbdd8d54f257e02d12813d207fbb920c50659977a42527d6f714b45b818
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2a3c0526d83176a4b34e111744ebac7cd0543fc1396462f0299dcda7c43ce31b12fb7b8ebb08898a08edddaa684ea16e3288d1985add5f35eaf61cc8ca4222e8
|
7
|
+
data.tar.gz: d658a42dd7aff881cb7cc9b31374af83417533185cca1c3e4b8763ec4c52bd85bd22749f4b5130dede0b42fdca0ec84663db469c5a9893d126cd9b49972fb198
|
data/lib/aliyun/log/config.rb
CHANGED
@@ -7,8 +7,8 @@ module Aliyun
|
|
7
7
|
@endpoint = 'https://cn-beijing.log.aliyuncs.com'
|
8
8
|
@open_timeout = 10
|
9
9
|
@read_timeout = 120
|
10
|
-
@
|
11
|
-
@
|
10
|
+
@log_level = Logger::WARN
|
11
|
+
@log_file = STDOUT
|
12
12
|
@timestamps = true
|
13
13
|
class << self
|
14
14
|
attr_accessor :endpoint, :access_key_id, :access_key_secret,
|
data/lib/aliyun/log/logstore.rb
CHANGED
@@ -30,13 +30,7 @@ module Aliyun
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def put_log(attributes)
|
33
|
-
|
34
|
-
log = Aliyun::Log::Protobuf::Log.new(
|
35
|
-
time: Time.now.to_i,
|
36
|
-
contents: contents
|
37
|
-
)
|
38
|
-
log_group = Aliyun::Log::Protobuf::LogGroup.new(logs: [log])
|
39
|
-
put_logs(log_group)
|
33
|
+
@protocol.put_log(project_name, name, attributes)
|
40
34
|
end
|
41
35
|
|
42
36
|
def get_logs(opts = {})
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Aliyun
|
2
|
+
module Log
|
3
|
+
class LogTail < Common::AttrStruct
|
4
|
+
attrs :name, :log_type, :log_path, :file_pattern, :localstore, :time_format,
|
5
|
+
:log_begin_regex, :regex, :key, :topic_format,
|
6
|
+
:filterKey, :filter_regex, :file_encoding
|
7
|
+
def initialize(opts, protocol)
|
8
|
+
super(opts)
|
9
|
+
@protocol = protocol
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/lib/aliyun/log/protocol.rb
CHANGED
@@ -108,7 +108,7 @@ module Aliyun
|
|
108
108
|
def build_log_pb(attrs, time = Time.now.to_i)
|
109
109
|
logs = attrs.is_a?(Array) ? attrs : [attrs]
|
110
110
|
logs.map do |log_attr|
|
111
|
-
contents = log_attr.
|
111
|
+
contents = log_attr.map { |k, v| { key: k, value: v.to_s } }
|
112
112
|
Protobuf::Log.new(time: time, contents: contents)
|
113
113
|
end
|
114
114
|
end
|
@@ -182,7 +182,7 @@ module Aliyun
|
|
182
182
|
keys: {}
|
183
183
|
}
|
184
184
|
fields.each do |k, v|
|
185
|
-
v[:token] = INDEX_DEFAULT_TOKEN if %w[text json].include?(v[:type].to_s) && v[:token].
|
185
|
+
v[:token] = INDEX_DEFAULT_TOKEN if %w[text json].include?(v[:type].to_s) && v[:token].nil?
|
186
186
|
body[:keys][k] = v
|
187
187
|
end
|
188
188
|
@http.post({ project: project_name, logstore: logstore_name, action: 'index' }, body.to_json)
|
@@ -196,7 +196,7 @@ module Aliyun
|
|
196
196
|
keys: {}
|
197
197
|
}
|
198
198
|
fields.each do |k, v|
|
199
|
-
v[:token] = INDEX_DEFAULT_TOKEN if %w[text json].include?(v[:type].to_s) && v[:token].
|
199
|
+
v[:token] = INDEX_DEFAULT_TOKEN if %w[text json].include?(v[:type].to_s) && v[:token].nil?
|
200
200
|
body[:keys][k] = v
|
201
201
|
end
|
202
202
|
@http.put({ project: project_name, logstore: logstore_name, action: 'index' }, body.to_json)
|
data/lib/aliyun/log/record.rb
CHANGED
@@ -9,7 +9,7 @@ require 'forwardable'
|
|
9
9
|
require_relative 'record/exception'
|
10
10
|
require_relative 'record/field'
|
11
11
|
require_relative 'record/persistence'
|
12
|
-
require_relative 'record/
|
12
|
+
require_relative 'record/scoping'
|
13
13
|
|
14
14
|
module Aliyun
|
15
15
|
module Log
|
@@ -27,45 +27,30 @@ module Aliyun
|
|
27
27
|
|
28
28
|
Log.included_models << self unless Log.included_models.include? self
|
29
29
|
|
30
|
-
field :created_at, :text if Config.timestamps
|
30
|
+
field :created_at, type: :text, cast_type: :datetime if Config.timestamps
|
31
31
|
|
32
32
|
define_model_callbacks :save, :create, :initialize
|
33
33
|
|
34
|
-
|
34
|
+
before_save :set_created_at
|
35
35
|
end
|
36
36
|
|
37
37
|
include Field
|
38
38
|
include Persistence
|
39
|
+
include Scoping
|
39
40
|
|
40
41
|
include ActiveModel::AttributeMethods
|
41
42
|
|
42
43
|
module ClassMethods
|
43
44
|
def logstore(options = {})
|
44
|
-
|
45
|
+
opt = options.dup
|
46
|
+
if opt[:timestamps] && !Config.timestamps
|
45
47
|
field :created_at, :text
|
46
|
-
elsif
|
48
|
+
elsif opt[:timestamps] == false && Config.timestamps
|
47
49
|
remove_field :created_at
|
48
50
|
end
|
49
|
-
self._schema_load = true if
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
Relation.instance_methods(false).each do |method|
|
54
|
-
define_method(method) do |args|
|
55
|
-
relation.public_send(method, args)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
%i[count load result].each do |method|
|
60
|
-
define_method(method) do
|
61
|
-
relation.public_send(method)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
private
|
66
|
-
|
67
|
-
def relation
|
68
|
-
Relation.new(self)
|
51
|
+
self._schema_load = true if opt[:auto_sync] == false
|
52
|
+
opt[:field_doc_value] = opt[:field_doc_value] != false
|
53
|
+
self.options = opt
|
69
54
|
end
|
70
55
|
end
|
71
56
|
|
@@ -101,7 +86,7 @@ module Aliyun
|
|
101
86
|
end.compact.join(', ')
|
102
87
|
else
|
103
88
|
'not initialized'
|
104
|
-
|
89
|
+
end
|
105
90
|
|
106
91
|
"#<#{self.class} #{inspection}>"
|
107
92
|
end
|
@@ -1,8 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
|
-
|
4
|
-
require "active_support/core_ext"
|
5
|
-
require "active_model"
|
2
|
+
|
3
|
+
require_relative 'type_casting'
|
6
4
|
|
7
5
|
module Aliyun
|
8
6
|
module Log
|
@@ -22,13 +20,16 @@ module Aliyun
|
|
22
20
|
|
23
21
|
included do
|
24
22
|
class_attribute :attributes, instance_accessor: false, default: {}
|
23
|
+
extend Common::Logging
|
25
24
|
end
|
26
25
|
|
27
26
|
module ClassMethods
|
28
|
-
def field(name,
|
27
|
+
def field(name, options = {})
|
28
|
+
type = options[:type] || :text
|
29
29
|
unless PERMITTED_KEY_TYPES.include?(type)
|
30
30
|
raise ArgumentError, "Field #{name} type(#{type}) error, key type only support text/long/double/json"
|
31
31
|
end
|
32
|
+
|
32
33
|
named = name.to_s
|
33
34
|
self.attributes = attributes.merge(name => { type: type }.merge(options))
|
34
35
|
|
@@ -36,7 +37,7 @@ module Aliyun
|
|
36
37
|
warn_about_method_overriding("#{named}=", name)
|
37
38
|
warn_about_method_overriding("#{named}?", name)
|
38
39
|
|
39
|
-
define_attribute_method(name)
|
40
|
+
define_attribute_method(name)
|
40
41
|
|
41
42
|
generated_methods.module_eval do
|
42
43
|
define_method(named) { read_attribute(named) }
|
@@ -64,7 +65,6 @@ module Aliyun
|
|
64
65
|
remove_method field
|
65
66
|
remove_method :"#{field}="
|
66
67
|
remove_method :"#{field}?"
|
67
|
-
remove_method :"#{field}_before_type_cast"
|
68
68
|
end
|
69
69
|
end
|
70
70
|
|
@@ -80,7 +80,8 @@ module Aliyun
|
|
80
80
|
|
81
81
|
def warn_about_method_overriding(method_name, field_name)
|
82
82
|
if instance_methods.include?(method_name.to_sym)
|
83
|
-
|
83
|
+
logger.warn("Method #{method_name} generated for the field #{field_name} " \
|
84
|
+
'overrides already existing method')
|
84
85
|
end
|
85
86
|
end
|
86
87
|
end
|
@@ -106,7 +107,7 @@ module Aliyun
|
|
106
107
|
attr_accessor :attributes
|
107
108
|
|
108
109
|
def write_attribute(name, value)
|
109
|
-
attributes[name.to_sym] = value
|
110
|
+
attributes[name.to_sym] = TypeCasting.cast_field(value, self.class.attributes[name.to_sym])
|
110
111
|
end
|
111
112
|
|
112
113
|
alias []= write_attribute
|
@@ -117,7 +118,7 @@ module Aliyun
|
|
117
118
|
alias [] read_attribute
|
118
119
|
|
119
120
|
def set_created_at
|
120
|
-
self.created_at ||= DateTime.now.in_time_zone(Time.zone).
|
121
|
+
self.created_at ||= DateTime.now.in_time_zone(Time.zone).iso8601 if timestamps_enabled?
|
121
122
|
end
|
122
123
|
|
123
124
|
def timestamps_enabled?
|
@@ -8,7 +8,8 @@ module Aliyun
|
|
8
8
|
|
9
9
|
module ClassMethods
|
10
10
|
def logstore_name
|
11
|
-
@logstore_name ||= options[:name] ||
|
11
|
+
@logstore_name ||= options[:name] ||
|
12
|
+
base_class.name.split('::').last.underscore.pluralize
|
12
13
|
end
|
13
14
|
|
14
15
|
def logstore_name=(value)
|
@@ -55,9 +56,8 @@ module Aliyun
|
|
55
56
|
def create(data, opts = {})
|
56
57
|
auto_load_schema
|
57
58
|
if data.is_a?(Array)
|
58
|
-
logs =
|
59
|
-
|
60
|
-
logs << new(log_attr).save_array
|
59
|
+
logs = data.map do |log_attr|
|
60
|
+
new(log_attr).save_array
|
61
61
|
end
|
62
62
|
res = Log.record_connection.put_log(project_name, logstore_name, logs, opts)
|
63
63
|
res.code == 200
|
@@ -79,10 +79,10 @@ module Aliyun
|
|
79
79
|
end
|
80
80
|
|
81
81
|
def field_indices
|
82
|
-
if options[:field_index] ==
|
83
|
-
attributes.reject { |_, value| value[:index] == false }
|
84
|
-
else
|
82
|
+
if options[:field_index] == false
|
85
83
|
attributes.select { |_, value| value[:index] == true }
|
84
|
+
else
|
85
|
+
attributes.reject { |_, value| value[:index] == false }
|
86
86
|
end
|
87
87
|
end
|
88
88
|
|
@@ -97,18 +97,38 @@ module Aliyun
|
|
97
97
|
def update_index
|
98
98
|
conf_res = Log.record_connection.get_index(project_name, logstore_name)
|
99
99
|
raw_conf = JSON.parse(conf_res)
|
100
|
-
index_conf = raw_conf.
|
101
|
-
|
102
|
-
index_conf['keys'][k.to_s] ||=
|
100
|
+
index_conf = raw_conf.deep_dup
|
101
|
+
field_index_types.each do |k, v|
|
102
|
+
index_conf['keys'][k.to_s] ||= {}
|
103
|
+
index_conf['keys'][k.to_s].merge!(v.as_json)
|
103
104
|
end
|
104
105
|
return if index_conf['keys'] == raw_conf['keys']
|
105
106
|
|
106
107
|
Log.record_connection.update_index(
|
107
108
|
project_name,
|
108
109
|
logstore_name,
|
109
|
-
index_conf['keys']
|
110
|
+
index_conf['keys'].with_indifferent_access
|
110
111
|
)
|
111
112
|
end
|
113
|
+
|
114
|
+
def field_index_types
|
115
|
+
field_indices.tap do |tap|
|
116
|
+
tap.each do |_, v|
|
117
|
+
v[:alias] ||= ''
|
118
|
+
v[:caseSensitive] ||= false
|
119
|
+
v[:chn] ||= false
|
120
|
+
v[:doc_value] = options[:field_doc_value] != false if v[:doc_value].nil?
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def dump_attributes
|
127
|
+
attributes.dup.tap do |tap|
|
128
|
+
tap.each do |k, v|
|
129
|
+
tap[k] = TypeCasting.dump_field(v, self.class.attributes[k])
|
130
|
+
end
|
131
|
+
end
|
112
132
|
end
|
113
133
|
|
114
134
|
def save
|
@@ -116,7 +136,11 @@ module Aliyun
|
|
116
136
|
run_callbacks(:create) do
|
117
137
|
run_callbacks(:save) do
|
118
138
|
if valid?
|
119
|
-
res = Log.record_connection.put_log(
|
139
|
+
res = Log.record_connection.put_log(
|
140
|
+
self.class.project_name,
|
141
|
+
self.class.logstore_name,
|
142
|
+
dump_attributes
|
143
|
+
)
|
120
144
|
res.code == 200
|
121
145
|
else
|
122
146
|
false
|
@@ -128,7 +152,7 @@ module Aliyun
|
|
128
152
|
def save_array
|
129
153
|
run_callbacks(:create) do
|
130
154
|
run_callbacks(:save) do
|
131
|
-
validate! &&
|
155
|
+
validate! && dump_attributes
|
132
156
|
end
|
133
157
|
end
|
134
158
|
end
|
@@ -8,7 +8,49 @@ module Aliyun
|
|
8
8
|
@klass = klass
|
9
9
|
@opts = opts
|
10
10
|
@klass.auto_load_schema
|
11
|
-
|
11
|
+
end
|
12
|
+
|
13
|
+
def inspect
|
14
|
+
"#<#{self.class}>"
|
15
|
+
end
|
16
|
+
|
17
|
+
def first(line = 1)
|
18
|
+
find_offset(0, line, false)
|
19
|
+
end
|
20
|
+
|
21
|
+
def second
|
22
|
+
find_offset(1)
|
23
|
+
end
|
24
|
+
|
25
|
+
def third
|
26
|
+
find_offset(2)
|
27
|
+
end
|
28
|
+
|
29
|
+
def fourth
|
30
|
+
find_offset(3)
|
31
|
+
end
|
32
|
+
|
33
|
+
def fifth
|
34
|
+
find_offset(4)
|
35
|
+
end
|
36
|
+
|
37
|
+
def last(line = 1)
|
38
|
+
find_offset(0, line, true)
|
39
|
+
end
|
40
|
+
|
41
|
+
def find_offset(nth, line = 1, reverse = false)
|
42
|
+
@opts[:line] = line
|
43
|
+
@opts[:offset] = nth
|
44
|
+
@opts[:reverse] = reverse
|
45
|
+
line <= 1 ? load[0] : load
|
46
|
+
end
|
47
|
+
|
48
|
+
def scoping
|
49
|
+
previous = @klass.current_scope
|
50
|
+
@klass.current_scope = self
|
51
|
+
yield
|
52
|
+
ensure
|
53
|
+
@klass.current_scope = previous
|
12
54
|
end
|
13
55
|
|
14
56
|
def from(from)
|
@@ -44,13 +86,18 @@ module Aliyun
|
|
44
86
|
self
|
45
87
|
end
|
46
88
|
|
47
|
-
def search(
|
48
|
-
|
89
|
+
def search(*statement)
|
90
|
+
ql = statement_ql(*statement)
|
91
|
+
@opts[:search] = ql if ql.present?
|
49
92
|
self
|
50
93
|
end
|
51
94
|
|
52
|
-
def sql(
|
53
|
-
|
95
|
+
def sql(*statement)
|
96
|
+
unless statement[0].is_a?(String)
|
97
|
+
raise ParseStatementInvalid, 'Only support string statement'
|
98
|
+
end
|
99
|
+
ql = sanitize_array(*statement)
|
100
|
+
@opts[:sql] = ql if ql.present?
|
54
101
|
self
|
55
102
|
end
|
56
103
|
|
@@ -77,7 +124,7 @@ module Aliyun
|
|
77
124
|
query[:line] ||= 100
|
78
125
|
query[:offset] = query[:page] * query[:line]
|
79
126
|
end
|
80
|
-
query[:query] = query[:search]
|
127
|
+
query[:query] = query[:search] || '*'
|
81
128
|
query[:query] = "#{query[:query]}|#{query[:sql]}" if query[:sql].present?
|
82
129
|
res = Log.record_connection.get_logs(@klass.project_name, @klass.logstore_name, query)
|
83
130
|
JSON.parse(res)
|
@@ -85,11 +132,131 @@ module Aliyun
|
|
85
132
|
|
86
133
|
def load
|
87
134
|
result.map do |json_attr|
|
88
|
-
|
89
|
-
|
90
|
-
|
135
|
+
record = @klass.new
|
136
|
+
json_attr.each do |key, value|
|
137
|
+
record.send("#{key}=", value) if record.respond_to?("#{key}=")
|
138
|
+
end
|
139
|
+
record
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
private
|
144
|
+
|
145
|
+
def statement_ql(*statement)
|
146
|
+
if statement.size == 1
|
147
|
+
sanitize_hash(statement.first)
|
148
|
+
elsif statement.size > 1
|
149
|
+
sanitize_array(*statement)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def sanitize_hash(search_content)
|
154
|
+
return search_content unless search_content.is_a?(Hash)
|
155
|
+
|
156
|
+
search_content.select { |_, v| v.present? }.map do |key, value|
|
157
|
+
options = @klass.attributes[:"#{key}"]
|
158
|
+
unless options
|
159
|
+
raise UnknownAttributeError, "unknown field '#{key}' for #{@klass.name}."
|
160
|
+
end
|
161
|
+
|
162
|
+
raise_if_hash_quote(value)
|
163
|
+
|
164
|
+
cast_type = options[:cast_type]
|
165
|
+
if value.is_a?(Array)
|
166
|
+
values = value.uniq.map { |v| _quote(cast_type, v) }
|
167
|
+
str_values = values.map { |v| "#{key}: #{v}" }.join(' OR ')
|
168
|
+
values.size > 1 ? "(#{str_values})" : str_values
|
169
|
+
elsif value.is_a?(Range)
|
170
|
+
"#{key} in [#{value.begin} #{value.end}]"
|
171
|
+
else
|
172
|
+
"#{key}: #{_quote(cast_type, value)}"
|
173
|
+
end
|
174
|
+
end.join(' AND ')
|
175
|
+
end
|
176
|
+
|
177
|
+
def sanitize_array(*ary)
|
178
|
+
statement, *values = ary
|
179
|
+
if values.first.is_a?(Hash) && /:\w+/.match?(statement)
|
180
|
+
replace_named_bind_variables(statement, values.first)
|
181
|
+
elsif statement.include?('?')
|
182
|
+
replace_bind_variables(statement, values)
|
183
|
+
elsif statement.blank? || values.blank?
|
184
|
+
statement
|
185
|
+
else
|
186
|
+
statement % values.collect(&:to_s)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def replace_named_bind_variables(statement, bind_vars)
|
191
|
+
statement.gsub(/(:?):([a-zA-Z]\w*)/) do |match|
|
192
|
+
if bind_vars.include?(match = Regexp.last_match(2).to_sym)
|
193
|
+
match_value = bind_vars[match]
|
194
|
+
raise_if_hash_quote(match_value)
|
195
|
+
if match_value.is_a?(Array) || match_value.is_a?(Range)
|
196
|
+
values = match_value.map { |v| _quote_type_value(v) }
|
197
|
+
values.join(', ')
|
198
|
+
else
|
199
|
+
_quote_type_value(match_value)
|
200
|
+
end
|
201
|
+
else
|
202
|
+
raise ParseStatementInvalid, "missing value for :#{match} in #{statement}"
|
91
203
|
end
|
92
|
-
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def replace_bind_variables(statement, values)
|
208
|
+
expected = statement.count('?')
|
209
|
+
provided = values.size
|
210
|
+
if expected != provided
|
211
|
+
raise ParseStatementInvalid, "wrong number of bind variables (#{provided} " \
|
212
|
+
"for #{expected}) in: #{statement}"
|
213
|
+
end
|
214
|
+
bound = values.dup
|
215
|
+
statement.gsub(/\?/) do
|
216
|
+
value = bound.shift
|
217
|
+
raise_if_hash_quote(value)
|
218
|
+
if value.is_a?(Array) || value.is_a?(Range)
|
219
|
+
values = value.map { |v| _quote_type_value(v) }
|
220
|
+
values.join(', ')
|
221
|
+
else
|
222
|
+
_quote_type_value(value)
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def _quote(type, value)
|
228
|
+
v = TypeCasting.dump_field(value, cast_type: type || :string)
|
229
|
+
case type
|
230
|
+
when :string, nil then "'#{v.to_s}'"
|
231
|
+
when :bigdecimal then v.to_s("F")
|
232
|
+
when :integer then v.to_s.to_i
|
233
|
+
when :datetime, :date then "'#{v.iso8601}'"
|
234
|
+
else
|
235
|
+
value.to_s
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
def _quote_type_value(value)
|
240
|
+
case value.class.name
|
241
|
+
when 'String' then "'#{value.to_s}'"
|
242
|
+
when 'BigDecimal' then value.to_s("F")
|
243
|
+
when 'Date', 'DateTime', 'Time' then "'#{value.iso8601}'"
|
244
|
+
else
|
245
|
+
value
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
def raise_if_hash_quote(value)
|
250
|
+
if value.is_a?(Hash) || value.is_a?(ActiveSupport::HashWithIndifferentAccess)
|
251
|
+
raise ParseStatementInvalid, "can't quote Hash"
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
def method_missing(method, *args, &block)
|
256
|
+
if @klass.respond_to?(method)
|
257
|
+
scoping { @klass.public_send(method, *args, &block) }
|
258
|
+
else
|
259
|
+
super
|
93
260
|
end
|
94
261
|
end
|
95
262
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Aliyun
|
4
|
+
module Log
|
5
|
+
module PerThreadRegistry
|
6
|
+
def self.extended(object)
|
7
|
+
object.instance_variable_set '@per_thread_registry_key', object.name.freeze
|
8
|
+
end
|
9
|
+
|
10
|
+
def instance
|
11
|
+
Thread.current[@per_thread_registry_key] ||= new
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def method_missing(name, *args, &block)
|
17
|
+
singleton_class.delegate name, to: :instance
|
18
|
+
|
19
|
+
send(name, *args, &block)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class ScopeRegistry
|
24
|
+
extend PerThreadRegistry
|
25
|
+
|
26
|
+
def initialize
|
27
|
+
@registry = Hash.new { |hash, key| hash[key] = {} }
|
28
|
+
end
|
29
|
+
|
30
|
+
def value_for(scope_type, model)
|
31
|
+
@registry[scope_type][model.name]
|
32
|
+
end
|
33
|
+
|
34
|
+
# Sets the +value+ for a given +scope_type+ and +model+.
|
35
|
+
def set_value_for(scope_type, model, value)
|
36
|
+
@registry[scope_type][model.name] = value
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'scope_registry'
|
4
|
+
require_relative 'relation'
|
5
|
+
|
6
|
+
module Aliyun
|
7
|
+
module Log
|
8
|
+
module Record
|
9
|
+
module Scoping
|
10
|
+
extend ActiveSupport::Concern
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
delegate :load, :result, :count, to: :all
|
14
|
+
delegate :where, :query, :search, :sql, :from, :to,
|
15
|
+
:page, :line, :limit, :offset, to: :all
|
16
|
+
delegate :first, :last, :second, :third, :fourth, :fifth, :find_offset, to: :all
|
17
|
+
|
18
|
+
def current_scope
|
19
|
+
ScopeRegistry.value_for(:current_scope, self)
|
20
|
+
end
|
21
|
+
|
22
|
+
def current_scope=(scope)
|
23
|
+
ScopeRegistry.set_value_for(:current_scope, self, scope)
|
24
|
+
end
|
25
|
+
|
26
|
+
def scope(name, body)
|
27
|
+
raise ArgumentError, 'The scope body needs to be callable.' unless body.respond_to?(:call)
|
28
|
+
|
29
|
+
singleton_class.send(:define_method, name) do |*args|
|
30
|
+
scope = all
|
31
|
+
scope = scope.scoping { body.call(*args) }
|
32
|
+
scope
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def unscoped
|
37
|
+
block_given? ? relation.scoping { yield } : relation
|
38
|
+
end
|
39
|
+
|
40
|
+
def all
|
41
|
+
scope = current_scope
|
42
|
+
scope ||= relation.from(0).to(Time.now.to_i)
|
43
|
+
scope
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def relation
|
49
|
+
Relation.new(self)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,191 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Aliyun
|
6
|
+
module Log
|
7
|
+
module Record
|
8
|
+
module TypeCasting
|
9
|
+
TYPE_MAPPING = {
|
10
|
+
text: :string,
|
11
|
+
long: :integer,
|
12
|
+
double: :bigdecimal,
|
13
|
+
json: :json
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
def self.cast_field(value, options)
|
17
|
+
options ||= {}
|
18
|
+
type = options[:cast_type]
|
19
|
+
type ||= TYPE_MAPPING[options[:type]]
|
20
|
+
|
21
|
+
return value if options.nil?
|
22
|
+
return nil if value.nil?
|
23
|
+
|
24
|
+
caster = Registry.lookup(type)
|
25
|
+
raise ArgumentError, "Unknown type #{options[:type]}" if caster.nil?
|
26
|
+
|
27
|
+
caster.new(options).cast(value)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.dump_field(value, options)
|
31
|
+
options ||= {}
|
32
|
+
type = options[:cast_type]
|
33
|
+
type ||= TYPE_MAPPING[options[:type]]
|
34
|
+
|
35
|
+
return value if options.nil?
|
36
|
+
return nil if value.nil?
|
37
|
+
|
38
|
+
dumper = Registry.lookup(type)
|
39
|
+
raise ArgumentError, "Unknown type #{options[:type]}" if dumper.nil?
|
40
|
+
|
41
|
+
dumper.new(options).dump(value)
|
42
|
+
end
|
43
|
+
|
44
|
+
class Value
|
45
|
+
def initialize(options)
|
46
|
+
@options = options
|
47
|
+
end
|
48
|
+
|
49
|
+
def cast(value)
|
50
|
+
value
|
51
|
+
end
|
52
|
+
|
53
|
+
def dump(value)
|
54
|
+
value
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class StringType < Value; end
|
59
|
+
|
60
|
+
class DateType < Value
|
61
|
+
def cast(value)
|
62
|
+
return nil unless value.respond_to?(:to_date)
|
63
|
+
|
64
|
+
value.to_date
|
65
|
+
end
|
66
|
+
|
67
|
+
def dump(value)
|
68
|
+
if value.respond_to?(:to_date)
|
69
|
+
value.to_date.to_s
|
70
|
+
else
|
71
|
+
value.to_s
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
class DateTimeType < Value
|
77
|
+
def cast(value)
|
78
|
+
return nil unless value.respond_to?(:to_datetime)
|
79
|
+
|
80
|
+
dt = begin
|
81
|
+
::DateTime.parse(value)
|
82
|
+
rescue StandardError
|
83
|
+
nil
|
84
|
+
end
|
85
|
+
if dt
|
86
|
+
seconds = string_utc_offset(value) || 0
|
87
|
+
offset = seconds_to_offset(seconds)
|
88
|
+
::DateTime.new(dt.year, dt.mon, dt.mday, dt.hour, dt.min, dt.sec, offset)
|
89
|
+
else
|
90
|
+
value.to_datetime
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def dump(value)
|
95
|
+
if value.respond_to?(:to_datetime)
|
96
|
+
value.to_datetime.iso8601
|
97
|
+
else
|
98
|
+
value.to_s
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def string_utc_offset(string)
|
105
|
+
Date._parse(string)[:offset]
|
106
|
+
end
|
107
|
+
|
108
|
+
def seconds_to_offset(seconds)
|
109
|
+
ActiveSupport::TimeZone.seconds_to_utc_offset(seconds)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
class IntegerType < Value
|
114
|
+
def cast(value)
|
115
|
+
if value == true
|
116
|
+
1
|
117
|
+
elsif value == false
|
118
|
+
0
|
119
|
+
elsif value.is_a?(String) && value.blank?
|
120
|
+
nil
|
121
|
+
elsif value.is_a?(Float) && !value.finite?
|
122
|
+
nil
|
123
|
+
elsif !value.respond_to?(:to_i)
|
124
|
+
nil
|
125
|
+
else
|
126
|
+
value.to_i
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
class BigDecimalType < Value
|
132
|
+
def cast(value)
|
133
|
+
if value == true
|
134
|
+
1
|
135
|
+
elsif value == false
|
136
|
+
0
|
137
|
+
elsif value.is_a?(Symbol)
|
138
|
+
value.to_s.to_d
|
139
|
+
elsif value.is_a?(String) && value.blank?
|
140
|
+
nil
|
141
|
+
elsif value.is_a?(Float) && !value.finite?
|
142
|
+
nil
|
143
|
+
elsif !value.respond_to?(:to_d)
|
144
|
+
nil
|
145
|
+
else
|
146
|
+
value.to_d
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
class JsonType < Value
|
152
|
+
def cast(value)
|
153
|
+
return value unless value.is_a?(String)
|
154
|
+
|
155
|
+
begin
|
156
|
+
ActiveSupport::JSON.decode(value)
|
157
|
+
rescue StandardError
|
158
|
+
nil
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def dump(value)
|
163
|
+
ActiveSupport::JSON.encode(value) unless value.nil?
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
module Registry
|
168
|
+
module_function
|
169
|
+
|
170
|
+
@registrations = {}
|
171
|
+
|
172
|
+
def register(name, klass = nil)
|
173
|
+
@registrations[name.to_sym] = klass
|
174
|
+
end
|
175
|
+
|
176
|
+
def lookup(name)
|
177
|
+
name ||= :string
|
178
|
+
@registrations[name.to_sym]
|
179
|
+
end
|
180
|
+
|
181
|
+
register(:string, StringType)
|
182
|
+
register(:date, DateType)
|
183
|
+
register(:datetime, DateTimeType)
|
184
|
+
register(:bigdecimal, BigDecimalType)
|
185
|
+
register(:integer, IntegerType)
|
186
|
+
register(:json, JsonType)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
data/lib/aliyun/log/request.rb
CHANGED
data/lib/aliyun/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: aliyun-log
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yingce Liu
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-06-
|
11
|
+
date: 2020-06-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -114,7 +114,7 @@ dependencies:
|
|
114
114
|
- - "~>"
|
115
115
|
- !ruby/object:Gem::Version
|
116
116
|
version: '3.0'
|
117
|
-
description: Aliyun Log SDK for Ruby 阿里云日志服务(SLS) Ruby SDK, 目前仅实现基于Restfull
|
117
|
+
description: Aliyun Log SDK for Ruby 阿里云日志服务(SLS) Ruby SDK, 目前仅实现基于Restfull部分接口和简单Model映射
|
118
118
|
email:
|
119
119
|
- yingce@live.com
|
120
120
|
executables: []
|
@@ -128,6 +128,7 @@ files:
|
|
128
128
|
- lib/aliyun/log/common/logging.rb
|
129
129
|
- lib/aliyun/log/config.rb
|
130
130
|
- lib/aliyun/log/logstore.rb
|
131
|
+
- lib/aliyun/log/logtail.rb
|
131
132
|
- lib/aliyun/log/project.rb
|
132
133
|
- lib/aliyun/log/protobuf.rb
|
133
134
|
- lib/aliyun/log/protocol.rb
|
@@ -136,6 +137,9 @@ files:
|
|
136
137
|
- lib/aliyun/log/record/field.rb
|
137
138
|
- lib/aliyun/log/record/persistence.rb
|
138
139
|
- lib/aliyun/log/record/relation.rb
|
140
|
+
- lib/aliyun/log/record/scope_registry.rb
|
141
|
+
- lib/aliyun/log/record/scoping.rb
|
142
|
+
- lib/aliyun/log/record/type_casting.rb
|
139
143
|
- lib/aliyun/log/request.rb
|
140
144
|
- lib/aliyun/log/server_error.rb
|
141
145
|
- lib/aliyun/version.rb
|