aliyun-log 0.1.0 → 0.2.2
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 +25 -2
- data/lib/aliyun/log/client.rb +10 -0
- data/lib/aliyun/log/common.rb +2 -1
- data/lib/aliyun/log/common/attr_struct.rb +2 -0
- data/lib/aliyun/log/common/logging.rb +21 -4
- data/lib/aliyun/log/config.rb +15 -9
- data/lib/aliyun/log/logstore.rb +3 -8
- data/lib/aliyun/log/project.rb +2 -0
- data/lib/aliyun/log/protobuf.rb +2 -0
- data/lib/aliyun/log/protocol.rb +19 -11
- data/lib/aliyun/log/record.rb +122 -0
- data/lib/aliyun/log/record/exception.rb +9 -0
- data/lib/aliyun/log/record/field.rb +129 -0
- data/lib/aliyun/log/record/persistence.rb +138 -0
- data/lib/aliyun/log/record/relation.rb +98 -0
- data/lib/aliyun/log/request.rb +20 -21
- data/lib/aliyun/log/server_error.rb +2 -0
- data/lib/aliyun/version.rb +3 -1
- metadata +38 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5b17c3b8727ec3dc44742d220f22f26014a201334889f888d858a0cbc33b9c39
|
4
|
+
data.tar.gz: df953904cf39fea58a11d99fab15ca115e3187667d062dfacec0a6f1301b2510
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dc5e4072953b2e687f7153230d23f3f6c6d68ac80abb52278819531cb6219eec1c0c9abf593e93d2b05dc697d74f6be75d94c522314088c416bb108631290bd7
|
7
|
+
data.tar.gz: 96b65f80b1fba4284d4472e524d8e424d58fff86ebac92cb281481af51134f8a75bb624d8339f67735038d3d36252bb4a6aa66bb3bc117c8706622a80eccefc1
|
data/lib/aliyun/log.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require_relative 'log/common'
|
3
4
|
require_relative 'log/client'
|
4
5
|
require_relative 'log/config'
|
@@ -8,11 +9,33 @@ require_relative 'log/protobuf'
|
|
8
9
|
require_relative 'log/protocol'
|
9
10
|
require_relative 'log/request'
|
10
11
|
require_relative 'log/server_error'
|
12
|
+
require_relative 'log/record'
|
11
13
|
|
12
14
|
module Aliyun
|
13
15
|
module Log
|
14
|
-
|
16
|
+
extend self
|
17
|
+
|
18
|
+
def configure
|
15
19
|
block_given? ? yield(Config) : Config
|
16
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
|
17
40
|
end
|
18
41
|
end
|
data/lib/aliyun/log/client.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Aliyun
|
2
4
|
module Log
|
3
5
|
class Client
|
@@ -10,6 +12,10 @@ module Aliyun
|
|
10
12
|
@protocol.list_projects(size, offset)
|
11
13
|
end
|
12
14
|
|
15
|
+
def projects(size = nil, offset = nil)
|
16
|
+
@protocol.projects(size, offset)
|
17
|
+
end
|
18
|
+
|
13
19
|
def get_project(name)
|
14
20
|
@protocol.get_project(name)
|
15
21
|
end
|
@@ -25,6 +31,10 @@ module Aliyun
|
|
25
31
|
def delete_project(name)
|
26
32
|
@protocol.delete_project(name)
|
27
33
|
end
|
34
|
+
|
35
|
+
def get_logstore(project_name, logstore_name)
|
36
|
+
@protocol.get_logstore(project_name, logstore_name)
|
37
|
+
end
|
28
38
|
end
|
29
39
|
end
|
30
40
|
end
|
data/lib/aliyun/log/common.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'logger'
|
2
4
|
|
3
5
|
module Aliyun
|
4
6
|
module Log
|
5
7
|
module Common
|
6
8
|
module Logging
|
7
|
-
# DEFAULT_LOG_FILE = './log/aliyun_log.log'.freeze
|
8
|
-
DEFAULT_LOG_FILE = STDOUT
|
9
9
|
MAX_NUM_LOG = 100
|
10
10
|
ROTATE_SIZE = 10 * 1024 * 1024
|
11
11
|
|
@@ -15,15 +15,32 @@ module Aliyun
|
|
15
15
|
|
16
16
|
# level = Logger::DEBUG | Logger::INFO | Logger::ERROR | Logger::FATAL
|
17
17
|
def self.log_level=(level)
|
18
|
+
@logger_level = level
|
18
19
|
Logging.logger.level = level
|
19
20
|
end
|
20
21
|
|
22
|
+
def self.logger=(logger)
|
23
|
+
@logger = logger
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.log_file=(log_file)
|
27
|
+
@logger = Logger.new(
|
28
|
+
log_file, MAX_NUM_LOG, ROTATE_SIZE
|
29
|
+
)
|
30
|
+
@logger.level = Logging.logger_level
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.logger_level
|
34
|
+
@logger_level ||= Config.log_level
|
35
|
+
@logger_level
|
36
|
+
end
|
37
|
+
|
21
38
|
def self.logger
|
22
39
|
unless @logger
|
23
40
|
@logger = Logger.new(
|
24
|
-
@log_file ||=
|
41
|
+
@log_file ||= Config.log_file, MAX_NUM_LOG, ROTATE_SIZE
|
25
42
|
)
|
26
|
-
@logger.level =
|
43
|
+
@logger.level = Logging.logger_level
|
27
44
|
end
|
28
45
|
@logger
|
29
46
|
end
|
data/lib/aliyun/log/config.rb
CHANGED
@@ -1,12 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'logger'
|
1
4
|
module Aliyun
|
2
5
|
module Log
|
3
6
|
class Config < Common::AttrStruct
|
4
7
|
@endpoint = 'https://cn-beijing.log.aliyuncs.com'
|
5
8
|
@open_timeout = 10
|
6
9
|
@read_timeout = 120
|
10
|
+
@log_file = 'aliyun_log.log'
|
11
|
+
@log_level = Logger::DEBUG
|
12
|
+
@timestamps = true
|
7
13
|
class << self
|
8
14
|
attr_accessor :endpoint, :access_key_id, :access_key_secret,
|
9
|
-
:open_timeout, :read_timeout
|
15
|
+
:open_timeout, :read_timeout, :log_file, :log_level,
|
16
|
+
:timestamps, :project
|
10
17
|
|
11
18
|
def configure
|
12
19
|
yield self
|
@@ -24,20 +31,19 @@ module Aliyun
|
|
24
31
|
@access_key_secret ||= self.class.access_key_secret
|
25
32
|
@endpoint ||= self.class.endpoint
|
26
33
|
normalize_endpoint
|
34
|
+
raise 'Missing AccessKeyID or AccessKeySecret' if @access_key_id.nil? || @access_key_secret.nil?
|
27
35
|
end
|
28
36
|
|
29
37
|
private
|
30
38
|
|
31
|
-
|
32
|
-
|
33
|
-
|
39
|
+
def normalize_endpoint
|
40
|
+
uri = URI.parse(endpoint)
|
41
|
+
uri = URI.parse("http://#{endpoint}") unless uri.scheme
|
34
42
|
|
35
|
-
|
36
|
-
raise 'Only HTTP and HTTPS endpoint are accepted.'
|
37
|
-
end
|
43
|
+
raise 'Only HTTP and HTTPS endpoint are accepted.' if (uri.scheme != 'http') && (uri.scheme != 'https')
|
38
44
|
|
39
|
-
|
40
|
-
|
45
|
+
@endpoint = uri.to_s
|
46
|
+
end
|
41
47
|
end
|
42
48
|
end
|
43
49
|
end
|
data/lib/aliyun/log/logstore.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Aliyun
|
2
4
|
module Log
|
3
5
|
class Logstore < Common::AttrStruct
|
4
|
-
|
5
6
|
attrs :name, :project_name, :ttl, :shared_count, :enable_tracking,
|
6
7
|
:auto_split, :max_split_shard, :create_time, :last_modify_time
|
7
8
|
|
@@ -29,13 +30,7 @@ module Aliyun
|
|
29
30
|
end
|
30
31
|
|
31
32
|
def put_log(attributes)
|
32
|
-
|
33
|
-
log = Aliyun::Log::Protobuf::Log.new(
|
34
|
-
time: Time.now.to_i,
|
35
|
-
contents: contents
|
36
|
-
)
|
37
|
-
log_group = Aliyun::Log::Protobuf::LogGroup.new(logs: [log])
|
38
|
-
put_logs(log_group)
|
33
|
+
@protocol.put_log(project_name, name, attributes)
|
39
34
|
end
|
40
35
|
|
41
36
|
def get_logs(opts = {})
|
data/lib/aliyun/log/project.rb
CHANGED
data/lib/aliyun/log/protobuf.rb
CHANGED
data/lib/aliyun/log/protocol.rb
CHANGED
@@ -5,6 +5,7 @@ require 'json'
|
|
5
5
|
module Aliyun
|
6
6
|
module Log
|
7
7
|
class Protocol
|
8
|
+
include Common::Logging
|
8
9
|
def initialize(config)
|
9
10
|
@http = Request.new(config)
|
10
11
|
end
|
@@ -22,6 +23,10 @@ module Aliyun
|
|
22
23
|
data
|
23
24
|
end
|
24
25
|
|
26
|
+
def projects(size = nil, offset = nil)
|
27
|
+
list_projects(size, offset)['projects']
|
28
|
+
end
|
29
|
+
|
25
30
|
def get_project(project_name)
|
26
31
|
query = { projectName: project_name }
|
27
32
|
attrs = @http.get({ project: project_name }, query)
|
@@ -61,7 +66,7 @@ module Aliyun
|
|
61
66
|
|
62
67
|
def create_logstore(project_name, logstore_name, opt = {})
|
63
68
|
body = {
|
64
|
-
|
69
|
+
logstoreName: logstore_name,
|
65
70
|
ttl: opt[:ttl] || 365,
|
66
71
|
shardCount: opt[:shard_count] || 2,
|
67
72
|
autoSplit: opt[:auto_split].nil? ? false : opt[:auto_split],
|
@@ -100,10 +105,17 @@ module Aliyun
|
|
100
105
|
@http.post({ project: project_name, logstore: logstore_name, is_pb: true }, content)
|
101
106
|
end
|
102
107
|
|
103
|
-
def
|
104
|
-
|
105
|
-
|
106
|
-
|
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.compact.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])
|
107
119
|
@http.post({ project: project_name, logstore: logstore_name, is_pb: true }, lg_pb)
|
108
120
|
end
|
109
121
|
|
@@ -170,10 +182,8 @@ module Aliyun
|
|
170
182
|
keys: {}
|
171
183
|
}
|
172
184
|
fields.each do |k, v|
|
185
|
+
v[:token] = INDEX_DEFAULT_TOKEN if %w[text json].include?(v[:type].to_s) && v[:token].blank?
|
173
186
|
body[:keys][k] = v
|
174
|
-
if %w[text json].include?(v[:type]) && v[:token].blank?
|
175
|
-
v[:token] = INDEX_DEFAULT_TOKEN
|
176
|
-
end
|
177
187
|
end
|
178
188
|
@http.post({ project: project_name, logstore: logstore_name, action: 'index' }, body.to_json)
|
179
189
|
end
|
@@ -186,10 +196,8 @@ module Aliyun
|
|
186
196
|
keys: {}
|
187
197
|
}
|
188
198
|
fields.each do |k, v|
|
199
|
+
v[:token] = INDEX_DEFAULT_TOKEN if %w[text json].include?(v[:type].to_s) && v[:token].blank?
|
189
200
|
body[:keys][k] = v
|
190
|
-
if v[:type] == 'text' && v[:token].blank?
|
191
|
-
v[:token] = INDEX_DEFAULT_TOKEN
|
192
|
-
end
|
193
201
|
end
|
194
202
|
@http.put({ project: project_name, logstore: logstore_name, action: 'index' }, body.to_json)
|
195
203
|
end
|
@@ -0,0 +1,122 @@
|
|
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
|
+
|
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, :text if Config.timestamps
|
31
|
+
|
32
|
+
define_model_callbacks :save, :create, :initialize
|
33
|
+
|
34
|
+
after_initialize :set_created_at
|
35
|
+
end
|
36
|
+
|
37
|
+
include Field
|
38
|
+
include Persistence
|
39
|
+
|
40
|
+
include ActiveModel::AttributeMethods
|
41
|
+
|
42
|
+
module ClassMethods
|
43
|
+
def logstore(options = {})
|
44
|
+
if options[:timestamps] && !Config.timestamps
|
45
|
+
field :created_at, :text
|
46
|
+
elsif options[:timestamps] == false && Config.timestamps
|
47
|
+
remove_field :created_at
|
48
|
+
end
|
49
|
+
self._schema_load = true if options[:auto_sync] == false
|
50
|
+
self.options = options
|
51
|
+
end
|
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)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def initialize(attrs = {})
|
73
|
+
run_callbacks :initialize do
|
74
|
+
@new_record = true
|
75
|
+
@attributes ||= {}
|
76
|
+
|
77
|
+
attrs_with_defaults = self.class.attributes.each_with_object({}) do |(attribute, options), res|
|
78
|
+
res[attribute] = if attrs.key?(attribute)
|
79
|
+
attrs[attribute]
|
80
|
+
elsif options.key?(:default)
|
81
|
+
evaluate_default_value(options[:default])
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
attrs_virtual = attrs.slice(*(attrs.keys - self.class.attributes.keys))
|
86
|
+
|
87
|
+
attrs_with_defaults.merge(attrs_virtual).each do |key, value|
|
88
|
+
if respond_to?("#{key}=")
|
89
|
+
send("#{key}=", value)
|
90
|
+
else
|
91
|
+
raise UnknownAttributeError, "unknown attribute '#{key}' for #{@record.class}."
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def inspect
|
98
|
+
inspection = if defined?(@attributes) && @attributes
|
99
|
+
self.class.attributes.keys.collect do |name|
|
100
|
+
"#{name}: #{attribute_for_inspect(name)}" if has_attribute?(name)
|
101
|
+
end.compact.join(', ')
|
102
|
+
else
|
103
|
+
'not initialized'
|
104
|
+
end
|
105
|
+
|
106
|
+
"#<#{self.class} #{inspection}>"
|
107
|
+
end
|
108
|
+
|
109
|
+
def attribute_for_inspect(attr_name)
|
110
|
+
value = read_attribute(attr_name)
|
111
|
+
|
112
|
+
if value.is_a?(String) && value.length > 50
|
113
|
+
"#{value[0, 50]}...".inspect
|
114
|
+
elsif value.is_a?(Date) || value.is_a?(Time)
|
115
|
+
%("#{value.to_s(:db)}")
|
116
|
+
else
|
117
|
+
value.inspect
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "active_support"
|
3
|
+
require "active_support/time"
|
4
|
+
require "active_support/core_ext"
|
5
|
+
require "active_model"
|
6
|
+
|
7
|
+
module Aliyun
|
8
|
+
module Log
|
9
|
+
module Record
|
10
|
+
module Field
|
11
|
+
extend ActiveSupport::Concern
|
12
|
+
|
13
|
+
# Types allowed in indexes:
|
14
|
+
PERMITTED_KEY_TYPES = %i[
|
15
|
+
text
|
16
|
+
long
|
17
|
+
double
|
18
|
+
json
|
19
|
+
].freeze
|
20
|
+
|
21
|
+
DEFAULT_INDEX_TOKEN = ", '\";=()[]{}?@&<>/:\n\t\r".split('')
|
22
|
+
|
23
|
+
included do
|
24
|
+
class_attribute :attributes, instance_accessor: false, default: {}
|
25
|
+
end
|
26
|
+
|
27
|
+
module ClassMethods
|
28
|
+
def field(name, type = :text, options = {})
|
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
|
+
named = name.to_s
|
33
|
+
self.attributes = attributes.merge(name => { type: type }.merge(options))
|
34
|
+
|
35
|
+
warn_about_method_overriding(name, name)
|
36
|
+
warn_about_method_overriding("#{named}=", name)
|
37
|
+
warn_about_method_overriding("#{named}?", name)
|
38
|
+
|
39
|
+
define_attribute_method(name) # Dirty API
|
40
|
+
|
41
|
+
generated_methods.module_eval do
|
42
|
+
define_method(named) { read_attribute(named) }
|
43
|
+
define_method("#{named}?") do
|
44
|
+
value = read_attribute(named)
|
45
|
+
case value
|
46
|
+
when true then true
|
47
|
+
when false, nil then false
|
48
|
+
else
|
49
|
+
!value.nil?
|
50
|
+
end
|
51
|
+
end
|
52
|
+
define_method("#{named}=") { |value| write_attribute(named, value) }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def remove_field(field)
|
57
|
+
field = field.to_sym
|
58
|
+
attributes.delete(field) || raise('No such field')
|
59
|
+
|
60
|
+
undefine_attribute_methods
|
61
|
+
define_attribute_methods attributes.keys
|
62
|
+
|
63
|
+
generated_methods.module_eval do
|
64
|
+
remove_method field
|
65
|
+
remove_method :"#{field}="
|
66
|
+
remove_method :"#{field}?"
|
67
|
+
remove_method :"#{field}_before_type_cast"
|
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
|
+
Common::Logging.logger.warn("Method #{method_name} generated for the field #{field_name} overrides already existing method")
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def attribute_names
|
89
|
+
@attributes.keys
|
90
|
+
end
|
91
|
+
|
92
|
+
def has_attribute?(attr_name)
|
93
|
+
@attributes.key?(attr_name.to_sym)
|
94
|
+
end
|
95
|
+
|
96
|
+
def evaluate_default_value(val)
|
97
|
+
if val.respond_to?(:call)
|
98
|
+
val.call
|
99
|
+
elsif val.duplicable?
|
100
|
+
val.dup
|
101
|
+
else
|
102
|
+
val
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
attr_accessor :attributes
|
107
|
+
|
108
|
+
def write_attribute(name, value)
|
109
|
+
attributes[name.to_sym] = value
|
110
|
+
end
|
111
|
+
|
112
|
+
alias []= write_attribute
|
113
|
+
|
114
|
+
def read_attribute(name)
|
115
|
+
attributes[name.to_sym]
|
116
|
+
end
|
117
|
+
alias [] read_attribute
|
118
|
+
|
119
|
+
def set_created_at
|
120
|
+
self.created_at ||= DateTime.now.in_time_zone(Time.zone).to_s if timestamps_enabled?
|
121
|
+
end
|
122
|
+
|
123
|
+
def timestamps_enabled?
|
124
|
+
self.class.options[:timestamps] || (self.class.options[:timestamps].nil? && Config.timestamps)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,138 @@
|
|
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] || base_class.name.split('::').last.downcase.pluralize
|
12
|
+
end
|
13
|
+
|
14
|
+
def logstore_name=(value)
|
15
|
+
if defined?(@logstore_name)
|
16
|
+
return if value == @logstore_name
|
17
|
+
end
|
18
|
+
|
19
|
+
@logstore_name = value
|
20
|
+
end
|
21
|
+
|
22
|
+
def project_name
|
23
|
+
unless @project_name
|
24
|
+
@project_name = options[:project] || Config.project
|
25
|
+
raise ProjectNameError, "project can't be empty" if @project_name.blank?
|
26
|
+
end
|
27
|
+
@project_name
|
28
|
+
end
|
29
|
+
|
30
|
+
def create_logstore(options = {})
|
31
|
+
Log.record_connection.get_logstore(project_name, logstore_name)
|
32
|
+
rescue ServerError => e
|
33
|
+
Log.record_connection.create_logstore(project_name, logstore_name, options)
|
34
|
+
end
|
35
|
+
|
36
|
+
def sync_index
|
37
|
+
has_index? ? update_index : create_index
|
38
|
+
end
|
39
|
+
|
40
|
+
def auto_load_schema
|
41
|
+
return if _schema_load
|
42
|
+
|
43
|
+
create_logstore
|
44
|
+
sync_index
|
45
|
+
self._schema_load = true
|
46
|
+
end
|
47
|
+
|
48
|
+
def has_index?
|
49
|
+
Log.record_connection.get_index(project_name, logstore_name)
|
50
|
+
true
|
51
|
+
rescue ServerError
|
52
|
+
false
|
53
|
+
end
|
54
|
+
|
55
|
+
def create(data, opts = {})
|
56
|
+
auto_load_schema
|
57
|
+
if data.is_a?(Array)
|
58
|
+
logs = []
|
59
|
+
data.each do |log_attr|
|
60
|
+
logs << 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] == true
|
83
|
+
attributes.reject { |_, value| value[:index] == false }
|
84
|
+
else
|
85
|
+
attributes.select { |_, value| value[:index] == true }
|
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.dup
|
101
|
+
field_indices.each do |k, v|
|
102
|
+
index_conf['keys'][k.to_s] ||= v
|
103
|
+
end
|
104
|
+
return if index_conf['keys'] == raw_conf['keys']
|
105
|
+
|
106
|
+
Log.record_connection.update_index(
|
107
|
+
project_name,
|
108
|
+
logstore_name,
|
109
|
+
index_conf['keys']
|
110
|
+
)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def save
|
115
|
+
self.class.auto_load_schema
|
116
|
+
run_callbacks(:create) do
|
117
|
+
run_callbacks(:save) do
|
118
|
+
if valid?
|
119
|
+
res = Log.record_connection.put_log(self.class.project_name, self.class.logstore_name, attributes)
|
120
|
+
res.code == 200
|
121
|
+
else
|
122
|
+
false
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def save_array
|
129
|
+
run_callbacks(:create) do
|
130
|
+
run_callbacks(:save) do
|
131
|
+
validate! && attributes
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,98 @@
|
|
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 from(from)
|
15
|
+
ts = from.is_a?(Integer) ? from : from.to_time.to_i
|
16
|
+
@opts[:from] = ts
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def to(to)
|
21
|
+
ts = to.is_a?(Integer) ? to : to.to_time.to_i
|
22
|
+
@opts[:to] = ts
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def line(val)
|
27
|
+
@opts[:line] = val.to_i
|
28
|
+
self
|
29
|
+
end
|
30
|
+
alias limit line
|
31
|
+
|
32
|
+
def offset(val)
|
33
|
+
@opts[:offset] = val.to_i
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
def page(val)
|
38
|
+
@opts[:page] = val - 1 if val >= 1
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
def where(opts)
|
43
|
+
@opts.merge!(opts)
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
def search(str)
|
48
|
+
@opts[:search] = str
|
49
|
+
self
|
50
|
+
end
|
51
|
+
|
52
|
+
def sql(str)
|
53
|
+
@opts[:sql] = str
|
54
|
+
self
|
55
|
+
end
|
56
|
+
|
57
|
+
def query(opts = {})
|
58
|
+
@opts[:query] = opts
|
59
|
+
self
|
60
|
+
end
|
61
|
+
|
62
|
+
def count
|
63
|
+
query = @opts.dup
|
64
|
+
if query[:query].blank?
|
65
|
+
where_cond = query[:sql].split(/where /i)[1] if query[:sql].present?
|
66
|
+
query[:query] = "#{query[:search]}|SELECT COUNT(*) as count"
|
67
|
+
query[:query] = "#{query[:query]} WHERE #{where_cond}" if where_cond.present?
|
68
|
+
end
|
69
|
+
res = Log.record_connection.get_logs(@klass.project_name, @klass.logstore_name, query)
|
70
|
+
res = JSON.parse(res.body)
|
71
|
+
res[0]['count'].to_i
|
72
|
+
end
|
73
|
+
|
74
|
+
def result
|
75
|
+
query = @opts.dup
|
76
|
+
if query[:page]
|
77
|
+
query[:line] ||= 100
|
78
|
+
query[:offset] = query[:page] * query[:line]
|
79
|
+
end
|
80
|
+
query[:query] = query[:search]
|
81
|
+
query[:query] = "#{query[:query]}|#{query[:sql]}" if query[:sql].present?
|
82
|
+
res = Log.record_connection.get_logs(@klass.project_name, @klass.logstore_name, query)
|
83
|
+
JSON.parse(res)
|
84
|
+
end
|
85
|
+
|
86
|
+
def load
|
87
|
+
result.map do |json_attr|
|
88
|
+
attrs = {}
|
89
|
+
@klass.attributes.keys.each do |k, _|
|
90
|
+
attrs[k] = json_attr[k.to_s]
|
91
|
+
end
|
92
|
+
@klass.new(attrs)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
data/lib/aliyun/log/request.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'rest-client'
|
2
4
|
require 'base64'
|
3
5
|
require 'openssl'
|
@@ -18,10 +20,10 @@ module Aliyun
|
|
18
20
|
resources ||= {}
|
19
21
|
res = '/'
|
20
22
|
if resources[:logstore]
|
21
|
-
res
|
22
|
-
res
|
23
|
+
res = "#{res}logstores"
|
24
|
+
res = "#{res}/#{resources[:logstore]}" unless resources[:logstore].empty?
|
23
25
|
end
|
24
|
-
res
|
26
|
+
res = "#{res}/#{resources[:action]}" if resources[:action]
|
25
27
|
res
|
26
28
|
end
|
27
29
|
|
@@ -51,28 +53,24 @@ module Aliyun
|
|
51
53
|
|
52
54
|
def do_request(verb, resources, payload)
|
53
55
|
resource_path = get_resource_path(resources)
|
54
|
-
headers = {}
|
55
|
-
if verb == 'GET'
|
56
|
-
headers = compact_headers
|
57
|
-
headers['Authorization'] = signature(verb, resource_path, headers, payload)
|
58
|
-
else
|
59
|
-
headers = compact_headers(payload, resources[:is_pb])
|
60
|
-
headers['Authorization'] = signature(verb, resource_path, headers)
|
61
|
-
end
|
62
56
|
request_options = {
|
63
57
|
method: verb,
|
64
58
|
url: get_request_url(resources),
|
65
|
-
headers: headers,
|
66
59
|
open_timeout: @config.open_timeout,
|
67
60
|
read_timeout: @config.read_timeout
|
68
61
|
}
|
69
62
|
if verb == 'GET'
|
70
|
-
|
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))
|
71
67
|
else
|
68
|
+
headers = compact_headers(payload, resources[:is_pb])
|
69
|
+
headers['Authorization'] = signature(verb, resource_path, headers)
|
70
|
+
request_options[:headers] = headers
|
72
71
|
payload = Zlib::Deflate.deflate(payload.encode) if resources[:is_pb]
|
73
72
|
request_options[:payload] = payload
|
74
73
|
end
|
75
|
-
logger.debug(request_options)
|
76
74
|
request = RestClient::Request.new(request_options)
|
77
75
|
response = request.execute do |resp|
|
78
76
|
if resp.code >= 300
|
@@ -99,11 +97,12 @@ module Aliyun
|
|
99
97
|
'User-Agent' => "aliyun-log ruby-#{RUBY_VERSION}/#{RUBY_PLATFORM}"
|
100
98
|
}
|
101
99
|
return headers if body.nil?
|
100
|
+
|
102
101
|
if is_pb
|
103
102
|
compressed = Zlib::Deflate.deflate(body.encode)
|
104
103
|
headers['Content-Length'] = compressed.bytesize.to_s
|
105
|
-
|
106
|
-
|
104
|
+
raise 'content length is larger than 3MB' if headers['Content-Length'].to_i > 3_145_728
|
105
|
+
|
107
106
|
headers['Content-MD5'] = Digest::MD5.hexdigest(compressed).upcase
|
108
107
|
headers['Content-Type'] = 'application/x-protobuf'
|
109
108
|
headers['x-log-compresstype'] = 'deflate'
|
@@ -122,7 +121,6 @@ module Aliyun
|
|
122
121
|
@config.access_key_secret,
|
123
122
|
string_to_sign(verb, resource, headers, query).chomp
|
124
123
|
)
|
125
|
-
logger.debug "\n#{string_to_sign(verb, resource, headers, query).chomp}"
|
126
124
|
base64_sign = Base64.strict_encode64(sha1_digest)
|
127
125
|
"LOG #{@config.access_key_id}:#{base64_sign}"
|
128
126
|
end
|
@@ -133,12 +131,12 @@ module Aliyun
|
|
133
131
|
#{headers['Content-MD5']}
|
134
132
|
#{headers['Content-Type']}
|
135
133
|
#{headers['Date']}
|
136
|
-
#{
|
134
|
+
#{canonicalized_headers(headers)}
|
137
135
|
#{canonicalized_resource(resource, query)}
|
138
136
|
DOC
|
139
137
|
end
|
140
138
|
|
141
|
-
def
|
139
|
+
def canonicalized_headers(headers)
|
142
140
|
h = {}
|
143
141
|
headers.each do |k, v|
|
144
142
|
h[k.downcase] = v if k =~ /x-log-.*/
|
@@ -151,11 +149,12 @@ module Aliyun
|
|
151
149
|
|
152
150
|
def canonicalized_resource(resource = '', query = {})
|
153
151
|
return resource if query.empty?
|
152
|
+
|
154
153
|
url = URI.parse(resource)
|
155
|
-
|
154
|
+
sort_str = query.keys.sort.map do |e|
|
156
155
|
"#{e}=#{query[e]}"
|
157
156
|
end.join('&')
|
158
|
-
"#{url}?#{
|
157
|
+
"#{url}?#{sort_str}"
|
159
158
|
end
|
160
159
|
end
|
161
160
|
end
|
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.2
|
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
|
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,11 @@ 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
|
106
139
|
- lib/aliyun/log/request.rb
|
107
140
|
- lib/aliyun/log/server_error.rb
|
108
141
|
- lib/aliyun/version.rb
|