OpenHarmony实战:使用宏、std::bind 巧妙实现进出函数日志打印

背景

我们始终渴望了解模块的调用、时序逻辑,每个人都会轻易地想到在函数的入口打印一条进入 enter 相关的日志,在函数的出口打印一条离开 leave 相关的日志。不能有遗漏,我们会复制这条日志到所有关心的函数中,为了表明是哪个模块,哪个类和哪个函数我们需要逐个将这些参数写入。
这样不可避免地出现第一个问题,每个函数至少添加两条日志,第二个问题当一个函数有多个出口(return),我们将要打印多条 leave 用以区分。
如果碰到复杂模块,我们仍然简单地这样操作,工作量会让你崩溃,这根本不是一个开发者的追求。有什么简单易行、以逸待劳的好办法?

一、打印函数进出日志的原理

如何在函数的入口只打印一条日志,出口不用打印,却自动产生进入、离开两条日志?我们知道函数内部变量的生命周期遵循的是先进后出原则。进入函数首先就定义一个局部对象,在离开这个函数的时刻自动释放这个对象。如果这个对象是类对象,则在定义时会进入构造函数,离开时进入析构函数。于是在构造函数和析构函数分别打印进出日志,测试结果表明的确是这样。
如何区分来自哪个模块、哪个类、哪个函数?很简单,将模块的 id、类名称 Tag 作为参数传入,函数名则用系统宏 __FUNCTION__ 即可。
经过一番操作,发现所有的进出函数日志名称、参数写的一模一样,低级重复不是一个开发者的追求,能不能不写参数。于是用一个宏去代替这条日志函数,参数仅写一次,最终效果达到预期......
如果能够管控不同级别的进出日志,就更加完美了。神奇的 std::bind()函数就能实现原函数的二次封装,在重新包装原函数成新接口(新规范)能力方面,这个功能特别有用。下面将对原理提及的知识点逐步进行讲解。

二、实现函数进出打印功能

第一步,日志类在共用头文件里面实现

例如,sensors 模块就在 utils\common 目录下的 sensors_errors.h 文件
.\base\sensors\miscdevice\utils\common\include\sensors_errors.h

第二步,实现一个简易的只包含构造和析构函数的类

里面会调用 HiviewDFX 日志模块 HiLog::Debug()函数,包含 #include "hilog/log.h"

#include "hilog/log.h"
class InnerFunctionTracer {
public:
    InnerFunctionTracer(const OHOS::HiviewDFX::HiLogLabel &label, const char *func)
        : label_ { label }, func_ { func }
    {
        OHOS::HiviewDFX::HiLog::Debug(label_, "wyyxiu in %{public}s, enter_func", func_);
    }
    ~InnerFunctionTracer()
    {
        OHOS::HiviewDFX::HiLog::Debug(label_, "wyyxiu in %{public}s, leave_func", func_);
    }
private:
    const OHOS::HiviewDFX::HiLogLabel &label_;
    const char *func_ { nullptr };
};

第三步,InnerFunctionTracer 的构造函数参数是通过下面定义对象时将参数传入的

#define CALL_LOG_ENTER InnerFunctionTracer ___innerFuncTracer___ { LABEL, __FUNCTION__ }
LABEL: 是通过宏 CALL_LOG_ENTER 替换时传入的
__FUNCTION__: 是通过宏 CALL_LOG_ENTER 替换时传入的

第四步,调用 CALL_LOG_ENTER 宏就会打印出这个函数进出日志

LABEL: 需要按照给定格式声明,最后一个字符串一般为该类名
__FUNCTION__: 是调用者的函数名称

// 首先,包含声明宏的头文件 sensors_errors.h;
#include "sensors_errors.h"

namespace Sensors {
using namespace OHOS::HiviewDFX;

namespace {
// 其次,声明 LABEL;
constexpr HiLogLabel LABEL = { LOG_CORE, MISC_LOG_DOMAIN, "MiscdeviceServiceStub" };
}  // namespace

// 最后,将 CALL_LOG_ENTER放置在需要打印进出日志的那个函数开头;
int32_t MiscdeviceServiceStub::PlayPatternStub(MessageParcel &data, MessageParcel &reply)
{
    CALL_LOG_ENTER;
    // ......
    return PlayPattern(pattern.value(), usage, parameter.value());
}

三、现在,看一个更加复杂一点的进出日志函数实现

参考示例的头文件如下

.\base\msdp\device_status\utils\common\include\fi_log.h,或者
.\foundation\multimodalinput\input\util\common\include\mmi_log.h

有两个显著不同点

