封装简易cpp日志模块

封装简易cpp日志模块

封装 cpp 简易的日志,可以在开发一个新项目马上引用进来。如下是一个简单的思路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#pragma once

#include <iostream>
#include <iomanip>
#include <string>
#include <fstream>
#include <utility>

// 日志级别
enum LogLevel {
INFO = 0,
WARNING,
ERROR,
};

class Logger {
public:
Logger() = default;
~Logger() {
get_stream() << std::endl << std::flush;
}
Logger(const Logger&) = delete;
Logger& operator=(const Logger&) = delete;
Logger(Logger&&) = delete;
Logger& operator=(Logger&&) = delete;

public:
static std::ostream& start(LogLevel log_level, const int line, const std::string& func) {
time_t tm;
time(&tm);
char time_str[128];
strftime(time_str, sizeof(time_str), "[%Y-%m-%d %X] ", localtime(&tm));
return get_stream() << time_str << "func[" << func << "] " << "line[" << line << "] ";
}

static std::ostream& get_stream() {
return file_.is_open() ? file_ : std::cout;
}

private:
friend void init_logger(const std::string& filename);

private:
LogLevel log_level_;
static std::ofstream file_;
};

std::ofstream Logger::file_;

void init_logger(const std::string& filename) {
Logger::file_.open(filename.c_str(), std::ofstream::app);
}

#define LOG(log_level) \
Logger().start(log_level, __LINE__, __FUNCTION__)

缺点是每次使用日志,都将创建一个 Logger 的对象,以及释放这个对象。

placement new 详解

placement new 详解

1
void* operator new(size_t, void* ptr) throw() { return ptr; }

placement new 是 operator new 的一个重载版本。允许我们在一个已经分配好的内存中(栈和堆中)构造一个新的对象。其中参数中 void* 实际上就是指向一个已经分配好的内存缓冲区的首地址。

我们在 new 的时候,需要分配内存,而分配内存的操作往往相对比较耗时。而使用 placement new 只是在已经分配好的内存中调用构造函数,在某些场景中,是非常适用的。

如下举一个例子:

查看更多

c++11 中 function 和 bind 的使用

c++11 中 function 和 bind 的使用

在 c++11 中提供了 std::functionstd::bind 两个方法来对可回调对象进行统一和封装。在我们设计回调函数时候,不可避免的需要传入一个函数对象或者函数指针的参数

c++ 有这么几种可调用对象:函数指针、lambda 表达式、仿函数。其中的 bind 机制是对旧版本中的 bind1stbind2st 的合并升级。

  • 函数指针和其他指针类型一致,只不过函数指针指向某种特定类型
  • lambda 表达式是一个匿名的可调用的代码,可以较好的保证不会出现不安全的访问
查看更多

断言的使用

断言的使用

断言可以先计算表达式的真假,如果其值为假(即为 0),那么他先向 stderr 输出一条错误,然后调用 abort 终止程序运行。

断言分为:静态断言和动态断言。也即:编译期断言和运行期断言。

一、编译期断言

static_assert(expression, message) 实现了编译器的断言,也叫静态断言。会调用 abort 终止程序的运行。

  • 如果第一个参数,常量表达式的值为 false,会产生一条编译错误
查看更多

空间配置器

整个 STL 的操作对象都存放在容器之内,而容器一定需要配置空间用来存放数据。所以就要用到空间配置器了。这里的空间一般都指的是内存空间,但是我们也可以写一个基于磁盘空间的空间配置器,只要有这个需求。

SGI STL 的配置器名称为 alloc,不接受任何参数,也就是说,如果要使用需要写成:

1
std::vector<int, std::alloc> iv;

一般情况下,SGI STL 的每一个容器都已经指定其缺省的空间配置器为 alloc。例如下的 vector 的声明:

查看更多

嵌入式链路监控方案

嵌入式链路监控方案

一、当前背景

我们当前的项目对于产出启动、功能闭环、服务之间的互相调用、服务协同工作 等等,这些场景可能来自于不同的开发人员、不同的开发语言,不同的模块等等因素。某个模块、或某个功能闭环,如果新增、修改、删除代码,可能造成的总体耗时增加,此时我们需要知道耗时的位置。

在产出启动到功能可用,这个启动耗时,影响用户的使用体验,因此我们定位初始化过程中的耗时点,进行针对性优化很有必要。

对于多进程,像 gaia 进程、多 pavaro 进程等等的场景,以及多线程的场景,我们统计执行流的耗时,是非常有必要的。

我们目前的耗时统计很原始,只是通过手工看日志的方式,效率极低,而且很费眼睛,统计也不一定做到精准。

因此我们的链路监控是非常有必要的。

查看更多

性能分析方案

性能分析方案

本文主要对 linux 下程序的性能如何分析进行讨论。

性能分析是一种动态的程序分析方法,在运行阶段采集程序的各种信息,再整合、研究,找出软件运行的“瓶颈”,为进一步优化性能提供依据,指明方向。性能分析的范围非常广,可以从 CPU 利用率、内存占用率、网络吞吐量、系统延迟等许多维度来评估。

本文目前主要针对 CPU、内存 这两个方面的性能进行分析。

一、调研

1. 系统级工具

Linux 的性能分析工具太多,这里我推荐几个“高性价比”的工具:top、pstack、strace 和 perf。

查看更多