aliyun-log 0.1.0 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
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