Nginx日志按天分割
需求、日志按照天的单位进行分割存储。
如果你直接百度,可能会搜到很多教你用各种脚本或是三方插件来按天分割的,这边我用nginx服务本身来分割日志。
方法一
通过使用 $time_iso8601 变量和 map 指令,实现了日志文件按天分割的功能。以下是详细的说明
1、map指令
map $time_iso8601 $logdate{'~^(?<ymd>\d{4}-\d{2}-\d{2})' $ymd;default 'date-not-found';
}
-
作用:
map指令将$time_iso8601变量映射为$logdate变量,其中$time_iso8601是 Nginx 内置的变量,表示请求的时间,格式为 ISO 8601(例如2024-08-13T20:35:06+08:00)。 -
正则表达式:
~^(?<ymd>\d{4}-\d{2}-\d{2})用于从$time_iso8601中提取日期部分(例如2024-08-13)。正则表达式中的(?<ymd>\d{4}-\d{2}-\d{2})捕获年-月-日部分,并将其赋值给变量$ymd。 -
映射结果: 提取的日期部分被映射为
$logdate变量,这个变量将用于日志文件的命名。
2、日志文件存储
access_log /data/logs/access_api.json_$logdate json;
error_log /data/logs/api-error.log$logdate error;
-
access_log: 配置了访问日志的路径,并使用$logdate变量生成按天分割的日志文件名。日志文件的完整路径格式为/data/logs/access_api.json_YYYY-MM-DD,其中YYYY-MM-DD是日期。 -
error_log: 错误日志的路径未使用$logdate,因此错误日志不会按天分割。
3、工作流程
- 每当有请求到达时,Nginx 会自动根据请求的时间,使用
map指令提取日期部分,生成$logdate变量。 - 访问日志会记录在以当前日期命名的日志文件中,如
access_api.json_2024-08-06。 - 随着日期的变化,
$logdate变量会自动更新,新的日志将记录到新的一天的日志文件中。
总结
通过 map 指令结合正则表达式,从 $time_iso8601 中提取日期部分,并使用该日期生成动态的日志文件名,从而实现了日志的按天分割功能。每一天都会生成一个新的日志文件,方便日后的日志分析与管理。
这里记得注意启动nginx的用户权限是否 有配置日志路径下的权限,记得日志写入权限的验证。
配置文件完整示例
user app;
worker_processes 2;
events {worker_connections 30000;
}
http {include mime.types;default_type application/octet-stream;sendfile on;keepalive_timeout 65;
###日志格式log_format json escape=json '{"timestamp":"$time_iso8601",''"remote_addr": "$remote_addr", ''"referer": "$http_referer", ''"request": "$request", ''"status": $status, ''"bytes": $body_bytes_sent, ''"agent": "$http_user_agent", ''"x_forwarded": "$http_x_forwarded_for", ''"up_addr": "$upstream_addr",''"up_host": "$upstream_http_host",''"up_resp_time": "$upstream_response_time",''"request_time": "$request_time",''"request_GlobalId": "$http_GlobalId",''"response_GlobalId": "$sent_http_GlobalId"''"response_body": "$resp_body",''"request_body": "$request_body"'' }';
###日志按天分割map $time_iso8601 $logdate{'~^(?<ymd>\d{4}-\d{2}-\d{2})' $ymd;default 'date-not-found';}
###charset utf-8 ;gzip on;
# 后端IP地址
upstream api-prod {server 10.66.66.88:8501 max_fails=5 fail_timeout=30s;server 10.66.66.66:8501 max_fails=5 fail_timeout=30s;
}server {listen 80 ;listen 7309 ;server_name api.xxxxxx.cn api-test.xxxxx.cn;charset utf-8 ;#日志配置lua_need_request_body on;set $resp_body "";body_filter_by_lua 'local resp_body = string.sub(ngx.arg[1], 1, 1000)ngx.ctx.buffered = (ngx.ctx.buffered or "") .. resp_bodyif ngx.arg[2] thenngx.var.resp_body = ngx.ctx.bufferedend';location / {proxy_pass http://api-prod;if ($http_user_agent ~* "SLBHealthCheck") {access_log off;return 200;}}access_log /data/logs/access_api.json_$logdate json;error_log /data/logs/api-error.log error;
}
}
查看是否在日志文件路径下,日志文件有按照天的维度来分割,观察日志是按照天的维度来分割的,配置完成。验证完成
-rw-r--r-- 1 app app 2 Aug 8 14:59 access_api.json_2024-06-05
-rw-r--r-- 1 app app 535976601 Aug 8 23:59 access_api.json_2024-08-08
-rw-r--r-- 1 app app 1236143585 Aug 9 23:59 access_api.json_2024-08-09
-rw-r--r-- 1 app app 499546906 Aug 10 23:59 access_api.json_2024-08-10
-rw-r--r-- 1 app app 313264627 Aug 11 23:59 access_api.json_2024-08-11
-rw-r--r-- 1 app app 675015315 Aug 12 23:59 access_api.json_2024-08-12
-rw-r--r-- 1 app app 469080433 Aug 13 23:59 access_api.json_2024-08-13
-rw-r--r-- 1 app app 174011220 Aug 14 11:47 access_api.json_2024-08-14
-rw-r--r-- 1 app app 2 Aug 8 14:59 api-error.log
[root@iZbp13xxxxxxxxxxw3yfqZ logs]# pwd
/data/logs
方法二
通过使用lua脚本来实现。
1、定义一个共享字典
lua_shared_dict logs 10m;
lua_shared_dict logs 10m;: 定义了一个共享字典,用于缓存日志文件句柄。这可以避免每次都重新打开日志文件,从而提高性能。10m表示分配了10MB的内存用于缓存。
2、Lua脚本实现
log_by_lua_block {local cjson = require "cjson"local log_file_cache = ngx.shared.logslocal today = os.date("%Y-%m-%d")local log_file = log_file_cache:get(today)if not log_file thenlog_file = io.open("/data/logs/access_api_" .. today .. ".json", "a")log_file_cache:set(today, log_file)endlocal log_line = cjson.encode({timestamp = ngx.var.time_iso8601,remote_addr = ngx.var.remote_addr,referer = ngx.var.http_referer,request = ngx.var.request,status = ngx.var.status,bytes = ngx.var.body_bytes_sent,agent = ngx.var.http_user_agent,x_forwarded = ngx.var.http_x_forwarded_for,up_addr = ngx.var.upstream_addr,up_host = ngx.var.upstream_http_host,up_resp_time = ngx.var.upstream_response_time,request_time = ngx.var.request_time,request_GlobalId = ngx.var.http_GlobalId,response_GlobalId = ngx.var.sent_http_GlobalId,response_body = ngx.var.resp_body,request_body = ngx.var.request_body})log_file:write(log_line .. "\n")log_file:flush()}
代码解释
-
local cjson = require "cjson": 通过require加载 Lua 的cjson模块,用于将 Lua 表转换为 JSON 格式的字符串。这样可以把日志以 JSON 格式写入文件。 -
local log_file_cache = ngx.shared.logs: 从共享字典logs中获取缓存的日志文件句柄。这可以避免每次请求都重新打开文件。 -
local today = os.date("%Y-%m-%d"): 获取当前日期,并将其格式化为"YYYY-MM-DD"的字符串形式,这样每天都有一个新的日志文件名。 -
local log_file = log_file_cache:get(today): 尝试从共享字典中获取当前日期对应的日志文件句柄。 -
if not log_file then: 如果日志文件句柄不存在,说明这是当天第一次写日志,因此我们需要打开一个新的日志文件。 -
log_file = io.open("/data/logs/access_api_" .. today .. ".json", "a"): 使用io.open打开一个新的日志文件,文件名包含当前日期。使用"a"模式以追加方式打开文件,这样不会覆盖已有的日志内容。 -
log_file_cache:set(today, log_file): 将新打开的日志文件句柄缓存到共享字典中,避免下次重复打开。 -
local log_line = cjson.encode({...}): 将日志信息封装成 Lua 表,并使用cjson.encode将其转换为 JSON 字符串。 -
log_file:write(log_line .. "\n"): 将生成的日志行写入文件,并添加换行符。 -
log_file:flush(): 将缓冲区的内容立即写入文件,以确保日志数据实时保存。
总结
- 权限问题: 需要确保 Nginx 对日志文件目录
/data/logs/有写权限。 cjson模块: 需要确认 OpenResty/Nginx 环境中已经安装了cjson模块。- 性能问题: 因为每个请求都涉及 Lua 脚本的执行,所以在高流量的生产环境中,性能开销需要评估。
配置文件完整示例
user app;
worker_processes 2;events {worker_connections 30000;
}http {include mime.types;default_type application/octet-stream;sendfile on;keepalive_timeout 65;charset utf-8;gzip on;lua_shared_dict logs 10m; # 用于缓存日志文件句柄# 定义日志格式log_format json escape=json '{"timestamp":"$time_iso8601",''"remote_addr": "$remote_addr", ''"referer": "$http_referer", ''"request": "$request", ''"status": $status, ''"bytes": $body_bytes_sent, ''"agent": "$http_user_agent", ''"x_forwarded": "$http_x_forwarded_for", ''"up_addr": "$upstream_addr",''"up_host": "$upstream_http_host",''"up_resp_time": "$upstream_response_time",''"request_time": "$request_time",''"request_GlobalId": "$http_GlobalId",''"response_GlobalId": "$sent_http_GlobalId",''"response_body": "$resp_body",''"request_body": "$request_body"''}';# 后端IP地址upstream api-prod {server 10.66.66.86:8501 max_fails=5 fail_timeout=30s;server 10.66.66.88:8501 max_fails=5 fail_timeout=30s;}server {listen 80;listen 7309;server_name api.xxxxxx.cn api-test.xxxxxx.cn;charset utf-8;lua_need_request_body on;# 获取响应体内容set $resp_body "";body_filter_by_lua 'local resp_body = string.sub(ngx.arg[1], 1, 1000)ngx.ctx.buffered = (ngx.ctx.buffered or "") .. resp_bodyif ngx.arg[2] thenngx.var.resp_body = ngx.ctx.bufferedend';# 动态生成日志文件路径并写入日志log_by_lua_block {local cjson = require "cjson"local log_file_cache = ngx.shared.logslocal today = os.date("%Y-%m-%d")local log_file = log_file_cache:get(today)-- 仅记录非 SLB 健康检查的日志if not ngx.var.http_user_agent:match("SLBHealthCheck") thenif not log_file thenlog_file = io.open("/data/logs/access_api_xxxxxx_" .. today .. ".json", "a")log_file_cache:set(today, log_file)endlocal log_line = cjson.encode({timestamp = ngx.var.time_iso8601,remote_addr = ngx.var.remote_addr,referer = ngx.var.http_referer,request = ngx.var.request,status = ngx.var.status,bytes = ngx.var.body_bytes_sent,agent = ngx.var.http_user_agent,x_forwarded = ngx.var.http_x_forwarded_for,up_addr = ngx.var.upstream_addr,up_host = ngx.var.upstream_http_host,up_resp_time = ngx.var.upstream_response_time,request_time = ngx.var.request_time,request_GlobalId = ngx.var.http_GlobalId,response_GlobalId = ngx.var.sent_http_GlobalId,response_body = ngx.var.resp_body,request_body = ngx.var.request_body})log_file:write(log_line .. "\n")log_file:flush()end}location / {proxy_pass http://api-prod;}error_log /data/logs/api-error.log error;}
}
查看是否在日志文件路径下,日志文件有按照天的维度来分割,观察日志是按照天的维度来分割的,配置完成。验证完成.(这里为了验证服务修改一下时间,观察nginx是否会生成新的日志文件)
[root@iZbp1axxxxxxxxp8q7ioZ logs]# sudo date 081615302024
Fri Aug 16 15:30:00 CST 2024
[root@iZbp1xxxxxxxxxq7ioZ logs]# ll
-rw-rw-rw- 1 app app 58961 Aug 13 15:30 access_api_2024-08-13.json
-rw-rw-rw- 1 app app 22945650 Aug 14 13:51 access_api_2024-08-14.json
-rw-rw-rw- 1 app app 10418 Aug 16 15:30 access_api_2024-08-16.json
相关文章:
Nginx日志按天分割
需求、日志按照天的单位进行分割存储。 如果你直接百度,可能会搜到很多教你用各种脚本或是三方插件来按天分割的,这边我用nginx服务本身来分割日志。 方法一 通过使用 $time_iso8601 变量和 map 指令,实现了日志文件按天分割的功能。以下是…...
文本摘要简介
文本摘要是从一段长文本中提取出最重要的信息,并生成一个简短而有意义的摘要。这个过程可以分为两种主要方法: 抽取式摘要(Extractive Summarization):从原文中直接提取出关键句子或段落,组成摘要…...
3.MySQL面试题之Redis 和 Mysql 如何保证数据一致性?
Redis 和 MySQL 数据一致性是分布式系统中的一个常见挑战。保证数据一致性通常涉及几种策略,我会详细解释这些策略并提供相应的代码示例。 先更新数据库,再更新缓存 这种方法先更新 MySQL,然后更新或删除 Redis 缓存。 Transactional publ…...
浅谈TCP协议、UDP协议
一、介绍说明 TCP(传输控制协议) 面向连接:TCP在数据传输之前必须建立连接。这通过一个称为三次握手的过程来完成,确保连接的两端都准备好进行数据传输。 可靠性:TCP提供可靠的数据传输,确保数据包正确无…...
SQL业务题: 从不订购的客户
1️⃣题目 Customers 表: ---------------------- | Column Name | Type | ---------------------- | id | int | | name | varchar | ---------------------- 在 SQL 中,id 是该表的主键。 该表的每一行都表示客户的 ID 和名…...
怎么直接在PDF上修改内容?随心编辑PDF内容
PDF(Portable Document Format)作为一种专用于阅读而非编辑的文档格式,其设计的核心目的是保持文档格式的一致性,确保文档在不同平台和设备上都能以相同的布局和格式呈现。然而,在实际工作和生活中,我们经常需要对PDF文档进行编辑…...
聊天室项目测试报告
项目介绍 本项目是一个基于Spring Boot框架开发的聊天室应用。一个实时的文本消息交流平台,允许多个用户同时在线聊天。系统采用了Spring Boot作为后端框架,集成了WebSocket技术以实现消息的实时推送与接收提供一个简单、易用且功能完备的在线聊天环境。…...
语音识别(实时语音转录)——funasr的详细部署和使用教程(包括实时语音转录)
阿里达摩院开源大型端到端语音识别工具包FunASR: FunASR提供了在大规模工业语料库上训练的模型,并能够将其部署到应用程序中。工具包的核心模型是Paraformer,这是一个非自回归的端到端语音识别模型,经过手动注释的普通话语音识别…...
【网络编程】TCP机械臂测试
通过w(红色臂角度增大)s(红色臂角度减小)d(蓝色臂角度增大)a(蓝色臂角度减小)按键控制机械臂 注意:关闭计算机的杀毒软件,电脑管家,防火墙 1)基于TCP服务器…...
笔记:在WPF中如何注册控件级全局事件和应用程序级全局事件
一、目的:在WPF中如何注册控件级全局事件和应用程序级全局事件 二、实现 应用程序级全局事件 //注册应用程序级全局事件 EventManager.RegisterClassHandler(typeof(Button), Button.ClickEvent, new RoutedEventHandler(ic_event_Click)); 如上代码既会注册全局…...
【Linux系列】telnet使用入门
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...
音视频相关知识
H.264编码格式 音频 PCM就是要把声音从模拟信号转换成数字信号的一种技术,他的原理简单地说就是利用一个固定的频率对模拟信号进行采样。 pcm是无损音频音频文件格式...
数据结构--第七天
递归 -递归的概念 递归其实就是一种解决问题的办法,在C语言中:递归就是函数自己调用自己 -递归的思想 递归的思考方式就是把大事化小的过程 递归的递就是递推的意思,归就是回归的意思 (递归是少量的代码完成大量的运算)…...
代码随想录Day34:62.不同路径、63.不同路径II、343.整数拆分、96.不同的二叉搜索树
62. 不同路径 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。 问总共有多少条不同的路径&…...
【信息学奥赛一本通】1008:计算(a+b)/c的值
1008:计算(ab)/c的值 时间限制: 1000 ms 内存限制: 66536 KB 提交数:164836 通过数: 142434 【题目描述】 给定3个整数a、b、c,计算表达式abc的值。 【输入】 输入仅一行,包括三个整数a、b、c, 数与数之间以一个空格分开。(-10,…...
使用 jstat 进行 Java 应用程序性能监控
jstat 使用经验笔记 1. 简介 jstat 是 Java 开发工具包 (JDK) 中的一个命令行工具,用于监控 Java 虚拟机 (JVM) 的运行时状态,特别是垃圾回收 (Garbage Collection, GC) 的行为。通过使用 jstat,你可以监控和诊断 Java 应用程序的内存使用情…...
Prompt指令调优大揭秘
Hey,技术达人们!今天咱们就来聊聊Prompt指令调优的那些事儿。想象一下,你有一个超级智能的AI小伙伴,但要让它更懂你,更给力,那就得靠点“魔法”——Prompt指令调优。准备好了吗?让我们一探究竟&…...
C语言中的⽂件操作
1. 为什么使⽤⽂件? 如果没有⽂件,我们写的程序的数据是存储在电脑的内存中,如果程序退出,内存回收,数据就丢失了,等再次运⾏程序,是看不到上次程序的数据的,如果要将数据进⾏持久化…...
黑马前端——days14_js
案例 1 页面框架文件 <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title>&l…...
【自动驾驶】ROS中参数服务器通信(c++)
目录 通信过程新建参数服务器包编写测试文件修改cmakelist:搭配launch文件启动测试及结果 通信过程 1.Talker 设置参数 Talker 通过 RPC 向参数服务器发送参数(包括参数名与参数值),ROS Master 将参数保存到参数列表中。 2.Listener 获取参数 Listener 通过 RPC 向…...
【kafka】Golang实现分布式Masscan任务调度系统
要求: 输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。 服务端程序: 从kafka消费者接收…...
微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...
RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...
python报错No module named ‘tensorflow.keras‘
是由于不同版本的tensorflow下的keras所在的路径不同,结合所安装的tensorflow的目录结构修改from语句即可。 原语句: from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后: from tensorflow.python.keras.lay…...
【生成模型】视频生成论文调研
工作清单 上游应用方向:控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...
【Redis】笔记|第8节|大厂高并发缓存架构实战与优化
缓存架构 代码结构 代码详情 功能点: 多级缓存,先查本地缓存,再查Redis,最后才查数据库热点数据重建逻辑使用分布式锁,二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...
通过 Ansible 在 Windows 2022 上安装 IIS Web 服务器
拓扑结构 这是一个用于通过 Ansible 部署 IIS Web 服务器的实验室拓扑。 前提条件: 在被管理的节点上安装WinRm 准备一张自签名的证书 开放防火墙入站tcp 5985 5986端口 准备自签名证书 PS C:\Users\azureuser> $cert New-SelfSignedCertificate -DnsName &…...
Python 训练营打卡 Day 47
注意力热力图可视化 在day 46代码的基础上,对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...
绕过 Xcode?使用 Appuploader和主流工具实现 iOS 上架自动化
iOS 应用的发布流程一直是开发链路中最“苹果味”的环节:强依赖 Xcode、必须使用 macOS、各种证书和描述文件配置……对很多跨平台开发者来说,这一套流程并不友好。 特别是当你的项目主要在 Windows 或 Linux 下开发(例如 Flutter、React Na…...