  1. 在构造和析构函数中可以打印任意级别的日志,它是将日志函数作为参数传入的,赋值给 logfn_
  2. 通过日志系统函数 HiLogIsLoggable(),过滤允许级别的日志
class InnerFunctionTracer {
public:
    using HilogFunc = std::function<int(const char *)>;

public:
    InnerFunctionTracer(HilogFunc logfn, const char* tag, LogLevel level)
        : logfn_ { logfn }, tag_ { tag }, level_ { level }
    {
        if (HiLogIsLoggable(OHOS::MMI::MMI_LOG_DOMAIN, tag_, level_)) {
            if (logfn_ != nullptr) {
                logfn_("wyyxiu in %{public}s, enter_func");
            }
        }
    }
    ~InnerFunctionTracer()
    {
        if (HiLogIsLoggable(OHOS::MMI::MMI_LOG_DOMAIN, tag_, level_)) {
            if (logfn_ != nullptr) {
                logfn_("wyyxiu in %{public}s, leave_func");
            }
        }
    }
private:
    HilogFunc logfn_ { nullptr };
    const char* tag_ { nullptr };
    LogLevel level_ { LOG_LEVEL_MIN };
};

奇妙的地方是采用了 std::bind()函数,将不同级别日志函数作为参数传入类中

需要过滤将日志级别 LogLevel 传入,关于 std::bind() 用法如后所述

#define CALL_DEBUG_ENTER        ::OHOS::MMI::InnerFunctionTracer ___innerFuncTracer_Debug___    \
    { std::bind(&::OHOS::HiviewDFX::HiLog::Debug, LABEL, std::placeholders::_1, __FUNCTION__), LABEL.tag, LOG_DEBUG }

#define CALL_INFO_TRACE         ::OHOS::MMI::InnerFunctionTracer ___innerFuncTracer_Info___     \
    { std::bind(&::OHOS::HiviewDFX::HiLog::Info, LABEL, std::placeholders::_1, __FUNCTION__), LABEL.tag, LOG_INFO }

HiLogIsLoggable()函数的系统实现

  • 只有系统使能了模块 DEBUG 日志,这类 CALL_DEBUG_ENTER 才被打印输出。
  • 以多模输入为例,需要在 hdc shell 窗口输入 hilog -b I -D 0xD002800,使能 DEBUG 级别,0xD002800 代表 OHOS::MMI::MMI_LOG_DOMAIN
  • 如果有编译环境,有一个省事的方法,将 HiLog::Debug 改为 HiLog::Info,LOG_DEBUG 修改为 LOG_INFO,该日志一般都会被打印出来,如 CALL_INFO_TRACE 所示。
  • HiLogIsLoggable()函数的实现在这个文件: .\base\hiviewdfx\hilog\frameworks\libhilog\hilog_printf.cpp

.\base\hiviewdfx\hilog\frameworks\libhilog\hilog_printf.cpp

/**
 * @brief Check whether log of a specified domain, tag and level can be printed.
 *
 * @param domain macro:LOG_DOMAIN
 * @param tag macro:LOG_TAG
 * @param level enum:LogLevel
 */
bool HiLogIsLoggable(unsigned int domain, const char *tag, LogLevel level)
{
    if ((level <= LOG_LEVEL_MIN) || (level >= LOG_LEVEL_MAX) || (tag == nullptr) || (domain >= DOMAIN_OS_MAX)) {
        return false;
    }
    if (level < GetFinalLevel(domain, tag)) {
        return false;
    }
    return true;
}

四、std::bind()函数的用法

c++11 及以上支持 std::bind

包含头文件 #include <functional>

使用示例

