c/c++日志库初识

C/C++日志库:从入门到实践的深度指南

在软件开发的世界里,日志(Logging)扮演着一个沉默却至关重要的角色。它像是飞行记录仪的“黑匣子”,记录着应用程序运行时的关键信息,帮助开发者在问题发生时追溯根源,在系统运行时监控状态,甚至在安全审计时提供证据。本文将带你深入了解C/C++日志库的应用场景、基本实现步骤、关键注意事项,并推荐一些优秀的开源项目及其使用方法。

一、为什么需要日志库?—— 应用场景探秘

想象一下,在一个漆黑的夜晚,你独自驾驶着一辆汽车在陌生的道路上飞驰,突然仪表盘熄灭了,引擎发出了异响,你却不知道发生了什么。日志系统就是软件的仪表盘和传感器,它告诉我们:

  1. 故障排查与调试 (Debugging & Troubleshooting)

    • 情感提示:当程序崩溃或行为异常时,那种抓狂和无助感是每个程序员都经历过的。日志是你的“夏洛克·福尔摩斯”,它能提供案发现场的关键线索。
    • 场景:记录关键变量的值、函数调用顺序、错误码、异常堆栈等,帮助快速定位问题。例如,线上服务突然出现大量500错误,通过错误日志可以迅速找到是哪个模块的哪个函数调用失败,以及失败的原因。
  2. 运行监控与告警 (Monitoring & Alerting)

    • 情感提示:看着自己开发的系统稳定运行,就像看着孩子健康成长一样令人欣慰。日志能让你实时掌握系统的“健康状况”。
    • 场景:记录系统启动/关闭、关键服务的状态、处理请求数、响应时间、资源使用率(CPU、内存、磁盘)等。当某些指标超过阈值(如错误率飙升、响应时间过长),可以触发告警通知运维人员。
  3. 用户行为分析 (User Behavior Analysis)

    • 情感提示:了解用户如何与你的产品互动,是优化产品、提升用户体验的关键。日志是洞察用户心声的窗口。
    • 场景:记录用户的操作路径、功能使用频率、在特定页面的停留时间等。这些数据可以帮助产品经理分析用户偏好,优化产品设计。
  4. 安全审计与合规 (Security Auditing & Compliance)

    • 情感提示:在安全事件频发的今天,保护用户数据和系统安全是我们的责任。日志是守护系统安全的“哨兵”。
    • 场景:记录用户登录尝试(成功/失败)、敏感操作(如修改密码、删除数据)、权限变更、异常访问等。这些日志在发生安全事件时可以用于追溯攻击路径,也满足某些行业的合规性要求。
  5. 性能分析与优化 (Performance Analysis & Optimization)

    • 情感提示:追求极致性能是许多技术人的浪漫。日志可以帮助我们找到性能瓶颈,让程序“飞”起来。
    • 场景:记录函数执行耗时、数据库查询时间、网络请求延迟等。通过分析这些耗时数据,可以找出性能瓶颈并进行针对性优化。

二、构建一个简单的日志库 —— 实现步骤解析

一个基础的日志库通常包含以下核心组件和步骤。我们将通过一个简化的概念模型来理解其实现:

核心组件:

  • 日志级别 (Log Level):定义日志信息的重要性(如 DEBUG, INFO, WARNING, ERROR, FATAL)。
  • 日志格式化器 (Log Formatter):定义日志输出的格式(如时间戳、级别、线程ID、文件名、行号、消息体)。
  • 日志输出地 (Log Appender/Handler):定义日志输出到哪里(如控制台、文件、网络、数据库)。
  • 日志过滤器 (Log Filter):根据级别或其他条件决定某些日志是否需要记录。
  • 日志记录器 (Logger):提供给用户调用的API接口。

