当前位置: 首页 > news >正文

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()}

代码解释

  1. local cjson = require "cjson": 通过 require 加载 Lua 的 cjson 模块,用于将 Lua 表转换为 JSON 格式的字符串。这样可以把日志以 JSON 格式写入文件。

  2. local log_file_cache = ngx.shared.logs: 从共享字典 logs 中获取缓存的日志文件句柄。这可以避免每次请求都重新打开文件。

  3. local today = os.date("%Y-%m-%d"): 获取当前日期,并将其格式化为 "YYYY-MM-DD" 的字符串形式,这样每天都有一个新的日志文件名。

  4. local log_file = log_file_cache:get(today): 尝试从共享字典中获取当前日期对应的日志文件句柄。

  5. if not log_file then: 如果日志文件句柄不存在,说明这是当天第一次写日志,因此我们需要打开一个新的日志文件。

  6. log_file = io.open("/data/logs/access_api_" .. today .. ".json", "a"): 使用 io.open 打开一个新的日志文件,文件名包含当前日期。使用 "a" 模式以追加方式打开文件,这样不会覆盖已有的日志内容。

  7. log_file_cache:set(today, log_file): 将新打开的日志文件句柄缓存到共享字典中,避免下次重复打开。

  8. local log_line = cjson.encode({...}): 将日志信息封装成 Lua 表,并使用 cjson.encode 将其转换为 JSON 字符串。

  9. log_file:write(log_line .. "\n"): 将生成的日志行写入文件,并添加换行符。

  10. 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日志按天分割

需求、日志按照天的单位进行分割存储。 如果你直接百度&#xff0c;可能会搜到很多教你用各种脚本或是三方插件来按天分割的&#xff0c;这边我用nginx服务本身来分割日志。 方法一 通过使用 $time_iso8601 变量和 map 指令&#xff0c;实现了日志文件按天分割的功能。以下是…...

文本摘要简介

文本摘要是从一段长文本中提取出最重要的信息&#xff0c;并生成一个简短而有意义的摘要。这个过程可以分为两种主要方法&#xff1a; 抽取式摘要&#xff08;Extractive Summarization&#xff09;&#xff1a;从原文中直接提取出关键句子或段落&#xff0c;组成摘要…...

3.MySQL面试题之Redis 和 Mysql 如何保证数据一致性?

Redis 和 MySQL 数据一致性是分布式系统中的一个常见挑战。保证数据一致性通常涉及几种策略&#xff0c;我会详细解释这些策略并提供相应的代码示例。 先更新数据库&#xff0c;再更新缓存 这种方法先更新 MySQL&#xff0c;然后更新或删除 Redis 缓存。 Transactional publ…...

浅谈TCP协议、UDP协议

一、介绍说明 TCP&#xff08;传输控制协议&#xff09; 面向连接&#xff1a;TCP在数据传输之前必须建立连接。这通过一个称为三次握手的过程来完成&#xff0c;确保连接的两端都准备好进行数据传输。 可靠性&#xff1a;TCP提供可靠的数据传输&#xff0c;确保数据包正确无…...

SQL业务题: 从不订购的客户

1️⃣题目 Customers 表&#xff1a; ---------------------- | Column Name | Type | ---------------------- | id | int | | name | varchar | ---------------------- 在 SQL 中&#xff0c;id 是该表的主键。 该表的每一行都表示客户的 ID 和名…...

怎么直接在PDF上修改内容?随心编辑PDF内容

PDF(Portable Document Format)作为一种专用于阅读而非编辑的文档格式&#xff0c;其设计的核心目的是保持文档格式的一致性&#xff0c;确保文档在不同平台和设备上都能以相同的布局和格式呈现。然而&#xff0c;在实际工作和生活中&#xff0c;我们经常需要对PDF文档进行编辑…...

聊天室项目测试报告

项目介绍 本项目是一个基于Spring Boot框架开发的聊天室应用。一个实时的文本消息交流平台&#xff0c;允许多个用户同时在线聊天。系统采用了Spring Boot作为后端框架&#xff0c;集成了WebSocket技术以实现消息的实时推送与接收提供一个简单、易用且功能完备的在线聊天环境。…...

语音识别(实时语音转录)——funasr的详细部署和使用教程(包括实时语音转录)

阿里达摩院开源大型端到端语音识别工具包FunASR&#xff1a; FunASR提供了在大规模工业语料库上训练的模型&#xff0c;并能够将其部署到应用程序中。工具包的核心模型是Paraformer&#xff0c;这是一个非自回归的端到端语音识别模型&#xff0c;经过手动注释的普通话语音识别…...

【网络编程】TCP机械臂测试

通过w(红色臂角度增大)s&#xff08;红色臂角度减小&#xff09;d&#xff08;蓝色臂角度增大&#xff09;a&#xff08;蓝色臂角度减小&#xff09;按键控制机械臂 注意&#xff1a;关闭计算机的杀毒软件&#xff0c;电脑管家&#xff0c;防火墙 1&#xff09;基于TCP服务器…...

笔记:在WPF中如何注册控件级全局事件和应用程序级全局事件

