Flutter 日志模块之参考设计

Flutter开发日志——部署开发环境 安装SDK Get the Flutter SDK 下载之后,需要将SDK路径添加到系统环境变量。以本人的路径为例。 创建APP APP状态 阅读详情

目录

1. 📂 简介

2. 🔱 核心架构设计

3. 💠 使用步骤

3.1 添加依赖

3.2 初始化

3.3 打印日志

4. ⚛️ 工程化封装

4.1 第一层:优雅的调用入口

4.1.1 快捷函数 (log_utils.dart)

4.1,2 日志管理器 (log_manager.dart)

4.2 第二层:配置与中控逻辑

4.2.1 灵活的配置 (log_config.dart)

4.2.2 逻辑中控 (log_core.dart)

4.3 第三层:性能优化与实现

4.3.1 异步缓冲区机制 (file_logger.dart)

4.3.2 控制台美化 (console_logger.dart)

5. ✅ 小结


1. 📂 简介

        在 Flutter 项目开发中,日志系统是调试和线上排查的“眼睛”。很多开发者习惯直接使用 print 或 debugPrint,但在中大型项目中,这显然不够:

  1. 无法持久化:线上用户报错,没日志等于盲飞。

  2. 性能隐患:高频的 IO 操作会阻塞 UI 线程。

  3. 职责混乱:打印逻辑散落在业务代码各处,难以统一开关。

        今天分享一套三层架构的日志模块设计方案,支持异步缓冲区写入、多处理器分发以及自动清理机制。

2. 🔱 核心架构设计

        为了降低维护成本,我们将日志系统分为三层,各司其职:

  • 第一层:门面层 (Facade):提供最简单的全局函数和单例入口,让开发者“无感”调用。

  • 第二层:核心层 (Core):负责配置解析、级别过滤、逻辑分发。

  • 第三层:实现层 (Handler):具体的落地执行者,如控制台着色打印、文件分片存储。

3. 💠 使用步骤

3.1 添加依赖

# pubspec.yaml
dependencies:
  #  用于国际化(i18n)和本地化(l10n)操作,如日期、时间、数字格式化等。
  intl: ^0.20.2
  #  用于在 Flutter 应用中进行日志记录。它支持多种输出形式,如控制台、文件、以及彩色日志。
  logger: ^1.1.0
  #  提供了在设备上查找常用文件目录的方法,如获取应用的文档目录、临时目录等。
  path_provider: ^2.1.3

3.2 初始化

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  // 根据编译模式选择配置
  await LogManager().initialize(
    config: kReleaseMode ? LogConfig.production() : LogConfig.defaultConfig()
  );
  
  runApp(const MyApp());
}

3.3 打印日志

logI("App Running...");

4. ⚛️ 工程化封装

4.1 第一层:优雅的调用入口

        我们不希望在业务代码里看到复杂的初始化逻辑,因此通过 log_utils.dart 提供一套快捷函数。

4.1.1 快捷函数 (log_utils.dart)

///
/// Description:    日志工具类
/// CreateDate:     2025/12/17 11:41
/// Author:         agg
///
void logD(String message, {String? tag = "MoJieGlass"}) {
  LogManager.debug(tag != null ? '[$tag] $message' : message);
}

void logI(String message, {String? tag = "MoJieGlass"}) {
  LogManager.info(tag != null ? '[$tag] $message' : message);
}

void logE(String message, {String? tag = "MoJieGlass"}) {
  LogManager.error(tag != null ? '[$tag] $message' : message);
}

4.1,2 日志管理器 (log_manager.dart)

        LogManager 采用单例模式,作为整个系统的“总线”,负责生命周期管理。

///
/// Description:    日志管理器(单例)
/// CreateDate:     2025/12/17 11:40
/// Author:         agg
///
class LogManager {
  // 声明静态变量
  static final LogManager _instance = LogManager._internal();

  // 工厂构造函数
  factory LogManager() => _instance;

  // 私有命名构造函数
  LogManager._internal();

  LogCore? _logCore;
  LogConfig? _config;

  /// 初始化日志系统
  Future<void> initialize({LogConfig? config}) async {
    if (_logCore != null) {
      await _logCore!.dispose();
    }

    _config = config ?? LogConfig.defaultConfig();
    _logCore = LogCore(config: _config!);
    await _logCore!.initialize();
  }