实现步骤:

  1. 定义日志级别 (Log Level)

    • 通常使用枚举类型定义,并赋予不同的严重程度值。
    enum LogLevel {DEBUG = 0,INFO,WARNING,ERROR,FATAL
    };
    
  2. 设计日志消息结构体/类 (Log Message)

    • 用于承载单条日志的全部信息。
    struct LogEvent {LogLevel level;long long timestamp; // e.g., milliseconds since epochunsigned int thread_id;const char* file_name;int line_number;std::string message;// ... other fields
    };
    
  3. 实现日志格式化器 (Formatter)

    • 一个函数或类,接收LogEvent对象,返回格式化后的字符串。
    • 例如,格式可以是 [YYYY-MM-DD HH:MM:SS.sss] [LEVEL] [thread_id] [file:line] message
  4. 实现日志输出地 (Appender/Handler)

    • 控制台输出:使用 std::coutprintf
    • 文件输出:使用 std::ofstream。需要考虑文件打开、写入、关闭,以及文件滚动(按大小或时间)。
    • 异步写入:为提高性能,通常会将日志消息放入一个队列,由单独的后台线程负责实际的I/O操作。
  5. 实现日志记录器 (Logger)

    • 提供宏或函数接口供用户调用,如 LOG_INFO("User %s logged in.", username)
    • 内部逻辑:
      • 检查当前设置的日志级别,如果消息级别低于设定级别,则忽略。
      • 获取当前时间、线程ID、调用处的文件名和行号(可使用 __FILE__, __LINE__ 宏)。
      • 组装 LogEvent 对象。
      • 调用格式化器。
      • 调用输出地进行输出。
  6. 配置与管理

    • 允许用户配置最低日志级别、输出格式、输出目标等。
    • 可以设计一个单例的日志管理器来统一管理这些配置和Logger实例。

流程图 (Simplified Log Flow):

graph TDA[应用程序调用日志接口 e.g., LOG_INFO("message")] --> B{日志级别判断};B -- 满足当前日志级别 --> C[获取上下文信息 (时间, 线程ID, 文件, 行号)];C --> D[创建LogEvent对象];D --> E[格式化LogEvent为字符串];E --> F{选择输出目标 (Appender)};F -- 控制台 --> G1[输出到Console];F -- 文件 --> G2[输出到File (可能涉及队列和异步写入)];F -- 网络 --> G3[发送到远程服务器];G1 --> H[完成];G2 --> H;G3 --> H;B -- 不满足级别 --> H;

情感提示:从零开始构建一个日志库,就像亲手打造一件工具,虽然过程可能复杂,但完成后会带来满满的成就感和对日志系统更深刻的理解。

