入门参考上一篇:logstash快速入门

实际上,filebeat的iis模块对日志的处理已经很完美了。但是,filebeat是用elasticsearch的pipeline去解析字段的,需要提前setup各种准备,且解析的负荷落在了elasticsearch上。所以,我还是研究了下用logstash去解析filebeat下来的信息。

filter

排除日志注释行

  if [message] =~ "^#" {drop {}}

[message]表示的是message这个字段,=~表示匹配,iis日志是#开头的。drop表示删除到达此过滤器的所有内容。

为了做对比,以下没有移除注释行。

grok vs dissect

我们使用grok来匹配message中的数据。
下面简单解析iis日志为例子学习filter

grok和dissect都有用定界符将非结构化事件数据提取到字段中的效果。

plugin difference
grok 使用正则表达式,数据一行行变化效果好
dissect 不使用正则表达式,速度更快,数据重复时效果好

对比可知,使用正则表达式的grok满足实际需求。

filter {grok {match => ["message","%{TIMESTAMP_ISO8601:iis.access.time} (?:-|%{IPORHOST:destination.address}) (?:-|%{WORD:http.request.method}) (?:-|%{NOTSPACE:url.path}) (?:-|%{NOTSPACE:url.query}) (?:-|%{NUMBER:destination.port:long}) (?:-|%{NOTSPACE:user.name}) (?:-|%{IPORHOST:source.address}) (?:-|%{NOTSPACE:user_agent.original}) (?:-|%{NOTSPACE:http.request.referrer}) (?:-|%{NUMBER:http.response.status_code:long}) (?:-|%{NUMBER:iis.access.sub_status:long}) (?:-|%{NUMBER:iis.access.win32_status:long}) (?:-|%{NUMBER:temp.duration:long})","message","%{TIMESTAMP_ISO8601:iis.access.time} (?:-|%{NOTSPACE:iis.access.site_name}) (?:-|%{WORD:http.request.method}) (?:-|%{NOTSPACE:url.path}) (?:-|%{NOTSPACE:url.query}) (?:-|%{NUMBER:destination.port:long}) (?:-|%{NOTSPACE:user.name}) (?:-|%{IPORHOST:source.address}) (?:-|%{NOTSPACE:user_agent.original}) (?:-|%{NOTSPACE:iis.access.cookie}) (?:-|%{NOTSPACE:http.request.referrer}) (?:-|%{NOTSPACE:destination.domain}) (?:-|%{NUMBER:http.response.status_code:long}) (?:-|%{NUMBER:iis.access.sub_status:long}) (?:-|%{NUMBER:iis.access.win32_status:long}) (?:-|%{NUMBER:http.response.body.bytes:long}) (?:-|%{NUMBER:http.request.body.bytes:long}) (?:-|%{NUMBER:temp.duration:long})","message","%{TIMESTAMP_ISO8601:iis.access.time} (?:-|%{NOTSPACE:iis.access.site_name}) (?:-|%{NOTSPACE:iis.access.server_name}) (?:-|%{IPORHOST:destination.address}) (?:-|%{WORD:http.request.method}) (?:-|%{NOTSPACE:url.path}) (?:-|%{NOTSPACE:url.query}) (?:-|%{NUMBER:destination.port:long}) (?:-|%{NOTSPACE:user.name}) (?:-|%{IPORHOST:source.address}) (?:-|HTTP/%{NUMBER:http.version}) (?:-|%{NOTSPACE:user_agent.original}) (?:-|%{NOTSPACE:iis.access.cookie}) (?:-|%{NOTSPACE:http.request.referrer}) (?:-|%{NOTSPACE:destination.domain}) (?:-|%{NUMBER:http.response.status_code:long}) (?:-|%{NUMBER:iis.access.sub_status:long}) (?:-|%{NUMBER:iis.access.win32_status:long}) (?:-|%{NUMBER:http.response.body.bytes:long}) (?:-|%{NUMBER:http.request.body.bytes:long}) (?:-|%{NUMBER:temp.duration:long})","message","%{TIMESTAMP_ISO8601:iis.access.time} \\[%{IPORHOST:destination.address}\\]\\(http://%{IPORHOST:destination.address}\\) (?:-|%{WORD:http.request.method}) (?:-|%{NOTSPACE:url.path}) (?:-|%{NOTSPACE:url.query}) (?:-|%{NUMBER:destination.port:long}) (?:-|%{NOTSPACE:user.name}) \\[%{IPORHOST:source.address}\\]\\(http://%{IPORHOST:source.address}\\) (?:-|%{NOTSPACE:user_agent.original}) (?:-|%{NUMBER:http.response.status_code:long}) (?:-|%{NUMBER:iis.access.sub_status:long}) (?:-|%{NUMBER:iis.access.win32_status:long}) (?:-|%{NUMBER:temp.duration:long})","message","%{TIMESTAMP_ISO8601:iis.access.time} (?:-|%{IPORHOST:destination.address}) (?:-|%{WORD:http.request.method}) (?:-|%{NOTSPACE:url.path}) (?:-|%{NOTSPACE:url.query}) (?:-|%{NUMBER:destination.port:long}) (?:-|%{NOTSPACE:user.name}) (?:-|%{IPORHOST:source.address}) (?:-|%{NOTSPACE:user_agent.original}) (?:-|%{NUMBER:http.response.status_code:long}) (?:-|%{NUMBER:iis.access.sub_status:long}) (?:-|%{NUMBER:iis.access.win32_status:long}) (?:-|%{NUMBER:temp.duration:long})"]tag_on_failure => ["fail_in_message"]}
}