  /// 获取日志核心实例
  LogCore get logger {
    if (_logCore == null) {
      throw StateError('LogManager not initialized. Call initialize() first.');
    }
    return _logCore!;
  }

  /// 获取配置
  LogConfig? get config => _config;

  /// 是否已初始化
  bool get isInitialized => _logCore != null;

  /// 快捷访问方法
  static void verbose(String message) {
    _instance.logger.verbose(message);
  }

  static void debug(String message) {
    _instance.logger.debug(message);
  }

  static void info(String message) {
    _instance.logger.info(message);
  }

  static void warning(String message) {
    _instance.logger.warning(message);
  }

  static void error(String message) {
    _instance.logger.error(message);
  }

  static void wtf(String message) {
    _instance.logger.wtf(message);
  }

  /// 网络日志
  static void network(
    String method,
    String url, {
    dynamic data,
    Map<String, dynamic>? headers,
    int? statusCode,
    String? response,
    int duration = 0,
  }) {
    _instance.logger.network(
      method,
      url,
      data: data,
      headers: headers,
      statusCode: statusCode,
      response: response,
      duration: duration,
    );
  }

  /// 销毁资源
  Future<void> dispose() async {
    await _logCore?.dispose();
    _logCore = null;
    _config = null;
  }
}

4.2 第二层:配置与中控逻辑

        这一层是整个模块的大脑,决定了日志“去哪里”和“存多久”。

4.2.1 灵活的配置 (log_config.dart)

        通过 LogConfig,我们可以轻松实现:开发环境看控制台,生产环境关掉控制台只存文件。

///
/// Description:    日志配置类
/// CreateDate:     2025/12/17 11:19
/// Author:         agg
///
class LogConfig {
  /// 是否启用文件日志
  final bool enableFileLog;

  /// 是否启用控制台日志
  final bool enableConsoleLog;

  /// 最小日志级别(低于此级别的日志不会被记录)
  final Level minLevel;

  /// 日志目录路径,为空则使用默认目录
  final String? logDirectory;

  /// 单个日志文件最大大小(字节),默认 5MB
  final int maxFileSize;

  /// 内存缓冲区最大日志条数
  final int maxBufferSize;

  /// 日志文件名前缀
  final String filePrefix;

  /// 日志文件扩展名
  final String fileExtension;

  /// 保留日志文件的天数
  final int daysToKeep;

  /// 默认配置
  factory LogConfig.defaultConfig() => LogConfig(
    enableFileLog: true,
    enableConsoleLog: true,
    minLevel: Level.verbose,
    maxFileSize: 5 * 1024 * 1024,
    maxBufferSize: 100,
    daysToKeep: 7,
  );

  /// 生产环境配置
  factory LogConfig.production() => LogConfig(
    enableFileLog: true,
    enableConsoleLog: true,
    // 生产环境关闭控制台日志
    minLevel: Level.info,
    // 生产环境只记录info及以上级别,10MB文件大小
    maxFileSize: 10 * 1024 * 1024,
    maxBufferSize: 200,
    // 生产环境保留更久
    daysToKeep: 30,
  );

  const LogConfig({
    required this.enableFileLog,
    required this.enableConsoleLog,
    this.minLevel = Level.verbose,
    this.logDirectory,
    this.maxFileSize = 5 * 1024 * 1024,
    this.maxBufferSize = 100,
    this.filePrefix = 'app',
    this.fileExtension = 'log',
    this.daysToKeep = 7,
  });

  /// 验证配置
  void validate() {
    if (maxFileSize <= 0) {
      throw ArgumentError('maxFileSize must be greater than 0');
    }
    if (maxBufferSize <= 0) {
      throw ArgumentError('maxBufferSize must be greater than 0');
    }
    if (daysToKeep <= 0) {
      throw ArgumentError('daysToKeep must be greater than 0');
    }
  }
}

4.2.2 逻辑中控 (log_core.dart)

        LogCore 持有两个处理器:ConsoleLogger 和 FileLogger。

///
/// Description:    核心日志类
/// CreateDate:     2025/12/17 11:39
/// Author:         agg
///
class LogCore {
  final LogConfig config;
  late ConsoleLogger _consoleLogger;
  late FileLogger _fileLogger;
  bool _isInitialized = false;
  bool _isDisposed = false;

  LogCore({required this.config}) {
    config.validate();
    _initLoggers();
  }