三、使用日志库的注意事项 —— 避坑指南

  1. 性能开销 (Performance Overhead)

    • 问题:日志记录,特别是磁盘I/O,是相对耗时的操作。过度或不当的日志记录会严重影响应用程序性能。
    • 对策
      • 异步日志:将日志写入操作放到单独的后台线程处理,主业务线程仅将日志消息放入队列,避免阻塞。
      • 级别控制:生产环境通常只开启INFO及以上级别的日志,DEBUG日志默认关闭,仅在需要时开启。
      • 避免在热点路径频繁记录:对于调用非常频繁的代码路径,谨慎添加日志。
      • 高效的格式化:避免复杂的字符串拼接,预编译格式化字符串。
  2. 日志内容与可读性 (Log Content & Readability)

    • 问题:日志信息不足或过于冗余,格式混乱,都会导致排查问题时效率低下。
    • 对策
      • 包含上下文:确保日志包含足够的信息(时间戳、级别、模块、线程ID、关键业务ID如订单号、用户ID)。
      • 结构化日志:考虑使用JSON或其他结构化格式,便于机器解析和后续的日志分析系统(如ELK Stack)处理。
      • 简洁明了:避免打印大量无用信息,消息应直指问题核心。
      • 统一格式:团队内或项目内应统一日志格式和规范。
  3. 日志文件管理 (Log File Management)

    • 问题:日志文件无限增长会耗尽磁盘空间。
    • 对策
      • 日志滚动 (Log Rotation):按文件大小(如每100MB一个文件)或时间(如每天一个文件)分割日志。
      • 日志清理 (Log Purging):定期删除旧的日志文件,只保留一定时间或一定数量的日志。
  4. 线程安全 (Thread Safety)

    • 问题:在多线程环境下,多个线程同时写入日志可能导致数据错乱或程序崩溃。
    • 对策
      • 确保日志库内部对共享资源(如文件句柄、内部队列)的访问是线程安全的(使用互斥锁、原子操作等)。
      • 异步日志本身通过队列解耦,有助于简化线程安全问题。
  5. 配置灵活性 (Configuration Flexibility)

    • 问题:硬编码日志配置(如级别、输出目标)导致无法在运行时动态调整。
    • 对策
      • 支持通过配置文件(如INI, XML, JSON, YAML)或环境变量来设置日志参数。
      • 理想情况下,应支持运行时动态修改日志级别,而无需重启应用。
  6. 安全性 (Security)

    • 问题:日志中可能不慎记录了敏感信息(如密码、身份证号、银行卡号、密钥)。
    • 对策
      • 数据脱敏:在记录敏感数据前进行脱敏处理(如密码用******替代)。
      • 代码审查:确保日志记录代码不会泄露敏感信息。
      • 访问控制:保护日志文件和日志系统的访问权限。
  7. 避免在日志代码中抛出异常 (No Exceptions from Logging Code)

    • 问题:如果日志库自身发生错误(如磁盘满无法写入)并抛出异常,可能会干扰主业务逻辑,甚至导致应用崩溃。
    • 对策:日志库应妥善处理内部错误,例如打印到标准错误流或记录一个内部错误状态,而不是向上抛出异常。

情感提示:遵循这些注意事项,就像给你的日志系统穿上“铠甲”,让它在服务你的同时,不会成为新的“麻烦制造者”。

四、优秀的开源C/C++日志库推荐与使用

社区已经有很多成熟且高性能的C/C++日志库,它们解决了上述大部分问题,通常比我们自己从零实现的更健壮、功能更丰富。