  1. std::bind 就是一个对原函数和函数参数的重新绑定。
  2. std::placeholders 是新函数的占位符标记。
  3. std::placeholders::_n 中的,_n 表示的是新函数的第 n 个参数,将代替原函数声明位置时的参数。
  4. 未出现 std::placeholders 的原函数位置,必须有默认值。
#include <iostream>
#include <functional>
using namespace std;

int TestFunc(int i, char c, float f)
{
    cout << i << endl;
    cout << c << endl;
    cout << f << endl;

    return i;
}

int main()
{
    auto bindFunc1 = bind(TestFunc, std::placeholders::_1, 'A', 999.9);
    bindFunc1(16);
    auto bindFunc2 = bind(TestFunc, std::placeholders::_2, std::placeholders::_1, 999.9);
    bindFunc2('B', 16);
    auto bindFunc3 = bind(TestFunc, std::placeholders::_2, std::placeholders::_3, std::placeholders::_1);
    auto ret = bindFunc3(999.9, 32, 'C');
    cout << ret << endl;
    return 0;
}

五、预期效果

为了方便查找进出日志,加入了特别字段,例如 "_func" 和 "wyyxiu"。

  • 首先出现 enter_func, 然后出现 leave_func, enter_func 和 leave_func 成对出现,打印出的日志片段如下:
  • 如果止步于看到日志,还有一点可惜。按照日志名称、出现规律,经过一番操作,很容易从日志文件整理出函数调用的时序图。
  • 如何快速将日志文件整理出函数调用的时序图,关注我、等待后续文章。

六、总结

本文巧妙使用了三个知识点完成了函数进出日志打印。首先利用函数局部类对象实现在函数的入口只打印一条日志,出口不用打印,却自动产生进入、离开两条日志的目的;其次利用宏替换将模块 id、类名称 Tag、函数名称写在宏函数中,实现了所有参数只写一次的目的;最后利用 std::bind()函数管控不同级别进出日志打印。

最后

有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)资料用来跟着学习是非常有必要的。 

这份鸿蒙(HarmonyOS NEXT)资料包含了鸿蒙开发必掌握的核心知识要点,内容包含了ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)技术知识点。

希望这一份鸿蒙学习资料能够给大家带来帮助,有需要的小伙伴自行领取,限时开源,先到先得~无套路领取!!

获取这份完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料

鸿蒙(HarmonyOS NEXT)最新学习路线

  •  HarmonOS基础技能

  • HarmonOS就业必备技能 
  •  HarmonOS多媒体技术

  • 鸿蒙NaPi组件进阶

  • HarmonOS高级技能

  • 初识HarmonOS内核 
  • 实战就业级设备开发

有了路线图,怎么能没有学习资料呢,小编也准备了一份联合鸿蒙官方发布笔记整理收纳的一套系统性的鸿蒙(OpenHarmony )学习手册(共计1236页)鸿蒙(OpenHarmony )开发入门教学视频,内容包含:ArkTS、ArkUI、Web开发、应用模型、资源分类…等知识点。

获取以上完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料

《鸿蒙 (OpenHarmony)开发入门教学视频》

《鸿蒙生态应用开发V2.0白皮书》

图片

《鸿蒙 (OpenHarmony)开发基础到实战手册》

OpenHarmony北向、南向开发环境搭建

图片

 《鸿蒙开发基础》

  • ArkTS语言
  • 安装DevEco Studio
  • 运用你的第一个ArkTS应用
  • ArkUI声明式UI开发
  • .……

图片

 《鸿蒙开发进阶》

  • Stage模型入门
  • 网络管理
  • 数据管理
  • 电话服务
  • 分布式应用开发
  • 通知与窗口管理
  • 多媒体技术
  • 安全技能
  • 任务管理
  • WebGL
  • 国际化开发
  • 应用测试
  • DFX面向未来设计
  • 鸿蒙系统移植和裁剪定制
  • ……

图片

《鸿蒙进阶实战》

  • ArkTS实践
  • UIAbility应用
  • 网络案例
  • ……

图片

 获取以上完整鸿蒙HarmonyOS学习资料,请点击→纯血版全套鸿蒙HarmonyOS学习资料

总结