  void _initLoggers() {
    if (config.enableConsoleLog) {
      _consoleLogger = ConsoleLogger();
    }

    if (config.enableFileLog) {
      _fileLogger = FileLogger(
        customDirectory: config.logDirectory,
        maxFileSize: config.maxFileSize,
        maxBufferSize: config.maxBufferSize,
        filePrefix: config.filePrefix,
        fileExtension: config.fileExtension,
        daysToKeep: config.daysToKeep,
      );
    }
  }

  /// 初始化日志系统
  Future<void> initialize() async {
    if (_isDisposed) {
      throw StateError('LogCore has been disposed');
    }

    try {
      if (config.enableFileLog) {
        await _fileLogger.initialize();
      }
      _isInitialized = true;
    } catch (e) {
      _isInitialized = false;
      // 文件日志初始化失败不影响控制台日志
      if (config.enableConsoleLog) {
        _consoleLogger.log(
          level: Level.error,
          message: 'Failed to initialize file logger: $e',
        );
      }
    }
  }

  void log({required Level level, required String message}) {
    if (!_isInitialized || _isDisposed) return;
    if (level.index < config.minLevel.index) return;

    try {
      // 控制台日志
      if (config.enableConsoleLog) {
        _consoleLogger.log(level: level, message: message);
      }

      // 文件日志
      if (config.enableFileLog) {
        _fileLogger.log(level: level, message: message);
      }
    } catch (e) {
      // 日志记录失败不应影响应用运行
      print('Logging failed: $e');
    }
  }

  void verbose(String message) {
    log(level: Level.verbose, message: message);
  }

  void debug(String message) {
    log(level: Level.debug, message: message);
  }

  void info(String message) {
    log(level: Level.info, message: message);
  }

  void warning(String message) {
    log(level: Level.warning, message: message);
  }

  void error(String message) {
    log(level: Level.error, message: message);
  }

  void wtf(String message) {
    log(level: Level.wtf, message: message);
  }

  /// 网络请求日志
  void network(
    String method,
    String url, {
    dynamic data,
    Map<String, dynamic>? headers,
    int? statusCode,
    String? response,
    int duration = 0,
  }) {
    final message =
        '''
[NETWORK] $method $url
Duration: ${duration}ms
Status: $statusCode
${headers != null ? 'Headers: $headers' : ''}
${data != null ? 'Request: $data' : ''}
${response != null ? 'Response: $response' : ''}
    '''
            .trim();

    info(message);
  }

  /// 获取日志文件
  Future<List<File>> getLogFiles() async {
    if (!_isInitialized || _isDisposed || !config.enableFileLog) {
      return [];
    }
    return await _fileLogger.getLogFiles();
  }

  /// 获取最近日志
  Future<String> getRecentLogs({int lines = 100, String? filePath}) async {
    if (!_isInitialized || _isDisposed || !config.enableFileLog) {
      return '';
    }
    return await _fileLogger.getRecentLogs(lines: lines, filePath: filePath);
  }

  /// 强制刷新缓冲区
  Future<void> flush() async {
    if (!_isInitialized || _isDisposed || !config.enableFileLog) {
      return;
    }
    await _fileLogger.flush();
  }

  /// 销毁资源
  Future<void> dispose() async {
    if (_isDisposed) return;

    _isDisposed = true;

    try {
      await _fileLogger.dispose();
    } catch (e) {
      // 忽略清理错误
    }

    try {
      _consoleLogger.dispose();
    } catch (e) {
      // 忽略清理错误
    }
  }
}

4.3 第三层:性能优化与实现

        最考验功力的地方在于 FileLogger 的实现。频繁打开/关闭文件是非常耗电且影响性能的。

4.3.1 异步缓冲区机制 (file_logger.dart)

        为了极致性能,我们引入了 Buffer (缓冲区)。日志产生后不立即写磁盘,而是先存在内存里。

  • 触发条件1:缓冲区达到 100 条日志。

  • 触发条件2:定时器每隔 1s 强制刷新。

///
/// Description:    文件日志处理器
/// CreateDate:     2025/12/17 11:37
/// Author:         agg
///
class FileLogger {
  final String? customDirectory;
  final int maxFileSize;
  final int maxBufferSize;
  final String filePrefix;
  final String fileExtension;
  final int daysToKeep;

  File? _currentFile;
  final List<String> _buffer = [];
  Timer? _periodicTimer;
  late String _logDirectory;
  final DateFormat _dateFormat = DateFormat('yyyy-MM-dd');
  final DateFormat _timeFormat = DateFormat('HH:mm:ss.SSS');
  bool _isInitialized = false;
  bool _isDisposed = false;