1. spdlog

  • 简介:一个非常快速、仅头文件(Header-only)的C++日志库。设计简洁,易于使用,性能极高。支持同步/异步模式、自定义格式、多种sink(输出目标,如控制台、文件、轮转文件、syslog等)。
  • 特点
    • 极高的性能,低延迟。
    • 线程安全。
    • 支持多种日志级别。
    • 灵活的格式化 %v (消息), %t (线程ID), %l (级别) 等。
    • 丰富的Sink选项。
    • 仅头文件,集成方便。
  • 使用方式 (CMake示例)
    1. 获取:可以直接下载头文件,或者通过Git submodule/FetchContent集成。

      # CMakeLists.txt
      cmake_minimum_required(VERSION 3.10)
      project(MyProject)set(CMAKE_CXX_STANDARD 11) # spdlog requires C++11 or laterinclude(FetchContent)
      FetchContent_Declare(spdlogGIT_REPOSITORY https://github.com/gabime/spdlog.gitGIT_TAG v1.x # Or a specific version tag like v1.12.0
      )
      FetchContent_MakeAvailable(spdlog)add_executable(MyApp main.cpp)
      target_link_libraries(MyApp PRIVATE spdlog::spdlog)
      # If using the header-only version, you might just need to include directories
      # target_include_directories(MyApp PRIVATE ${spdlog_SOURCE_DIR}/include)
      
    2. 代码示例

      // main.cpp
      #include "spdlog/spdlog.h"
      #include "spdlog/sinks/basic_file_sink.h" // for basic file logging
      #include "spdlog/sinks/rotating_file_sink.h" // for rotating file logging
      #include "spdlog/async.h" // for async loggingvoid basic_usage() {spdlog::info("Welcome to spdlog!");spdlog::error("Some error message with arg: {}", 1);spdlog::warn("Easy padding in numbers like {:08d}", 12);spdlog::critical("Support for int: {0:d};  hex: {0:x};  oct: {0:o}; bin: {0:b}", 42);spdlog::info("Support for floats {:03.2f}", 1.23456);spdlog::info("Positional args are {1} {0}..", "too", "supported");spdlog::info("{:<30}", "left aligned");spdlog::set_level(spdlog::level::debug); // Set global log level to debugspdlog::debug("This message should be displayed..");    
      }void file_logger_example() {try {// Create a file logger (single file)auto file_logger = spdlog::basic_logger_mt("basic_logger", "logs/basic-log.txt");file_logger->info("This is a message to the basic file logger.");spdlog::register_logger(file_logger); // Register to use globally with spdlog::get()// Create a rotating file logger (e.g., 5MB size limit, 3 rotated files)auto rotating_logger = spdlog::rotating_logger_mt("rotating_logger", "logs/rotating.txt", 1024 * 1024 * 5, 3);rotating_logger->warn("This is a warning to the rotating file logger.");// Use a globally registered loggerspdlog::get("basic_logger")->info("Another message from global access.");} catch (const spdlog::spdlog_ex &ex) {spdlog::error("Log initialization failed: {}", ex.what());}
      }void async_logger_example() {// Default thread pool settings can be modified via spdlog::init_thread_pool()spdlog::init_thread_pool(8192, 1); // queue size of 8192 and 1 worker threadauto async_file = spdlog::basic_logger_mt<spdlog::async_factory>("async_file_logger", "logs/async_log.txt");async_file->info("This is an async log message!");// ... more logsspdlog::drop_all(); // Release all loggers and flush all messages under async mode
      }int main() {// Set global pattern - [timestamp] [logger_name] [level] [thread_id] messagespdlog::set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%n] [%^%l%$] [thread %t] %v");spdlog::info("Application starting...");basic_usage();file_logger_example();async_logger_example(); // Make sure to call spdlog::drop_all() or let loggers go out of scope for asyncspdlog::info("Application finished.");spdlog::shutdown(); // Release all spdlog resourcesreturn 0;
      }
      

2. Glog (Google Logging Library)

  • 简介:Google出品的C++日志库,功能强大,广泛应用于Google内部项目和许多开源项目中。它提供了基于命令行的标志来控制日志行为。
  • 特点
    • 级别控制(INFO, WARNING, ERROR, FATAL)。
    • FATAL日志会终止程序。
    • 条件日志:LOG_IF(INFO, condition) << "message";
    • 频次日志:LOG_EVERY_N(INFO, 10) << "Logged every 10th occurrence";
    • 调试模式下的DLOG宏,在非调试模式下不编译。
    • 日志输出到文件,并根据严重性分文件存储。
    • 通过命令行参数配置日志行为(如 -logtostderr, -log_dir, -v (for VLOG))。
  • 使用方式
    1. 安装:通常通过包管理器(如apt, yum, brew)或从源码编译安装。
      # Example for Ubuntu
      # sudo apt-get install libgoogle-glog-dev
      
    2. CMake集成
      # CMakeLists.txt
      cmake_minimum_required(VERSION 3.10)
      project(MyGlogApp)
      set(CMAKE_CXX_STANDARD 11)find_package(glog REQUIRED)add_executable(MyApp main_glog.cpp)
      target_link_libraries(MyApp PRIVATE glog::glog)
      
    3. 代码示例
      // main_glog.cpp
      #include <glog/logging.h>int main(int argc, char* argv[]) {// Initialize Google's logging library.google::InitGoogleLogging(argv[0]);// Optional: configure logging flags (can also be done via command line)// FLAGS_logtostderr = 1; // Log to stderr instead of filesFLAGS_log_dir = "./glogs"; // Directory to save log filesFLAGS_minloglevel = google::INFO; // Minimum log level to recordLOG(INFO) << "Found " << google::COUNTER << " cookies"; // google::COUNTER is a simple counterLOG(WARNING) << "A warning message.";LOG(ERROR) << "An error occurred!";int num_cookies = 10;LOG_IF(INFO, num_cookies > 5) << "We have more than 5 cookies, yum!";for (int i = 0; i < 25; ++i) {LOG_EVERY_N(INFO, 5) << "Logged at iteration " << i << " (every 5th)";}// VLOG is verbose logging, controlled by -v=<level> command line flag// or FLAGS_v = <level>;FLAGS_v = 2;VLOG(1) << "This is a VLOG(1) message."; // Will be logged if -v>=1VLOG(2) << "This is a VLOG(2) message."; // Will be logged if -v>=2VLOG(3) << "This is a VLOG(3) message."; // Will NOT be logged if -v=2DLOG(INFO) << "This is a debug log, only compiled in debug mode."; // (NDEBUG not defined)// To make FATAL not abort for this example, but in real app it does.// google::InstallFailureSignalHandler(); // For better stack traces on crash// LOG(FATAL) << "A fatal error! Program will terminate."; // This would normally abort.LOG(INFO) << "Application shutting down.";google::ShutdownGoogleLogging();return 0;
      }
      
      编译运行后,可以在 ./glogs 目录下找到日志文件,如 MyGlogApp.INFO, MyGlogApp.WARNING 等。