总的来说,华为鸿蒙不再兼容安卓,对中年程序员来说是一个挑战,也是一个机会。只有积极应对变化,不断学习和提升自己,他们才能在这个变革的时代中立于不败之地。 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

桶排序---

1、算法概念 桶排序&#xff1a;一种非比较的排序算法。桶排序采用了一些分类和分治的思想&#xff0c;把元素的值域分成若干段&#xff0c;每一段对应一个桶。在排序的时候&#xff0c;首先把每一个元素放到其对应的桶中&#xff0c;再对每一个桶中的元素分别排序&#xff0c…

【数据库系统工程师】软考2024年5月报名流程及注意事项

2024年5月软考数据库系统工程师报名入口&#xff1a; 中国计算机技术职业资格网&#xff08;http://www.ruankao.org.cn/&#xff09; 2024年软考报名时间暂未公布&#xff0c;考试时间上半年为5月25日到28日&#xff0c;下半年考试时间为11月9日到12日。不想错过考试最新消息…

分享一种快速移植OpenHarmony Linux内核的方法

移植概述 本文面向希望将 OpenHarmony 移植到三方芯片平台硬件的开发者&#xff0c;介绍一种借助三方芯片平台自带 Linux 内核的现有能力&#xff0c;快速移植 OpenHarmony 到三方芯片平台的方法。 移植到三方芯片平台的整体思路 内核态层和用户态层 为了更好的解释整个内核…

Unity 使用 IL2CPP 发布项目

一、为什么用 IL2CPP Unity的IL2CPP&#xff08;Intermediate Language to C&#xff09;是一个编译技术&#xff0c;它将C#代码转换为C代码&#xff0c;然后再编译成平台相关的二进制代码。IL2CPP提供了几个优点&#xff0c;特别是在性能和跨平台部署方面。以下是IL2CPP的一些…

未来的智能起航:探索AI技术的创业新天地

在科技飞速发展的当今世界&#xff0c;人工智能&#xff08;AI&#xff09;已经成为一个热门话题。不再是科幻小说中的概念&#xff0c;AI正逐渐融入我们的生活和工作中&#xff0c;开创了全新的创业市场和机会。人工智能&#xff08;AI&#xff09;的飞速发展不仅引领了科技的…

iOS苹果签名共享签名是什么以及如何获取?

哈喽&#xff0c;大家好呀&#xff0c;咕噜淼淼又来和大家见面啦&#xff0c;最近有很多朋友都来向我咨询共享签名iOS苹果IPA共享签名是什么&#xff0c;针对这个问题&#xff0c;淼淼来解答一下大家的疑惑并告诉大家iOS苹果ipa共享签名需要如何获取。 现在苹果签名在市场上的…

boost库搜索引擎

文章目录 0. 前言1. 搜索引擎原理2. 技术栈和项目环境3. 正排索引和倒排索引3.1 正排索引3.2 倒排索引3.3 模拟查找 4. 获取数据源5. 数据清洗5.1 保存路径5.2 解析文件提取标题提取内容构造url 5.4 保存内容 6. 建立索引6.1 建立正排索引6.2 建立倒排索引6.3 构建索引 7. 搜索…

为什么高校都在做数字化转型?智慧校园建设该如何落地?

作为高校的多年合作伙伴&#xff0c;很多时候我们在与各大高校信息处的老师对接和联系的时候&#xff0c;常常听到部分老师在头疼学校的信息化管理&#xff0c;从而提出需求。归纳下来&#xff0c;高校的信息管理主要面对着三大难题&#xff1a; 第一&#xff0c;资源管理效率…

精彩解读:短链接应用全方位探究

title: 精彩解读&#xff1a;短链接应用全方位探究 date: 2024/4/2 17:44:50 updated: 2024/4/2 17:44:50 tags: 短链接定义映射算法原理简洁美化优势工作流程解析安全隐私保护商业营销应用技术趋势发展 1. 短链接的定义和原理 短链接是一种将长网址转换为短网址的服务&#…

SeLinux 的编译逻辑

在Android 11 init进程对Selinux的处理一文中&#xff0c;我们知道&#xff0c;在init进程对Selinux的处理过程中&#xff0c;会将precompiled_sepolicy或者动态编译相关目录下的cil文件得到的compiled_sepolicy写入给内核。那么precompiled_sepolicy文件和cil文件是从哪里来的…