  FileLogger({
    this.customDirectory,
    this.maxFileSize = 5 * 1024 * 1024,
    this.maxBufferSize = 100,
    this.filePrefix = 'app',
    this.fileExtension = 'log',
    this.daysToKeep = 7,
  });

  /// 初始化文件日志
  Future<void> initialize() async {
    if (_isDisposed) {
      throw StateError('FileLogger has been disposed');
    }

    try {
      Directory directory;
      if (customDirectory != null && customDirectory!.isNotEmpty) {
        directory = Directory(customDirectory!);
      } else {
        Directory? appDir;
        if (Platform.isAndroid) {
          appDir = await getExternalStorageDirectory();
        } else {
          appDir = await getApplicationDocumentsDirectory();
        }
        directory = Directory('${appDir?.path}/logs');
        // Android 日志路径: /sdcard/Android/data/<package_name>/files/logs/
        // iOS 日志路径: <AppHome>/Documents/logs
      }

      _logDirectory = directory.path;

      if (!await directory.exists()) {
        await directory.create(recursive: true);
      }

      await _setupLogFile();
      await _cleanOldLogs();

      _isInitialized = true;

      // 写入初始化日志
      await _writeToBuffer(
        '========== Log System Initialized ==========',
        skipBuffer: true,
      );

      // 启动定时器1s定期刷新缓冲区
      _periodicTimer = Timer.periodic(Duration(seconds: 1), (_) {
        if (_buffer.isNotEmpty) {
          _flushBuffer();
        }
      });
    } catch (e) {
      _isInitialized = false;
      rethrow;
    }
  }

  /// 设置日志文件
  Future<void> _setupLogFile() async {
    final now = DateTime.now();
    final dateStr = _dateFormat.format(now);
    final baseName = '${_logDirectory}/$filePrefix\_$dateStr';

    // 检查是否存在今天的日志文件
    String filePath = '$baseName.$fileExtension';
    File file = File(filePath);

    // 如果文件存在且超过大小限制,创建带时间戳的新文件
    if (await file.exists()) {
      final length = await file.length();
      if (length >= maxFileSize) {
        final timeStr = DateFormat('HH-mm-ss').format(now);
        filePath = '${baseName}_$timeStr.$fileExtension';
        file = File(filePath);
      }
    }

    _currentFile = file;
  }

  /// 写入日志
  Future<void> log({required Level level, required String message}) async {
    if (!_isInitialized || _isDisposed) return;

    try {
      final timestamp = _timeFormat.format(DateTime.now());
      var levelStr = "";
      if (level == Level.info) {
        levelStr = "INFOS";
      } else {
        levelStr = level.name.toUpperCase();
      }

      await _writeToBuffer('[$timestamp][$levelStr] $message');
    } catch (e) {
      // 文件日志失败时不应影响应用运行
      print('File logger error: $e');
    }
  }

  /// 写入缓冲区
  Future<void> _writeToBuffer(
    String logEntry, {
    bool skipBuffer = false,
  }) async {
    _buffer.add('$logEntry\n');

    // 如果缓冲区满了或者需要立即写入,则刷新到文件
    if (_buffer.length >= maxBufferSize || skipBuffer) {
      await _flushBuffer();
    }
  }

  /// 刷新缓冲区到文件
  Future<void> _flushBuffer() async {
    if (_buffer.isEmpty || _currentFile == null || _isDisposed) return;

    try {
      await _currentFile!.writeAsString(
        _buffer.join(),
        mode: FileMode.append,
        flush: true,
      );
      _buffer.clear();
    } catch (e) {
      // 如果写入失败,保留缓冲区内容下次再试
      print('Failed to flush log buffer: $e');
    }
  }

  /// 清理旧日志文件
  Future<void> _cleanOldLogs() async {
    try {
      final directory = Directory(_logDirectory);
      if (!await directory.exists()) return;

      final files = await directory
          .list()
          .where((entity) => entity is File)
          .cast<File>()
          .where((file) => file.path.endsWith('.$fileExtension'))
          .toList();

      final cutoffDate = DateTime.now().subtract(Duration(days: daysToKeep));

      for (final file in files) {
        try {
          final stat = await file.stat();
          if (stat.modified.isBefore(cutoffDate)) {
            await file.delete();
          }
        } catch (e) {
          // 忽略单个文件删除失败
        }
      }
    } catch (e) {
      // 清理失败不应影响主要功能
    }
  }

