推送实时日志
本文档将指引您如何将日志推送到指定的服务内。
步骤1:选择日志源
1. 登录 边缘安全加速平台控制台,在左侧菜单栏中,单击站点列表,在站点列表内单击需配置的站点,进入站点详情页面。
2. 在站点详情页面,单击日志服务 > 实时日志。
3. 在实时日志页面,单击新建推送任务。
4. 在选择日志源页面,选择需要推送的日志源信息,配置相关参数,单击下一步。
日志类型:可选站点加速日志、四层代理日志、速率限制和 CC 攻击防护日志、Web 攻击防护日志、自定义规则日志、Bot 管理日志;
服务区域:选择需要推送的日志区域,EdgeOne 实时日志推送任务可分别推送「中国大陆可用区」或「全球可用区(不包括中国大陆)」的日志,但无法直接推送「全球可用区」的日志。如果您需要推送「全球可用区」的日志,请建立两个推送任务,一个针对「中国大陆可用区」,另一个针对「全球可用区(不包括中国大陆)」。
域名:选择需要推送日志的子域名或四层实例。同一份日志不支持配置多个推送任务,即相同地域下子域名/四层代理实例的日志仅支持配置一个推送任务,例如:
www.example.com
的「中国大陆可用区」站点加速日志配置了日志推送任务 A,此时日志推送任务 B 无法选择到 www.example.com
。步骤2:选择日志字段
1. 在选择日志字段中,配置需要推送的字段内容,您可以在字段列表中,通过勾选进行选择;相关字段说明请参考:实时日志字段说明。
说明:
目前仅 站点加速日志 和 四层代理日志 支持自定义选择需要推送的日志。
2. (可选)如果您需要推送 HTTP 请求头,HTTP 响应头或 Cookie 中的某些元素记录以进行分析时,您可以点击添加自定义字段,配置需要推送的 HTTP 请求头、HTTP 响应头或 Cookie 名称。您可以通过键值对形式将此类信息精确记录在日志中。以
Accept-Language
头为例,其对应的信息可以直接通过日志中的 Accept-Language
字段获取。说明:
1. 字段默认区分大小写,因此需要与原始字段完成匹配;
2. 目前仅站点加速日志支持添加自定义字段。
3. (可选)如果您的日志量非常大,实时日志推送后仅用于监控及分析,不需要全量的日志数据,您可以点击高级配置,配置采样比例来降低日志推送的数量。配置后,EdgeOne 将按照设定的百分比随机抽取日志,然后将其推送到您指定的目的地。
4. 配置完成日志字段后,点击下一步,进入步骤3。
步骤3:选择推送目的地
选您可以根据需要推送的实时日志目的地,选择推送至腾讯云 CLS、S3 兼容存储桶或者指定的 HTTP 服务器内,参考如下步骤进行配置:
如果当前您还未自建数据分析系统,腾讯云提供了日志服务(CLS)可帮助您一站式完成实时日志的采集、推送与检索分析,减少您的开发及维护成本。您可以参考以下步骤将实时日志推送至腾讯云 CLS 服务内:
前提条件
操作步骤
创建推送任务
1. 在 第 ③ 步 中选择目的地为 腾讯云日志服务(CLS),并点击 下一步 。
2. 填写相关参数信息,参数说明如下:
地域:选择需要推送的目标地域。
目标集名称:选择目标地域下的日志集。
说明
若此处为空或需要新建日志集,请单击创建,在所选地域下创建日志集。
日志主题名称:可输入1-200个字符,允许的字符为
a-z, A-Z, 0-9, _, -
。日志保存时间:请输入1-366间正整数。
相关参考
日志检索
您可后续通过 日志服务(CLS) 侧管理日志集等模块,如修改日志集名称。
日志集
日志集(Logset)是腾讯云日志服务(CLS)的项目管理单元,用于区分不同项目的日志,一个日志集对应合集。腾讯云 EdgeOne 日志集有以下基本属性信息:
地域:日志集所属 地域 。
日志集名称:日志集命名。
日志保留时间:当前日志集里数据的保存时间周期。
创建时间:日志集创建时间。
日志主题
日志主题(Topic)是腾讯云日志服务(CLS)的基本管理单元,一个日志集可以包含多个日志主题。一个日志主题对应一类应用或服务,建议将不同机器上的同类日志收集到同一个日志主题中。例如,一个业务项目有三种日志:操作日志、应用程序日志、访问日志,每种类型可以创建对应日志主题。
日志服务系统以日志主题为单位,区分管理用户不同的日志数据,每个日志主题都可以配置不同的数据源、不同的索引规则和投递规则。因此,日志主题是日志服务配置、管理日志数据的基本单元,创建日志主题后需配置相关规则,才能如期有效地进行日志采集,并使用检索分析和投递等功能。
从场景功能上理解,日志主题主要提供:
采集日志到日志主题。
以日志主题为单元存储管理日志。
以日志主题为单元检索分析日志。
以日志主题为单元投递日志到其他平台。
从日志主题下载、消费日志。
如果您当前已有自建的数据源,需要将实时日志推送到兼容 S3 存储桶,您可以参考以下步骤操作继续操作:
操作步骤
1. 在 第 ③ 步 中选择目的地为 S3 兼容,并点击 下一步 。
2. 填写对应的目的地参数:
端点 URL:不包含存储桶名称或路径的 URL,例如:
https://storage.googleapis.com
、https://s3.ap-northeast-2.amazonaws.com
。存储桶地域:存储桶所在的地域,例如:
ap-northeast-2
。存储桶:存储桶名称和对应的日志存储路径:例如:
your_bucket_name/EO-logs/
。文件压缩:是否使用 gzip 压缩日志文件,勾选后,推送的日志文件将使用 gzip 压缩,文件名称将改为
filename.log.gz
。SecretId:访问存储桶使用的 Access Key ID。
SecretKey:访问存储桶使用的 secret key。
说明:
1. 存储桶需要兼容 AWS Signature Version 4 鉴权算法,具体兼容情况请参考您的存储桶提供方的说明。
2. 文件名称说明:日志将会在指定存储桶路径下以
UploadTime_Random.log
格式存储,且会以日期(UTC +00:00)为一个文件夹归档日志,例如:logs/20230331/20230331T185917Z_2aadf5ce.log
。UploadTime:日志文件上传时间,使用 ISO-8601 格式,UTC+00:00 时区。
Random:随机字符,当日志量较大的情况,可能会出现同一个上传时间有多个日志文件,通过此串随机字符来标识不同的文件。
3. 单击推送,下发实时日志推送任务后,EdgeOne 将推送一个测试文件至目标存储桶路径以校验连通性,例如
1699874755_edgeone_push_test.txt
,文件内容为固定字符串“test”。如果您当前已有自建的数据源,EdgeOne 可通过 HTTP POST 请求调用您提供的后端接口地址,将日志在 HTTP body 中传输到您指定的服务器上。
说明:
1. HTTP 是明文传输 ,因此接口地址建议您使用 HTTPS 加密后地址。
2. 为了进一步加强请求来源的安全性验证,我们提供了请求鉴权方案,可在配置推送目的地信息中填写相关鉴权信息,鉴权算法见:鉴权算法参考。
3. 推送日志格式为多个 JSON 对象组成的数组,每个 JSON 对象为一条日志。
操作指引
创建推送任务
1. 在 第 ③ 步 中选择目的地为 HTTP 服务(POST),并点击 下一步 。
2. 填写相关目的地及参数信息,参数说明如下:
接口地址:填入您的数据源接口地址,例如:
https://www.example.com/log
文件压缩:为减少日志文件的大小,节约流量开销,您可以通过勾选 使用 gzip 压缩日志文件 开启文件压缩,EdgeOne 将会使用 gzip 格式压缩日志后再传输日志,并且会增加 HTTP 头部 `
content-encoding = gzip
来标明压缩格式。源站鉴权:选择为加密鉴权时,推送日志时将携带鉴权信息供源站进行验证,保证数据来源身份的安全性。
自定义 HTTP 请求头:添加需要 EdgeOne 发起请求时携带的 HTTP 头部。例如:您需要通过头部识别日志来源的厂商是 EdgeOne,您可以添加一个头部
log-source = EdgeOne
来识别日志是来源。
3. 单击推送,即可下发实时日志推送任务。
4. 实时日志推送任务在配置阶段为了校验接口连通性,将向接口地址发送一个空数据进行验证,数据格式如下所示:
[{"BotClassAccountTakeOver": "-","BotClassAttacker": "-","BotClassMaliciousBot": "-","BotClassProxy": "-","BotClassScanner": "-","ClientDeviceType": "-","ClientIP": "-","ClientISP": "-","ClientRegion": "-","ClientState": "-","EdgeCacheStatus": "-","EdgeEndTime": "-","EdgeInternalTime": "-","EdgeResponseBodyBytes": "-","EdgeResponseBytes": "-","EdgeResponseStatusCode": "-","EdgeResponseTime": "-","EdgeServerID": "-","EdgeServerIP": "-","EdgeSeverRegion": "-","LogTime": "-","OriginDNSResponseDuration": "-","OriginIP": "-","OriginRequestHeaderSendDuration": "-","OriginResponseHeaderDuration": "-","OriginResponseStatusCode": "-","OriginSSLProtocol": "-","OriginTCPHandshakeDuration": "-","OriginTLSHandshakeDuration": "-","ParentRequestID": "-","RemotePort": "-","RequestBytes": "-","RequestHost": "-","RequestID": "-","RequestMethod": "-","RequestProtocol": "-","RequestRange": "-","RequestReferer": "-","RequestSSLProtocol": "-","RequestTime": "-","RequestUA": "-","RequestUrl": "-","RequestUrlQueryString": "-"}]
相关参考
请求鉴权算法
如果您在推送目的地信息中,源站鉴权内选择了加密签名,可自定义输入您自定义配置 SecretId 和 SecretKey,EdgeOne 将在请求 URL 中增加签名
auth_key
和 access_key
,签名算法详情如下:1. 请求URL构成
如下所示,请求 URL 将在?后携带
auth_key
和 access_key
。http://DomainName[:port]/[uri]?auth_key=timestamp-rand-md5hash&access_key=SecretID
参数说明:
timestamp:请求当前时间,使用 Unix 秒级10位时间戳。
rand:随机数
access_key:用于标识接口请求方的身份,即您所自定义配置的 SecretID。
SecretKey:固定长度32,即您所自定义配置的 SecretKey。
uri:资源标识符,例如:
/access_log/post
。md5hash:md5hash =
md5sum(string_to_sign)
,其中 string_to_sign ="uri-timestamp-rand-SecretKey"
。通过md5算法计算出的验证串,数字0-9和小写英文字母 a-z 混合,固定长度32。2. 计算示例
假定填入参数为:
接口地址:
https://www.example.com/access_log/post
SecretID = YourID
SecretKey = YourKey
uri = /access_log/post
timestamp = 1571587200
rand = 0
string_to_sign = "/access_log/post-1571587200-0-YourKey"
基于该字符串计算出
md5hash=md5sum("/access_log/post-1571587200-0-YourKey")=1f7ffa7bff8f06bbfbe2ace0f14b7e16
最终推送时的请求 url 为:
https://www.example.com
/cdnlog/post?auth_key=1571587200-0-1f7ffa7bff8f06bbfbe2ace0f14b7e16&access_key=YourID
服务端在接收到推送请求后,提取
auth_key
的值. 对auth_key的值进行拆分,获取timestamp
,rand
和md5hash
。可先检查 timestamp 是否过期,过期时间建议为300s
,并基于上述规则拼装加密字符串,利用 SecretKey 拼装出需加密的字符串,加密后与 auth_key
中的 md5hash
值进行比较, 相同则说明鉴权通过。3. 服务端解析鉴权请求代码示例
import hashlibfrom flask import Flask, requestapp = Flask(__name__)def get_rsp(msg, result={}, code=0):return {"respCode": code,"respMsg": msg,"result": result}def get_secret_key(access_key):return "secret_key"@app.route("/access_log/post", methods=['POST'])def access_log():if request.method == 'POST':if request.content_type.startswith('application/json'):current_time_ts, rand_num, md5hash = request.args.get("auth_key").split("-")# 判断请求时间是否是在有效期内if time.time() - int(current_time_ts) > 300:return get_rsp(msg="The request is out of time", code=-1)access_key = request.args.get("access_key")# 通过access_key(SecretID)获取secret_keysecret_key = get_secret_key(access_key)raw_str = "%s-%s-%s-%s" % (request.path, current_time_ts, rand_num, secret_key)auth_md5hash = hashlib.md5(raw_str.encode("utf-8")).hexdigest()if auth_md5hash == md5hash:# 认证通过if request.headers['content-encoding'] == 'gzip':# 解压数据pass# 数据处理return get_rsp("ok")return get_rsp(msg="Please use content_type by application/json", code=-1)return get_rsp(msg="The request method not find, method == %s" % request.method, code=-1)if __name__ == '__main__':app.run(host='0.0.0.0', port=8888, debug=True)python
package mainimport ("context""crypto/md5""fmt""log""net/http""os""os/signal""strings""syscall")func main() {mux := http.NewServeMux()mux.Handle("/access_log/post", &logHandler{})server := &http.Server{Addr: ":5000",Handler: mux,}// 创建系统信号接收器done := make(chan os.Signal)signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)go func() {<-doneif err := server.Shutdown(context.Background()); err != nil {log.Fatal("Shutdown server:", err)}}()err := server.ListenAndServe()if err != nil {if err == http.ErrServerClosed {log.Print("Server closed under request")} else {log.Fatal("Server closed unexpected")}}}type logHandler struct{}func (*logHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {if r.Method == "POST" {query := r.URL.Query()authKey := query.Get("auth_key")accessKey := query.Get("access_key")//access_key 即您提供的SecretIDauthKeys := strings.Split(authKey, "-")if len(authKeys) == 3 {currentTimeTs := authKeys[0]//进行时间戳有效期判断RandNum := authKeys[1]md5Hash := authKeys[2]secretKey := getSecretKey(accessKey)authStr := fmt.Sprintf("%s-%s-%s-%s", "/access_log/post", currentTimeTs, RandNum, secretKey)data := []byte(authStr)has := md5.Sum(data)authMd5 := fmt.Sprintf("%x", has) //转换成字符串进行比较if authMd5 == md5Hash {// todo 认证成功if r.Header.Get("Content-Encoding") == "gzip" {//解压数据}//数据处理}} else {//异常处理}}}// 获取SecretKeyfunc getSecretKey(accessKey string) string {if accessKey != "" {// 通过Access_key(SecretID)获取Secret_Keyreturn "secret_key"}return ""}