我们知道w3c的iis日志可选字段,所以在匹配的过程中有可能你的日志中并没有该字段。为了更灵活,这里列举了更多的模式供实际匹配。

解析失败将fail_in_message写入字段tags数组中

{"source.address" => "192.168.0.9","iis.access.time" => "2020-06-01 00:00:00","iis.access.win32_status" => "0","temp.duration" => "343","destination.address" => "192.168.0.10","message" => "2020-06-01 00:00:00 10.122.123.22 GET /XXinfoAPI/HeheferEx/haha02-5641 format=json 80 - 12.123.23.226 Mozilla/5.0+(Windows;+U;+Windows+NT+5.1;+zh-CN;+rv:1.9.2)+Gecko/20100115+Firefox/3.6 - 200 0 0 343\r","http.response.status_code" => "200","destination.port" => "80","@timestamp" => 2020-07-09T02:00:57.354Z,"url.query" => "format=json","iis.access.sub_status" => "0","http.request.method" => "GET","url.path" => "/XXinfoAPI/HeheferEx/haha02-5641","user_agent.original" => "Mozilla/5.0+(Windows;+U;+Windows+NT+5.1;+zh-CN;+rv:1.9.2)+Gecko/20100115+Firefox/3.6","@version" => "1","host" => "Janey-deMacBook-Pro.local","path" => "/Users/janeydeng/projects/ELK/mock_logs/u_ex200601.log"
}
{"tags" => [[0] "fail_in_message"],"message" => "#Fields: date time s-ip cs-method cs-uri-stem cs-uri-query s-port cs-username c-ip cs(User-Agent) cs(Referer) sc-status sc-substatus sc-win32-status time-taken\r","@version" => "1","@timestamp" => 2020-07-09T02:00:57.353Z,"host" => "Janey-deMacBook-Pro.local","path" => "/Users/janeydeng/projects/ELK/mock_logs/u_ex200601.log"
}

当该行为注释时,解析失败,fail_in_message写入tags。

除了grok自己的选项,以下选项对所有的logstash的过滤插件都适用。
参考:来源:plugins-filters-grok-common-options

Setting Input type Required
add_field hash No
add_tag array No
enable_metric boolean No
id string No
periodic_flush boolean No
remove_field array No
remove_tag array No

解析完的message字段没有意义了,我们将其删除;如果出错,我们保留message

  if "fail_in_message" not in [tags] {mutate {remove_field => ["message"]} }

此时,数据如下

{"host" => "Janey-deMacBook-Pro.local","@timestamp" => 2020-07-09T02:54:11.232Z,"url.query" => "format=json","destination.port" => "80","@version" => "1","iis.access.sub_status" => "0","iis.access.time" => "2020-06-01 00:00:00","url.path" => "/XXinfoAPI/FindEventEx/haha02-6325","temp.duration" => "265","user_agent.original" => "Mozilla/5.0+(Windows;+U;+Windows+NT+5.1;+zh-CN;+rv:1.9.2)+Gecko/20100115+Firefox/3.6","source.address" => "12.123.23.226","iis.access.win32_status" => "0","path" => "/Users/janeydeng/projects/ELK/mock_logs/u_ex200601.log","http.response.status_code" => "200","destination.address" => "10.122.123.22","http.request.method" => "GET"
}