  /// 获取所有日志文件
  Future<List<File>> getLogFiles() async {
    if (!_isInitialized || _isDisposed) return [];

    final directory = Directory(_logDirectory);
    if (!await directory.exists()) return [];

    try {
      final files = await directory
          .list()
          .where((entity) => entity is File)
          .cast<File>()
          .where((file) => file.path.endsWith('.$fileExtension'))
          .toList();

      // 按修改时间排序(最新的在前)
      files.sort((a, b) {
        try {
          final statA = a.statSync();
          final statB = b.statSync();
          return statB.modified.compareTo(statA.modified);
        } catch (e) {
          return 0;
        }
      });

      return files;
    } catch (e) {
      return [];
    }
  }

  /// 获取最近日志
  Future<String> getRecentLogs({int lines = 100, String? filePath}) async {
    if (!_isInitialized || _isDisposed) return '';

    try {
      File file;
      if (filePath != null) {
        file = File(filePath);
      } else {
        final files = await getLogFiles();
        if (files.isEmpty) return 'No log files found';
        file = files.first;
      }

      if (!await file.exists()) return 'Log file not found';

      final content = await file.readAsString();
      final linesList = content.split('\n');

      final start = linesList.length > lines ? linesList.length - lines : 0;
      return linesList.sublist(start).join('\n');
    } catch (e) {
      return 'Error reading logs: $e';
    }
  }

  /// 强制刷新缓冲区
  Future<void> flush() => _flushBuffer();

  /// 销毁资源
  Future<void> dispose() async {
    if (_isDisposed) return;

    _isDisposed = true;
    _periodicTimer?.cancel();
    await _flushBuffer();
    _buffer.clear();
    _currentFile = null;
  }
}

4.3.2 控制台美化 (console_logger.dart)

        通过集成 logger 库,我们可以让输出带上边框、时间戳和颜色,方便快速定位问题。

///
/// Description:    控制台日志处理器
/// CreateDate:     2025/12/17 11:34
/// Author:         agg
///
class ConsoleLogger {
  late Logger _logger;

  ConsoleLogger() {
    _initLogger();
  }

  void _initLogger() {
    _logger = Logger(
      printer: PrettyPrinter(
        methodCount: 0,
        errorMethodCount: 8,
        lineLength: 120,
        colors: false,
        printEmojis: false,
        printTime: false,
        noBoxingByDefault: true,
      ),
      filter: DevelopmentFilter(),
      level: Level.verbose,
    );
  }

  /// 记录日志到控制台
  void log({
    required Level level,
    required String message,
    dynamic error,
    StackTrace? stackTrace,
  }) {
    try {
      final fullMessage = error != null ? '$message\nError: $error' : message;

      switch (level) {
        case Level.verbose:
          _logger.v(fullMessage);
          break;
        case Level.debug:
          _logger.d(fullMessage);
          break;
        case Level.info:
          _logger.i(fullMessage);
          break;
        case Level.warning:
          _logger.w(fullMessage);
          break;
        case Level.error:
          _logger.e(fullMessage);
          break;
        case Level.wtf:
          _logger.wtf(fullMessage);
          break;
        case Level.nothing:
          break;
      }
    } catch (e) {
      // 防止日志记录本身导致应用崩溃
      print('Console logger error: $e');
    }
  }

  /// 销毁资源
  void dispose() {
    // Logger库没有dispose方法,这里预留接口
  }
}

5. ✅ 小结

        这套方案通过三层架构实现了职责分离,通过异步缓冲区解决了性能瓶颈,通过 LogConfig 适配了多环境需求。

        另外,由于本人能力有限,如有错误,敬请批评指正,谢谢。

Flutter跨平台移动应用实时日志远程查看系统_支持多终端同步显示和ANSI彩色日志输出的远程调试工具_用于开发测试阶段实时监控和排查移动端应用运行问题_基于Flutter_We.zip 立即下载

相关推荐

Flutter实战之自定义日志打印组件详解

主要介绍了Flutter实战之自定义日志打印组件详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

Flutter ーー logger 组件记录日志

Flutter ーー logger 组件记录日志 原文 https://medium.com/@simbu/flutter-logging-with-logger-6227308ca199 前言 是时候添加一些日志记录了,我希望能够检查发生的网络请求和关键操作,在应用程序和后端之间的交互变得越来越复杂的情况下,给出一些反馈和验证。 我使用 pub.dev 上的流行程度来选择 logger 包。 https://pub.dev/packages/logger Popular logging packag

