2025-01-15 18:09:21
在命令行界面(CLI)中输出带颜色的日志不仅能提升可读性,还能帮助开发人员在调试时迅速区分不同类型的日志信息。
通过使用 ANSI 转义序列,我们可以很方便地控制输出文本的颜色、样式和其他显示效果,如加粗、下划线、反显等。
本文将详细介绍如何使用这些序列输出带颜色的日志。
ANSI 转义序列是一种用于控制终端文本格式的字符序列。
它通常以 \033[
或 \e[
开头,后接不同的控制代码,最后以 m
结尾。
例如,\033[32m
表示设置文本颜色为绿色,\033[0m
用来重置样式。
利用 ANSI 转义序列,开发者可以灵活地在命令行中输出不同颜色和效果的文本。
\033[0m
:关闭所有属性,恢复为默认设置\033[1m
:设置高亮度(加深显示)\033[4m
:设置下划线\033[5m
:设置闪烁\033[7m
:反显(替换背景色和前景色)\033[8m
:消隐(隐藏文本)\033[30m
到 \033[37m
:设置前景色(字体颜色)\033[40m
到 \033[47m
:设置背景色\033[nA
:光标上移 n 行\033[nB
:光标下移 n 行\033[nC
:光标右移 n 行\033[nD
:光标左移 n 行\033[y;xH
:设置光标位置为 y 行 x 列\033[2J
:清屏\033[K
:清除从光标到行尾的内容\033[s
:保存光标位置\033[u
:恢复光标位置\033[?25l
:隐藏光标\033[?25h
:显示光标最简单的颜色控制是设置文本的前景色。例如,以下代码将输出绿色文本:
echo "\033[32m绿色\033[0m"
033[32m
设置文本为绿色,033[0m
用于重置所有属性,使后续输出恢复默认样式。
我们可以结合多种样式来增强文本的可读性。例如,下面的代码将输出一个带下划线的红色文本:
echo "\033[4;31m下划线红色\033[0m"
这里,4
表示下划线,31
表示红色。
除了颜色和样式,ANSI 转义序列还可以控制终端的其他行为,比如发出声音。
这行命令会在终端发出一声铃声,同时输出一段普通文本:
echo "\007发出'咚~'一声\033[0m"
请注意,在某些终端环境下,铃声可能不会响起,尤其是在没有扬声器的设备上。
你还可以同时设置文本的前景色和背景色。例如,以下代码将输出一个白色背景和红色前景的文本:
echo "\033[47;31m白底红字\033[0m"
47
是背景色(白色),31
是前景色(红色)。
通过组合多个效果,你可以创建更具视觉冲击力的输出。比如,以下代码将输出一个蓝色加粗下划线的文本:
echo "\033[1;4;34m蓝色加粗下划线\033[0m"
在这个示例中,1
表示加粗,4
表示下划线,34
表示蓝色。
ANSI 转义序列还允许控制光标的位置和终端屏幕的清理。例如:
echo "\033[2J" # 清屏
echo "\033[10;5H光标移动到第10行第5列\033[0m"
你还可以隐藏和显示光标:
echo "\033[?25l" # 隐藏光标
echo "\033[?25h" # 显示光标
通过使用 ANSI 转义序列,我们可以轻松地为命令行中的输出添加颜色和样式。
这不仅能让调试日志变得更加易读,还能增强命令行工具的用户体验。
你可以根据需求结合不同的颜色、效果和光标控制,创建自定义的命令行输出,通过这些技术,命令行的输出变得更加生动和富有表现力,有助于开发人员快速识别关键信息。
2025-01-02 12:05:37
今天在对接一个 API 的时候,发现需要生成高精度的时间戳,格式为yyyyMMddHH24mmssSSS
。
本文将介绍两种常见的实现方式,并讨论它们的优缺点。
格式 yyyyMMddHH24mmssSSS
的含义如下:
例如,时间 2025-01-02 11:30:45.123
的格式化结果为:20250102113045123
。
DateTime
类实现以下是使用 DateTime
类生成毫秒时间戳的代码示例:
<?php
$dateTime = new DateTime();
// 获取当前时间的微秒数并计算为毫秒
$milliseconds = intval($dateTime->format('u') / 1000);
// 格式化时间
$formattedTime = $dateTime->format("YmdHi") . $dateTime->format("s") . sprintf("%03d", $milliseconds);
echo $formattedTime;
$dateTime->format('u')
返回当前时间的微秒(6 位数,例如 123456
)。intval($dateTime->format('u') / 1000)
将微秒转换为毫秒(3 位数,例如 123
)。sprintf("%03d", $milliseconds)
确保毫秒部分始终为 3 位数(不足时补零)。假设当前时间为 2025-01-02 11:30:45.123456
,输出结果为:
20250102113045123
microtime
函数实现另一种方法是结合 microtime()
和 date()
函数:
<?php
$microtime = microtime(true);
// 格式化时间到秒
$formattedDate = date('YmdHis', floor($microtime));
// 获取毫秒部分
$milliseconds = sprintf('%03d', ($microtime - floor($microtime)) * 1000);
// 拼接毫秒
$formattedDate .= $milliseconds;
echo $formattedDate;
microtime(true)
返回当前 Unix 时间戳,包含秒和小数部分。floor($microtime)
获取整数秒部分。($microtime - floor($microtime)) * 1000
提取小数部分并转换为毫秒。假设当前时间为 2025-01-02 15:30:45.123456
,输出结果为:
20250102153045123
特性 | 使用 DateTime 类 |
使用 microtime()
|
---|---|---|
代码简洁性 | 更加现代化,语义清晰 | 较为传统,需要手动处理毫秒 |
精度 | 取决于系统支持的时间精度 | 依赖 microtime() 的实现 |
扩展性 | 更容易与其他 DateTime 操作结合 |
适合处理与 Unix 时间戳相关的逻辑 |
为了验证两种方法的输出是否一致,可以添加以下代码:
if ($formattedTime === $formattedDate) {
echo "两种方法的输出一致:$formattedTime\n";
} else {
echo "两种方法的输出不一致:\n第一种方法:$formattedTime\n第二种方法:$formattedDate\n";
}
DateTime
类,建议采用第一种方法,代码语义更加清晰。microtime()
或 Unix 时间戳直接交互,可以选择第二种方法。选择哪种方式主要取决于项目需求和代码风格偏好。希望本文对你在生成带毫秒的时间戳方面有所帮助!
2025-01-01 19:20:35
2024 年,对于我而言,是一段充满挑战与成长的旅程。
从工作中的挫折到家庭的变故,这一年让我更加体会到坚韧的意义,也在风雨中积累了成长的力量。
虽然充满波折,但我仍然选择在逆境中寻找方向,努力让自己变得更加强大。
在 2024 年中生活与工作的双重挑战交织而来,让我猝不及防。
其实在去年的年终总结中也提到了,自己已经内耗很久了,而且大部分同事其实也是,职业道路面临着严峻考验,加上大环境不好,工作和家庭多方面带来的压力并不小。
内耗了大概有快一年吧,今年最终也是爆发了:我和一位关系较好的同事一起离职了。
主要原因也是多方面,一是内耗这么久了,不想耗了,世界这么大我想去看看;二是自己的直性格和新同事的背刺;三是公司内部种种事件的发生,使得原本热爱的工作逐渐成为负担。
内耗严重,消耗了我的热情与精力。经过一段时间的挣扎,我决定离开这个让我不再快乐的环境。
离职后的日子可能并不轻松,但也让我重新掌握了生活的主动权,离职后也有听到有几位关系好的同事也先后离开了公司,活成了吃瓜群众...
因为房租还未到期,自己也选择了继续在青岛待上一段时间,跟关系好的同事们陆续告别,吃饭喝酒,侃侃而谈,大家也在聊着各自的看法和感受。
离职后,尝试让自己暂时放下压力,为未来的职业方向重新规划,也有疑问为什么不等到过年后再离职?答案还是上面提到的不想耗了,世界这么大我想去看看。
于是我也答应了跟我同一天离职的同事,帮她送兔子去北京,顺便溜达散心,从北京回来之后再起程回老家。
在北京我俩也是喝酒畅聊,还去了一趟雍和宫,在上班和上进之间选择了上香。时间仓促,第三天早上我便出发返程了,不料半路下雨,在出服务区时过了一个小水坑,结果车出现了问题(后来检查得知是电子转向机由于进水坏了),没办法只能一路坚持从高速开回了住处,找了地方维修车辆,避免耽误回老家。
由于转向机突然损坏,不得不承担一笔不小的维修费用,这让本不富裕的家庭雪上加霜,虽然看似是一次小意外,却让我深刻意识到,人生的计划往往会被突如其来的问题打断,我们能做的,是从容面对。
青岛的短暂停留给了我片刻喘息,与老同事的聚会让我感受到人情的温暖。然而,在房租到期后回到老家,奶奶的去世又一次让我面对人生的无常,同时也第一次在医院过夜。
这段时间,我更多地陪伴家人,感悟到亲情的重要性,也开始学会放慢脚步。
就在我努力整理心情,接受上海新工作的同时,命运再次掀起波澜:父亲突然离世,很急很急,我只能在视频中去医院之前见了最后一眼,没想到隔了不到几个小时就得到了人已经走了的消息。
这一噩耗让我深陷悲痛,当晚就请假第二天乘坐最早的飞机回家,奶奶去世时还有父亲在前面撑着,而父亲突然走了,让我手足无措,但我知道我不能垮掉,还有更多的事情在等着我处理。
这个家的生活还要继续,处理完父亲的身后事,直到三七结束,我也重返工作岗位。这段经历不仅考验了我的承受能力,也让我更加清楚地理解了责任的真正含义。
生活和工作的波折让我意识到,坚韧不仅仅是一种态度,更是一种能力。
面对工作中的失落,我学会了取舍,明白了有时候“止损”是对自己最好的成全;面对生活中的离别,我选择直面悲伤,在亲情的回忆中汲取前行的力量。
这一年,尽管前路坎坷,但我始终没有停下脚步。
在适应新的工作节奏中,我不断调整状态,力求用最好的表现迎接挑战。生活的种种考验让我学会了平衡,懂得了如何在压力中找到一丝安慰,让自己变得更坚强、更从容。
2024 年的经历让我深刻体会到,成长往往藏在不经意的每一个瞬间。
离职后的三个月是我重新审视自己的机会,除了本质工作以外,开源事业也是我发自内心热爱的方向。
Docsify 成为 Gitee 成为 GVP 的那一刻,我感受到努力被认可的喜悦,也让我更加坚定地投入到技术与开源社区中。
此外,成为 Apache Answer 的 PPMC Member 也是今年的一大收获。
这份荣誉不仅是一种认可,更是一份责任,促使我不断提升自己,为开源社区贡献更多价值。
无论是在工作还是生活中,我都在这些起起伏伏中找到了成长的契机。
从经历挫折到收获信心,这一年的每一步都让我离更好的自己更近了一些。
回顾 2024 年,我经历了从失望到解脱,从低谷到重生的全过程。
这些经历让我更加明白,生活并不会因为我们的疲惫而停下脚步,而我们能做的就是在每一次风雨中找到支撑自己的力量。
展望未来,我希望自己能更加沉着应对工作中的挑战,也会投入更多精力到开源社区中,用技术赋能更多人,在社区活动中认识更多人;在生活中,我会更加珍惜与家人、朋友的每一刻,用心感受生活带来的温暖与感动。
这一年让我更加坚信,无论面对什么,只要心中有光,终能走出阴霾。
2025 年,希望是一个充满希望和新机遇的年份,我期待着更多的可能性,也期待着与成长同行,继续前行。
2024-12-25 10:40:09
在 Web 开发中,日志记录和 HTTP 头部信息的传递非常重要。
Nginx 和 PHP 作为常见的 Web 服务器和处理引擎,结合使用时可以提供强大的日志记录功能和灵活的头部管理。
本文将介绍如何通过 Nginx 配置自定义日志格式、隐藏特定的 HTTP 头信息,并在 PHP 端输出特殊的 Header 信息,以便在 Nginx 日志中记录详细的用户信息。
通过修改 Nginx 配置,可以定义一个新的日志格式,用于记录详细的用户信息。这个配置将帮助记录 PHP 动态生成的用户信息(如用户 ID、账号等)。
在 Nginx 配置文件中,定义自定义的日志格式,并在访问日志中加入 X-User-Info
头部:
http {
# 定义自定义日志格式
log_format custom_log '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'$request_time $upstream_response_time '
'$http_x_user_info'; # 记录 X-User-Info 头部信息
# 使用自定义格式记录日志
access_log /var/log/nginx/custom_access.log custom_log;
}
在这个配置中:
log_format custom_log
定义了日志格式,其中 $http_x_user_info
用来记录通过 HTTP 头传递的用户信息。access_log
指令将日志输出到指定的文件 /var/log/nginx/custom_access.log
,并使用定义的 custom_log
格式。在某些情况下,可能不希望某些敏感的 HTTP 头暴露给客户端。Nginx 提供了 proxy_hide_header
指令来隐藏这些头部信息。
此功能特别有用,当需要阻止某些信息(如用户的详细信息)暴露给客户端时。
假设希望隐藏 X-User-Info
头部信息,可以在 location
配置中使用 proxy_hide_header
指令:
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://backend_server;
# 隐藏 X-User-Info 头部信息
proxy_hide_header X-User-Info;
}
}
此配置确保即使 X-User-Info
头部信息在请求中被发送到 Nginx,Nginx 也不会将该头部返回给客户端,从而保护敏感信息。
在 PHP 中,可以动态生成和输出特定的 HTTP 头部信息,并将其传递给 Nginx。比如,在响应头中输出用户信息(如用户 ID、账号等),然后在 Nginx 日志中记录这些信息。
在 PHP 中,可以使用 header()
函数来输出自定义的 HTTP 头。
以下是一个示例,展示如何在 PHP 中生成并发送 X-User-Info
头部信息:
<?php
// 启动会话并获取用户信息
session_start();
$user_id = isset($_SESSION['user_id']) ? $_SESSION['user_id'] : 'guest';
// 构造用户信息字符串
$user_info = "user_id={$user_id};";
// 设置 HTTP 头部
header("X-User-Info: {$user_info}");
// 其他 PHP 逻辑...
?>
在这段 PHP 代码中:
session_start()
获取当前用户的 ID(假设它存储在会话中)。如果用户未登录,则默认为 'guest'
。header()
函数,将这些信息作为 X-User-Info
头部发送到 Nginx。在 Nginx 中配置了自定义日志格式,并在 PHP 中输出了 X-User-Info
头部信息。接下来,可以将这两部分结合使用,确保详细的用户信息能够记录到日志中,同时确保这些信息不会暴露给客户端。
http {
# 定义自定义日志格式
log_format custom_log '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'$request_time $upstream_response_time '
'$http_x_user_info'; # 记录 X-User-Info 头部信息
# 使用自定义格式记录日志
access_log /var/log/nginx/custom_access.log custom_log;
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://backend_server;
# 隐藏 X-User-Info 头部信息
proxy_hide_header X-User-Info;
}
}
}
在这个完整配置中:
custom_log
,其中 $http_x_user_info
用来记录通过 PHP 生成的 X-User-Info
头部。location
块中,使用 proxy_hide_header
隐藏了 X-User-Info
头部,确保敏感信息不会被返回给客户端。通过结合 Nginx 和 PHP,可以实现以下目标:
X-User-Info
头部将用户的 ID、账号等记录到 Nginx 日志中,便于后续的行为分析和问题排查。proxy_hide_header
隐藏特定的 HTTP 头,确保敏感信息不被暴露给客户端。这种配置适用于需要详细日志记录的高流量站点,尤其是在需要跟踪用户活动、分析访问模式或保护敏感数据时。
通过合理的日志管理和头部处理,可以在保障系统性能和安全性的同时,提供更丰富的数据支持。
2024-12-13 10:20:33
在配置 Nginx SSL 证书时,如果遇到以下错误:
nginx: [emerg] SSL_CTX_use_PrivateKey failed (SSL: error:0B080074:x509 certificate routines:X509_check_private_key:key values mismatch)
这应该是 SSL 证书和私钥之间的公私钥对不匹配。这篇文章就展开地分析这个问题的原因和解决方案。
该错误表示 Nginx 在加载私钥时,检测到证书和私钥之间不匹配。常见原因如下:
通过 OpenSSL 命令检查证书和私钥的 MD5 值是否匹配:
查看私钥的 MD5 值:
openssl rsa -noout -modulus -in your_private.key | openssl md5
查看证书的 MD5 值:
openssl x509 -noout -modulus -in your_certificate.crt | openssl md5
CSR 的 MD5 值(如果有):
openssl req -noout -modulus -in your_request.csr | openssl md5
如果 MD5 值不匹配,请确保使用了正确的私钥和证书。
使用下列命令检查私钥是否有效:
openssl rsa -check -in your_private.key
如果私钥损坏,需重新生成私钥和证书。
为了确保证书链完整,可将证书和中间证书合并:
cat your_certificate.crt intermediate_certificate.crt > full_chain.crt
然后在 Nginx 配置文件中指向此合并证书:
ssl_certificate /path/to/full_chain.crt;
ssl_certificate_key /path/to/private.key;
如果不能确保证书和私钥是否一致,可考虑重新生成 CSR 和私钥:
生成新的私钥:
openssl genrsa -out new_private.key 2048
生成 CSR:
openssl req -new -key new_private.key -out new_request.csr
然后将 CSR 提交给 CA (证书授权机构),以获取新证书。
确保配置文件中指向正确的证书和私钥路径,如在 Nginx 中的配置示例:
ssl_certificate /etc/nginx/ssl/full_chain.crt;
ssl_certificate_key /etc/nginx/ssl/private.key;
配置保存后,重启 Nginx:
sudo nginx -t
sudo systemctl reload nginx
这个错误通常是由于证书和私钥不匹配或配置错误导致。通过检查证书和私钥的 MD5 值、检查配置文件和确保证书链完整,可以解决这个问题。
2024-11-20 14:05:13
PHP 8.4 是一个重要的版本,它带来了主要的新功能、对构建依赖项和底层库的几项更新,以及相当多的弃用,以消除旧版 PHP 中一些遗留的不良行为和功能。
它包含许多新功能,例如属性钩子、不对称可见性、更新的 DOM API、性能改进、错误修复和常规清理等。
PHP 8.4 中最重要的功能之一是新增了 属性钩子 和为 get 和 set 操作分别声明可见性 的功能。
属性钩子允许在访问或设置属性时执行“钩子”逻辑:
class Locale
{
public string $languageCode;
public string $countryCode
{
set (string $countryCode) {
$this->countryCode = strtoupper($countryCode);
}
}
public string $combinedCode
{
get => \sprintf("%s_%s", $this->languageCode, $this->countryCode);
set (string $value) {
[$this->countryCode, $this->languageCode] = explode('_', $value, 2);
}
}
public function __construct(string $languageCode, string $countryCode)
{
$this->languageCode = $languageCode;
$this->countryCode = $countryCode;
}
}
$brazilianPortuguese = new Locale('pt', 'br');
var_dump($brazilianPortuguese->countryCode); // BR
var_dump($brazilianPortuguese->combinedCode); // pt_BR
非对称可见性允许为 get
和 set
操作定义不同的作用域:
class PhpVersion
{
public private(set) string $version = '8.4';
}
$phpVersion = new PhpVersion();
var_dump($phpVersion->version); // string(3) "8.4"
$phpVersion->version = 'PHP 8.3'; // Visibility error
PHP 8.4 的 DOM 扩展也进行了一次重大功能更新。之前,DOM 扩展仅提供 libxml2 来解析 HTML,而 libxml2 并未跟上 HTML5 的进展。现在,DOM 扩展新增了 Dom\HTMLDocument
和 Dom\XMLDocument
类,其中前者支持 HTML5 合规的解析。
在这一领域有很多新的改进,包括不仅仅是HTML5 解析支持,还包括DOM 规范合规性,以及若干小的增强,比如添加了对 CSS 选择器的支持。
Number
类和新函数PHP 8.4 中的 BCMath 扩展现在支持运算符重载,提供了新的类支持!
use BcMath\Number;
$num1 = new Number('22');
$num2 = new Number('7');
$num3 = new Number('100');
$result = ($num1 / $num2) + $num1 - $num2;
echo $result; // 18.1428571428
现在,不再需要使用 BCMath 函数如 bcadd
、bcsub
、bcdiv
等,可以直接使用标准运算符(+
、-
、/
等)。
新的 BcMath\Number
类支持运算符重载,虽然用户自定义 PHP 类尚不支持此功能,但 BCMath 扩展已经实现了这一点,因此可以像使用常规数字一样使用这些对象。
BcMath\Number
类实现了 Stringable
接口,因此这些对象可以在需要字符串的地方使用(比如上例中的 echo
调用)。此外,该类实现了所有的 bc*
函数。例如,还可以调用 $num->add($num2)
或 $num->add('5')
,它会返回一个新的 BcMath\Number
对象,而不会修改原始对象,这使得这些对象是不可变的。
array_find
、array_find_key
、array_any
和 array_all
bcdivmod
、bcround
、bcceil
和 bcfloor
mb_trim
、mb_ltrim
和 mb_rtrim
mb_ucfirst
和 mb_lcfirst
grapheme_str_split
fpow
http_get_last_response_headers
和 http_clear_last_response_headers
PDO 驱动特定子类 RFC 已在 PHP 8.4 中实现。它曾在 PHP 8.3 中进行投票,但由于 8.3 的功能冻结,未能实现。
PHP 8.4 现在新增了 Pdo\Mysql
、Pdo\Pgsql
、Pdo\Sqlite
、Pdo\DbLib
和 Pdo\Firebird
类,这些类扩展了 PDO
类。现在可以在驱动特定子类中使用驱动特定的方法、属性和常量。驱动特定的子类还允许通过只接受/返回驱动特定的子类来使 API 更加明确和限制。
IMAP、Pspell、OCI8 和 PDO_OCI8 扩展已从 PHP 核心中解耦,现在作为 PECL 扩展提供,用户可以通过 PIE 轻松安装这些扩展。
2024 年 4 月,PHP 投票并通过了一项 RFC 提案,以更新 PHP 的发布周期政策。
此前,PHP 核心团队提供两年活跃支持,然后是一年的安全修复支持。
而现在从 PHP 8.1(2021 年 11 月发布)起,所有 PHP 版本将获得 两年安全修复支持,活跃支持期保持两年不变。
此外,活跃支持和生命周期终止(EOL)的日期将调整为日历年的 12 月 31 日,使这些日期更加可预测。
以下是当前 PHP 版本的更新支持和生命周期终止日期,变更部分用加粗标注:
PHP 版本 | 发布时间 | 活跃支持至 | 生命周期终止日期 |
---|---|---|---|
PHP 8.1 | 2021-11-25 | 2023-11-25 | 2025-12-31 |
PHP 8.2 | 2022-12-08 | 2024-12-31 | 2026-12-31 |
PHP 8.3 | 2023-11-23 | 2025-12-31 | 2027-12-31 |
PHP 8.4 | 2024-11-21 | 2026-12-31 | 2028-12-31 |
PHP 8.5 | 2025-11 | 2027-12-31 | 2029-12-31 |
更多信息可以查看 PHP 版本发布页面