当前位置: 首页 > news >正文

Flutter/Dart:使用日志模块Logger Easier

Flutter笔记
Flutter/Dart:使用日志模块Logger Easier

Logger Easier 是一个为 Dart 和 Flutter 应用程序量身定制的现代化日志管理解决方案。它提供了一个高度灵活、功能丰富的日志记录系统,旨在简化开发者的日志管理工作,同时提供一定的定制能力。
【注】本文对模块的功能、基本用法、架构思想做了介绍。一切API以模块当前版本实际提供的API为准。

- 文章信息 - Author: 李俊才 (jcLee95)
Visit me at CSDN: https://jclee95.blog.csdn.net
My WebSitehttp://thispage.tech/
Email: 291148484@163.com.
Shenzhen China
Address of this article:https://blog.csdn.net/qq_28550263/article/details/144636200
HuaWei:https://bbs.huaweicloud.com/blogs/443063

在这里插入图片描述

组件库地址

  • Pub: https://pub.dev/packages/logger_easier
  • GitHub:https://github.com/jacklee1995/flutter_logger_easier
  • Gitee: https://gitee.com/jacklee1995/flutter_logger_easier

在这里插入图片描述

1. 介绍

为了能使用类似于其它语言那样的日志,比如 Logger for Java(l4j),logger for JavaScript(l4js)等日志库,但又希望尽可能简化配置,同时参考Dart其它的日志库实现,也结合Dart语言本身的特点,完成了flutter_logger_easier。

这个日志插件可以通过自定义日志 中间件 来对 日志 进行加工或处理,比如 控制台输出、文件保存、日志文件轮转和压缩,这都可以使用一个或者多个中间件来处理。但是这些不是必须,如果你需要使用它,最简单的方式只需要直接实例化一个Logger()。

Logger Easier模块具有如下特点:

  1. 多级日志管理

    • 支持 7 个日志级别:Trace, Debug, Info, Warn, Error, Critical, Fatal;
    • 细粒度的日志级别控制;
    • 可配置的最小日志记录级别;
  2. 灵活的日志输出

    • 控制台输出(支持彩色日志);
    • 文件日志记录;
    • 自定义输出目标;
    • 日志文件轮转和压缩;
  3. 高级日志格式化

    • 可定制的日志格式;
    • 支持多种日志格式模板;
    • 丰富的日志记录元数据(时间戳、来源、错误信息等);
  4. 性能监控(开发中)

    • 内置性能度量方法;
    • 异步和同步操作性能追踪;
    • 性能指标自动记录和报告;
  5. 错误处理

    • 自动错误报告
    • 堆栈跟踪记录
    • 可插拔的错误报告器
  6. 单例模式

    • 全局统一的日志管理
    • 简单的初始化和使用

最后一点就是可扩展性,正如之前所述,你可以将日志看作生产线上的产品,可以在流水线上通过多个中间件执行相应操作。

2. 安装

pubspec.yaml 中添加依赖:

dependencies:logger_easier: 替换为最新版本

运行 dart pub getflutter pub get 安装依赖。

也可以使用add命令直接安装最新版本:

dart pub add logger_easier
# 或
flutter pub add logger_easier

3. 用法

3.1 最简单的用法

// app/logger.dart
final logger = Logger();

然后你就可以打印各个级别的日志了。比如下面是一个在Flutter中使用的例子(未必是Flutter,完全可以用于纯Dart项目):

// main.dart
import 'package:flutter/material.dart';import 'app/logger.dart';void main() {logger.trace('The logger is ready.');logger.debug('The logger is ready.');logger.info('The logger is ready.');logger.warn('The logger is ready.');logger.error('The logger is ready.');logger.critical('The logger is ready.');logger.fatal('The logger is ready.');runApp(const LoggerDemoApp());
}// ...

在这里插入图片描述

3.2 文件转存

实际上在内存文件转存也是由插件来实现的,你可以自定义插件来实现你的转存形式。不过基本的文件转存你只需要指定所保存的位置,否则日志记录器也不知道你想要保存到哪里。比如最简单的方式是你直接传入一个绝对路径,比如在Windows上就像这样:

final logger = Logger(minLevel: LogLevel.trace,outputFunction: debugPrint,logDirectory: r'C:\Users\jclee95\Documents\logs',baseFileName: 'app.log',maxFileSize: 10 * 1024 * 1024, // 10MBmaxBackupIndex: 5,compress: true,
);

Windows这里显然就涉及具体的用户名,比如我当前的用户名为jclee95。

但是在其它系统平台上的路径写法可能又是另外一个情况,比如在安卓上目录就可能成了这样:/data/user/0/com.example.example/app_flutter/logs。这里就涉及一个应用域名,比如这里的“com.example.example”(在andriod子项目里面可以设置,也可以在创建Flutter应用时指定)。

可见在不同用户、不同系统上,如果指定绝对路径是没有通用性的。因此顺便提一句,有客户端开发经验的读者一般是不会直接去拼接一个绝对路径的,即使仅用于几种PC端Electron中就是如此,更不要说全平台开发的Flutter。在Flutter中,目前这个是使用 path_provider 包来获取的。比如:

import 'package:path_provider/path_provider.dart';

对于应用程序可能放置用户生成的数据或应用程序无法重新创建的数据的目录路径,可使用path_providergetApplicationDocumentsDirectory方法。这在Windows上是用户的Documents目录,而在安卓上则为应用目录。

这样,我们可以在getApplicationDocumentsDirectory获取的目录下使用一个logs子目录来写入、读取日志。

final appDocDir = await getApplicationDocumentsDirectory();
final logDirectory = path.join(appDocDir.path, 'logs');

不过这个logs子目录不一定存在,比如一般你没创建它时就一定不存在,并且还需要一些检查目录是否可写入文件的东西。这就可以加入下面这些代码:

// 确保日志目录存在
final directory = Directory(logDirectory);
if (!await directory.exists()) {await directory.create(recursive: true);debugPrint('Created log directory: $logDirectory');
}// 测试目录是否可写
try {final testFile = File(path.join(logDirectory, 'test.txt'));await testFile.writeAsString('permission test');await testFile.delete();debugPrint('Log directory is writable');
} catch (e, s) {debugPrint('Cannot write to log directory: $e\n$s');
}

最后,你就可以舒适地创建一个基本的日志器了:

Logger(minLevel: LogLevel.trace,outputFunction: debugPrint,logDirectory: logDirectory,baseFileName: 'app.log',maxFileSize: 10 * 1024 * 1024, // 10MBmaxBackupIndex: 5,compress: true,
);

然后尝试打印一些日志,你就可以在指定的目录看到同时被保存下来了:
在这里插入图片描述

3.3 日志级别

日志级别是日志系统中非常重要的概念,它帮助开发者控制日志的详细程度和重要性。Logger Easier 提供了 7 个日志级别,从最详细到最严重,每个级别都有其特定的使用场景。

Trace 级别是最详细的日志级别,主要用于非常细粒度的诊断信息。在开发和深度调试阶段,你可能需要记录非常详细的执行路径和状态变化。

logger.trace('进入方法 calculateSum,参数:a = $a, b = $b');

Debug 级别适用于开发和诊断阶段,记录对开发者有帮助的详细信息,但在生产环境中通常会被禁用。

logger.debug('计算中间结果:intermediateResult = $intermediateResult');

Info 级别记录应用程序的重要事件和关键流程,这些信息在生产环境中是有意义的,可以帮助理解系统的运行状态。

logger.info('用户 $userId 成功登录');

Warn 级别用于记录潜在的问题或异常情况,这些情况可能不会立即导致系统失败,但值得关注。

logger.warn('数据库连接池接近最大连接数');

Error 级别表示导致功能异常的问题,这些错误会影响特定功能的正常运行,但不会完全中断系统。

try {// 某些可能抛出异常的操作
} catch (e) {logger.error('文件读取失败', error: e);
}

Critical 级别表示严重错误,这些错误可能会严重影响系统的运行,需要立即关注和处理。