date

数据导入elasticsearch时,需要用@timestamp选取一段时间做图表。而此时的@timestamp代表的是logstash解析的时间,而不是iis数据生成的时间。如果我们今天之内将一年的数据都解析完了,我们需要选取某段时间的数据出来分析,而@timestamp只会是今天的日期。

  mutate {rename => ["@timestamp","event.created"]}date {match => [ "iis.access.time", "YYYY-MM-dd HH:mm:ss" ]target => "@timestamp"tag_on_failure => ["fail_in_timestamp"]timezone => "Etc/GMT+8"}

为了保留logstash采集时间,我们将@timestamp的值赋与新字段event.created

我们将数据的生成时间iis.access.time设置给@timestamp,同时iis日志的时间是零时区的,我们需要+8小时。

{"source.address" => "12.123.23.226","iis.access.win32_status" => "0","@timestamp" => 2020-06-01T08:00:00.000Z,"event.created" => 2020-07-09T03:45:31.441Z,"iis.access.time" => "2020-06-01 00:00:00","http.request.method" => "GET","host" => "Janey-deMacBook-Pro.local","url.path" => "/XXinfoAPI/FindEventEx/haha02-4565","url.query" => "format=json","path" => "/Users/janeydeng/projects/ELK/mock_logs/u_ex200601.log","user_agent.original" => "Mozilla/5.0+(Windows;+U;+Windows+NT+5.1;+zh-CN;+rv:1.9.2)+Gecko/20100115+Firefox/3.6","destination.address" => "10.122.123.22","http.response.status_code" => "200","@version" => "1","destination.port" => "80","temp.duration" => "281","iis.access.sub_status" => "0"
}

user_agent

客户端的数据对程序调试也很重要。

  useragent {source => "user_agent.original"prefix => "user_agent."remove_field => "user_agent.original"}

因为iis的一条客户代理信息解析出多个信息,有些版本信息的字段名非常泛,容易与主机信息等混淆,所以加上前缀“user_agent_“,信息可读性更强。

              "user_agent.name" => "Firefox","user_agent.os_name" => "Windows","user_agent.original" => "Mozilla/5.0+(Windows;+U;+Windows+NT+5.1;+zh-CN;+rv:1.9.2)+Gecko/20100115+Firefox/3.6","user_agent.minor" => "6","user_agent.os" => "Windows","user_agent.major" => "3",

geoip

免费的有2种数据库: GeoLite2-City 和 GeoLite2-ASN

# 解析库来自于 GeoLite2-City(默认值)"source.ip" => {"longitude" => 123.3452,"region_name" => "Guangdong","timezone" => "Asia/Shanghai","country_name" => "China","latitude" => 23.5289,"location" => {"lon" => 123.3452,"lat" => 23.5289},"ip" => "218.233.233.226","country_code2" => "CN","country_code3" => "CN","city_name" => "Jieyang","continent_code" => "AS","region_code" => "GD"},
# 解析库来自于 GeoLite2-ASN,设置default_database_type => "ASN""source.ip" => {"asn" => 9890,"ip" => "218.233.233.226","as_org" => "Guangdong Mobile Communication Co.Ltd."},

对来访的ip,我们需要挖掘出更多的信息,供我们分析。同一个ip地址解析库不同,获取的信息也不同。
因此,为了获取更多信息,我们可以对ip地址解析两次,target到不同的字段名称。

  geoip {source => "source.address"target => "source.geo"default_database_type => "ASN"}geoip {source => "source.address"target => "source.as"default_database_type => "ASN"}

实际需要

因为需要知道iis出错最多的终端编号,信息已经包含在访问路径中了,需要提取出来做分析。
/XXinfoAPI/FindEventEx/haha02-6325中的haha02-6325就是终端编号。
分析终端编号是由字母te+2位数字+“-”+4位数字组成

  grok {match => ["url.path","(?<haha>(?=haha)haha[0-9]{1,2}-[0-9]{4}$)"]tag_on_failure => [""]}

因为并不是所有的访问路径都是带终端编号的。所以如果没有带编号的,不用写对信息进入tags中了。
表示该字段名称,(?=haha)表示是否包含了haha字符,后面为正则表达式。

可在Grok Debug(需科学上网)中先测试好

