服务端埋点参数获取
-
进入管理控制台-->点击采集信息
-
主域名:服务端埋点数据上报地址
-
副域名:默认和主域名一致,私部署客户可以提供副域名作为备用域名
-
ServiceSecret和ServiceID为校验参数,注意保密不可泄露。
注意:因为上述信息涉及系统安全,所以只有用管理员权限才可以看到!
请求
接口地址:https://<收数域名+端口>/server
请求参数content-type:application/json
请求参数
上报事件请求JSON参数
JSON字段名 (大小写也许保持完全一致) |
数据类型 |
是否必传 |
字段描述 |
示例 |
sign |
string |
是 |
校验签名 |
f564cae6a8ad458648id9d607a124322 |
app_id |
string |
是 |
对应服务端埋点中的 serviceID |
OA8kI9Jis7YJNh5uh |
appkey |
string |
是 |
应用key,从QT管理后台获取 |
9moqdsuia8hvxm7k8shf82n |
id |
string |
是 |
事件编码,用户在QT后台创建事件时设置的编码 |
click |
umid |
string |
二者必须至少有一个上传,建议可以获取puid就尽量上传,保证分析时的IDMapping符合业务需求 |
设备ID |
djajdjdk1 |
puid |
string |
用户ID |
user_001 |
|
page_name |
string |
否 |
页面名称 |
page_index |
ts |
string |
是 |
上报的毫秒级时间戳 |
1659493170125 |
cusp |
object |
否 |
事件属性 |
{"foo": "bar"} |
gp |
object |
否 |
全局属性 |
{"foo": "bar"} |
sdk_type |
string |
是 |
SDK类型,固定值为httpapi |
httpapi |
server_ts |
string |
否,QT服务端可自动生成。 若您手动上传,则以您上传的服务端时间为准,QT服务端不再自动生成。 |
服务端毫秒级时间戳 |
1659493170125 |
uuid |
string |
否, 生成log_id的因子(2.4.17版本支持) |
如果希望log_id可以有较强的唯一性,可以为每条日志上报唯一的uuid,QT服务端将使用该uuid生成log_id |
1234567890 |
log_id |
string |
否,QT服务端自动生成 |
日志标识,默认生成方式为uuid生成,若uuid没有上报,则使用时间戳生成随机id。 |
1234567890 |
事件 报文协议
{
"sign":"27c95768438645138cfb010ded871091", //必填,签名
"app_id":"xxxxxxxxx", //对应服务端埋点信息中的:ServiceID
"appkey":"1344444", //必填,应用appkey
"id" : "get_coupons", //必填,事件编码
"umid" : "edfafs", //设备id
"puid" : "123456", //登录用户id
//umid和puid务必至少上传一个
"page_name":"home_page", //选填,页面编码
"ts" : "1614667799165", //必填,事件发生的时间戳
"cusp" : { //事件自定义参数
"p1" : "1",
"p2" : "2",
"p3" : "3"
"sdk_type":"httpapi", // 固定值,采集服务自动加上该字段
"server_ts":"1614667799165", // 如果主动传入,会使用传入值,不传入会自动使用采集服务采集日志时间戳,采集服务自动加上该字段
"_id":'123344' //日志唯一编码,采集服务自动加上该字段
}
上报用户属性请求JSON参数
JSON字段名 (大小写也需保持完全一致) |
数据类型 |
是否必传 |
字段描述 |
示例 |
sign |
string |
是 |
校验签名 |
f564cae6a8ad458648id9d607a124322 |
app_id |
string |
是 |
对应服务端埋点中的 serviceID |
OA8kI9Jis7YJNh5uh |
appkey |
string |
是 |
应用key,从QT管理后台获取 |
9moqdsuia8hvxm7k8shf82n |
id |
string |
是 |
上报用户属性的事件编码,固定为$$_user_profile |
$$_user_profile |
umid |
string |
否 |
设备ID |
djajdjdk1 |
puid |
string |
是 |
用户ID |
user_001 |
ts |
string |
是 |
上报的毫秒级时间戳 |
1659493170125 |
cusp |
object |
是 |
用户属性 |
{"name": "tom", "gender": "1"} |
sdk_type |
string |
是 |
SDK类型,固定值为httpapi |
httpapi |
server_ts |
string |
否,QT服务端可自动生成。 若您手动上传,则以您上传的服务端时间为准,QT服务端不再自动生成。 |
服务端毫秒级时间戳 |
1659493170125 |
uuid |
string |
否, 生成log_id的因子(2.4.17版本支持) |
如果希望log_id可以有较强的唯一性,可以为每条日志上报唯一的uuid,QT服务端将使用该uuid生成log_id |
1234567890 |
log_id |
string |
否,QT服务端自动生成 |
日志标识,默认生成方式为uuid生成,若uuid没有上报,则使用时间戳生成随机id。 |
1234567890 |
用户属性 报文协议
{
"sign":"27c95768438645138cfb010ded871091", //必填,签名
"app_id":"xxxxxxxxx", //必填,对应服务端埋点信息中的:ServiceID
"appkey":"1344444", //必填,应用appkey
"id" : "$$_user_profile", //必填,标识为用户属性
"umid" : "edfafs", //选填,设备id
"puid" : "123456", //必填,登录用户账号ID
"ts" : "1614667799165", //必填,事件发生的时间戳
"cusp" : { //必填,自定义用户属性
"gender" : "1", //性别
"birthday" : "1988-12-24" //出生年月
"sdk_type" : "httpapi", //固定值,采集服务自动加上该字段
"server_ts":"1614667799165", // 如果主动传入,会使用传入值,不传入会自动使用采集服务采集日志时间戳,采集服务自动加上该字段
"_id":'123344' //日志唯一编码,采集服务自动加上该字段
}
sign生成规则说明
-
先将除了sign字段的其他所有请求字段的key和value封装成一个map对象
-
将这个map对象序列化成JSON字符串,生成时需要将key按照自然排序即ascii码排序,java可使用JSONObject.toJSONString(mapObject, SerializerFeature.MapSortField),(注:json需要进行压缩,去掉个字段之间的空格,如后面的代码块)
-
获取 serviceSecret
-
sign = MD5(排序完生成的JSON字符串 + serviceSecret)
-
将计算得到的sign放到原始的map对象中,再序列化成JSON字符串即为最终完整的JSON请求报文
sign校验流程图
响应说明
响应数据content-type:application/json
响应的JSON字符串中有两个字段,分别为code、message
code |
message |
描述 |
Httpapi_300_200 |
上报成功 |
成功 |
Httpapi_300_101 |
非法的签名 |
签名校验失败,请参考sign生成规则说明 |
Httpapi_300_102 |
上报的数据类型非JSON格式 |
请求参数的数据格式不是JSON格式 |
Httpapi_300_103 |
缺少必要字段 |
参考请求参数表,检查是否遗漏了必填的参数 |
Httpapi_300_104 |
用户属性缺少必要字段 |
参考请求参数表,检查是否遗漏了必填的参数 |
Httpapi_300_105 |
非法事件ID |
事件ID不正确 |
Httpapi_300_106 |
ak/sk不正确 |
|
Java Demo参考
package com.aliyun.app.quickaplus.mock.once_mock;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.aliyun.app.quickaplus.mock.util.MD5Util;
import okhttp3.*;
import java.io.IOException;
* 服务端ApiDemo
* @author chengtao
public class ServiceApiDemo {
* 主域名
private static final String API_URL = "*****";
* 对应服务端埋点信息中的:ServiceID
private static final String SERVICE_ID = "*******";
* 对应服务端埋点信息中的:ServiceSecret
private static final String SERVICE_SECRET = "*******";
* client日志
private static final OkHttpClient client = new OkHttpClient();
* 主函数
* @param args 参数
public static void main(String[] args) {
String srcJsonString = getMockData();
JSONObject json = JSON.parseObject(srcJsonString);
//向报文中添加服务ID
json.put("app_id",SERVICE_ID);
//向报文中添加ts
json.put("ts", String.valueOf(System.currentTimeMillis()));
//计算签名
String sign = MD5Util.md5(JSONObject.toJSONString(json,
SerializerFeature.MapSortField) + SERVICE_SECRET);
json.put("sign", sign);
String desJsonString = JSON.toJSONString(json,
SerializerFeature.DisableCircularReferenceDetect);
Request request = new Request.Builder()
.url(API_URL + "/server")
.post(RequestBody.create(desJsonString,
MediaType.parse("application/json")))
.build();
try {
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) {
System.out.printf("[DEMO] 发送日志失败 %s%n", response);
} else {
System.out.printf("[DEMO] 发送成功 %s%n",
response.body().string());
} catch (IOException e) {
e.printStackTrace();
* 获取MockData
* @return 返回值
private static String getMockData() {
return "{\n" +
" \"appkey\": \"1344444\",\n" +
" \"id\": \"get_coupons\",\n" +
" \"umid\": \"uuid()\",\n" +
" \"puid\": \"puid1\",\n" +
" \"page_name\": \"home_page\",\n" +
" \"cusp\": {\n" +
" \"p1\": \"1\",\n" +
" \"p2\": \"2\",\n" +
" \"p3\": \"3\"\n" +
" },\n" +
" \"gp\": {\n" +
" \"p1\": \"1\",\n" +
" \"p2\": \"2\",\n" +
" \"p3\": \"3\"\n" +
" },\n" +
" \"sdk_type\": \"httpapi\"\n" +
}
Python Demo 参考
# 正确的签名格式
{"appkey": "4b6G49PAkLUb4212","cusp":{"p1":"1","p2":"2","p3":"3"},"gp":{"p1":"1","p2":"2","p3":"3"},"id":"get_coupons","page_name":"home_page","puid":"puid1","sdk_type":"httpapi","umid":"uuid()"}tEkNnx8VDuR0mwEl3hXd7aozYh8Q2qS4
# 错误的签名格式
{"appkey": "您环境下的AppKey", "cusp": {"p1": "1", "p2": "2", "p3": "3"}, "gp": {"p1": "1", "p2": "2", "p3": "3"}, "id": "get_coupons", "page_name": "home_page", "puid": "puid1", "sdk_type": "httpapi", "umid": "uuid()"}tEkNnx8VDuR0mwEl3hXd7aozYh8Q2qS4
import json
import requests
import hashlib
# service_id
service_id = 您环境下的service_id
# service_secret
service_secret = 您环境下的service_secret
# 收数服务配置
qlc_end_point = 您环境下的收数服务地址
# Mock的报文
data = {
"app_id": service_id,
"id": "get_coupons",
"umid": "uuid1",
"puid": "puid1",
"page_name": "home_page",
"ts": 123,
"cusp": {
"p1": "1",
"p2": "2",
"p3": "3"
"gp": {
"p1": "1",
"p2": "2",
"p3": "3"
"sdk_type": "httpapi"
# 按照key的字典序排序
sorted_data = sorted(data.items(), key=lambda item: item[0])
sorted_dict = dict(sorted_data)
string = json.dumps(sorted_dict, separators=(',', ':')) + service_secret
data['sign'] = hashlib.md5(string.encode('utf-8')).hexdigest()
response = requests.post(qlc_end_point + "/server", json=data)
print(response.text)
MD5Util类代码
package com.aliyun.app.quickaplus.mock.util;
import java.security.MessageDigest;
* md5加密工具类
* @author zheng.zhang
public class MD5Util {
public static String md5(String s) {
char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
try {
byte[] btInput = s.getBytes();
// 获得MD5摘要算法的 MessageDigest 对象
MessageDigest mdInst = MessageDigest.getInstance("MD5");
// 使用指定的字节更新摘要
mdInst.update(btInput);
// 获得密文
byte[] md = mdInst.digest();
// 把密文转换成十六进制的字符串形式
int j = md.length;
char str[] = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
return new String(str);
} catch (Exception e) {
return null;
* 单元测试
public static void main(String[] args) {
StringBuilder dtskey = new StringBuilder("abc12390");
dtskey.append('\0').append("~!@#$%^&*()_+");
System.out.println(MD5Util.md5(dtskey.toString()));
System.out.println(MD5Util.md5("20121221"));
System.out.println(MD5Util.md5("加密"));