一、目的&#xff1a;在WPF中如何注册控件级全局事件和应用程序级全局事件 二、实现 应用程序级全局事件 //注册应用程序级全局事件 EventManager.RegisterClassHandler(typeof(Button), Button.ClickEvent, new RoutedEventHandler(ic_event_Click)); 如上代码既会注册全局…...

【Linux系列】telnet使用入门

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...

音视频相关知识

H.264编码格式 音频 PCM就是要把声音从模拟信号转换成数字信号的一种技术&#xff0c;他的原理简单地说就是利用一个固定的频率对模拟信号进行采样。 pcm是无损音频音频文件格式...

数据结构--第七天

递归 -递归的概念 递归其实就是一种解决问题的办法&#xff0c;在C语言中&#xff1a;递归就是函数自己调用自己 -递归的思想 递归的思考方式就是把大事化小的过程 递归的递就是递推的意思&#xff0c;归就是回归的意思 &#xff08;递归是少量的代码完成大量的运算&#xff09…...

代码随想录Day34:62.不同路径、63.不同路径II、343.整数拆分、96.不同的二叉搜索树

62. 不同路径 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish” &#xff09;。 问总共有多少条不同的路径&…...

【信息学奥赛一本通】1008:计算(a+b)/c的值

1008&#xff1a;计算(ab)/c的值 时间限制: 1000 ms 内存限制: 66536 KB 提交数:164836 通过数: 142434 【题目描述】 给定3个整数a、b、c&#xff0c;计算表达式abc的值。 【输入】 输入仅一行&#xff0c;包括三个整数a、b、c, 数与数之间以一个空格分开。(&#xff0d;10,…...

使用 jstat 进行 Java 应用程序性能监控

jstat 使用经验笔记 1. 简介 jstat 是 Java 开发工具包 (JDK) 中的一个命令行工具&#xff0c;用于监控 Java 虚拟机 (JVM) 的运行时状态&#xff0c;特别是垃圾回收 (Garbage Collection, GC) 的行为。通过使用 jstat&#xff0c;你可以监控和诊断 Java 应用程序的内存使用情…...

Prompt指令调优大揭秘

Hey&#xff0c;技术达人们&#xff01;今天咱们就来聊聊Prompt指令调优的那些事儿。想象一下&#xff0c;你有一个超级智能的AI小伙伴&#xff0c;但要让它更懂你&#xff0c;更给力&#xff0c;那就得靠点“魔法”——Prompt指令调优。准备好了吗&#xff1f;让我们一探究竟&…...

C语言中的⽂件操作

1. 为什么使⽤⽂件&#xff1f; 如果没有⽂件&#xff0c;我们写的程序的数据是存储在电脑的内存中&#xff0c;如果程序退出&#xff0c;内存回收&#xff0c;数据就丢失了&#xff0c;等再次运⾏程序&#xff0c;是看不到上次程序的数据的&#xff0c;如果要将数据进⾏持久化…...

黑马前端——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 向参数服务器发送参数(包括参数名与参数值)&#xff0c;ROS Master 将参数保存到参数列表中。 2.Listener 获取参数 Listener 通过 RPC 向…...

手游刚开服就被攻击怎么办?如何防御DDoS?

开服初期是手游最脆弱的阶段&#xff0c;极易成为DDoS攻击的目标。一旦遭遇攻击&#xff0c;可能导致服务器瘫痪、玩家流失&#xff0c;甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案&#xff0c;帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...

React第五十七节 Router中RouterProvider使用详解及注意事项

前言 在 React Router v6.4 中&#xff0c;RouterProvider 是一个核心组件&#xff0c;用于提供基于数据路由&#xff08;data routers&#xff09;的新型路由方案。 它替代了传统的 <BrowserRouter>&#xff0c;支持更强大的数据加载和操作功能&#xff08;如 loader 和…...

shell脚本--常见案例

1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件&#xff1a; 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...

React19源码系列之 事件插件系统

事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

Web 架构之 CDN 加速原理与落地实践

文章目录 一、思维导图二、正文内容&#xff08;一&#xff09;CDN 基础概念1. 定义2. 组成部分 &#xff08;二&#xff09;CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 &#xff08;三&#xff09;CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 &#xf…...

Python ROS2【机器人中间件框架】 简介

销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...

【笔记】WSL 中 Rust 安装与测试完整记录

#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统&#xff1a;Ubuntu 24.04 LTS (WSL2)架构&#xff1a;x86_64 (GNU/Linux)Rust 版本&#xff1a;rustc 1.87.0 (2025-05-09)Cargo 版本&#xff1a;cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...

【若依】框架项目部署笔记

参考【SpringBoot】【Vue】项目部署_no main manifest attribute, in springboot-0.0.1-sn-CSDN博客 多一个redis安装 准备工作&#xff1a; 压缩包下载&#xff1a;http://download.redis.io/releases 1. 上传压缩包&#xff0c;并进入压缩包所在目录&#xff0c;解压到目标…...

Python 高级应用10:在python 大型项目中 FastAPI 和 Django 的相互配合

无论是python&#xff0c;或者java 的大型项目中&#xff0c;都会涉及到 自身平台微服务之间的相互调用&#xff0c;以及和第三发平台的 接口对接&#xff0c;那在python 中是怎么实现的呢&#xff1f; 在 Python Web 开发中&#xff0c;FastAPI 和 Django 是两个重要但定位不…...