"te" => "haha02-6247"

附上正则表达式连接:
https://github.com/kkos/oniguruma/blob/master/doc/RE

logstash: grok-patterns

最后,一条iis日志
2020-06-01 00:00:00 10.122.123.22 GET /XXinfoAPI/HeheferEx/haha02-6247 format=json 80 - 12.123.23.226 Mozilla/5.0+(Windows;+U;+Windows+NT+5.1;+zh-CN;+rv:1.9.2)+Gecko/20100115+Firefox/3.6 - 200 0 0 265
就被解析成了如下信息

{"iis.access.win32_status": "0","http.request.method": "GET","user_agent.device": "Other","source.address": "12.123.23.226","user_agent.build": "","path": "/Users/janeydeng/projects/ELK/mock_logs/u_ex200601.log","iis.access.sub_status": "0","@timestamp": "2020-06-01T08:00:00.000Z","user_agent.os": "Windows","source.geo": {"country_name": "China","region_name": "Guangdong","latitude": 23.5189,"region_code": "GD","longitude": 118.3942,"ip": "12.123.23.226","location": {"lon": 118.3942,"lat": 23.5189},"continent_code": "AS","timezone": "Asia/Shanghai","country_code3": "CN","country_code2": "CN","city_name": "Jieyang"},"@version": "1","url.query": "format=json","te": "haha02-6247","source.as": {"ip": "12.123.23.226","asn": 9808,"as_org": "Guangdong Mobile Communication Co.Ltd."},"user_agent.os_name": "Windows","destination.address": "10.122.123.22","url.path": "/XXinfoAPI/HeheferEx/haha02-6247","destination.port": "80","user_agent.minor": "6","user_agent.original": "Mozilla/5.0+(Windows;+U;+Windows+NT+5.1;+zh-CN;+rv:1.9.2)+Gecko/20100115+Firefox/3.6","user_agent.name": "Firefox","iis.access.time": "2020-06-01 00:00:00","http.response.status_code": "200","host": "Janey-deMacBook-Pro.local","temp.duration": "265","event.created": "2020-07-09T09:27:11.050Z","user_agent.major": "3"
}

附录logstash.conf:

# Sample Logstash configuration for creating a simple
# Beats -> Logstash -> Elasticsearch pipeline.input {file {path => "/Users/janeydeng/projects/ELK/mock_logs/*.log"start_position => "beginning"}
} filter {# 排除注释行if [message] =~ "^#" {drop {}}# 解析messagegrok {match => ["message","%{TIMESTAMP_ISO8601:iis.access.time} (?:-|%{IPORHOST:destination.address}) (?:-|%{WORD:http.request.method}) (?:-|%{NOTSPACE:url.path}) (?:-|%{NOTSPACE:url.query}) (?:-|%{NUMBER:destination.port:long}) (?:-|%{NOTSPACE:user.name}) (?:-|%{IPORHOST:source.address}) (?:-|%{NOTSPACE:user_agent.original}) (?:-|%{NOTSPACE:http.request.referrer}) (?:-|%{NUMBER:http.response.status_code:long}) (?:-|%{NUMBER:iis.access.sub_status:long}) (?:-|%{NUMBER:iis.access.win32_status:long}) (?:-|%{NUMBER:temp.duration:long})","message","%{TIMESTAMP_ISO8601:iis.access.time} (?:-|%{NOTSPACE:iis.access.site_name}) (?:-|%{WORD:http.request.method}) (?:-|%{NOTSPACE:url.path}) (?:-|%{NOTSPACE:url.query}) (?:-|%{NUMBER:destination.port:long}) (?:-|%{NOTSPACE:user.name}) (?:-|%{IPORHOST:source.address}) (?:-|%{NOTSPACE:user_agent.original}) (?:-|%{NOTSPACE:iis.access.cookie}) (?:-|%{NOTSPACE:http.request.referrer}) (?:-|%{NOTSPACE:destination.domain}) (?:-|%{NUMBER:http.response.status_code:long}) (?:-|%{NUMBER:iis.access.sub_status:long}) (?:-|%{NUMBER:iis.access.win32_status:long}) (?:-|%{NUMBER:http.response.body.bytes:long}) (?:-|%{NUMBER:http.request.body.bytes:long}) (?:-|%{NUMBER:temp.duration:long})","message","%{TIMESTAMP_ISO8601:iis.access.time} (?:-|%{NOTSPACE:iis.access.site_name}) (?:-|%{NOTSPACE:iis.access.server_name}) (?:-|%{IPORHOST:destination.address}) (?:-|%{WORD:http.request.method}) (?:-|%{NOTSPACE:url.path}) (?:-|%{NOTSPACE:url.query}) (?:-|%{NUMBER:destination.port:long}) (?:-|%{NOTSPACE:user.name}) (?:-|%{IPORHOST:source.address}) (?:-|HTTP/%{NUMBER:http.version}) (?:-|%{NOTSPACE:user_agent.original}) (?:-|%{NOTSPACE:iis.access.cookie}) (?:-|%{NOTSPACE:http.request.referrer}) (?:-|%{NOTSPACE:destination.domain}) (?:-|%{NUMBER:http.response.status_code:long}) (?:-|%{NUMBER:iis.access.sub_status:long}) (?:-|%{NUMBER:iis.access.win32_status:long}) (?:-|%{NUMBER:http.response.body.bytes:long}) (?:-|%{NUMBER:http.request.body.bytes:long}) (?:-|%{NUMBER:temp.duration:long})","message","%{TIMESTAMP_ISO8601:iis.access.time} \\[%{IPORHOST:destination.address}\\]\\(http://%{IPORHOST:destination.address}\\) (?:-|%{WORD:http.request.method}) (?:-|%{NOTSPACE:url.path}) (?:-|%{NOTSPACE:url.query}) (?:-|%{NUMBER:destination.port:long}) (?:-|%{NOTSPACE:user.name}) \\[%{IPORHOST:source.address}\\]\\(http://%{IPORHOST:source.address}\\) (?:-|%{NOTSPACE:user_agent.original}) (?:-|%{NUMBER:http.response.status_code:long}) (?:-|%{NUMBER:iis.access.sub_status:long}) (?:-|%{NUMBER:iis.access.win32_status:long}) (?:-|%{NUMBER:temp.duration:long})","message","%{TIMESTAMP_ISO8601:iis.access.time} (?:-|%{IPORHOST:destination.address}) (?:-|%{WORD:http.request.method}) (?:-|%{NOTSPACE:url.path}) (?:-|%{NOTSPACE:url.query}) (?:-|%{NUMBER:destination.port:long}) (?:-|%{NOTSPACE:user.name}) (?:-|%{IPORHOST:source.address}) (?:-|%{NOTSPACE:user_agent.original}) (?:-|%{NUMBER:http.response.status_code:long}) (?:-|%{NUMBER:iis.access.sub_status:long}) (?:-|%{NUMBER:iis.access.win32_status:long}) (?:-|%{NUMBER:temp.duration:long})"]tag_on_failure => ["fail_in_message"]}# 如果message解析成功,则删除message;若失败,保留if "fail_in_message" not in [tags] {mutate {remove_field => ["message"]} }# @timestamp处理mutate {rename => ["@timestamp","event.created"]}date {match => [ "iis.access.time", "YYYY-MM-dd HH:mm:ss" ]target => "@timestamp"tag_on_failure => ["fail_in_timestamp"]timezone => "Etc/GMT+8"}# 客户端解析# urldecode {#   field => "user_agent.original"# }useragent {source => "user_agent.original"prefix => "user_agent."}# ip地址解析geoip {source => "source.address"target => "source.geo"}geoip {source => "source.address"target => "source.as"default_database_type => "ASN"}# 解析终端编号grok {match => ["url.path","(?<te>(?=te)te[0-9]{1,2}-[0-9]{4}$)"]tag_on_failure => [""]}
}output {# stdout { codec => rubydebug }stdout { codec => json }
}

注意

解析的urldecode和user_agent都是有问题的。解析的信息不全。iis的空格被编码成了+,导致解析user_agent解析出的信息不全(无法正确解析os_major和os_minor)。可用elasticsearch的urldecode和user_agent的pipeline proccesor去解。

