将 AzureBlob 的日志通过 Azure Event Hubs 发给 Elasticsearch(3.纯python的实惠版)
前情:
将 AzureBlob 的日志通过 Azure Event Hubs 发给 Elasticsearch(1.标准版)-CSDN博客
将 AzureBlob 的日志通过 Azure Event Hubs 发给 Elasticsearch(2.换掉付费的Event Hubs)-CSDN博客
python脚本实现
厉害的小伙伴最终使用python脚本免费实现了这个功能
文件结构
以下是cursor对代码的评审意见:
这是一个用于下载 Azure Storage 访问日志并将其导入到 Elasticsearch 的项目。让我逐个文件分析:
1. 整体架构
项目采用了良好的模块化设计,主要包含以下组件:
- main.py: 主程序入口,协调整体流程
- azure_log_downloader.py: Azure blob 存储日志下载器
- elasticsearch_sender.py: Elasticsearch 数据发送器
- models.py: 数据模型定义
- log_config.py: 日志配置
- .env: 环境配置文件
2. 具体评审
main.py
优点:
- 支持日期范围处理
- 良好的错误处理和日志记录
- 模块化的设计,职责划分清晰
建议改进:
- 可以考虑添加命令行参数支持,使日期范围可配置
- 可以添加并发处理支持,提高处理速度
azure_log_downloader.py
优点:
- 清晰的类设计
- 良好的错误处理
建议改进:
- 可以添加重试机制
- 可以考虑支持异步操作
elasticsearch_sender.py
优点:
- 实现了批量处理
- 包含重试机制
- 良好的连接测试功能
建议改进:
- 可以考虑添加索引模板管理
- 可以添加更多的 ES 配置选项
models.py
优点:
- 使用 dataclass 简化了类定义
- 详细的字段解析逻辑
- 良好的类型注解
建议改进:
- 可以添加字段验证
- 可以考虑使用 Pydantic 进行数据验证
log_config.py
优点:
- 完善的日志配置
- 支持文件和控制台输出
建议改进:
- 可以考虑添加日志轮转
- 可以添加日志级别配置选项
3. 安全性建议
- 建议使用更安全的方式存储敏感信息(如 API keys)
- 建议添加 SSL 证书验证
- 建议添加请求速率限制
4. 性能建议
- 考虑添加并发处理
- 可以实现增量更新机制
- 考虑添加缓存机制
5. 可维护性建议
- 添加单元测试
- 添加更详细的文档
- 考虑添加监控指标
- 添加 CI/CD 配置
总体来说,这是一个设计良好的项目,代码质量较高,有良好的错误处理和日志记录。主要可以在性能优化、安全性和可维护性方面进行进一步改进。
具体脚本
1. azure_log_downloader.py:
from azure.storage.blob import BlobServiceClient
import json
from models import StorageOperationLogclass AzureLogDownloader:def __init__(self, connection_string, container_name, blob_name, logger=None):self.logger = loggerself.connection_string = connection_stringself.container_name = container_nameself.blob_name = blob_namedef _get_blob_client(self):blob_service_client = BlobServiceClient.from_connection_string(self.connection_string)blob_client = blob_service_client.get_blob_client(container=self.container_name, blob=self.blob_name)return blob_clientdef download_and_transform(self):"""Download and transform log data from Azure storage"""try:blob_client = self._get_blob_client()if not blob_client.exists():self.logger.info(f"Blob does not exist, skipping: {self.blob_name}")return []blob_data = blob_client.download_blob().readall().decode('utf-8')transformed_entries = []for line in blob_data.splitlines():if line.strip():try:log_entry = json.loads(line)log_obj = StorageOperationLog.from_log_entry(log_entry, self.logger)if log_obj:transformed_entries.append(log_obj)except json.JSONDecodeError as e:self.logger.error(f"Error parsing line: {str(e)}")continueself.logger.info(f"Downloaded and transformed {len(transformed_entries)} logs")return transformed_entriesexcept Exception as e:self.logger.error(f"Error downloading blob: {str(e)}")self.logger.error(f"Blob: {self.blob_name}, Container: {self.container_name}")self.logger.error(f"Error type: {type(e).__name__}")return []
2. elasticsearch_sender.py:
from elasticsearch import Elasticsearch, helpers
import time
import uuidclass ElasticsearchSender:def __init__(self, host, api_key=None, index_name="logs", logger=None):self.logger = loggerself.config = {'hosts': host,'timeout': 30,'retry_on_timeout': True,'max_retries': 3,'verify_certs': False,'ssl_show_warn': False,'use_ssl': True}if api_key:self.config['api_key'] = api_keyself.index_name = index_nameself.es = Elasticsearch(**self.config)def test_connection(self):"""Test Elasticsearch connection"""try:info = self.es.info()self.logger.info("\nElasticsearch Server Info:")self.logger.info(f"Version: {info['version']['number']}")self.logger.info(f"Cluster Name: {info['cluster_name']}")return Trueexcept Exception as e:self.logger.error(f"\nElasticsearch connection failed: {str(e)}")return Falsedef send_logs(self, log_entries, batch_size=500, max_retries=3):"""Send logs to Elasticsearch"""def generate_actions():for entry in log_entries:doc_data = entry.__dict__.copy()if 'time' in doc_data:doc_data['@timestamp'] = doc_data.pop('time')action = {'_index': self.index_name,'_id': str(uuid.uuid4()),'_source': doc_data}yield actionsuccess_count = 0failure_count = 0retry_count = 0while retry_count < max_retries:try:success, failed = helpers.bulk(self.es,generate_actions(),chunk_size=batch_size,raise_on_error=False,raise_on_exception=False)success_count += successfailure_count += len(failed) if failed else 0self.logger.info(f"\nBatch processing results:")self.logger.info(f"- Successfully indexed: {success_count} documents")self.logger.info(f"- Failed: {failure_count} documents")if not failed:breakretry_count += 1if retry_count < max_retries:self.logger.info(f"Retrying... (Attempt {retry_count + 1}/{max_retries})")time.sleep(2 ** retry_count)except Exception as e:self.logger.error(f"\nBulk indexing error: {str(e)}")retry_count += 1if retry_count < max_retries:self.logger.info(f"Retrying... (Attempt {retry_count + 1}/{max_retries})")time.sleep(2 ** retry_count)else:self.logger.info("Maximum retry attempts reached")breakreturn success_count, failure_count
3. log_config.py:
import logging
import os
from datetime import UTC, datetimedef setup_logger(target_date: datetime = None, log_prefix: str = "app"):base_dir = os.path.dirname(os.path.abspath(__file__))log_dir = os.path.join(base_dir, 'logs')if not os.path.exists(log_dir):os.makedirs(log_dir)current_time = datetime.now(UTC).strftime("%Y%m%d_%H%M%S")target_date_str = target_date.strftime("%Y%m%d") if target_date else "None"log_file = os.path.join(log_dir, f'{log_prefix}_target_date_{target_date_str}_export_at_{current_time}.log')logger = logging.getLogger('AccessLog')logger.setLevel(logging.INFO)file_handler = logging.FileHandler(log_file, encoding='utf-8')file_handler.setLevel(logging.INFO)console_handler = logging.StreamHandler()console_handler.setLevel(logging.INFO)formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')file_handler.setFormatter(formatter)console_handler.setFormatter(formatter)logger.addHandler(file_handler)logger.addHandler(console_handler)return logger
4. models.py:
from dataclasses import dataclass
from datetime import datetime
import re
from typing import Optional@dataclass
class StorageOperationLog:time: datetimecategory: Optional[str]operationName: Optional[str]callerIpAddress: Optional[str]location: Optional[str]uri: Optional[str]durationMs: Optional[int]referrerHeader: Optional[str]userAgentHeader: Optional[str]requestBodySize: Optional[int]responseBodySize: Optional[int]serverLatencyMs: Optional[int]objectKey: Optional[str]functionName: Optional[str]file_extension: Optional[str]@staticmethoddef parse_object_key(object_key: str, logger=None) -> tuple[Optional[str], Optional[str]]:"""Parse objectKey to get institution_id and functionName"""try:container_match = re.search(r'container-(\d+)', object_key)parts = object_key.split('/')function_name = Noneif container_match:container_index = next((i for i, part in enumerate(parts) if 'container-' in part), None)if container_index is not None and container_index + 1 < len(parts):function_name = parts[container_index + 1]file_extension = Noneif parts and '.' in parts[-1]:file_extension = parts[-1].split('.')[-1].lower()return function_name, file_extensionexcept Exception as e:if logger:logger.error(f"Error parsing object_key {object_key}: {str(e)}")return None, None@classmethoddef from_log_entry(cls, entry: dict[str, any], logger=None) -> Optional['StorageOperationLog']:"""Create StorageOperationLog instance from raw log entry"""try:properties = entry.get('properties', {})object_key = properties.get('objectKey', '')function_name, file_extension = cls.parse_object_key(object_key)return cls(time=entry.get('time'),category=entry.get('category'),operationName=entry.get('operationName'),callerIpAddress=entry.get('callerIpAddress'),location=entry.get('location'),uri=entry.get('uri'),durationMs=int(entry.get('durationMs')) if entry.get('durationMs') is not None else None,referrerHeader=properties.get('referrerHeader'),userAgentHeader=properties.get('userAgentHeader'),requestBodySize=int(properties.get('requestBodySize')) if properties.get('requestBodySize') is not None else None,responseBodySize=int(properties.get('responseBodySize')) if properties.get('responseBodySize') is not None else None,serverLatencyMs=int(properties.get('serverLatencyMs')) if properties.get('serverLatencyMs') is not None else None,objectKey=object_key,functionName=function_name,file_extension=file_extension)except Exception as e:if logger:logger.error(f"Error creating StorageOperationLog: {str(e)}")return Nonedef __post_init__(self):if isinstance(self.time, str):if 'Z' in self.time:time_parts = self.time.split('.')if len(time_parts) > 1:microseconds = time_parts[1].replace('Z', '')[:6]time_str = f"{time_parts[0]}.{microseconds}Z"self.time = datetime.strptime(time_str, "%Y-%m-%dT%H:%M:%S.%fZ")else:self.time = datetime.strptime(self.time, "%Y-%m-%dT%H:%M:%SZ")
5. main.py:
from log_config import setup_logger
from azure_log_downloader import AzureLogDownloader
from elasticsearch_sender import ElasticsearchSender
from datetime import datetime, timedelta, UTC
from dotenv import load_dotenv
import osload_dotenv()def _get_index_name(target_date: datetime):"""Get full index name for the specified date"""return os.getenv('ELASTICSEARCH_INDEX_TEMPLATE', 'logs-{year}-{month}').format(year=target_date.year,month=target_date.month)def _get_blob_name_list(target_date: datetime):"""Get blob paths for all hours of the specified date"""blobs = []for hour in range(24):blob_time = target_date.replace(hour=hour, minute=0, second=0, microsecond=0)blob_name = os.getenv('AZURE_STORAGE_BLOB_TEMPLATE', 'logs/y={year}/m={month}/d={day}/h={hour}').format(year=blob_time.year,month=blob_time.month,day=blob_time.day,hour=blob_time.hour)blobs.append(blob_name)return blobsdef main():start_date = datetime(2024, 1, 1, tzinfo=UTC)end_date = datetime(2024, 1, 2, tzinfo=UTC)current_date = start_datewhile current_date <= end_date:target_date = current_datelogger = setup_logger(target_date, os.getenv('LOG_PREFIX', 'app'))try:logger.info(f"\nProcessing data for {current_date.date()}")elasticsearch_index = _get_index_name(target_date)sender = ElasticsearchSender(os.getenv('ELASTICSEARCH_HOST', 'http://localhost:9200'),os.getenv('ELASTICSEARCH_API_KEY'),elasticsearch_index,logger)if not sender.test_connection():logger.error("Elasticsearch connection failed")current_date += timedelta(days=1)continuetotal_logs = total_success = total_failed = 0blobs = _get_blob_name_list(target_date)for container in os.getenv('AZURE_STORAGE_CONTAINERS', 'logs').split(','):logger.info(f"\nProcessing container: {container}")for blob_name in blobs:logger.info(f"\nProcessing blob: {blob_name}")downloader = AzureLogDownloader(os.getenv('AZURE_STORAGE_URI'),container,blob_name,logger)try:log_entries = downloader.download_and_transform()success, failed = sender.send_logs(log_entries)total_logs += len(log_entries)total_success += successtotal_failed += failedexcept Exception as e:logger.error(f"Error processing {blob_name}: {str(e)}")continuelogger.info(f"\n{current_date.date()} Processing completed:")logger.info(f"Total documents processed: {total_logs}")logger.info(f"Successfully indexed: {total_success}")logger.info(f"Failed: {total_failed}")finally:for handler in logger.handlers[:]:handler.close()logger.removeHandler(handler)current_date += timedelta(days=1)if __name__ == "__main__":main()
6. .env :
ELASTICSEARCH_HOST=http://localhost:9200
ELASTICSEARCH_API_KEY=your_api_key
ELASTICSEARCH_INDEX_TEMPLATE=logs-{year}-{month}
AZURE_STORAGE_URI=your_storage_connection_string
AZURE_STORAGE_CONTAINERS=logs
AZURE_STORAGE_BLOB_TEMPLATE=logs/y={year}/m={month}/d={day}/h={hour}
LOG_PREFIX=app
前情后续:
将 AzureBlob 的日志通过 Azure Event Hubs 发给 Elasticsearch(1.标准版)-CSDN博客
将 AzureBlob 的日志通过 Azure Event Hubs 发给 Elasticsearch(2.换掉付费的Event Hubs)-CSDN博客
将 AzureBlob 的日志通过 Azure Event Hubs 发给 Elasticsearch(3.纯python的实惠版)-CSDN博客
相关文章:
将 AzureBlob 的日志通过 Azure Event Hubs 发给 Elasticsearch(3.纯python的实惠版)
前情: 将 AzureBlob 的日志通过 Azure Event Hubs 发给 Elasticsearch(1.标准版)-CSDN博客 将 AzureBlob 的日志通过 Azure Event Hubs 发给 Elasticsearch(2.换掉付费的Event Hubs)-CSDN博客 python脚本实现 厉害的…...
成就与远见:2024年技术与思维的升华
个人主页:chian-ocean 前言: 2025年1月17日,2024年博客之星年度评选——创作影响力评审的入围名单公布。我很荣幸能够跻身Top 300,虽然与顶尖博主仍有一定差距,但这也为我提供了更加明确的发展方向与指引。展望崭新的2025年&…...
BGP分解实验·9——路由聚合与条件性通告(1)
路由聚合是有效控制缩减BGP路由表的方法之一,路由聚合的前提和IGP一样,需要有路由目标存在BGP表中,与IGP不同的是,BGP路由聚合可以定义按需抑制路由的能力。 实验拓扑如下所示: 现在开始把从R1的R5的基础配置先准备好…...
栈和队列(C语言)
目录 数据结构之栈 定义 实现方式 基本功能实现 1)定义,初始化栈 2)入栈 3)出栈 4)获得栈顶元素 5)获得栈中有效元素个数 6)检测栈是否为空 7)销毁栈 数据结构之队列 定义 实现方…...
Jenkins-获取build用户信息
需求: 代码发布后,将发布结果发送至相关运维同学邮箱,需要获取发布人的信息。jenkins默认是没有相关内置变量的。 需要通过插件的方式进行解决: 插件: user build vars plugin 部署后,可使用的变量&…...
大数据学习(37)- Flink运行时架构
&&大数据学习&& 🔥系列专栏: 👑哲学语录: 承认自己的无知,乃是开启智慧的大门 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言📝支持一下博主哦ᾑ…...
opengrok_windows_多工程环境搭建
目录 多工程的目录 工程代码下载和log配置 工程的索引 工程部署 工程测试 参考列表 多工程的目录 工程代码下载和log配置 工程代码下载 在每个工程的src目录下,下载工程代码,以下载pulseaudio的代码为例。 git clone gitgithub.com…...
Python网络自动化运维---SSH模块
目录 SSH建立过程 实验环境准备 一.SSH模块 1.1.Paramiko模块 1.1.1实验代码 1.1.2代码分段讲解 1.1.3代码运行过程 1.2Netmiko模块 Netmiko模块对比paramiko模块的改进: 1.2.1实验代码 1.2.2代码分段讲解 1.2.3代码运行过程 二.Paramiko模块和Ne…...
Failed to restart nginx.service Unit nginx.service not found
当你遇到 Failed to restart nginx.service: Unit nginx.service not found 错误时,这意味着系统无法找到 Nginx 的服务单元文件。这通常是因为 Nginx 没有通过 systemd 管理,或者 Nginx 没有正确安装。 解决方法 1. 检查 Nginx 是否正确安装 首先&am…...
学习记录之原型,原型链
构造函数创建对象 Person和普通函数没有区别,之所以是构造函数在于它是通过new关键字调用的,p就是通过构造函数Person创建的实列对象 function Person(age, name) {this.age age;this.name name;}let p new Person(18, 张三);prototype prototype n…...
【Redis】5种基础数据结构介绍及应用
考察频率难度60%⭐⭐ 这个方向的问题也是非常基础的,所以一般不会直接被当做一个单独的问题。常见的形式是结合你简历上的项目或者场景题来提问,即实际应用场景、是否可以优化、如何选择等。 由于场景题和实际项目差异较大,所以本文就只做基…...
基于GRU实现股价多变量时间序列预测(PyTorch版)
前言 系列专栏:【深度学习:算法项目实战】✨︎ 涉及医疗健康、财经金融、商业零售、食品饮料、运动健身、交通运输、环境科学、社交媒体以及文本和图像处理等诸多领域,讨论了各种复杂的深度神经网络思想,如卷积神经网络、循环神经网络、生成对抗网络、门控循环单元、长短期记…...
Java Web开发高级——Spring Boot与Docker容器化部署
随着云计算和微服务架构的快速发展,容器化已成为现代应用部署的重要手段。Docker作为最受欢迎的容器化技术之一,使得开发者能够将应用及其所有依赖打包到一个可移植的容器中,简化了开发、测试、部署和运维的流程。本篇文章将通过以下内容讲解…...
计算机网络——网络层
重点内容: (1) 虚拟互连网络的概念。 (2) IP 地址与物理地址的关系。 (3) 传统的分类的 IP 地址(包括子网掩码)和无分类域间路由选择 CIDR 。 (4) 路由选择协议的工作原理。 目录 重点内容: 一.网络层提供的两种服务 二…...
每打开一个chrome页面都会【自动打开F12开发者模式】,原因是 使用HBuilderX会影响谷歌浏览器的浏览模式
打开 HBuilderX,点击 运行 -> 运行到浏览器 -> 设置web服务器 -> 添加chrome浏览器安装路径 chrome谷歌浏览器插件 B站视频下载助手插件: 参考地址:Chrome插件 - B站下载助手(轻松下载bilibili哔哩哔哩视频)…...
cesium绕点旋转
绕点旋转的原理可以理解为相机一直看向一个点,不断改变相机的位置 let position Cesium.Cartesian3.fromDegrees(longitude, latitude) let lookAtTimer setInterval(() > {let heading viewer.camera.heading;let pitch viewer.camera.pitch;if (heading &…...
JavaScript系列(36)--微服务架构详解
JavaScript微服务架构详解 🏗️ 今天,让我们深入了解JavaScript的微服务架构,这是构建大规模分布式系统的关键技术。 微服务基础概念 🌟 💡 小知识:微服务架构是一种将应用程序构建为一组小型服务的方法&…...
神经网络基础 | 给定条件下推导对应的卷积层参数
神经网络基础 | 给定条件下推导对应的卷积层参数 按照 PyTorch 文档中 给定的设置: H o u t ⌊ H i n 2 padding [ 0 ] − dilation [ 0 ] ( kernel_size [ 0 ] − 1 ) − 1 stride [ 0 ] 1 ⌋ H_{out} \left\lfloor\frac{H_{in} 2 \times \text{padding}[0]…...
面向CTF的python_requests库的学习笔记
看师傅们写的各种脚本羡慕不已,自己却只会一点一点手搓,于是来做个笔记 requests库是干嘛的? 顾名思义,request就是请求,可以用来向服务器发送请求。它可以代替你在网站上发送请求报文,并接受回应报文。简…...
MIAOYUN信创云原生项目亮相西部“中试”生态对接活动
近日,以“构建‘中试’生态,赋能科技成果转化”为主题的“科创天府智汇蓉城”西部“中试”生态对接活动在成都高新区菁蓉汇隆重开幕。活动分为成果展览、“中试”生态主场以及成果路演洽谈对接三大板块。在成果展览环节,成都元来云志科技有限…...
Papers with Code:从代码索引到AI创新引擎
标题:Papers with Code:从代码索引到AI创新引擎 文章信息摘要: Papers with Code从解决机器学习论文代码复现的特定需求起步,通过建立全面的ML资源库和首个系统性leaderboard系统,快速积累了大量用户基础。被Meta收购…...
FastExcel 新一代的潮流 (EasyExcel)
目录 简介 FastExcel的特点 FastExcel使用方法详解 创建实体类和监听器 实现写入和读取功能 Excel转换为PDF 小结 FastExcel与EasyExcel的区别 结论 简介 FastExcel是由原EasyExcel作者在阿里巴巴宣布停止维护EasyExcel之后推出的升级版框架。它继承了EasyExcel的所有…...
Linux静态库与动态库的理解
Linux静态库与动态库的理解 一、静态库如何创建和使用静态库 二、动态库如何创建和使用动态库 三、静态库与动态库的关键区别四、总结 在 Linux 编程中,静态库和动态库是非常重要的概念 一、静态库 静态库是将多个目标文件(.o 文件)打包成一…...
mongoose 支持https踩坑纪实
简述 mongoose是C编写的嵌入式web服务,它能够支持https协议,可以简单的部署,但要做到完美部署,不是那么容易。 部署方法 本人使用的是最新的7.16版,以前版本似乎是要通过修改 头文件中的 MG_ENABLE_SSL 宏定义&…...
人工智能之数学基础:线性表达和线性组合
本文重点 线性表达和线性组合作为线性代数的核心概念,不仅深刻揭示了向量空间的基本性质,也为解决复杂问题提供了直观而有效的方法。 向量组 要想学习线性表达和线性组合,先来理解向量组。在线性代数中,向量组是指由若干个向量构成的集合。这些向量可以是二维的、三维的…...
uniapp——App 监听下载文件状态,打开文件(三)
5 实现下载文件并打开 这里演示,导出Excel 表格 文章目录 5 实现下载文件并打开DEMO监听下载进度效果图为什么 totalSize 一直为0? 相关Api: downloader DEMO 提示: 请求方式支持:GET、POST;POST 方式需要…...
Microsoft SQL Serve的下载与安装
1.访问Microsoft SQL Serve官网 SQL Server 下载 | Microsoft开始使用 Microsoft SQL Server 下载。选择最符合你的数据和工作负载需求的 SQL Server 试用版、版本、工具或连接器。[这里是图片001]https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.下载SQL…...
JMeter 测试Dubbo 接口
在使用 JMeter 进行 Dubbo 接口的测试时,JMeter 本身并没有直接支持 Dubbo 协议(基于 RPC)的插件。但是,我们可以通过以下几种方式来测试 Dubbo 接口: 1. 使用 JMeter 的 Java 请求(JDBC 请求)…...
Java 日志技术、Logback日志框架、日志级别
一. 日志 1. 日志:程序中的日志,通常就是一个文件,里面记录的是程序运行过程中的各种信息。 二. 日志技术 1. 日志技术:可以将系统执行的信息,方便的记录到指定的位置(控制台、文件中、数据库中) 2. 可以随时以开关的…...
美妆系列圣罗兰气垫粉色与黑色有什么区别?
在美妆界,圣罗兰的气垫一直备受青睐,而其中粉色款和黑色款更是有着各自的特点,存在不少区别呢。 从外观设计来看,粉色款整体给人一种温柔、甜美的感觉,外壳颜色清新,很容易击中少女心,携带在身边…...
HTML中的`<!DOCTYPE html>`是什么意思?
诸神缄默不语-个人CSDN博文目录 在学习HTML时,我们经常会看到HTML文档的开头出现<!DOCTYPE html>,它是HTML文件的第一行。很多初学者可能会疑惑,为什么需要这行代码?它到底有什么作用呢?在这篇文章中࿰…...
Sudo命令的配置及使用
概念 sudo 命令是 Linux 系统中一个非常重要的工具,它允许普通用户以超级用户(通常是 root)或其他用户的身份执行命令。从概念上来说,在普通用户在权限不够的时候,通过 sudo 命令 “摇人”,这个 “人” 就是…...
【HarmonyOS NEXT】鸿蒙三方应用跳转到系统浏览器
【HarmonyOS NEXT】鸿蒙三方应用跳转到系统浏览器 一、前言: 从三方应用跳转到系统浏览器是比较常见的功能。 拓展应用功能边界: 三方应用的功能通常相对聚焦和特定,无法涵盖用户可能需要的所有网络浏览需求。跳转到系统浏览器能让用户访问…...
电梯系统的UML文档06
系统传感器 系统值是用于控制系统的。在类图中系统传感器用一个箭头和系统控制对象连接。 类图中的系统传感器包括AtFloor、电梯呼叫器、关门、开门、门反转、楼层呼叫器和驱动(AtFloor,CarCall,DoorClosed,DoorOpen,…...
指针之旅:从基础到进阶的全面讲解
大家好,这里是小编的博客频道 小编的博客:就爱学编程 很高兴在CSDN这个大家庭与大家相识,希望能在这里与大家共同进步,共同收获更好的自己!!! 本文目录 引言正文(1)内置数…...
AT8870单通道直流电机驱动芯片
AT8870单通道直流电机驱动芯片 典型应用原理图 描述 AT8870是一款刷式直流电机驱动器,适用于打印机、电器、工业设备以及其他小型机器。两个逻辑输入控制H桥驱动器,该驱动器由四个N-MOS组成,能够以高达3.6A的峰值电流双向控制电机。利用电流…...
RISC-V读书笔记
目录 关于RISC-V 关于RISC-V 简洁 RISC-V是一个崭新的架构,比起来传统的x86-64架构,他更加的简约和现代。下面这种图像阐述了传统的x86的指令集的膨胀速度有多么的吓人(汗颜 这是可以理解的,我们的ISA(Computer Syst…...
游戏开发中常用的设计模式
目录 前言一、工厂模式简单工厂模式工厂方法模式抽象工厂模式 二、单例模式三、观察者模式观察者模式的优势 四、状态模式状态模式的优势 五、策略模式策略模式的优势 六、组合模式七、命令模式八、装饰器模式策略模式与状态模式有什么区别呢? 前言 本文介绍了游戏开发中常用…...
Android 11适配全攻略:从理论到实践
随着Google正式发布Android 11,开发者们迎来了新的挑战和机遇。Android 11不仅带来了全新的用户体验和功能提升,还要求开发者们对应用进行相应的适配,以确保应用的兼容性和稳定性。本文将从理论到实践,全面解析Android 11的适配攻…...
Grafana 统一可视化了,告警如何统一?
对于大部分公司,通常都不止一套监控、可观测性相关的系统,云上的、云下的,开源的、商业的,指标的、日志的、链路的,各个系统体验不同,权限难管,如何统一化并为各个团队赋能,是很多技…...
ubuntu20.04有亮度调节条但是调节时亮度不变
尝试了修改grub文件,没有作用,下载了brightness-controllor,问题解决了。 sudo add-apt-repository ppa:apandada1/brightness-controller sudo apt update sudo apt install brightness-controller 之后在应用软件中找到brightness-contro…...
抖音小程序一键获取手机号
前端代码组件 <button v-if"!isFromOrderList"class"get-phone-btn" open-type"getPhoneNumber"getphonenumber"onGetPhoneNumber">一键获取</button>// 获取手机号回调onGetPhoneNumber(e) {var that this tt.login({f…...
某政务行业基于 SeaTunnel 探索数据集成平台的架构实践
分享嘉宾:某政务公司大数据技术经理 孟小鹏 编辑整理:白鲸开源 曾辉 导读:本篇文章将从数据集成的基础概念入手,解析数据割裂给企业带来的挑战,阐述数据集成的重要性,并对常见的集成场景与工具进行阐述&…...
学习ASP.NET Core的身份认证(基于JwtBearer的身份认证8)
为进一步测试通过请求头传递token进行身份验证,在main.htm中增加layui的数据表格组件,并调用后台服务分页显示数据,后台分页查询数据接口如下所示(测试时,直接将数据写死到代码中,没有查询数据库࿰…...
Android 高德地图API(新版)
新版高德地图 前言正文一、创建应用① 获取PackageName② 获取调试版安全码SHA1③ 获取发布版安全码SHA1 二、配置项目① 导入SDK② 配置AndroidManifest.xml 三、获取当前定位信息① ViewBinding使用和导包② 隐私合规设置③ 权限请求④ 初始化定位⑤ 获取定位信息 四、显示地…...
51c~缺陷检测~合集2
我自己的原文哦~ https://blog.51cto.com/whaosoft/12386431 一、缺陷检测~使用深度学习1 这里研究工业ai, 在制造业中任何公司的主要目标都是为客户生产无缺陷产品。如果在产品开发过程中出现任何内部孔、凹坑、磨损或划痕(由于多种原因,从生产设备…...
强化学习与ai黑科技实例
一.强化学习简介和其应用 (1)强化学习,深度学习,有监督,无监督区别与联系。 1)强化学习讨论的核心就是智能机(agent)怎么在复杂,不确定的环境中最大化它能获得的奖励。 2)人工智能包括机器学习,机器学习包括有监督学习,无监督学习(例如聚类…...
《TikTok归来:机遇与挑战并存》
TikTok 回归:波折中的 “重生” 在全球社交媒体的版图中,TikTok 的存在无疑是一颗璀璨的明星。它以独特的短视频形式、强大的算法推荐以及丰富多样的内容,迅速风靡全球,吸引了数以亿计的用户。然而,其发展并非一帆风顺…...
Rust语言的正则表达式
Rust语言的正则表达式 正则表达式(Regular Expressions,简称Regex)是一种强大的文本处理工具,广泛应用于字符串的搜索、匹配、替换和解析。在Rust语言中,正则表达式的支持既高效又功能强大,非常适合开发者…...
三维扫描赋能文化:蔡司3D扫描仪让木质文化遗产焕发新生-沪敖3D
挪威文化历史博物馆在其修复工作中融入现代3D扫描技术,让数百年的历史焕发新生。 文化历史博物馆的工作 文化历史博物馆是奥斯陆大学的一个院系。凭借其在文化历史管理、研究和传播方面的丰富专业知识,该博物馆被誉为挪威博物馆研究领域的领先机构。馆…...