会煮咖啡的猫咪 1900

Clion编译stm32cube初始化工程时non constant or forward reference address expression for section .ARM.extab报错

相信应该有同学在看了稚晖君关于clion开发stm32的文章之后也想在自己PC上实现“优雅の嵌入式开发”遇到和我类似的问题。他的文章是22年写的,我个人猜测应当是版本更新导致的不适配。意思就是说 READONLY 只能在gcc11以后的版本中使用,gcc10及以前的版本将无法正确识别,也就有如下错误。或者就可以将gcc升级至最新的版本。记得在clion工具链中修改c,cpp编译器的位置。可以将文本括号中的 READONLY 全部删除。当时由于没有注意到这行注释导致在这里卡了好久。

2301_77256849的博客 2511

flutter 线上apm监控 远程日志 emas_tlog

emas_tlog ali emas tlog 阿里巴巴flutter版本 远程日志 TLog 介绍 远程日志服务提供远程手机日志拉取功能,解决移动App线上异常排查困难的问题。 远程日志服务支持Android/iOS应用类型。 产品架构 1.移动App集成SDK。 2.远程日志服务通过心跳探测识别已安装App的移动终端,并进行日志拉取配置。 3.远程日志服务拉取指定移动终端App的用户日志,并对拉取任务进行管理。 4.远程日志服务查看已从终端设备拉取至控制台的用户日志。 官网地址:官网地址 本项目是根据

s297165331的专栏 2423

RK3568(buildroot)利用squashfs+overlay实现嵌入式系统高可靠存储方案

本文详细介绍了在RK3568嵌入式平台上,基于Buildroot系统,利用squashfs只读根文件系统与overlayfs可写层相结合的高可靠存储方案。该方案能有效防止设备意外断电导致的系统文件损坏,通过内核配置、启动脚本编写及镜像制作,实现系统核心的只读保护与用户数据的持久化分离,显著提升嵌入式设备的现场稳定性。

weixin_29189381的博客 547

Coze知识库搭建:智能化管理与高效检索实践

知识管理系统是现代企业信息资产管理的核心基础设施,其核心原理是通过结构化存储和智能检索技术实现知识的快速流转。在技术实现上,基于NLP的语义理解引擎和知识图谱构建是关键,能够显著提升检索准确率和知识关联性。以Coze平台为例,其针对中文场景优化的分词算法和多模态处理能力,可有效解决技术文档管理中的术语识别和格式兼容问题。这类系统在开发团队知识沉淀、新员工培训等场景中价值显著,特别是当集成RBAC权限模型和自动化备份策略后,既能保障信息安全,又能实现持续的知识迭代。通过合理配置同义词扩展和权重调整,可使搜索命

weixin_29041195的博客 262

MMPose:133关键点全身姿态估计的技术突破与工业应用价值

在计算机视觉的快速发展浪潮中,人体姿态估计技术正从实验室走向产业应用。然而,传统方案面临着精度与速度难以兼得、多场景适应性不足、部署复杂度高等核心挑战。OpenMMLab推出的MMPose工具箱,通过133关键点全身姿态估计技术,为这一领域带来了革命性突破。 ## 技术困境:传统姿态估计的三大瓶颈 当前姿态估计技术在实际应用中普遍面临三个关键问题:**精度与速度的平衡困境**、**多部位协同检

gitblog_00410的博客 990

从Frenet坐标系到多项式拟合:Lattice Planner核心原理拆解与调参指南

本文深入解析了自动驾驶轨迹规划中Lattice Planner的核心原理,重点阐述了其在Frenet坐标系下解耦横纵向规划、利用五次和四次多项式拟合轨迹的数学基础。文章提供了从坐标系转换、采样策略到成本函数设计的完整工程实践指南,并分享了关键的参数调优经验,旨在帮助开发者高效实现稳定、平滑且安全的轨迹规划。

tech5的博客 1111

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

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

jclee95的个人博客 2547

【HarmonyOS】ArkUI的组件扩展

ArkuI的组件扩展主要是通过等装饰器来对于一些常用的自定义组件进行封装, 来简化UI开发流程, 提高自定义组件的可复用性。

2302_79763237的博客 1166