logstash解析iis日志相关推荐

  1. iis日志字段解析 网站运维工具使用iis日志分析工具分析iis日志(iis日志的配置)

    网站运维工具使用iis日志分析工具分析iis日志(iis日志的配置) https://www.cnblogs.com/fuqiang88/p/5870306.html 我们只能通过各种系统日志来分析网 ...

  2. IIS 日志解析,增强 IIS 服务器安全性

    企业严重依赖 Microsoft Internet 信息服务 (IIS) 服务器来托管其网页和 Web 应用程序,以及存储其文件.请务必妥善保护您的 IIS 服务器(包括 Web 和 FTP). 什么 ...

  3. IIS日志-网站运维的好帮手

    目录 IIS日志包含了哪些信息 IIS日志的配置 如何分析IIS日志 推荐的IIS日志分析方法 IIS日志中的异常记录 再谈 scwin32status=64 寻找性能问题 寻找可改进的目标 程序架构 ...

  4. logstash中无法解析nginx日志中的\x09类似字符导致服务停止

    logstash中无法解析nginx日志中的"\x09"类似字符导致服务停止 logstash正常情况是一直稳定运行,突然有一天报告logstash服务宕机,排查日志,在日志中找到 ...

  5. 使用filebeat和logstash解析java的log4j日志

    目的:我们使用filebeat来接受日志,并将日志做一个简单的处理,然后发给logstash,使用logstash的filter的grok来匹配日志,将日志解析成json格式并将日志输出在logsta ...

  6. 解析IIS安装的FTP服务器的日志

    解析IIS安装的FTP服务器的日志 一.ftp日志文件 打开IIS管理器中,ftp站点 日志文件类型:默认为W3C,记录的时间采用UTC 格式,文件名前缀为u_ex eg:u_ex210318表示该日 ...

  7. logstash解析系统的messages日志

    logstash解析系统日志的写法,output中的stdout为调试,生产可以移除 input {redis {host => "192.168.1.181"port =& ...

  8. CYQ.IISLogViewer 一款IIS 日志分析工具 V1.0 发布[提供源码]

    说几句:      昨天在 秋色开源团队  群里和网友聊天,有网友提到了一个概念,做站需要知道的:分析IIS日志. 然后上网找了一下资料看了下,可是 秋色园 寄放在人家虚拟目录的子目录中,根本没有II ...

  9. windows服务器系统的iis日志,Windows server2012 IIs 8 自定义日志记录

    问题: 通过CDN加速的网站,记录日志时无法追踪源IP,日志的IP都为CDN节点ip. 分析: 1.在解析记录header时,CDN实际会把源IP以其它header的形式回传,如网宿为[Cdn-Src ...

最新文章

  1. LTE: 系统内移动性知识点总结
  2. R语言中的esttab命令_R语言︱基本函数、统计量、常用操作函数
  3. Sql Server 常用日期格式
  4. c语言常用指令翻译,c语言常见专业词汇带翻译
  5. C/C++ http协议发送字段,文件,单个和多张图片
  6. C# .net 中 Timeout 的处理及遇到的问题
  7. jsp页面中出现“String cannot be resolved to a type”
  8. jquery报变量没定义错误的原因
  9. vue+node多条件查询 分页_SpringBoot+JPA框架分页、带条件查询等操作
  10. asp在线html编辑器,ASP下使用FCKeditor在线编辑器的方法
  11. 基于ssm java jsp的酒店管理系统 前后台
  12. Ubuntu恢复官方默认源
  13. 软件扫描出rsh漏洞,但是并无安装rsh服务,原因是为何?
  14. 如何卸载avast free antivirus软件?
  15. 前端安全攻防大全--专注于攻击和防御
  16. 查看oracle负载过大的原因,Oracle备份时系统负载过高导致ORA-3136错误和AIX系统的3D32B80D错误...
  17. 我的个人博客是如何申请百度联盟通过的?
  18. acwing——数学知识(四)Nim游戏
  19. 富士胶片消毒喷雾及湿巾在日本证实可抑制新冠病毒感染
  20. Migrating from REDWOOD CRONACLE TO CA WORKLOAD AUTOMATION GUIDE

热门文章

  1. 使用c++开发excel插件 (第3章动态链接库(dynamic-link library))
  2. 2016 版 Laravel 系列入门教程(一)【最适合中国人的 Laravel 教程】
  3. 关于Windows许可证过期解决方案
  4. elang 游戏 生成全局id
  5. Zigbee 入网过程详解
  6. 什么是多租户saas架构设计
  7. Unity 语音识别以及音频可视化
  8. SQL Server基础操作(此随笔仅作为本人学习进度记录四 !--索引和视图)
  9. 用C语言构造康托集,洛谷——P1014 Cantor表
  10. 要不要启用苹果wapi_苹果“史上最强”系统ios13来了,要不要升级?