自己动手写数据库:基于哈希的静态索引设计

数据库设计中有一项至关重要的技术难点&#xff0c;那就是给定特定条件进行查询时&#xff0c;我们需要保证速度尽可能快。假设我们有一个 STUDENT 表&#xff0c;表中包含学生名字&#xff0c;年龄&#xff0c;专业等字段&#xff0c;当我们要查询给定年龄数值的记录&#xff…

如何一键展示全平台信息?Python手把手教你搭建自己的自媒体展示平台

前言 灵感源于之前写过的Github中Readme.md中可以插入自己的js图片和动态api解析模块&#xff0c;在展示方面十分的美观&#xff1a; 这方面原理可以简化为&#xff0c;在Markdown中&#xff0c;你可以使用HTML标签来添加图像&#xff0c;就像这样&#xff1a; <tr><…

轻松设置Facebook自动隐藏评论和删除评论功能

Facebook作为海外营销的最大流量平台之一&#xff0c;是很多跨境卖家争夺的市场&#xff0c;希望可以通过Facebook这个全球性的平台来推广自己的产品或服务。身处这个竞争激烈的市场&#xff0c;任何一条负面评论或不当言论出现在你的品牌页面上都可能影响到品牌形象&#xff0…

晶核新手必备攻略,干货满满!

晶核游戏以其独特的玩法和丰富的内容吸引着众多玩家。然而&#xff0c;对于一些追求效率和资源的玩家来说&#xff0c;单开游戏往往难以满足他们的需求。多开游戏成为了一个不错的选择&#xff0c;它能帮助玩家更快地获取资源&#xff0c;提升账号实力。下面将为大家分享一些晶…

ardupilot开发 --- 远程标识 篇

1. wifi协议 https://zhuanlan.zhihu.com/p/660568077 AP 无线接入点 路由器STA 站点 接入路由器的终端SSID 标识符 无线网络的名称信标祯 Beacon AP通过广播Beacon祯来告诉想要接入者(STA)无线网络的信息&#xff0c;如SSIDWLAN数据帧 Wi-Fi网络中传输数据时所使用的数据帧格…

Docker部署Nexus Maven私服并且实现远程访问Nexus界面

目录 ⛳️推荐 1. Docker安装Nexus 2. 本地访问Nexus 3. Linux安装Cpolar 4. 配置Nexus界面公网地址 5. 远程访问 Nexus界面 6. 固定Nexus公网地址 7. 固定地址访问Nexus ⛳️推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&am…

Linux:基本指令篇

文章目录 前言1.ls 指令2.pwd命令3.cd 指令4.touch指令5.mkdir指令&#xff08;重要&#xff09;6.rmdir指令 && rm 指令&#xff08;重要&#xff09;7.man指令&#xff08;重要&#xff09;8.cp指令&#xff08;重要&#xff09;9.mv指令&#xff08;重要&#xff09…

【THM】Passive Reconnaissance(被动侦察)-初级渗透测试

介绍 欢迎来到网络安全模块的第一个房间,该模块涵盖: 1.被动侦察 2.主动侦察 3.Nmap实时主机发现 4.Nmap基本端口扫描 5.Nmap高级端口扫描 6.Nmap后端口扫描 7.协议和服务器 8.协议和服务器2 9.网络安全挑战 在这个房间里,在我们定义被动侦察和主动侦察之后,我们…

友思特方案 | 构建缤纷:可调谐光源的荧光成像的应用

导读 生物荧光分析常常伴随使用多种荧光染料的需求。结合多通道光源技术与高性能成像设备&#xff0c;友思特可调谐光源荧光检测成像方案&#xff0c;以其灵活的系统组成&#xff0c;满足了丰富的荧光检测应用需求。 生物荧光分析技术 激发荧光成像技术是研究生物学过程的一种…

Python基于微博的大数据舆论情感分析、微博大数据舆论分析可视化系统

博主介绍&#xff1a;✌IT徐师兄、7年大厂程序员经历。全网粉丝15W、csdn博客专家、掘金/华为云//InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3…