【漏洞复现】Palo Alto Networks PAN-OS GlobalProtect 命令注入漏洞 CVE-2024-3400

在特定PAN-OS版本和不同功能配置下,未经身份验证的攻击者可能利用此漏洞在防火墙上以root权限执行任意代码。技术文章仅供参考,任何个人和组织使用网络应当遵守宪法法律,遵守公共秩序,尊重社会公德,不得利用网络从事危害国家安全、荣誉和利益,未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作。本文所提供的工具仅用于学习,禁止用于其他!poc如下,注入点在Cookie中,将其中的DNSlog地址替换为你的dnslog地址,借助DNSLOG来复现,可以使用http://dnslog.pw/

失心少年 1225

鸿蒙 Flutter 日志系统:分级日志与鸿蒙 Hilog 集成

本文介绍了在鸿蒙(HarmonyOS)生态下为Flutter应用构建集成Hilog日志系统的完整方案。针对Flutter原生日志功能简陋和无法直接调用鸿蒙系统日志的问题,通过MethodChannel实现跨平台通信,将Flutter日志分级映射到鸿蒙Hilog。方案包含四个关键部分:1)鸿蒙Hilog基础配置与使用;2)Flutter侧分级日志工具类封装;3)通过MethodChannel实现Flutter与原生Hilog的集成;4)日志系统优化,包括环境差异化配置、本地持久化和日志上报。该方案既保留了Fl

song501的博客 1114

6.Burp Suite 入门篇 —— Burp Scanner 漏洞扫描

Burp Scanner 既可以是独立的全自动扫描器,也可以在手动测试中当作强大的辅助手段,而且随着技术改进,Burp Scanner 能检测到的漏洞数量也在不断增加。Burp Scanner 功能只在 burpsuite 专业版和企业版中里有,本篇教程讲的是专业版的 Burp Scanner 用法。

YouTheFreedom的博客 1万+

告别日志混乱:CocoaLumberjack+Flutter构建跨平台日志系统

移动端开发中,日志调试常面临三大痛点:原生与Flutter日志割裂、关键信息丢失、跨平台配置复杂。CocoaLumberjack作为iOS/macOS端的高性能日志框架,通过桥接层实现与Flutter的无缝集成,可统一管理全平台日志流。本文将分四步实现从环境配置到高级功能的完整解决方案,包含6处代码示例与2种架构方案。 ## 环境准备与依赖集成 ### 原生层配置 通过CocoaPods集成C...

gitblog_00633的博客 1145

Android 13.0仿ios的hotseat效果修改hotseat样式

在13.0系统产品rom定制化开发中,在项目需求的需要,系统原生Launcher的布局样式很一般,所以需要重新设计ui对布局样式做调整,产品在看到 ios的hotseat效果觉得特别美观,所以要仿ios一样不需要横屏铺满的效果 居中显示就行了,所以就要看hotseat的具体布局显示了

安卓兼职framework和app工程师的博客 1144

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

flutter log4d 日志包的使用

最近闲来无事,撸了一个日志插件,功能很简单 在开发机上开启一个服务端 然后客户端(不仅是 flutter,而是只要可以使用dart:io就可以)开启远程连接,然后就可以发送日志到服务端了 好处 这样的好处是,开发机可以实时接收到日志,并且保存到文件中 因为我们很多时候会发生日志超出一个长度(好像是 1000 个),然后在 console 中不能完整显示的问题,比如网络网文请求,如果响应的报文不完整...

SimpleCoder's Blog 4795

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

Flutter 调用原生系统日志库

这里以Flutter 调用原生系统日志库,来理解下 Flutter 与 Android 交互 这里创建的时候用到了 Kotlin 、AndroidX Android端 进入 android 目录下的 build.gradle ,点击 Open for Editing in Android Studio ,打开新的 android项目。 App 目录下 build.gradle 引入...

milan-xiao-tiejiang的博客 1585

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

Flutter实战之自定义日志打印组件

前言 在Flutter中,如果我们需要打印日志,如果不进行自定义,我们只能使用自带的print()或者debugPrint()方法进行打印,但是这两种打印,日志都是默认Info层级的日志,很不友好,所以如果需要日志打印层级分明,我们就需要自定义一个日志打印组件,以下就来介绍如何自定义日志打印组件。 如何让输出的日志层级分明? 换种方式想,如果我们能在Flutter代码中,能够调用到原始Androi...

weixin_33905756的博客 2976

