aliyun-log 0.1.1 → 0.2.6
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/client.rb +4 -0
- data/lib/aliyun/log/common/logging.rb +1 -1
- data/lib/aliyun/log/config.rb +4 -2
- data/lib/aliyun/log/logstore.rb +1 -7
- data/lib/aliyun/log/protocol.rb +14 -7
- data/lib/aliyun/log/record.rb +141 -0
- data/lib/aliyun/log/record/exception.rb +9 -0
- data/lib/aliyun/log/record/field.rb +126 -0
- data/lib/aliyun/log/record/persistence.rb +145 -0
- data/lib/aliyun/log/record/relation.rb +151 -0
- data/lib/aliyun/log/record/scope_registry.rb +40 -0
- data/lib/aliyun/log/request.rb +11 -15
- data/lib/aliyun/version.rb +1 -1
- metadata +39 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ed78b39497d6a671405b2d815292ca1e1e11ee301d816395264add76a9bc1235
|
4
|
+
data.tar.gz: 1b2f92c27745b27be61ca5f8fbb41470eaffcd710b22979671604173646c5450
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 19e6a18d2c168882054e14875da1baa18bab32217b2626a33e209e25c867b011deb5cba7d0b6de2f4af40a67ff4c66fb12ff3908881d9e28ced5ca8020c152b4
|
7
|
+
data.tar.gz: 7cfdddb1c6d804b4f62ee34cac200d4eb869ff32c70cf5bda5a4fd10947d06c5df61ba273662aff908a57e685a8ce2c586d1e3efed8d71e1dc2c8a59b649e117
|
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/client.rb
CHANGED
data/lib/aliyun/log/config.rb
CHANGED
@@ -7,11 +7,12 @@ module Aliyun
|
|
7
7
|
@endpoint = 'https://cn-beijing.log.aliyuncs.com'
|
8
8
|
@open_timeout = 10
|
9
9
|
@read_timeout = 120
|
10
|
-
@log_file = 'aliyun_log.log'
|
11
10
|
@log_level = Logger::DEBUG
|
11
|
+
@timestamps = true
|
12
12
|
class << self
|
13
13
|
attr_accessor :endpoint, :access_key_id, :access_key_secret,
|
14
|
-
:open_timeout, :read_timeout, :log_file, :log_level
|
14
|
+
:open_timeout, :read_timeout, :log_file, :log_level,
|
15
|
+
:timestamps, :project
|
15
16
|
|
16
17
|
def configure
|
17
18
|
yield self
|
@@ -29,6 +30,7 @@ module Aliyun
|
|
29
30
|
@access_key_secret ||= self.class.access_key_secret
|
30
31
|
@endpoint ||= self.class.endpoint
|
31
32
|
normalize_endpoint
|
33
|
+
raise 'Missing AccessKeyID or AccessKeySecret' if @access_key_id.nil? || @access_key_secret.nil?
|
32
34
|
end
|
33
35
|
|
34
36
|
private
|
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,141 @@
|
|
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/relation'
|
13
|
+
require_relative 'record/scope_registry'
|
14
|
+
|
15
|
+
module Aliyun
|
16
|
+
module Log
|
17
|
+
module Record
|
18
|
+
extend ActiveSupport::Concern
|
19
|
+
|
20
|
+
included do
|
21
|
+
extend ActiveModel::Callbacks
|
22
|
+
include ActiveModel::Validations
|
23
|
+
|
24
|
+
class_attribute :options, instance_accessor: false, default: {}
|
25
|
+
class_attribute :base_class, instance_accessor: false, default: self
|
26
|
+
class_attribute :log_connection, instance_accessor: false
|
27
|
+
class_attribute :_schema_load, default: false
|
28
|
+
|
29
|
+
Log.included_models << self unless Log.included_models.include? self
|
30
|
+
|
31
|
+
field :created_at, :text if Config.timestamps
|
32
|
+
|
33
|
+
define_model_callbacks :save, :create, :initialize
|
34
|
+
|
35
|
+
before_save :set_created_at
|
36
|
+
end
|
37
|
+
|
38
|
+
include Field
|
39
|
+
include Persistence
|
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
|
+
|
56
|
+
delegate :load, :result, :count, to: :all
|
57
|
+
delegate :where, :query, :search, :sql, :from, :to, :page, :line, :limit, :offset, to: :all
|
58
|
+
delegate :first, :last, :second, :third, :fourth, :fifth, :find_offset, to: :all
|
59
|
+
|
60
|
+
def current_scope
|
61
|
+
ScopeRegistry.value_for(:current_scope, self)
|
62
|
+
end
|
63
|
+
|
64
|
+
def current_scope=(scope)
|
65
|
+
ScopeRegistry.set_value_for(:current_scope, self, scope)
|
66
|
+
end
|
67
|
+
|
68
|
+
def scope(name, body)
|
69
|
+
raise ArgumentError, 'The scope body needs to be callable.' unless body.respond_to?(:call)
|
70
|
+
|
71
|
+
singleton_class.send(:define_method, name) do |*args|
|
72
|
+
scope = all
|
73
|
+
scope = scope.scoping { body.call(*args) }
|
74
|
+
scope
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def all
|
79
|
+
scope = current_scope
|
80
|
+
scope ||= relation.from(0).to(Time.now.to_i)
|
81
|
+
scope
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def relation
|
87
|
+
Relation.new(self)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def initialize(attrs = {})
|
92
|
+
run_callbacks :initialize do
|
93
|
+
@new_record = true
|
94
|
+
@attributes ||= {}
|
95
|
+
|
96
|
+
attrs_with_defaults = self.class.attributes.each_with_object({}) do |(attribute, options), res|
|
97
|
+
res[attribute] = if attrs.key?(attribute)
|
98
|
+
attrs[attribute]
|
99
|
+
elsif options.key?(:default)
|
100
|
+
evaluate_default_value(options[:default])
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
attrs_virtual = attrs.slice(*(attrs.keys - self.class.attributes.keys))
|
105
|
+
|
106
|
+
attrs_with_defaults.merge(attrs_virtual).each do |key, value|
|
107
|
+
if respond_to?("#{key}=")
|
108
|
+
send("#{key}=", value)
|
109
|
+
else
|
110
|
+
raise UnknownAttributeError, "unknown attribute '#{key}' for #{@record.class}."
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def inspect
|
117
|
+
inspection = if defined?(@attributes) && @attributes
|
118
|
+
self.class.attributes.keys.collect do |name|
|
119
|
+
"#{name}: #{attribute_for_inspect(name)}" if has_attribute?(name)
|
120
|
+
end.compact.join(', ')
|
121
|
+
else
|
122
|
+
'not initialized'
|
123
|
+
end
|
124
|
+
|
125
|
+
"#<#{self.class} #{inspection}>"
|
126
|
+
end
|
127
|
+
|
128
|
+
def attribute_for_inspect(attr_name)
|
129
|
+
value = read_attribute(attr_name)
|
130
|
+
|
131
|
+
if value.is_a?(String) && value.length > 50
|
132
|
+
"#{value[0, 50]}...".inspect
|
133
|
+
elsif value.is_a?(Date) || value.is_a?(Time)
|
134
|
+
%("#{value.to_s(:db)}")
|
135
|
+
else
|
136
|
+
value.inspect
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Aliyun
|
4
|
+
module Log
|
5
|
+
module Record
|
6
|
+
module Field
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
# Types allowed in indexes:
|
10
|
+
PERMITTED_KEY_TYPES = %i[
|
11
|
+
text
|
12
|
+
long
|
13
|
+
double
|
14
|
+
json
|
15
|
+
].freeze
|
16
|
+
|
17
|
+
DEFAULT_INDEX_TOKEN = ", '\";=()[]{}?@&<>/:\n\t\r".split('')
|
18
|
+
|
19
|
+
included do
|
20
|
+
class_attribute :attributes, instance_accessor: false, default: {}
|
21
|
+
end
|
22
|
+
|
23
|
+
module ClassMethods
|
24
|
+
def field(name, type = :text, options = {})
|
25
|
+
unless PERMITTED_KEY_TYPES.include?(type)
|
26
|
+
raise ArgumentError, "Field #{name} type(#{type}) error, key type only support text/long/double/json"
|
27
|
+
end
|
28
|
+
|
29
|
+
named = name.to_s
|
30
|
+
self.attributes = attributes.merge(name => { type: type }.merge(options))
|
31
|
+
|
32
|
+
warn_about_method_overriding(name, name)
|
33
|
+
warn_about_method_overriding("#{named}=", name)
|
34
|
+
warn_about_method_overriding("#{named}?", name)
|
35
|
+
|
36
|
+
define_attribute_method(name) # Dirty API
|
37
|
+
|
38
|
+
generated_methods.module_eval do
|
39
|
+
define_method(named) { read_attribute(named) }
|
40
|
+
define_method("#{named}?") do
|
41
|
+
value = read_attribute(named)
|
42
|
+
case value
|
43
|
+
when true then true
|
44
|
+
when false, nil then false
|
45
|
+
else
|
46
|
+
!value.nil?
|
47
|
+
end
|
48
|
+
end
|
49
|
+
define_method("#{named}=") { |value| write_attribute(named, value) }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def remove_field(field)
|
54
|
+
field = field.to_sym
|
55
|
+
attributes.delete(field) || raise('No such field')
|
56
|
+
|
57
|
+
undefine_attribute_methods
|
58
|
+
define_attribute_methods attributes.keys
|
59
|
+
|
60
|
+
generated_methods.module_eval do
|
61
|
+
remove_method field
|
62
|
+
remove_method :"#{field}="
|
63
|
+
remove_method :"#{field}?"
|
64
|
+
remove_method :"#{field}_before_type_cast"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def generated_methods
|
71
|
+
@generated_methods ||= begin
|
72
|
+
Module.new.tap do |mod|
|
73
|
+
include(mod)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def warn_about_method_overriding(method_name, field_name)
|
79
|
+
if instance_methods.include?(method_name.to_sym)
|
80
|
+
Common::Logging.logger.warn("Method #{method_name} generated for the field #{field_name} overrides already existing method")
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def attribute_names
|
86
|
+
@attributes.keys
|
87
|
+
end
|
88
|
+
|
89
|
+
def has_attribute?(attr_name)
|
90
|
+
@attributes.key?(attr_name.to_sym)
|
91
|
+
end
|
92
|
+
|
93
|
+
def evaluate_default_value(val)
|
94
|
+
if val.respond_to?(:call)
|
95
|
+
val.call
|
96
|
+
elsif val.duplicable?
|
97
|
+
val.dup
|
98
|
+
else
|
99
|
+
val
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
attr_accessor :attributes
|
104
|
+
|
105
|
+
def write_attribute(name, value)
|
106
|
+
attributes[name.to_sym] = value
|
107
|
+
end
|
108
|
+
|
109
|
+
alias []= write_attribute
|
110
|
+
|
111
|
+
def read_attribute(name)
|
112
|
+
attributes[name.to_sym]
|
113
|
+
end
|
114
|
+
alias [] read_attribute
|
115
|
+
|
116
|
+
def set_created_at
|
117
|
+
self.created_at ||= DateTime.now.in_time_zone(Time.zone).to_s if timestamps_enabled?
|
118
|
+
end
|
119
|
+
|
120
|
+
def timestamps_enabled?
|
121
|
+
self.class.options[:timestamps] || (self.class.options[:timestamps].nil? && Config.timestamps)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,145 @@
|
|
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 = []
|
60
|
+
data.each do |log_attr|
|
61
|
+
logs << new(log_attr).save_array
|
62
|
+
end
|
63
|
+
res = Log.record_connection.put_log(project_name, logstore_name, logs, opts)
|
64
|
+
res.code == 200
|
65
|
+
else
|
66
|
+
new(data).save
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def evaluate_default_value(val)
|
73
|
+
if val.respond_to?(:call)
|
74
|
+
val.call
|
75
|
+
elsif val.duplicable?
|
76
|
+
val.dup
|
77
|
+
else
|
78
|
+
val
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def field_indices
|
83
|
+
indices = if options[:field_index] == false
|
84
|
+
attributes.select { |_, value| value[:index] == true }
|
85
|
+
else
|
86
|
+
attributes.reject { |_, value| value[:index] == false }
|
87
|
+
end
|
88
|
+
indices.each do |_, v|
|
89
|
+
next unless v[:doc_value].nil?
|
90
|
+
|
91
|
+
v[:doc_value] = options[:field_doc_value] != false
|
92
|
+
end
|
93
|
+
indices
|
94
|
+
end
|
95
|
+
|
96
|
+
def create_index
|
97
|
+
Log.record_connection.create_index(
|
98
|
+
project_name,
|
99
|
+
logstore_name,
|
100
|
+
field_indices
|
101
|
+
)
|
102
|
+
end
|
103
|
+
|
104
|
+
def update_index
|
105
|
+
conf_res = Log.record_connection.get_index(project_name, logstore_name)
|
106
|
+
raw_conf = JSON.parse(conf_res)
|
107
|
+
index_conf = raw_conf.dup
|
108
|
+
field_indices.each do |k, v|
|
109
|
+
index_conf['keys'][k.to_s] ||= v
|
110
|
+
end
|
111
|
+
return if index_conf['keys'] == raw_conf['keys']
|
112
|
+
|
113
|
+
Log.record_connection.update_index(
|
114
|
+
project_name,
|
115
|
+
logstore_name,
|
116
|
+
index_conf['keys']
|
117
|
+
)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def save
|
122
|
+
self.class.auto_load_schema
|
123
|
+
run_callbacks(:create) do
|
124
|
+
run_callbacks(:save) do
|
125
|
+
if valid?
|
126
|
+
res = Log.record_connection.put_log(self.class.project_name, self.class.logstore_name, attributes)
|
127
|
+
res.code == 200
|
128
|
+
else
|
129
|
+
false
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def save_array
|
136
|
+
run_callbacks(:create) do
|
137
|
+
run_callbacks(:save) do
|
138
|
+
validate! && attributes
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,151 @@
|
|
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
|
+
@opts[:search] ||= '*'
|
12
|
+
end
|
13
|
+
|
14
|
+
def inspect
|
15
|
+
"#<#{self.class}>"
|
16
|
+
end
|
17
|
+
|
18
|
+
def first(line = 1)
|
19
|
+
find_offset(0, line, false)
|
20
|
+
end
|
21
|
+
|
22
|
+
def second
|
23
|
+
find_offset(1)
|
24
|
+
end
|
25
|
+
|
26
|
+
def third
|
27
|
+
find_offset(2)
|
28
|
+
end
|
29
|
+
|
30
|
+
def fourth
|
31
|
+
find_offset(3)
|
32
|
+
end
|
33
|
+
|
34
|
+
def fifth
|
35
|
+
find_offset(4)
|
36
|
+
end
|
37
|
+
|
38
|
+
def last(line = 1)
|
39
|
+
find_offset(0, line, true)
|
40
|
+
end
|
41
|
+
|
42
|
+
def find_offset(nth, line = 1, reverse = false)
|
43
|
+
@opts[:line] = line
|
44
|
+
@opts[:offset] = nth
|
45
|
+
@opts[:reverse] = reverse
|
46
|
+
line <= 1 ? load[0] : load
|
47
|
+
end
|
48
|
+
|
49
|
+
def scoping
|
50
|
+
previous = @klass.current_scope
|
51
|
+
@klass.current_scope = self
|
52
|
+
yield
|
53
|
+
ensure
|
54
|
+
@klass.current_scope = previous
|
55
|
+
end
|
56
|
+
|
57
|
+
def from(from)
|
58
|
+
ts = from.is_a?(Integer) ? from : from.to_time.to_i
|
59
|
+
@opts[:from] = ts
|
60
|
+
self
|
61
|
+
end
|
62
|
+
|
63
|
+
def to(to)
|
64
|
+
ts = to.is_a?(Integer) ? to : to.to_time.to_i
|
65
|
+
@opts[:to] = ts
|
66
|
+
self
|
67
|
+
end
|
68
|
+
|
69
|
+
def line(val)
|
70
|
+
@opts[:line] = val.to_i
|
71
|
+
self
|
72
|
+
end
|
73
|
+
alias limit line
|
74
|
+
|
75
|
+
def offset(val)
|
76
|
+
@opts[:offset] = val.to_i
|
77
|
+
self
|
78
|
+
end
|
79
|
+
|
80
|
+
def page(val)
|
81
|
+
@opts[:page] = val - 1 if val >= 1
|
82
|
+
self
|
83
|
+
end
|
84
|
+
|
85
|
+
def where(opts)
|
86
|
+
@opts.merge!(opts)
|
87
|
+
self
|
88
|
+
end
|
89
|
+
|
90
|
+
def search(str)
|
91
|
+
@opts[:search] = str
|
92
|
+
self
|
93
|
+
end
|
94
|
+
|
95
|
+
def sql(str)
|
96
|
+
@opts[:sql] = str
|
97
|
+
self
|
98
|
+
end
|
99
|
+
|
100
|
+
def query(opts = {})
|
101
|
+
@opts[:query] = opts
|
102
|
+
self
|
103
|
+
end
|
104
|
+
|
105
|
+
def count
|
106
|
+
query = @opts.dup
|
107
|
+
if query[:query].blank?
|
108
|
+
where_cond = query[:sql].split(/where /i)[1] if query[:sql].present?
|
109
|
+
query[:query] = "#{query[:search]}|SELECT COUNT(*) as count"
|
110
|
+
query[:query] = "#{query[:query]} WHERE #{where_cond}" if where_cond.present?
|
111
|
+
end
|
112
|
+
res = Log.record_connection.get_logs(@klass.project_name, @klass.logstore_name, query)
|
113
|
+
res = JSON.parse(res.body)
|
114
|
+
res[0]['count'].to_i
|
115
|
+
end
|
116
|
+
|
117
|
+
def result
|
118
|
+
query = @opts.dup
|
119
|
+
if query[:page]
|
120
|
+
query[:line] ||= 100
|
121
|
+
query[:offset] = query[:page] * query[:line]
|
122
|
+
end
|
123
|
+
query[:query] = query[:search]
|
124
|
+
query[:query] = "#{query[:query]}|#{query[:sql]}" if query[:sql].present?
|
125
|
+
res = Log.record_connection.get_logs(@klass.project_name, @klass.logstore_name, query)
|
126
|
+
JSON.parse(res)
|
127
|
+
end
|
128
|
+
|
129
|
+
def load
|
130
|
+
result.map do |json_attr|
|
131
|
+
attrs = {}
|
132
|
+
@klass.attributes.keys.each do |k, _|
|
133
|
+
attrs[k] = json_attr[k.to_s]
|
134
|
+
end
|
135
|
+
@klass.new(attrs)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
private
|
140
|
+
|
141
|
+
def method_missing(method, *args, &block)
|
142
|
+
if @klass.respond_to?(method)
|
143
|
+
scoping { @klass.public_send(method, *args, &block) }
|
144
|
+
else
|
145
|
+
super
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
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
|
data/lib/aliyun/log/request.rb
CHANGED
@@ -20,10 +20,10 @@ module Aliyun
|
|
20
20
|
resources ||= {}
|
21
21
|
res = '/'
|
22
22
|
if resources[:logstore]
|
23
|
-
res
|
24
|
-
res
|
23
|
+
res = "#{res}logstores"
|
24
|
+
res = "#{res}/#{resources[:logstore]}" unless resources[:logstore].empty?
|
25
25
|
end
|
26
|
-
res
|
26
|
+
res = "#{res}/#{resources[:action]}" if resources[:action]
|
27
27
|
res
|
28
28
|
end
|
29
29
|
|
@@ -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'
|
72
|
-
|
63
|
+
headers = compact_headers
|
64
|
+
headers['Authorization'] = signature(verb, resource_path, headers, payload)
|
65
|
+
request_options[:headers] = headers
|
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.6
|
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-09 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
|
@@ -30,14 +58,14 @@ dependencies:
|
|
30
58
|
requirements:
|
31
59
|
- - "~>"
|
32
60
|
- !ruby/object:Gem::Version
|
33
|
-
version: 2.
|
61
|
+
version: 2.0.0
|
34
62
|
type: :runtime
|
35
63
|
prerelease: false
|
36
64
|
version_requirements: !ruby/object:Gem::Requirement
|
37
65
|
requirements:
|
38
66
|
- - "~>"
|
39
67
|
- !ruby/object:Gem::Version
|
40
|
-
version: 2.
|
68
|
+
version: 2.0.0
|
41
69
|
- !ruby/object:Gem::Dependency
|
42
70
|
name: bundler
|
43
71
|
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,12 @@ 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
|
106
140
|
- lib/aliyun/log/request.rb
|
107
141
|
- lib/aliyun/log/server_error.rb
|
108
142
|
- lib/aliyun/version.rb
|