thinkingdata-ruby 1.2.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,57 +0,0 @@
1
- require 'logger'
2
- require 'thinkingdata-ruby/errors'
3
-
4
- module TDAnalytics
5
- # 将数据写入本地文件, 需配合 LogBus 将数据上传到服务器
6
- # 由于 LogBus 有完善的失败重传机制,因此建议用户首先考虑此方案
7
- class LoggerConsumer
8
- # LoggerConsumer 构造函数
9
- # log_path: 日志文件存放目录
10
- # mode: 日志文件切分模式,可选 daily/hourly
11
- # prefix: 日志文件前缀,默认为 'tda.log', 日志文件名格式为: tda.log.2019-11-15
12
- def initialize(log_path='.', mode='daily', prefix:'tda.log')
13
- case mode
14
- when 'hourly'
15
- @suffix_mode = '%Y-%m-%d-%H'
16
- when 'daily'
17
- @suffix_mode = '%Y-%m-%d'
18
- else
19
- raise IllegalParameterError.new("#{mode} is unsupported for LoggerConsumer. Replaced it by daily or hourly")
20
- end
21
-
22
- raise IllegalParameterError.new("prefix couldn't be empty") if prefix.nil? || prefix.length == 0
23
-
24
- @current_suffix = Time.now.strftime(@suffix_mode)
25
-
26
- @full_prefix = "#{log_path}/#{prefix}."
27
-
28
- _reset
29
- end
30
-
31
- def add(msg)
32
- unless Time.now.strftime(@suffix_mode) == @current_suffix
33
- @logger.close
34
- @current_suffix = Time.now.strftime(@suffix_mode)
35
- _reset
36
- end
37
- @logger.info(msg.to_json)
38
- end
39
-
40
- # 关闭 logger
41
- def close
42
- @logger.close
43
- end
44
-
45
- private
46
-
47
- # 重新创建 logger 对象. LogBus 判断新文件会同时考虑文件名和 inode,因此默认的切分方式会导致数据重传
48
- def _reset
49
- @logger = Logger.new("#{@full_prefix}#{@current_suffix}")
50
- @logger.level = Logger::INFO
51
- @logger.formatter = proc do |severity, datetime, progname, msg|
52
- "#{msg}\n"
53
- end
54
- end
55
-
56
- end
57
- end
@@ -1,402 +0,0 @@
1
- require 'securerandom'
2
- require 'thinkingdata-ruby/errors'
3
- require 'thinkingdata-ruby/version'
4
-
5
- module TDAnalytics
6
- # TDAnalytics::Tracker 是数据上报的核心类,使用此类上报事件数据和更新用户属性.
7
- # 创建 Tracker 类需要传入 consumer 对象,consumer 决定了如何处理格式化的数据(存储在本地日志文件还是上传到服务端).
8
- #
9
- # ta = TDAnalytics::Tracker.new(consumer)
10
- # ta.track('your_event', distinct_id: 'distinct_id_of_user')
11
- #
12
- # TDAnalytics 提供了三种 consumer 实现:
13
- # LoggerConsumer: 数据写入本地文件
14
- # DebugConsumer: 数据逐条、同步的发送到服务端,并返回详细的报错信息
15
- # BatchConsumer: 数据批量、同步的发送到服务端
16
- #
17
- # 您也可以传入自己实现的 Consumer,只需实现以下接口:
18
- # add(message): 接受 hash 类型的数据对象
19
- # flush: (可选) 将缓冲区的数据发送到指定地址
20
- # close: (可选) 程序退出时用户可以主动调用此接口以保证安全退出
21
- class Tracker
22
-
23
- LIB_PROPERTIES = {
24
- '#lib' => 'ruby',
25
- '#lib_version' => TDAnalytics::VERSION,
26
- }
27
-
28
- # SDK 构造函数,传入 consumer 对象
29
- #
30
- # 默认情况下,除参数不合法外,其他 Error 会被忽略,如果您希望自己处理接口调用中的 Error,可以传入自定义的 error handler.
31
- # ErrorHandler 的定义可以参考 thinkingdata-ruby/errors.rb
32
- #
33
- # uuid 如果为 true,每条数据都会被带上随机 UUID 作为 #uuid 属性的值上报,该值不会入库,仅仅用于后台做数据重复检测
34
- def initialize(consumer, error_handler = nil, uuid: false)
35
- @error_handler = error_handler || ErrorHandler.new
36
- @consumer = consumer
37
- @super_properties = {}
38
- @uuid = uuid
39
- end
40
-
41
- # 设置公共事件属性,公共事件属性是所有事件都会带上的属性. 此方法会将传入的属性与当前公共属性合并.
42
- # 如果希望跳过本地格式校验,可以传入值为 true 的 skip_local_check 参数
43
- def set_super_properties(properties, skip_local_check = false)
44
- unless skip_local_check || _check_properties(:track, properties)
45
- @error_handler.handle(IllegalParameterError.new("Invalid super properties"))
46
- return false
47
- end
48
- properties.each do |k, v|
49
- if v.is_a?(Time)
50
- @super_properties[k] = _format_time(v)
51
- else
52
- @super_properties[k] = v
53
- end
54
- end
55
- end
56
-
57
- # 清除公共事件属性
58
- def clear_super_properties
59
- @super_properties = {}
60
- end
61
-
62
- # 上报事件. 每个事件都包含一个事件名和 Hash 对象的时间属性. 其参数说明如下:
63
- # event_name: (必须) 事件名 必须是英文字母开头,可以包含字母、数字和 _, 长度不超过 50 个字符.
64
- # distinct_id: (可选) 访客 ID
65
- # account_id: (可选) 账号ID distinct_id 和 account_id 不能同时为空
66
- # properties: (可选) Hash 事件属性。支持四种类型的值:字符串、数值、Time、boolean
67
- # time: (可选)Time 事件发生时间,如果不传默认为系统当前时间
68
- # ip: (可选) 事件 IP,如果传入 IP 地址,后端可以通过 IP 地址解析事件发生地点
69
- # skip_local_check: (可选) boolean 表示是否跳过本地检测
70
- def track(event_name: nil, distinct_id: nil, account_id: nil, properties: {}, time: nil, ip: nil,first_check_id:nil, skip_local_check: false)
71
- begin
72
- _check_name event_name
73
- _check_id(distinct_id, account_id)
74
- unless skip_local_check
75
- _check_properties(:track, properties)
76
- end
77
- rescue TDAnalyticsError => e
78
- @error_handler.handle(e)
79
- return false
80
- end
81
-
82
- data = {}
83
- data[:event_name] = event_name
84
- data[:distinct_id] = distinct_id if distinct_id
85
- data[:account_id] = account_id if account_id
86
- data[:time] = time if time
87
- data[:ip] = ip if ip
88
- data[:first_check_id] = first_check_id if first_check_id
89
- data[:properties] = properties
90
-
91
- _internal_track(:track, data)
92
- end
93
-
94
- # 上报事件数据可进行更新. 每个事件都包含一个事件名和事件ID以及 Hash 对象的时间属性. 其参数说明如下:
95
- # event_name: (必须) 事件名 必须是英文字母开头,可以包含字母、数字和 _, 长度不超过 50 个字符.
96
- # event_id:(必须) event_name + event_id 会作为一条事件的唯一键
97
- # distinct_id: (可选) 访客 ID
98
- # account_id: (可选) 账号ID distinct_id 和 account_id 不能同时为空
99
- # properties: (可选) Hash 事件属性。支持四种类型的值:字符串、数值、Time、boolean
100
- # time: (可选)Time 事件发生时间,如果不传默认为系统当前时间
101
- # ip: (可选) 事件 IP,如果传入 IP 地址,后端可以通过 IP 地址解析事件发生地点
102
- # skip_local_check: (可选) boolean 表示是否跳过本地检测
103
- def track_overwrite(event_name: nil,event_id: nil, distinct_id: nil, account_id: nil, properties: {}, time: nil, ip: nil, skip_local_check: false)
104
- begin
105
- _check_name event_name
106
- _check_event_id event_id
107
- _check_id(distinct_id, account_id)
108
- unless skip_local_check
109
- _check_properties(:track_overwrite, properties)
110
- end
111
- rescue TDAnalyticsError => e
112
- @error_handler.handle(e)
113
- return false
114
- end
115
-
116
- data = {}
117
- data[:event_name] = event_name
118
- data[:event_id] = event_id
119
- data[:distinct_id] = distinct_id if distinct_id
120
- data[:account_id] = account_id if account_id
121
- data[:time] = time if time
122
- data[:ip] = ip if ip
123
- data[:properties] = properties
124
- _internal_track(:track_overwrite, data)
125
- end
126
-
127
-
128
-
129
- # 上报事件数据可进行覆盖. 每个事件都包含一个事件名和事件ID以及 Hash 对象的时间属性. 其参数说明如下:
130
- # event_name: (必须) 事件名 必须是英文字母开头,可以包含字母、数字和 _, 长度不超过 50 个字符.
131
- # event_id:(必须) event_name + event_id 会作为一条事件的唯一键
132
- # distinct_id: (可选) 访客 ID
133
- # account_id: (可选) 账号ID distinct_id 和 account_id 不能同时为空
134
- # properties: (可选) Hash 事件属性。支持四种类型的值:字符串、数值、Time、boolean
135
- # time: (可选)Time 事件发生时间,如果不传默认为系统当前时间
136
- # ip: (可选) 事件 IP,如果传入 IP 地址,后端可以通过 IP 地址解析事件发生地点
137
- # skip_local_check: (可选) boolean 表示是否跳过本地检测
138
- def track_update(event_name: nil,event_id: nil, distinct_id: nil, account_id: nil, properties: {}, time: nil, ip: nil, skip_local_check: false)
139
- begin
140
- _check_name event_name
141
- _check_event_id event_id
142
- _check_id(distinct_id, account_id)
143
- unless skip_local_check
144
- _check_properties(:track_update, properties)
145
- end
146
- rescue TDAnalyticsError => e
147
- @error_handler.handle(e)
148
- return false
149
- end
150
-
151
- data = {}
152
- data[:event_name] = event_name
153
- data[:event_id] = event_id
154
- data[:distinct_id] = distinct_id if distinct_id
155
- data[:account_id] = account_id if account_id
156
- data[:time] = time if time
157
- data[:ip] = ip if ip
158
- data[:properties] = properties
159
- _internal_track(:track_update, data)
160
- end
161
-
162
- # 设置用户属性. 如果出现同名属性,则会覆盖之前的值.
163
- # distinct_id: (可选) 访客 ID
164
- # account_id: (可选) 账号ID distinct_id 和 account_id 不能同时为空
165
- # properties: (可选) Hash 用户属性。支持四种类型的值:字符串、数值、Time、boolean
166
- def user_set(distinct_id: nil, account_id: nil, properties: {}, ip: nil)
167
- begin
168
- _check_id(distinct_id, account_id)
169
- _check_properties(:user_set, properties)
170
- rescue TDAnalyticsError => e
171
- @error_handler.handle(e)
172
- return false
173
- end
174
-
175
- _internal_track(:user_set,
176
- distinct_id: distinct_id,
177
- account_id: account_id,
178
- properties: properties,
179
- ip: ip,
180
- )
181
- end
182
-
183
- # 设置用户属性. 如果有重名属性,则丢弃, 参数与 user_set 相同
184
- def user_set_once(distinct_id: nil, account_id: nil, properties: {}, ip: nil)
185
- begin
186
- _check_id(distinct_id, account_id)
187
- _check_properties(:user_setOnce, properties)
188
- rescue TDAnalyticsError => e
189
- @error_handler.handle(e)
190
- return false
191
- end
192
-
193
- _internal_track(:user_setOnce,
194
- distinct_id: distinct_id,
195
- account_id: account_id,
196
- properties: properties,
197
- ip: ip,
198
- )
199
- end
200
-
201
- # 追加用户的一个或多个列表类型的属性
202
- def user_append(distinct_id: nil, account_id: nil, properties: {})
203
- begin
204
- _check_id(distinct_id, account_id)
205
- _check_properties(:user_append, properties)
206
- rescue TDAnalyticsError => e
207
- @error_handler.handle(e)
208
- return false
209
- end
210
-
211
- _internal_track(:user_append,
212
- distinct_id: distinct_id,
213
- account_id: account_id,
214
- properties: properties,
215
- )
216
- end
217
-
218
- # 删除用户属性, property 可以传入需要删除的用户属性的 key 值,或者 key 值数组
219
- def user_unset(distinct_id: nil, account_id: nil, property: nil)
220
- properties = {}
221
- if property.is_a?(Array)
222
- property.each do |k|
223
- properties[k] = 0
224
- end
225
- else
226
- properties[property] = 0
227
- end
228
-
229
- begin
230
- _check_id(distinct_id, account_id)
231
- _check_properties(:user_unset, properties)
232
- rescue TDAnalyticsError => e
233
- @error_handler.handle(e)
234
- return false
235
- end
236
-
237
- _internal_track(:user_unset,
238
- distinct_id: distinct_id,
239
- account_id: account_id,
240
- properties: properties,
241
- )
242
- end
243
-
244
- # 累加用户属性, 如果用户属性不存在,则会设置为 0,然后再累加
245
- # distinct_id: (可选) 访客 ID
246
- # account_id: (可选) 账号ID distinct_id 和 account_id 不能同时为空
247
- # properties: (可选) Hash 数值类型的用户属性
248
- def user_add(distinct_id: nil, account_id: nil, properties: {})
249
- begin
250
- _check_id(distinct_id, account_id)
251
- _check_properties(:user_add, properties)
252
- rescue TDAnalyticsError => e
253
- @error_handler.handle(e)
254
- return false
255
- end
256
-
257
- _internal_track(:user_add,
258
- distinct_id: distinct_id,
259
- account_id: account_id,
260
- properties: properties,
261
- )
262
- end
263
-
264
- # 删除用户,用户之前的事件数据不会被删除
265
- def user_del(distinct_id: nil, account_id: nil)
266
- begin
267
- _check_id(distinct_id, account_id)
268
- rescue TDAnalyticsError => e
269
- @error_handler.handle(e)
270
- return false
271
- end
272
-
273
- _internal_track(:user_del,
274
- distinct_id: distinct_id,
275
- account_id: account_id,
276
- )
277
- end
278
-
279
- # 立即上报数据,对于 BatchConsumer 会触发上报
280
- def flush
281
- return true unless defined? @consumer.flush
282
- ret = true
283
- begin
284
- @consumer.flush
285
- rescue TDAnalyticsError => e
286
- @error_handler.handle(e)
287
- ret = false
288
- end
289
- ret
290
- end
291
-
292
- # 退出前调用,保证 Consumer 安全退出
293
- def close
294
- return true unless defined? @consumer.close
295
- ret = true
296
- begin
297
- @consumer.close
298
- rescue TDAnalyticsError => e
299
- @error_handler.handle(e)
300
- ret = false
301
- end
302
- ret
303
- end
304
-
305
- private
306
-
307
- # 出现异常的时候返回 false, 否则 true
308
- def _internal_track(type, properties: {}, event_name: nil, event_id:nil, account_id: nil, distinct_id: nil, ip: nil,first_check_id: nil, time: Time.now)
309
- if account_id == nil && distinct_id == nil
310
- raise IllegalParameterError.new('account id or distinct id must be provided.')
311
- end
312
-
313
- if type == :track || type == :track_update || type == :track_overwrite
314
- raise IllegalParameterError.new('event name is empty') if event_name == nil
315
- properties = {'#zone_offset': time.utc_offset / 3600.0}.merge(LIB_PROPERTIES).merge(@super_properties).merge(properties)
316
- end
317
-
318
- # 格式化 Time 类型
319
- properties.each do |k, v|
320
- if v.is_a?(Time)
321
- properties[k] = _format_time(v)
322
- end
323
- end
324
-
325
- data = {
326
- '#type' => type,
327
- '#time' => _format_time(time),
328
- 'properties' => properties,
329
- }
330
-
331
- data['#event_name'] = event_name if (type == :track || type == :track_update || :track_overwrite)
332
- data['#event_id'] = event_id if (type == :track_update || type == :track_overwrite)
333
- data['#account_id'] = account_id if account_id
334
- data['#distinct_id'] = distinct_id if distinct_id
335
- data['#ip'] = ip if ip
336
- data['#first_check_id'] = first_check_id if first_check_id
337
- data['#uuid'] = SecureRandom.uuid if @uuid
338
-
339
- ret = true
340
- begin
341
- @consumer.add(data)
342
- rescue TDAnalyticsError => e
343
- @error_handler.handle(e)
344
- ret = false
345
- end
346
-
347
- ret
348
- end
349
-
350
- # 将 Time 类型格式化为数数指定格式的字符串
351
- def _format_time(time)
352
- time.strftime("%Y-%m-%d %H:%M:%S.#{((time.to_f * 1000.0).to_i % 1000).to_s.rjust(3, "0")}")
353
- end
354
-
355
- def _check_event_id(event_id)
356
- raise IllegalParameterError.new("the event_id or property cannot be nil") if event_id.nil?
357
- true
358
- end
359
-
360
- # 属性名或者事件名检查
361
- def _check_name(name)
362
- raise IllegalParameterError.new("the name of event or property cannot be nil") if name.nil?
363
-
364
- unless name.instance_of?(String) || name.instance_of?(Symbol)
365
- raise IllegalParameterError.new("#{name} is invalid. It must be String or Symbol")
366
- end
367
- true
368
- end
369
-
370
- # 属性类型检查
371
- def _check_properties(type, properties)
372
- unless properties.instance_of? Hash
373
- return false
374
- end
375
-
376
- properties.each do |k, v|
377
- _check_name k
378
- next if v.nil?
379
- unless v.is_a?(Integer) || v.is_a?(Float) || v.is_a?(Symbol) || v.is_a?(String) || v.is_a?(Time) || !!v == v || v.is_a?(Array)
380
- raise IllegalParameterError.new("The value of properties must be type in Integer, Float, Symbol, String, Array,and Time")
381
- end
382
-
383
- if type == :user_add
384
- raise IllegalParameterError.new("Property value for user add must be numbers") unless v.is_a?(Integer) || v.is_a?(Float)
385
- end
386
- if v.is_a?(Array)
387
- v.each_index do |i|
388
- if v[i].is_a?(Time)
389
- v[i] = _format_time(v[i])
390
- end
391
- end
392
- end
393
- end
394
- true
395
- end
396
-
397
- # 检查用户 ID 合法性
398
- def _check_id(distinct_id, account_id)
399
- raise IllegalParameterError.new("account id or distinct id must be provided.") if distinct_id.nil? && account_id.nil?
400
- end
401
- end
402
- end
@@ -1,3 +0,0 @@
1
- module TDAnalytics
2
- VERSION = '1.2.0'
3
- end