aliyun-log 0.1.3 → 0.2.9
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.rb +23 -1
- data/lib/aliyun/log/common/logging.rb +1 -1
- data/lib/aliyun/log/config.rb +5 -3
- data/lib/aliyun/log/logstore.rb +1 -7
- data/lib/aliyun/log/protocol.rb +14 -7
- data/lib/aliyun/log/record.rb +107 -0
- data/lib/aliyun/log/record/exception.rb +10 -0
- data/lib/aliyun/log/record/field.rb +130 -0
- data/lib/aliyun/log/record/persistence.rb +162 -0
- data/lib/aliyun/log/record/relation.rb +265 -0
- 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 +7 -11
- data/lib/aliyun/version.rb +1 -1
- metadata +39 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0365cd8d407b5198a7e6ce3ea9e0aeac77f4af1954a7373a5817e3454a0d72ad
|
4
|
+
data.tar.gz: '0842353fac8ae79d4972e351437b8435137169eb41929085796bc70c30973d88'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 91b72a67855c9850b9b21eb1930f4d1c1118cb8068e3172e9ffd6db15e214003592671f1b5705b2b60f32abe458b4843683946bc837de8118d075855a7ba2cbd
|
7
|
+
data.tar.gz: 5926791f61bb63e367337c4bb7b8c284d3aa04d4c94f4ba9966aa54317db53ce75e5dbc4953d804088ec0cd96bfca25af88d18f7e828b3712b06316d457e9cd5
|
data/lib/aliyun/log.rb
CHANGED
@@ -9,11 +9,33 @@ require_relative 'log/protobuf'
|
|
9
9
|
require_relative 'log/protocol'
|
10
10
|
require_relative 'log/request'
|
11
11
|
require_relative 'log/server_error'
|
12
|
+
require_relative 'log/record'
|
12
13
|
|
13
14
|
module Aliyun
|
14
15
|
module Log
|
15
|
-
|
16
|
+
extend self
|
17
|
+
|
18
|
+
def configure
|
16
19
|
block_given? ? yield(Config) : Config
|
17
20
|
end
|
21
|
+
alias config configure
|
22
|
+
|
23
|
+
def included_models
|
24
|
+
@included_models ||= []
|
25
|
+
end
|
26
|
+
|
27
|
+
def record_connection
|
28
|
+
unless @record_connection
|
29
|
+
@record_connection = Protocol.new(Config.new)
|
30
|
+
end
|
31
|
+
@record_connection
|
32
|
+
end
|
33
|
+
|
34
|
+
def init_logstore
|
35
|
+
@included_models.each do |model|
|
36
|
+
model.create_logstore
|
37
|
+
modle.sync_index
|
38
|
+
end
|
39
|
+
end
|
18
40
|
end
|
19
41
|
end
|
data/lib/aliyun/log/config.rb
CHANGED
@@ -7,11 +7,13 @@ 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
|
+
@timestamps = true
|
12
13
|
class << self
|
13
14
|
attr_accessor :endpoint, :access_key_id, :access_key_secret,
|
14
|
-
:open_timeout, :read_timeout, :log_file, :log_level
|
15
|
+
:open_timeout, :read_timeout, :log_file, :log_level,
|
16
|
+
:timestamps, :project
|
15
17
|
|
16
18
|
def configure
|
17
19
|
yield self
|
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 = {})
|
data/lib/aliyun/log/protocol.rb
CHANGED
@@ -66,7 +66,7 @@ module Aliyun
|
|
66
66
|
|
67
67
|
def create_logstore(project_name, logstore_name, opt = {})
|
68
68
|
body = {
|
69
|
-
|
69
|
+
logstoreName: logstore_name,
|
70
70
|
ttl: opt[:ttl] || 365,
|
71
71
|
shardCount: opt[:shard_count] || 2,
|
72
72
|
autoSplit: opt[:auto_split].nil? ? false : opt[:auto_split],
|
@@ -105,10 +105,17 @@ module Aliyun
|
|
105
105
|
@http.post({ project: project_name, logstore: logstore_name, is_pb: true }, content)
|
106
106
|
end
|
107
107
|
|
108
|
-
def
|
109
|
-
|
110
|
-
|
111
|
-
|
108
|
+
def build_log_pb(attrs, time = Time.now.to_i)
|
109
|
+
logs = attrs.is_a?(Array) ? attrs : [attrs]
|
110
|
+
logs.map do |log_attr|
|
111
|
+
contents = log_attr.map { |k, v| { key: k, value: v.to_s } }
|
112
|
+
Protobuf::Log.new(time: time, contents: contents)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def put_log(project_name, logstore_name, logs, opts = {})
|
117
|
+
logs = build_log_pb(logs, opts[:time] || Time.now.to_i)
|
118
|
+
lg_pb = Protobuf::LogGroup.new(logs: logs, topic: opts[:topic])
|
112
119
|
@http.post({ project: project_name, logstore: logstore_name, is_pb: true }, lg_pb)
|
113
120
|
end
|
114
121
|
|
@@ -175,8 +182,8 @@ module Aliyun
|
|
175
182
|
keys: {}
|
176
183
|
}
|
177
184
|
fields.each do |k, v|
|
185
|
+
v[:token] = INDEX_DEFAULT_TOKEN if %w[text json].include?(v[:type].to_s) && v[:token].nil?
|
178
186
|
body[:keys][k] = v
|
179
|
-
v[:token] = INDEX_DEFAULT_TOKEN if %w[text json].include?(v[:type]) && v[:token].blank?
|
180
187
|
end
|
181
188
|
@http.post({ project: project_name, logstore: logstore_name, action: 'index' }, body.to_json)
|
182
189
|
end
|
@@ -189,8 +196,8 @@ module Aliyun
|
|
189
196
|
keys: {}
|
190
197
|
}
|
191
198
|
fields.each do |k, v|
|
199
|
+
v[:token] = INDEX_DEFAULT_TOKEN if %w[text json].include?(v[:type].to_s) && v[:token].nil?
|
192
200
|
body[:keys][k] = v
|
193
|
-
v[:token] = INDEX_DEFAULT_TOKEN if v[:type] == 'text' && v[:token].blank?
|
194
201
|
end
|
195
202
|
@http.put({ project: project_name, logstore: logstore_name, action: 'index' }, body.to_json)
|
196
203
|
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support'
|
4
|
+
require 'active_support/time'
|
5
|
+
require 'active_support/core_ext'
|
6
|
+
require 'active_model'
|
7
|
+
require 'forwardable'
|
8
|
+
|
9
|
+
require_relative 'record/exception'
|
10
|
+
require_relative 'record/field'
|
11
|
+
require_relative 'record/persistence'
|
12
|
+
require_relative 'record/scoping'
|
13
|
+
|
14
|
+
module Aliyun
|
15
|
+
module Log
|
16
|
+
module Record
|
17
|
+
extend ActiveSupport::Concern
|
18
|
+
|
19
|
+
included do
|
20
|
+
extend ActiveModel::Callbacks
|
21
|
+
include ActiveModel::Validations
|
22
|
+
|
23
|
+
class_attribute :options, instance_accessor: false, default: {}
|
24
|
+
class_attribute :base_class, instance_accessor: false, default: self
|
25
|
+
class_attribute :log_connection, instance_accessor: false
|
26
|
+
class_attribute :_schema_load, default: false
|
27
|
+
|
28
|
+
Log.included_models << self unless Log.included_models.include? self
|
29
|
+
|
30
|
+
field :created_at, type: :text, cast_type: :datetime if Config.timestamps
|
31
|
+
|
32
|
+
define_model_callbacks :save, :create, :initialize
|
33
|
+
|
34
|
+
before_save :set_created_at
|
35
|
+
end
|
36
|
+
|
37
|
+
include Field
|
38
|
+
include Persistence
|
39
|
+
include Scoping
|
40
|
+
|
41
|
+
include ActiveModel::AttributeMethods
|
42
|
+
|
43
|
+
module ClassMethods
|
44
|
+
def logstore(options = {})
|
45
|
+
opt = options.dup
|
46
|
+
if opt[:timestamps] && !Config.timestamps
|
47
|
+
field :created_at, :text
|
48
|
+
elsif opt[:timestamps] == false && Config.timestamps
|
49
|
+
remove_field :created_at
|
50
|
+
end
|
51
|
+
self._schema_load = true if opt[:auto_sync] == false
|
52
|
+
opt[:field_doc_value] = opt[:field_doc_value] != false
|
53
|
+
self.options = opt
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def initialize(attrs = {})
|
58
|
+
run_callbacks :initialize do
|
59
|
+
@new_record = true
|
60
|
+
@attributes ||= {}
|
61
|
+
|
62
|
+
attrs_with_defaults = self.class.attributes.each_with_object({}) do |(attribute, options), res|
|
63
|
+
res[attribute] = if attrs.key?(attribute)
|
64
|
+
attrs[attribute]
|
65
|
+
elsif options.key?(:default)
|
66
|
+
evaluate_default_value(options[:default])
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
attrs_virtual = attrs.slice(*(attrs.keys - self.class.attributes.keys))
|
71
|
+
|
72
|
+
attrs_with_defaults.merge(attrs_virtual).each do |key, value|
|
73
|
+
if respond_to?("#{key}=")
|
74
|
+
send("#{key}=", value)
|
75
|
+
else
|
76
|
+
raise UnknownAttributeError, "unknown attribute '#{key}' for #{@record.class}."
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def inspect
|
83
|
+
inspection = if defined?(@attributes) && @attributes
|
84
|
+
self.class.attributes.keys.collect do |name|
|
85
|
+
"#{name}: #{attribute_for_inspect(name)}" if has_attribute?(name)
|
86
|
+
end.compact.join(', ')
|
87
|
+
else
|
88
|
+
'not initialized'
|
89
|
+
end
|
90
|
+
|
91
|
+
"#<#{self.class} #{inspection}>"
|
92
|
+
end
|
93
|
+
|
94
|
+
def attribute_for_inspect(attr_name)
|
95
|
+
value = read_attribute(attr_name)
|
96
|
+
|
97
|
+
if value.is_a?(String) && value.length > 50
|
98
|
+
"#{value[0, 50]}...".inspect
|
99
|
+
elsif value.is_a?(Date) || value.is_a?(Time)
|
100
|
+
%("#{value.to_s(:db)}")
|
101
|
+
else
|
102
|
+
value.inspect
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'type_casting'
|
4
|
+
|
5
|
+
module Aliyun
|
6
|
+
module Log
|
7
|
+
module Record
|
8
|
+
module Field
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
|
11
|
+
# Types allowed in indexes:
|
12
|
+
PERMITTED_KEY_TYPES = %i[
|
13
|
+
text
|
14
|
+
long
|
15
|
+
double
|
16
|
+
json
|
17
|
+
].freeze
|
18
|
+
|
19
|
+
DEFAULT_INDEX_TOKEN = ", '\";=()[]{}?@&<>/:\n\t\r".split('')
|
20
|
+
|
21
|
+
included do
|
22
|
+
class_attribute :attributes, instance_accessor: false, default: {}
|
23
|
+
extend Common::Logging
|
24
|
+
end
|
25
|
+
|
26
|
+
module ClassMethods
|
27
|
+
def field(name, options = {})
|
28
|
+
type = options[:type] || :text
|
29
|
+
unless PERMITTED_KEY_TYPES.include?(type)
|
30
|
+
raise ArgumentError, "Field #{name} type(#{type}) error, key type only support text/long/double/json"
|
31
|
+
end
|
32
|
+
|
33
|
+
named = name.to_s
|
34
|
+
self.attributes = attributes.merge(name => { type: type }.merge(options))
|
35
|
+
|
36
|
+
warn_about_method_overriding(name, name)
|
37
|
+
warn_about_method_overriding("#{named}=", name)
|
38
|
+
warn_about_method_overriding("#{named}?", name)
|
39
|
+
|
40
|
+
define_attribute_method(name)
|
41
|
+
|
42
|
+
generated_methods.module_eval do
|
43
|
+
define_method(named) { read_attribute(named) }
|
44
|
+
define_method("#{named}?") do
|
45
|
+
value = read_attribute(named)
|
46
|
+
case value
|
47
|
+
when true then true
|
48
|
+
when false, nil then false
|
49
|
+
else
|
50
|
+
!value.nil?
|
51
|
+
end
|
52
|
+
end
|
53
|
+
define_method("#{named}=") { |value| write_attribute(named, value) }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def remove_field(field)
|
58
|
+
field = field.to_sym
|
59
|
+
attributes.delete(field) || raise('No such field')
|
60
|
+
|
61
|
+
undefine_attribute_methods
|
62
|
+
define_attribute_methods attributes.keys
|
63
|
+
|
64
|
+
generated_methods.module_eval do
|
65
|
+
remove_method field
|
66
|
+
remove_method :"#{field}="
|
67
|
+
remove_method :"#{field}?"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def generated_methods
|
74
|
+
@generated_methods ||= begin
|
75
|
+
Module.new.tap do |mod|
|
76
|
+
include(mod)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def warn_about_method_overriding(method_name, field_name)
|
82
|
+
if instance_methods.include?(method_name.to_sym)
|
83
|
+
logger.warn("Method #{method_name} generated for the field #{field_name} " \
|
84
|
+
'overrides already existing method')
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def attribute_names
|
90
|
+
@attributes.keys
|
91
|
+
end
|
92
|
+
|
93
|
+
def has_attribute?(attr_name)
|
94
|
+
@attributes.key?(attr_name.to_sym)
|
95
|
+
end
|
96
|
+
|
97
|
+
def evaluate_default_value(val)
|
98
|
+
if val.respond_to?(:call)
|
99
|
+
val.call
|
100
|
+
elsif val.duplicable?
|
101
|
+
val.dup
|
102
|
+
else
|
103
|
+
val
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
attr_accessor :attributes
|
108
|
+
|
109
|
+
def write_attribute(name, value)
|
110
|
+
attributes[name.to_sym] = TypeCasting.cast_field(value, self.class.attributes[name.to_sym])
|
111
|
+
end
|
112
|
+
|
113
|
+
alias []= write_attribute
|
114
|
+
|
115
|
+
def read_attribute(name)
|
116
|
+
attributes[name.to_sym]
|
117
|
+
end
|
118
|
+
alias [] read_attribute
|
119
|
+
|
120
|
+
def set_created_at
|
121
|
+
self.created_at ||= DateTime.now.in_time_zone(Time.zone).iso8601 if timestamps_enabled?
|
122
|
+
end
|
123
|
+
|
124
|
+
def timestamps_enabled?
|
125
|
+
self.class.options[:timestamps] || (self.class.options[:timestamps].nil? && Config.timestamps)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Aliyun
|
4
|
+
module Log
|
5
|
+
module Record
|
6
|
+
module Persistence
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def logstore_name
|
11
|
+
@logstore_name ||= options[:name] ||
|
12
|
+
base_class.name.split('::').last.underscore.pluralize
|
13
|
+
end
|
14
|
+
|
15
|
+
def logstore_name=(value)
|
16
|
+
if defined?(@logstore_name)
|
17
|
+
return if value == @logstore_name
|
18
|
+
end
|
19
|
+
|
20
|
+
@logstore_name = value
|
21
|
+
end
|
22
|
+
|
23
|
+
def project_name
|
24
|
+
unless @project_name
|
25
|
+
@project_name = options[:project] || Config.project
|
26
|
+
raise ProjectNameError, "project can't be empty" if @project_name.blank?
|
27
|
+
end
|
28
|
+
@project_name
|
29
|
+
end
|
30
|
+
|
31
|
+
def create_logstore(options = {})
|
32
|
+
Log.record_connection.get_logstore(project_name, logstore_name)
|
33
|
+
rescue ServerError => e
|
34
|
+
Log.record_connection.create_logstore(project_name, logstore_name, options)
|
35
|
+
end
|
36
|
+
|
37
|
+
def sync_index
|
38
|
+
has_index? ? update_index : create_index
|
39
|
+
end
|
40
|
+
|
41
|
+
def auto_load_schema
|
42
|
+
return if _schema_load
|
43
|
+
|
44
|
+
create_logstore
|
45
|
+
sync_index
|
46
|
+
self._schema_load = true
|
47
|
+
end
|
48
|
+
|
49
|
+
def has_index?
|
50
|
+
Log.record_connection.get_index(project_name, logstore_name)
|
51
|
+
true
|
52
|
+
rescue ServerError
|
53
|
+
false
|
54
|
+
end
|
55
|
+
|
56
|
+
def create(data, opts = {})
|
57
|
+
auto_load_schema
|
58
|
+
if data.is_a?(Array)
|
59
|
+
logs = data.map do |log_attr|
|
60
|
+
new(log_attr).save_array
|
61
|
+
end
|
62
|
+
res = Log.record_connection.put_log(project_name, logstore_name, logs, opts)
|
63
|
+
res.code == 200
|
64
|
+
else
|
65
|
+
new(data).save
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def evaluate_default_value(val)
|
72
|
+
if val.respond_to?(:call)
|
73
|
+
val.call
|
74
|
+
elsif val.duplicable?
|
75
|
+
val.dup
|
76
|
+
else
|
77
|
+
val
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def field_indices
|
82
|
+
if options[:field_index] == false
|
83
|
+
attributes.select { |_, value| value[:index] == true }
|
84
|
+
else
|
85
|
+
attributes.reject { |_, value| value[:index] == false }
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def create_index
|
90
|
+
Log.record_connection.create_index(
|
91
|
+
project_name,
|
92
|
+
logstore_name,
|
93
|
+
field_indices
|
94
|
+
)
|
95
|
+
end
|
96
|
+
|
97
|
+
def update_index
|
98
|
+
conf_res = Log.record_connection.get_index(project_name, logstore_name)
|
99
|
+
raw_conf = JSON.parse(conf_res)
|
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)
|
104
|
+
end
|
105
|
+
return if index_conf['keys'] == raw_conf['keys']
|
106
|
+
|
107
|
+
Log.record_connection.update_index(
|
108
|
+
project_name,
|
109
|
+
logstore_name,
|
110
|
+
index_conf['keys'].with_indifferent_access
|
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
|
132
|
+
end
|
133
|
+
|
134
|
+
def save
|
135
|
+
self.class.auto_load_schema
|
136
|
+
run_callbacks(:create) do
|
137
|
+
run_callbacks(:save) do
|
138
|
+
if valid?
|
139
|
+
res = Log.record_connection.put_log(
|
140
|
+
self.class.project_name,
|
141
|
+
self.class.logstore_name,
|
142
|
+
dump_attributes
|
143
|
+
)
|
144
|
+
res.code == 200
|
145
|
+
else
|
146
|
+
false
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def save_array
|
153
|
+
run_callbacks(:create) do
|
154
|
+
run_callbacks(:save) do
|
155
|
+
validate! && dump_attributes
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
@@ -0,0 +1,265 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Aliyun
|
4
|
+
module Log
|
5
|
+
module Record
|
6
|
+
class Relation
|
7
|
+
def initialize(klass, opts = {})
|
8
|
+
@klass = klass
|
9
|
+
@opts = opts
|
10
|
+
@klass.auto_load_schema
|
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
|
54
|
+
end
|
55
|
+
|
56
|
+
def from(from)
|
57
|
+
ts = from.is_a?(Integer) ? from : from.to_time.to_i
|
58
|
+
@opts[:from] = ts
|
59
|
+
self
|
60
|
+
end
|
61
|
+
|
62
|
+
def to(to)
|
63
|
+
ts = to.is_a?(Integer) ? to : to.to_time.to_i
|
64
|
+
@opts[:to] = ts
|
65
|
+
self
|
66
|
+
end
|
67
|
+
|
68
|
+
def line(val)
|
69
|
+
@opts[:line] = val.to_i
|
70
|
+
self
|
71
|
+
end
|
72
|
+
alias limit line
|
73
|
+
|
74
|
+
def offset(val)
|
75
|
+
@opts[:offset] = val.to_i
|
76
|
+
self
|
77
|
+
end
|
78
|
+
|
79
|
+
def page(val)
|
80
|
+
@opts[:page] = val - 1 if val >= 1
|
81
|
+
self
|
82
|
+
end
|
83
|
+
|
84
|
+
def where(opts)
|
85
|
+
@opts.merge!(opts)
|
86
|
+
self
|
87
|
+
end
|
88
|
+
|
89
|
+
def search(*statement)
|
90
|
+
ql = statement_ql(*statement)
|
91
|
+
@opts[:search] = ql if ql.present?
|
92
|
+
self
|
93
|
+
end
|
94
|
+
|
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?
|
101
|
+
self
|
102
|
+
end
|
103
|
+
|
104
|
+
def query(opts = {})
|
105
|
+
@opts[:query] = opts
|
106
|
+
self
|
107
|
+
end
|
108
|
+
|
109
|
+
def count
|
110
|
+
query = @opts.dup
|
111
|
+
if query[:query].blank?
|
112
|
+
where_cond = query[:sql].split(/where /i)[1] if query[:sql].present?
|
113
|
+
query[:query] = "#{query[:search]}|SELECT COUNT(*) as count"
|
114
|
+
query[:query] = "#{query[:query]} WHERE #{where_cond}" if where_cond.present?
|
115
|
+
end
|
116
|
+
res = Log.record_connection.get_logs(@klass.project_name, @klass.logstore_name, query)
|
117
|
+
res = JSON.parse(res.body)
|
118
|
+
res[0]['count'].to_i
|
119
|
+
end
|
120
|
+
|
121
|
+
def result
|
122
|
+
query = @opts.dup
|
123
|
+
if query[:page]
|
124
|
+
query[:line] ||= 100
|
125
|
+
query[:offset] = query[:page] * query[:line]
|
126
|
+
end
|
127
|
+
query[:query] = query[:search] || '*'
|
128
|
+
query[:query] = "#{query[:query]}|#{query[:sql]}" if query[:sql].present?
|
129
|
+
res = Log.record_connection.get_logs(@klass.project_name, @klass.logstore_name, query)
|
130
|
+
JSON.parse(res)
|
131
|
+
end
|
132
|
+
|
133
|
+
def load
|
134
|
+
result.map do |json_attr|
|
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}"
|
203
|
+
end
|
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
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
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_datetime.iso8601
|
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
@@ -53,24 +53,21 @@ module Aliyun
|
|
53
53
|
|
54
54
|
def do_request(verb, resources, payload)
|
55
55
|
resource_path = get_resource_path(resources)
|
56
|
-
headers = {}
|
57
|
-
if verb == 'GET'
|
58
|
-
headers = compact_headers
|
59
|
-
headers['Authorization'] = signature(verb, resource_path, headers, payload)
|
60
|
-
else
|
61
|
-
headers = compact_headers(payload, resources[:is_pb])
|
62
|
-
headers['Authorization'] = signature(verb, resource_path, headers)
|
63
|
-
end
|
64
56
|
request_options = {
|
65
57
|
method: verb,
|
66
58
|
url: get_request_url(resources),
|
67
|
-
headers: headers,
|
68
59
|
open_timeout: @config.open_timeout,
|
69
60
|
read_timeout: @config.read_timeout
|
70
61
|
}
|
71
62
|
if verb == 'GET'
|
63
|
+
headers = compact_headers
|
64
|
+
headers['Authorization'] = signature(verb, resource_path, headers, payload)
|
65
|
+
request_options[:headers] = headers
|
72
66
|
request_options[:url] = URI.escape(canonicalized_resource(request_options[:url], payload))
|
73
67
|
else
|
68
|
+
headers = compact_headers(payload, resources[:is_pb])
|
69
|
+
headers['Authorization'] = signature(verb, resource_path, headers)
|
70
|
+
request_options[:headers] = headers
|
74
71
|
payload = Zlib::Deflate.deflate(payload.encode) if resources[:is_pb]
|
75
72
|
request_options[:payload] = payload
|
76
73
|
end
|
@@ -86,7 +83,7 @@ module Aliyun
|
|
86
83
|
end
|
87
84
|
|
88
85
|
logger.debug("Received HTTP response, code: #{response.code}, headers: " \
|
89
|
-
"#{response.headers}, body: #{response.body}")
|
86
|
+
"#{response.headers}, body: #{response.body.force_encoding('UTF-8')}")
|
90
87
|
|
91
88
|
response
|
92
89
|
end
|
@@ -104,7 +101,6 @@ module Aliyun
|
|
104
101
|
if is_pb
|
105
102
|
compressed = Zlib::Deflate.deflate(body.encode)
|
106
103
|
headers['Content-Length'] = compressed.bytesize.to_s
|
107
|
-
# 日志内容包含的日志必须小于3MB和4096条。
|
108
104
|
raise 'content length is larger than 3MB' if headers['Content-Length'].to_i > 3_145_728
|
109
105
|
|
110
106
|
headers['Content-MD5'] = Digest::MD5.hexdigest(compressed).upcase
|
data/lib/aliyun/version.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: aliyun-log
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.9
|
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-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activemodel
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activesupport
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
13
41
|
- !ruby/object:Gem::Dependency
|
14
42
|
name: protobuf
|
15
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -86,7 +114,7 @@ dependencies:
|
|
86
114
|
- - "~>"
|
87
115
|
- !ruby/object:Gem::Version
|
88
116
|
version: '3.0'
|
89
|
-
description: Aliyun Log SDK for Ruby 阿里云日志服务(SLS) Ruby SDK, 目前仅实现基于Restfull
|
117
|
+
description: Aliyun Log SDK for Ruby 阿里云日志服务(SLS) Ruby SDK, 目前仅实现基于Restfull部分接口和简单Model映射
|
90
118
|
email:
|
91
119
|
- yingce@live.com
|
92
120
|
executables: []
|
@@ -103,6 +131,14 @@ files:
|
|
103
131
|
- lib/aliyun/log/project.rb
|
104
132
|
- lib/aliyun/log/protobuf.rb
|
105
133
|
- lib/aliyun/log/protocol.rb
|
134
|
+
- lib/aliyun/log/record.rb
|
135
|
+
- lib/aliyun/log/record/exception.rb
|
136
|
+
- lib/aliyun/log/record/field.rb
|
137
|
+
- lib/aliyun/log/record/persistence.rb
|
138
|
+
- lib/aliyun/log/record/relation.rb
|
139
|
+
- lib/aliyun/log/record/scope_registry.rb
|
140
|
+
- lib/aliyun/log/record/scoping.rb
|
141
|
+
- lib/aliyun/log/record/type_casting.rb
|
106
142
|
- lib/aliyun/log/request.rb
|
107
143
|
- lib/aliyun/log/server_error.rb
|
108
144
|
- lib/aliyun/version.rb
|