3. Boost.Log

  • 简介:Boost库集合中的一员,功能极其强大和灵活,但配置也相对复杂。它提供了非常细致的控制,包括过滤、格式化、多种sink的组合等。
  • 特点
    • 非常全面的功能集。
    • 高度可定制的格式化和过滤。
    • 支持线程安全的异步日志。
    • 丰富的sink(文本文件、syslog、Windows事件日志、网络等)。
  • 情感提示:Boost.Log 像是日志库中的“瑞士军刀”,功能强大,但可能需要更多时间来学习和掌握。对于追求极致定制化和复杂场景的项目,它是一个不错的选择。

五、总结与展望

日志是软件开发中不可或缺的一环。一个好的日志系统能显著提高开发效率、运维能力和系统的可靠性。

  • 对于初学者或中小型项目spdlog 因其易用性、高性能和仅头文件的特性,是非常棒的选择。
  • 对于大型项目或有特定需求(如命令行配置)的项目glog 是一个经过验证的、可靠的选择。
  • 对于需要高度定制化和复杂日志处理逻辑的场景Boost.Log 提供了无与伦比的灵活性。

情感提示:选择或构建日志库,就像为你的项目选择一位忠实的记录者。它默默无闻,却在关键时刻为你提供最有力的支持。希望这篇博文能为你打开C/C++日志库的大门,让你在未来的开发旅程中,不再为“迷雾”所困,而是拥有清晰的“航行日志”,指引你乘风破浪!

不断实践、不断优化你的日志策略,让日志真正成为你项目的得力助手。祝你编码愉快!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/80296.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C 语言图形编程 | 界面 / 动画 / 字符特效

注&#xff1a;本文为 “C 语言图形编程” 相关文章合辑。 略作重排&#xff0c;如有内容异常&#xff0c;请看原文。 C 语言图形化界面——含图形、按钮、鼠标、进度条等部件制作&#xff08;带详细代码、讲解及注释&#xff09; 非线性光学元件于 2020-02-15 09:42:37 发布…

开发狂飙VS稳定刹车:Utility Tree如何让架构决策“快而不失控”

大家好&#xff0c;我是沛哥儿。 在软件技术架构的世界里&#xff0c;架构师们常常面临灵魂拷问&#xff1a;高并发和低成本哪个优先级更高&#xff1f; 功能迭代速度和系统稳定性该如何平衡&#xff1f; 当多个质量属性相互冲突时&#xff0c;该如何做出科学决策&#xff1f; …

