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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0af7f849c9e5e9e6d59647f4b184c994977407f5b970f5d867505062401621c7
4
- data.tar.gz: bfd3fafdac4839ac11d428638d44d15a4126d283195ddd6e5b033238f3e7b034
3
+ metadata.gz: 5b17c3b8727ec3dc44742d220f22f26014a201334889f888d858a0cbc33b9c39
4
+ data.tar.gz: df953904cf39fea58a11d99fab15ca115e3187667d062dfacec0a6f1301b2510
5
5
  SHA512:
6
- metadata.gz: 033ae7fd59ab6e49b40c071942f289623226ca9a506f88cec0eafdce2edabcc56c5b2daecd7f7c8a6e27df39c11ebf62deab06c3d11b175c4b1f7eeef5284f5f
7
- data.tar.gz: f31dc59e00955ecc7b0f6682356005b7ea2b872527818ea352418a99ddd81a8d3b0fd4d66f7fbda9c92073d9e8d51eca38b7af11364f2bf2d359ad565ac0299b
6
+ metadata.gz: dc5e4072953b2e687f7153230d23f3f6c6d68ac80abb52278819531cb6219eec1c0c9abf593e93d2b05dc697d74f6be75d94c522314088c416bb108631290bd7
7
+ data.tar.gz: 96b65f80b1fba4284d4472e524d8e424d58fff86ebac92cb281481af51134f8a75bb624d8339f67735038d3d36252bb4a6aa66bb3bc117c8706622a80eccefc1
@@ -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
- def self.configure
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
@@ -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
@@ -1,3 +1,4 @@
1
- #
1
+ # frozen_string_literal: true
2
+
2
3
  require_relative 'common/attr_struct'
3
4
  require_relative 'common/logging'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Aliyun
2
4
  module Log
3
5
  module Common
@@ -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 ||= DEFAULT_LOG_FILE, MAX_NUM_LOG, ROTATE_SIZE
41
+ @log_file ||= Config.log_file, MAX_NUM_LOG, ROTATE_SIZE
25
42
  )
26
- @logger.level = Logger::DEBUG
43
+ @logger.level = Logging.logger_level
27
44
  end
28
45
  @logger
29
46
  end
@@ -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
- def normalize_endpoint
32
- uri = URI.parse(endpoint)
33
- uri = URI.parse("http://#{endpoint}") unless uri.scheme
39
+ def normalize_endpoint
40
+ uri = URI.parse(endpoint)
41
+ uri = URI.parse("http://#{endpoint}") unless uri.scheme
34
42
 
35
- if (uri.scheme != 'http') && (uri.scheme != 'https')
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
- @endpoint = uri.to_s
40
- end
45
+ @endpoint = uri.to_s
46
+ end
41
47
  end
42
48
  end
43
49
  end
@@ -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
- contents = attributes.map { |k, v| { key: k.to_s, value: v.to_s } }
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 = {})
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Aliyun
2
4
  module Log
3
5
  class Project < Common::AttrStruct
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'protobuf'
2
4
 
3
5
  module Aliyun
@@ -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
- logstore_name: logstore_name,
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 put_log(project_name, logstore_name, log_attr)
104
- contents = log_attr.compact.map { |k, v| { key: k, value: v } }
105
- log_pb = Protobuf::Log.new(time: Time.now.to_i, contents: contents)
106
- lg_pb = Protobuf::LogGroup.new(logs: [log_pb])
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,9 @@
1
+ module Aliyun
2
+ module Log
3
+ module Record
4
+ class ArgumentError < StandardError; end
5
+ class UnknownAttributeError < StandardError; end
6
+ class ProjectNameError < StandardError; end
7
+ end
8
+ end
9
+ 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
@@ -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 << 'logstores'
22
- res << "/#{resources[:logstore]}" unless resources[:logstore].empty?
23
+ res = "#{res}logstores"
24
+ res = "#{res}/#{resources[:logstore]}" unless resources[:logstore].empty?
23
25
  end
24
- res << "/#{resources[:action]}" if resources[:action]
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
- request_options[:url] = canonicalized_resource(request_options[:url], payload)
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
- # 日志内容包含的日志必须小于3MB和4096条。
106
- raise 'content length is larger than 3MB' if headers['Content-Length'].to_i > 3145728
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
- #{canonicalized_sls_headers(headers)}
134
+ #{canonicalized_headers(headers)}
137
135
  #{canonicalized_resource(resource, query)}
138
136
  DOC
139
137
  end
140
138
 
141
- def canonicalized_sls_headers(headers)
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
- q_str = query.keys.sort.map do |e|
154
+ sort_str = query.keys.sort.map do |e|
156
155
  "#{e}=#{query[e]}"
157
156
  end.join('&')
158
- "#{url}?#{q_str}"
157
+ "#{url}?#{sort_str}"
159
158
  end
160
159
  end
161
160
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'json'
2
4
 
3
5
  module Aliyun
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Aliyun
2
4
  module Log
3
- VERSION = "0.1.0"
5
+ VERSION = '0.2.2'
4
6
  end
5
7
  end
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.1.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-04 00:00:00.000000000 Z
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.1.0
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.1.0
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