告别混沌日志:GSYGithubApp Flutter版的Talker日志系统实战指南

在移动应用开发中,日志系统如同开发者的"眼睛",帮助我们洞察应用运行状态、定位问题根源。GSYGithubApp Flutter版作为功能丰富的开源项目,采用Talker日志系统构建了高效、可扩展的日志解决方案。本文将带你深入了解这一实现,从基础集成到高级应用,让你的日志系统从"混沌"走向"有序"。 ## 初识Talker:现代日志系统的优势 传统print调试面临三大痛点:日志分散难以追踪、...

gitblog_00041的博客 492

Talker完全指南:Dart与Flutter应用的终极错误处理与日志记录工具

Talker是Dart与Flutter应用的高级错误处理和日志记录工具,它能够帮助开发者轻松捕获、管理和分析应用中的错误与日志信息,提升开发效率和应用质量。无论是在开发阶段还是生产环境,Talker都能为你的应用提供强大的错误处理和日志记录支持。 ## 为什么选择Talker? 在开发Dart和Flutter应用时,有效的错误处理和日志记录对于确保应用稳定性和快速排查问题至关重要。Talker

gitblog_01196的博客 452

鸿蒙Flutter日志调试:roggle纯Dart日志库迁移实践与适配指南

在鸿蒙(HarmonyOS)生态逐步成熟的背景下,Flutter应用迁移过程中日志调试成为开发者绕不开的痛点。日志作为软件可观测性的基础能力,不仅关乎错误定位效率,更直接影响跨端工程的质量保障。传统依赖原生通道的日志方案在鸿蒙上往往因引擎差异、tag过滤和ANSI色码兼容等问题而失效。roggle作为纯Dart实现的轻量日志库,天然规避插件层适配压力,通过自定义Printer可灵活控制输出格式,并借助hilog与hdc建立结构化过滤链路。从基础打印到多维度调试追踪,roggle帮助开发者在鸿蒙真机上高效定位

weixin_29015801的博客 323

Flutter 三方库 OpenHarmony 鸿蒙适配实战:OneSignal 推送通知插件 ArkTS 原生适配全流程

Flutter 三方库 OpenHarmony 鸿蒙适配实战:OneSignal 推送通知插件 ArkTS 原生适配全流程

wei_shuo的博客 1万+

【Flutter】Flutter 调试 ( Debug 调试窗口 | 手机日志信息查看 | 设置普通断点 | 设置表达式断点 )

一、Flutter 调试简介、 二、Debug 调试窗口、 三、手机日志信息查看、 四、设置普通断点、 五、设置表达式断点、 六、相关资源、

让 学习 成为一种 习惯 ( 韩曙亮 の 技术博客 ) 8670

Flutter Deer:一个带完整 UI 设计图与集成测试的 Flutter 实战练习项目深度解析

[Flutter Deer](https://link.gitcode.com/i/88c6a200c0bada5389067d61b6b65333) 是一个以"贴近真实项目"为目标设计的个人 Flutter 练习项目,它不仅提供了一份完整的 UI 设计图资源,还覆盖了 MVP 架构、provider 状态管理、dio 网络封装、深色模式、本地化、集成测试与可访问性测试等日常开发高频能力。读完本文

gitblog_00668的博客 501

Flutter 开发学习笔记(1):第一个简单的Flutter项目(上)

为了得到的得到Uniapp的上位替代,最近打算使用Flutter开发。一般开发需要3个UI框架,icons库+UI框架+统计图。

qq_44695769的博客 1799

绳网错误处理与日志系统:Flutter应用稳定性保障指南

在移动应用开发中,错误处理与日志系统是保障应用稳定性的关键环节。绳网(inter-knot)作为一款基于Flutter的跨平台应用,采用了系统化的错误处理策略和灵活的日志记录机制,确保应用在各种场景下都能提供可靠的用户体验。本文将深入解析绳网应用的错误处理架构和日志系统实现,帮助开发者掌握Flutter应用稳定性保障的核心技巧。 ## 日志系统基础:高效调试与问题追踪 日志是应用开发和维护的重

gitblog_01083的博客 670
上一篇: Flutter 数据库模块之 Draft 设计
下一篇: Flutter 探索入门
Swuagg
Swuagg 新星创作者: 移动开发技术领域 新星创作者: 移动开发技术领域 领域专家: 操作系统技术领域 领域专家: 操作系统技术领域
博客等级 码龄10年 2355粉丝 153原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值