SCI论文图数据提取软件——GetData Graph Digitizer

在写综述或者毕业论文的时候一般会引用前人的文献数据图&#xff0c;但是直接截图获取来的数据图通常质量都不太高。因此我们需要从新画一张图&#xff0c;可以通过origin绘图来实现&#xff0c;今天介绍一个新的软件GetData Graph Digitizer 感谢下面博主分享的破解安装教程 …

深入探索 Apache Spark:从初识到集群运行原理

深入探索 Apache Spark&#xff1a;从初识到集群运行原理 在当今大数据时代&#xff0c;数据如同奔涌的河流&#xff0c;蕴藏着巨大的价值。如何高效地处理和分析这些海量数据&#xff0c;成为各行各业关注的焦点。Apache Spark 正是为此而生的强大引擎&#xff0c;它以其卓越…

场景可视化与数据编辑器:构建数据应用情境​

场景可视化是将数据与特定的应用场景相结合&#xff0c;借助数据编辑器对数据进行灵活处理和调整&#xff0c;通过模拟和展示真实场景&#xff0c;使企业能够更直观地理解数据在实际业务中的应用和影响&#xff0c;为企业的决策和运营提供有力支持。它能够将抽象的数据转化为具…

攻防世界-php伪协议和文件包含

fileinclude 可以看到正常回显里面显示lan参数有cookie值表示为language 然后进行一个判断&#xff0c;如果参数不是等于英语&#xff0c;就加上.php&#xff0c;那我们就可以在前面进行注入一个参数&#xff0c;即flag&#xff0c; payload&#xff1a;COOKIE:languageflag …

手撕LFU

博主介绍&#xff1a;程序喵大人 35- 资深C/C/Rust/Android/iOS客户端开发10年大厂工作经验嵌入式/人工智能/自动驾驶/音视频/游戏开发入门级选手《C20高级编程》《C23高级编程》等多本书籍著译者更多原创精品文章&#xff0c;首发gzh&#xff0c;见文末&#x1f447;&#x1f…

火影bug,未保证短时间数据一致性,拿这个例子讲一下Redis

本文只拿这个游戏的bug来举例Redis&#xff0c;如果有不妥的地方&#xff0c;联系我进行删除 描述&#xff1a;今天在高速上打火影&#xff08;有隧道&#xff0c;有时候会卡&#xff09;&#xff0c;发现了个bug&#xff0c;我点了两次-1000的忍玉&#xff08;大概用了1千七百…

KRaft (Kafka 4.0) 集群配置指南(超简单,脱离 ZooKeeper 集群)还包含了简化测试指令的脚本!!!

docker-compose方式部署kafka集群 Kafka 4.0 引入了 KRaft 模式&#xff08;Kafka Raft Metadata Mode&#xff09;&#xff0c;它使 Kafka 集群不再依赖 ZooKeeper 进行元数据管理。KRaft 模式简化了 Kafka 部署和管理&#xff0c;不需要额外配置 ZooKeeper 服务&#xff0c;…

Admyral - 可扩展的GRC工程自动化平台

文章目录 一、关于 Admyral相关链接资源关键特性 二、安装系统要求 三、快速开始1、启动服务 四、核心功能1、自动化即代码2、AI增强工作流3、双向同步编辑器4、工作流监控5、企业级基础设施 五、示例应用六、其他信息许可证遥测说明 一、关于 Admyral Admyral 是一个基于 Pyt…

DDR在PCB布局布线时的注意事项及设计要点

一、布局注意事项 控制器与DDR颗粒的布局 靠近原则&#xff1a;控制器与DDR颗粒应尽量靠近&#xff0c;缩短时钟&#xff08;CLK&#xff09;、地址/控制线&#xff08;CA&#xff09;、数据线&#xff08;DQ/DQS&#xff09;的走线长度&#xff0c;减少信号延迟差异。 分组隔…