logger.critical('关键服务不可用', error: serviceUnavailableException);

Fatal 级别是最严重的日志级别,表示系统已经无法继续运行,通常意味着需要立即重启或进行紧急恢复。

logger.fatal('系统核心组件崩溃', error: criticalSystemError);

在实际使用中,你可以通过设置最小日志级别来控制记录哪些日志。例如,在生产环境中,你可能只想记录 Info 及以上级别的日志:

final logger = Logger(minLevel: LogLevel.info, // 只记录 Info 及以上级别的日志
);

这种灵活的日志级别设计使得 Logger Easier 能够适应不同的开发和运行环境,帮助开发者更好地管理和分析系统日志。

3.4 中间件

中间件是 Logger Easier 灵活性的关键,它用于允许各位开发者自定义日志处理流程。中间件可以看作是日志处理的流水线,每个中间件都可以对日志进行过滤、格式化、打印或输出。

Logger Easier 的中间件由三个核心组件组成:打印器(Printer)、格式化器(Formatter)和过滤器(Filter)。这种设计提供了极高的灵活性和可扩展性。

最简单的中间件配置是使用默认实现:

final logger = Logger(middlewares: [LogMiddleware(printer: ConsolePrinter(),formatter: SimpleFormatter(),filter: LevelFilter(LogLevel.debug),)]
);

你可以创建自定义的中间件组件来满足特定需求。例如,创建一个自定义的打印器:

class CustomPrinter extends BasePrinter {String printf(LogRecord record) {// 自定义日志打印逻辑return '[CUSTOM] ${record.timestamp} - ${record.message}';}String get name => 'CustomPrinter';Map<String, dynamic> get config => {};
}

自定义格式化器同样简单:

class CustomFormatter implements LogFormatter {String format(LogRecord record) {// 自定义日志格式return '${record.level}|${record.timestamp}|${record.message}';}// 实现其他必需的接口方法String get name => 'CustomFormatter';
}

过滤器允许更精细的日志控制:

class CustomFilter implements LogFilter {bool shouldLog(LogRecord record) {// 复杂的日志过滤逻辑return record.level.value >= LogLevel.warn.value && !record.message.contains('ignore');}
}

多个中间件可以串联使用,创建复杂的日志处理管道:

final logger = Logger(middlewares: [// 控制台输出中间件LogMiddleware(printer: ConsolePrinter(useColor: true),formatter: SimpleFormatter(),filter: LevelFilter(LogLevel.debug),),// 文件输出中间件LogMiddleware(printer: FilePrinter(logDirectory: '/path/to/logs',baseFileName: 'app.log',),formatter: JsonFormatter(),filter: LevelFilter(LogLevel.info),),// 网络日志中间件(假设存在)LogMiddleware(printer: NetworkPrinter(url: 'https://log-collector.example.com'),formatter: CompactFormatter(),filter: LevelFilter(LogLevel.error),)]
);

这种中间件架构的优势在于其极高的灵活性。你可以轻松地添加、移除或替换日志处理组件,而不需要修改核心日志记录逻辑。每个中间件都专注于单一职责:打印器负责输出,格式化器负责格式,过滤器负责筛选,这种设计遵循了单一职责原则。

通过自定义中间件,你可以实现各种高级日志需求,如敏感信息脱敏、日志加密、实时日志分析等。Logger Easier 为开发者提供了一个强大而灵活的日志解决方案。

3.5 打印器

3.5.1 打印器简介

打印器是日志中间件的核心组件,负责日志的最终输出和呈现。在日志处理流水线中,打印器处于最后一个环节,它决定了日志信息的最终展现方式和目的地。每个打印器都实现了 BasePrinter 抽象类,提供了灵活且可扩展的日志输出机制。

打印器的主要职责是将格式化后的日志记录转换为可读的字符串,并将其输出到特定的目标。这个目标可以是控制台、文件、网络接口,甚至是自定义的输出通道。不同的打印器可以实现完全不同的输出策略,使得日志系统具有极高的灵活性。

3.5.2 BasePrinter接口

BasePrinter 接口定义了打印器的基本行为和约定:

abstract class BasePrinter {/// 打印日志记录String printf(LogRecord record);/// 初始化打印器Future<void> init() async {}/// 关闭打印器Future<void> close() async {}/// 获取打印器名称String get name;/// 获取打印器配置Map<String, dynamic> get config;// ...}

3.5.3 内置打印器

Logger Easier 提供了三种内置打印器,每个都针对不同的使用场景:

控制台打印器(ConsolePrinter,已内置)是最常用的打印器,提供彩色输出和丰富的格式化选项:

final consolePrinter = ConsolePrinter(useColor: true,maxLineLength: 120,
);

文件打印器(FilePrinter)支持日志文件的自动管理,包括文件轮转和压缩:

final filePrinter = FilePrinter(logDirectory: '/path/to/logs',baseFileName: 'app.log',maxFileSize: 10 * 1024 * 1024, // 10MBmaxBackupIndex: 5,compress: true,
);

JSON打印器(JsonPrinter)专门用于生成结构化的日志输出:

final jsonPrinter = JsonPrinter(prettyPrint: true,includeStackTrace: true,
);

3.5.4 自定义打印器

自定义打印器可以通过继承 BasePrinter 来实现特殊的日志输出需求,比如:

class NetworkPrinter extends BasePrinter {final String logServerUrl;NetworkPrinter(this.logServerUrl);String printf(LogRecord record) {// 实现日志通过网络发送到远程服务器_sendLogToServer(record);return record.message;}Future<void> _sendLogToServer(LogRecord record) async {// 网络日志发送逻辑}// ...
}

打印器不仅仅是输出日志,它们还提供了丰富的配置和统计功能。例如,可以获取打印器的统计信息、重置统计数据,甚至动态更新配置:

// 获取打印器统计信息
final stats = consolePrinter.getStats();
print('已打印日志数量: ${stats['printedLogs']}');// 重置统计信息
consolePrinter.resetStats();

通过这种设计,Logger Easier 的打印器不仅提供了灵活的日志输出方式,还为开发者提供了强大的日志管理和定制能力。无论是简单的控制台输出,还是复杂的分布式日志系统,开发者都可以轻松实现。

3.6 格式化器

格式化器是日志中间件的关键组件,负责将原始日志记录转换为人类可读或机器可解析的字符串。在日志处理流水线中,格式化器位于过滤器之后、打印器之前,它决定了日志信息的最终呈现形式。

格式化器的主要职责是提取日志记录中的关键信息,并按照预定义或自定义的模式进行排列和展示。这种灵活的设计使得开发者可以根据不同的使用场景定制日志输出格式。

Logger Easier 定义了 LogFormatter 接口,为所有格式化器提供了统一的约定:

abstract class LogFormatter {/// 格式化日志记录String format(LogRecord record);/// 获取格式化器的名称String get name;/// 获取格式化器的配置Map<String, dynamic> get config;
}

内置的格式化器提供了多种常用的日志格式化方案。简单格式化器(SimpleFormatter)提供了基础的日志格式:

final simpleFormatter = SimpleFormatter(includeTimestamp: true,includeLevel: true,includeStackTrace: false,
);

JSON格式化器(JsonFormatter)专门用于生成结构化的日志输出:

final jsonFormatter = JsonFormatter(prettyPrint: true, // 美化JSON输出
);

自定义格式化器可以通过实现 LogFormatter 接口来满足特定需求:

class CustomFormatter implements LogFormatter {String format(LogRecord record) {// 自定义日志格式return '${record.level}|${record.timestamp}|${record.message}';}String get name => 'CustomFormatter';Map<String, dynamic> get config => {};
}

格式化器还支持更高级的功能,如占位符和格式字符串:

class AdvancedFormatter implements LogFormatter {List<String> get supportedPlaceholders => ['timestamp', 'level', 'message', 'error', 'source'];bool isValidFormatString(String formatString) {// 验证自定义格式字符串的有效性return formatString.contains('{timestamp}') && formatString.contains('{message}');}String format(LogRecord record) {// 使用自定义格式字符串return '[{timestamp}] ({level}) {message}';}
}

在中间件中使用格式化器非常简单:

final logger = Logger(middlewares: [LogMiddleware(printer: ConsolePrinter(),formatter: JsonFormatter(prettyPrint: true),filter: LevelFilter(LogLevel.info),)]
);

格式化器的设计遵循单一职责原则,专注于将日志记录转换为特定格式的字符串。这种解耦的设计使得日志系统具有极高的灵活性和可扩展性。开发者可以轻松地为不同的使用场景(如调试、监控、审计)定制日志格式。

通过提供丰富的内置格式化器和简单的自定义机制,Logger Easier 为开发者提供了强大且灵活的日志格式化解决方案。无论是简单的控制台输出,还是复杂的结构化日志记录,开发者都可以轻松实现。

3.7 过滤器

3.7.1 过滤器简介

过滤器用于负责决定哪些日志记录应该被处理和记录。在日志处理流水线中,过滤器位于最前端,充当日志的"守门员",根据预定义的规则精确控制日志的输出。

过滤器的核心职责是评估每个日志记录是否满足特定条件。这种机制使得开发者能够根据日志级别、内容、时间戳或任何自定义逻辑来控制日志的记录。通过灵活的过滤策略,可以显著减少不必要的日志输出,提高系统性能和日志的可读性。

3.7.2 LevelFilter

基础级别过滤器(LevelFilter)是最常用的过滤器,它根据日志级别进行过滤:

// 只记录 INFO 及以上级别的日志
final levelFilter = LevelFilter(LogLevel.info);

3.7.3 CompositeFilter

复合过滤器(CompositeFilter)允许组合多个过滤器,只有当所有过滤器都通过时才记录日志:

final compositeFilter = CompositeFilter([LevelFilter(LogLevel.debug),CustomFilter((record) => !record.message.contains('sensitive')),
]);

3.7.4 RegexFilter

正则表达式过滤器(RegexFilter)允许基于消息内容的高级过滤:

final regexFilter = RegexFilter(RegExp(r'error|critical', caseSensitive: false));

3.7.5 TimeRangeFilter

时间范围过滤器(TimeRangeFilter)可以限制特定时间段内的日志:

final timeFilter = TimeRangeFilter(DateTime.now().subtract(Duration(days: 1)),DateTime.now()
);

过滤器的真正强大之处在于它们可以在中间件中灵活组合:

final logger = Logger(middlewares: [LogMiddleware(printer: ConsolePrinter(),formatter: SimpleFormatter(),filter: CompositeFilter([LevelFilter(LogLevel.info),CustomFilter((record) {// 排除包含特定敏感词的日志return !record.message.contains('password');}),TimeRangeFilter(DateTime.now().subtract(Duration(hours: 24)),DateTime.now())]),)]
);

3.7.6 自定义过滤器

自定义过滤器(CustomFilter)提供了最大的灵活性,可以实现任意复杂的日志过滤逻辑:

final customFilter = CustomFilter((record) {// 复杂的过滤逻辑return record.level.index >= LogLevel.warn.index &&!record.message.contains('ignore') &&record.timestamp.isAfter(DateTime.now().subtract(Duration(hours: 1)));
});

开发者还可以通过继承 LogFilter 接口创建完全自定义的过滤器:

class AdvancedCustomFilter implements LogFilter {final List<String> _ignoredModules;AdvancedCustomFilter(this._ignoredModules);bool shouldLog(LogRecord record) {// 复杂的过滤逻辑:根据模块、级别和其他条件过滤return !_ignoredModules.contains(record.source) &&record.level.index >= LogLevel.warn.index;}
}

相关文章:

Flutter/Dart:使用日志模块Logger Easier

Flutter笔记 Flutter/Dart&#xff1a;使用日志模块Logger Easier Logger Easier 是一个为 Dart 和 Flutter 应用程序量身定制的现代化日志管理解决方案。它提供了一个高度灵活、功能丰富的日志记录系统&#xff0c;旨在简化开发者的日志管理工作&#xff0c;同时提供一定的定制…...

阿里云云服务器初始化

如果我们的云服务器出现无法挽回的错误时&#xff0c;我们可以尝试初始化云服务器进行解决。 首先搜索阿里云&#xff08;你要先确认自己已经购买了阿里云的云服务器&#xff09;&#xff1a; 登录账号后主页向下划 进入后点击管理控制台 点击进入后可以看到正在运行&#xff0…...

Python中SKlearn的K-means使用详解

文章目录 Python中SKlearn的K-means使用详解一、引言二、K-means算法原理三、使用SKlearn进行K-means聚类的步骤1、导入必要的库2、生成数据集3、创建K-means模型并设置参数4、训练模型5、预测簇标签6、可视化结果 四、总结 Python中SKlearn的K-means使用详解 一、引言 K-mea…...

红帽RHCE认证适用哪些人学习

红帽 RHCE工程师认证有着广泛的适用人群。对于初入 IT 行业的新手来说&#xff0c;RHCE 是快速建立专业基础、提升自身竞争力的绝佳途径。它能帮助新人系统地学习 Linux 系统知识&#xff0c;从基础的安装配置到复杂的网络服务管理&#xff0c;一步一个脚印地构建起坚实的技术框…...

FFmpeg 框架简介和文件解复用

文章目录 ffmpeg框架简介libavformat库libavcodec库libavdevice库 复用&#xff08;muxers&#xff09;和解复用&#xff08;demuxers&#xff09;容器格式FLVScript Tag Data结构&#xff08;脚本类型、帧类型&#xff09;Audio Tag Data结构&#xff08;音频Tag&#xff09;V…...

《Java核心技术I》Swing中的边框

边框 BorderFactory静态方法创建边框&#xff0c;凹斜面&#xff0c;凸斜面&#xff0c;蚀刻&#xff0c;直线&#xff0c;蒙版&#xff0c;空白。 边框添加标题&#xff0c;BorderFactory.createTitledBorder 组合边框&#xff0c;BorderFactory.createCompoundBorder JCo…...

MySQL 中的常见错误与排查

在 MySQL 数据库的日常运维中&#xff0c;管理员可能会遇到各种错误。无论是查询性能问题、连接异常、数据一致性问题&#xff0c;还是磁盘空间不足等&#xff0c;及时排查并解决这些问题是保证数据库稳定运行的关键。本文将列出 MySQL 中一些常见的错误及其排查方法。 一、连接…...

SQL 查询方式比较:子查询与自连接

在 SQL 中&#xff0c;子查询和自连接是两种常见的查询方式&#xff0c;它们的功能虽然可以相同&#xff0c;但实现的方式不同。本文通过具体示例&#xff0c;深入探讨这两种查询方式&#xff0c;并配合数据展示&#xff0c;帮助大家理解它们的使用场景和差异。 数据示例 假设…...

Linux下学【MySQL】所有常用类型详解( 配实操图 通俗易懂 )

每日激励&#xff1a;“当你觉得你会幸运时&#xff0c;幸运就会眷顾你&#xff0c;所以努力吧&#xff0c;只要你把事情做好&#xff0c;并觉得你会幸运&#xff0c;你将会变得幸运且充实。” 绪论​&#xff1a; 本章继续学习MySQL的知识&#xff0c;本章主要讲到mysql中的所…...

Gin-vue-admin(1):环境配置和安装

目录 环境配置如果443网络连接问题&#xff0c;需要添加代理服务器 后端运行前端运行 环境配置 git clone https://gitcode.com/gh_mirrors/gi/gin-vue-admin.git到server文件目录下 go mod tidygo mod tidy 是 Go 语言模块系统中的一个命令&#xff0c;用于维护 go.mod 文件…...

如何在centos系统上挂载U盘

在CentOS上挂载NTFS格式的U盘,需要执行一系列步骤,包括识别U盘设备、安装必要的软件、创建挂载点,并最终挂载U盘。以下是在CentOS上挂载NTFS格式U盘的详细步骤: 一、准备工作 确认CentOS版本: 确保你的CentOS系统已经安装并正常运行。不同版本的CentOS在命令和工具方面可能…...

2024年12月大语言模型最新对比:GPT-4、Claude 3、文心一言等详细评测

前言 随着人工智能技术的快速发展&#xff0c;大语言模型(LLM)已经成为了技术领域最热门的话题。本文将详细对比目前主流的大语言模型&#xff0c;帮助大家选择最适合的工具。 一、OpenAI GPT系列 1. GPT-4 核心优势&#xff1a; 多模态理解能力强 逻辑推理能力出色 创造…...

openjdk17 从C++视角看 String的intern的jni方法JVM_InternString方法被gcc编译器连接

symbols-unix 文件部分内容 JVM_IHashCode JVM_InitClassName JVM_InitStackTraceElement JVM_InitStackTraceElementArray JVM_InitializeFromArchive JVM_InternString 要理解在 symbols-unix 文件中包含 JVM_InternString 方法的原因&#xff0c;我们需要从构建过程、符号…...

day16 python(4)——UnitTest

【没有所谓的运气&#x1f36c;&#xff0c;只有绝对的努力✊】 目录 1、UnitTest框架介绍 1.1 UnitTest框架 1.2 unitTest的组成&#xff08;5部分&#xff09; 1.2.1 TestCase&#xff08;测试用例&#xff09; 1.2.2 TestSuit 和 TestRunner 【方法1】 【方法2】 1…...

Kafka快速扫描

Architecture 系统间解耦&#xff0c;异步通信&#xff0c;削峰填谷 Topic 消息主题&#xff0c;用于存储消息 Partition 分区&#xff0c;通过扩大分区&#xff0c;可以提高存储量 Broker 部署Kafka服务的设备 Leader kafka主分区 Follwer kafka从分区 高性能之道&#xff1a…...

python打包时候遇到问题:ImportError: DLL load failed while importing _ufuncs: 找不到指定的模块

问题&#xff1a;python打包时候遇到问题&#xff1a;ImportError: DLL load failed while importing _ufuncs: 找不到指定的模块 解决方法 pip uninstall scipy pip install scipy总结&#xff1a;卸载出问题的库并重新安装&#xff0c;再次通过pyinstaller -F -w xxx.py 打包…...

【6】期末复习C#第6套

1.两个指针变量不能 2.函数可以有也可以没有形参 3.开始执行点是程序中的main函数 4.调用函数时形参和实参各占一个独立的存储单元 5.给指针赋NULL值和地址值 6.函数头和函数体 7.&#xff08;C&#xff09;形式参数是局部变量 8.在C语言中&#xff0c;一维数组的定义方…...

开源轮子 - EasyExcel01(核心api)

EasyExcel01 - 核心api 本文整理自掘金大佬 - 竹子爱熊猫 https://juejin.cn/post/7405158045662576640 文章目录 EasyExcel01 - 核心api一&#xff1a;初相识EasyExcel1&#xff1a;写入excel入门2&#xff1a;读取Excel入门 二&#xff1a;数据模型注解1&#xff1a;读写通用…...

SpringBoot3+Vue3开发在线考试系统

项目介绍 项目分为3种角色&#xff0c;分别为&#xff1a;超级管理员、老师、学生。超级管理员&#xff0c;负责系统的设置、角色的创建、菜单的管理、老师的管理等功能&#xff0c;也可以叫做系统管理员&#xff1b;老师角色&#xff0c;负责系统业务的管理&#xff0c;包括学…...

2. Kafka入门-开发环境准备

Kafka入门-开发环境准备 1. 环境准备2. Centos7安装2.1 镜像安装2.2 初始化配置2.3 JDK1.8安装 ---------------------------------------------------------------------------------------------- 1. 环境准备 2. Centos7安装 2.1 镜像安装 2.2 初始化配置 设置系统时区 …...

Halcon中histo_2dim(Operator)算子原理及应用详解

在Halcon中&#xff0c;histo_2dim算子是一个用于计算双通道灰度值图像的直方图的工具。以下是对该算子的原理及应用的详细解释&#xff1a; 一、原理 histo_2dim算子的函数原型为&#xff1a;histo_2dim(Regions, ImageCol, ImageRow : Histo2Dim : : )。 输入参数&#xff…...

TCP 与 UDP

TCP与UDP的区别分析 TCP&#xff08;传输控制协议&#xff09;和UDP&#xff08;用户数据报协议&#xff09;是互联网协议族&#xff08;TCP/IP&#xff09;中的两种重要传输层协议。它们在数据传输的方式、特性以及应用场景方面存在显著差异。以下将从多个方面详细分析TCP与U…...

ubuntu 安装更新 ollama新版本

ubuntu 安装更新 ollama新版本 我这里是 2024-12-18 ollama 版本是 0.5.3 1手动下载 ollama-linux-amd64.tgz https://github.com/ollama/ollama/releases 2下载脚本 https://ollama.com/install.sh install.sh 和 ollama-linux-amd64.tgz 在相同路径下 修改&#xff1a;…...

Numpy基本操作

目录 1、生成数组的方法 1.1、生成0和1的数组 1.2、从现有数组生成 1.2.1、生成方式 1.3、生成固定范围的数组 1.4、生成随机数组 1.4.1、使用模块介绍 1.4.2、均匀分布 1.4.3、正态分布 1.4.4、正态分布创建方式 1、生成数组的方法 1.1、生成0和1的数组 import numpy…...

本地部署webrtc应用怎么把http协议改成https协议?

环境&#xff1a; WSL2 Ubuntu22.04 webrtc视频聊天应用 问题描述&#xff1a; 本地部署webrtc应用怎么把http协议改成https协议&#xff1f; http协议在安卓手机浏览器上用不了麦克风本&#xff0c;来地应用webrtc 本来是http协议&#xff0c;在安卓手机上浏览器不支持使…...

基于xiaothink对Wanyv-50M模型进行c-eval评估

使用pypi安装xiaothink&#xff1a; pip install xiaothink1.0.2下载模型&#xff1a; 万语-50M 开始评估(修改模型路径后即可直接开始运行&#xff0c;结果保存在output文件夹里)&#xff1a; import os import json import pandas as pd import re from tqdm import tqdm i…...

使用k6进行kafka负载测试

1.安装环境 kafka环境 参考Docker搭建kafka环境-CSDN博客 xk6-kafka环境 ./xk6 build --with github.com/mostafa/xk6-kafkalatest 查看安装情况 2.编写脚本 test_kafka.js // Either import the module object import * as kafka from "k6/x/kafka";// Or in…...

Unity A*算法实现+演示

注意&#xff1a; 本文是对基于下方文章链接的理论&#xff0c;并最终代码实现&#xff0c;感谢作者大大的描述&#xff0c;非常详细&#xff0c;流程稍微做了些改动&#xff0c;文末有工程网盘链接&#xff0c;感兴趣的可以下载。 A*算法详解(个人认为最详细,最通俗易懂的一…...

浏览器要求用户确认 Cookies Privacy(隐私相关内容)是基于隐私法规的要求,VUE 实现,html 代码

Cookie Notices and Cookie Consent | Cookiepedia 1. 法律法规要求 许多国家和地区的隐私法律要求网站在存储或处理用户数据&#xff08;包括 Cookies&#xff09;之前必须获得用户的明确同意&#xff1a; GDPR&#xff08;欧盟通用数据保护条例&#xff09; 要求&#xff…...

如何设计高效的商品系统并提升扩展性:从架构到实践的全方位探索

在现代电商、零售及企业资源管理系统中&#xff0c;商品管理无疑是核心模块之一。随着市场的变化与企业规模的扩展&#xff0c;商品系统需要具备强大的功能支持以及高效的扩展能力&#xff0c;以应对日益复杂的业务需求。一个设计良好的商品系统不仅仅是一个商品信息的容器&…...