计算机网络-LDP工作过程详解

前面我们已经学习了LDP的基础概念&#xff0c;了解了LDP会话的建立、LDP的标签控制等知识&#xff0c;今天来整体过一遍LDP的一个工作过程&#xff0c;后面我们再通过实验深入学习。 一、LDP标签分发 标签分发需要基于基础的路由协议建立LDP会话&#xff0c;激活MPLS和LDP。以…

解构与重构:自动化测试框架的进阶认知之旅

目录 一、自动化测试的介绍 &#xff08;一&#xff09;自动化测试的起源与发展 &#xff08;二&#xff09;自动化测试的定义与目标 &#xff08;三&#xff09;自动化测试的适用场景 二、什么是自动化测试框架 &#xff08;一&#xff09;自动化测试框架的定义 &#x…

跑不出的循环 | LoveySelf 系列定位

最近开始陷入一轮一轮的循环状态&#xff0c;无奈&#xff0c;只能自我整理一下。23年暑假&#xff0c;在计算机系折腾了一年后&#xff0c;重新打开博客&#xff0c;回想在数学系摸索博客写作的日子&#xff0c;思绪涌上心头&#xff0c;我们决定拾起这份力量。当时觉得 hexo …

Redis最新入门教程

文章目录 Redis最新入门教程1.安装Redis2.连接Redis3.Redis环境变量配置4.入门Redis4.1 Redis的数据结构4.2 Redis的Key4.3 Redis-String4.4 Redis-Hash4.5 Redis-List4.6 Redis-Set4.7 Redis-Zset 5.在Java中使用Redis6.缓存雪崩、击穿、穿透6.1 缓存雪崩6.2 缓冲击穿6.3 缓冲…

一文读懂Python之requests模块(36)

一、requests模块简介 requests模块是python中原生的一款基于网络请求的模块&#xff0c;功能强大&#xff0c;简单便捷且高效 &#xff0c;该模块可以模拟浏览器发送请求&#xff0c;主要包括指定url、发起请求、获取响应数据和持久化存储&#xff0c;包括 GET、POST、PUT、…

WPF之布局流程

文章目录 1. 概述2. 布局元素的边界框3. 布局系统原理3.1 布局流程时序图 4. 测量阶段(Measure Phase)4.1 测量过程4.2 MeasureOverride方法 5. 排列阶段(Arrange Phase)5.1 排列过程5.2 ArrangeOverride方法 6. 渲染阶段(Render Phase)7. 布局事件7.1 主要布局事件7.2 布局事件…

uniapp|获取当前用户定位、与系统设定位置计算相隔米数、实现打卡签到(可自定义设定位置、位置有效范围米数)

基于UniApp阐述移动应用开发中定位功能的实现全流程,涵盖实时定位获取、动态距离计算与自定义位置、有效范围设定等功能。文章提供完整的代码示例与适配方案,适用于社交签到、课堂教室打卡等场景。 目录 引言定位功能在移动应用中的价值(社交、导航、O2O等场景)UniApp跨平台…

Yii2.0 模型规则(rules)详解

一、基本语法结构 public function rules() {return [// 规则1[[attribute1, attribute2], validator, options > value, ...],// 规则2[attribute, validator, options > value, ...],// 规则3...]; }二、规则类型分类 1、核心验证器&#xff08;内置验证器&#xff0…

数据结构(三)——栈和队列

一、栈和队列的定义和特点 栈&#xff1a;受约束的线性表&#xff0c;只允许栈顶元素入栈和出栈 对栈来说&#xff0c;表尾端称为栈顶&#xff0c;表头端称为栈底&#xff0c;不含元素的空表称为空栈 先进后出&#xff0c;后进先出 队列&#xff1a;受约束的线性表&#xff0…