
redis日志分析

核心代码

东方仙盟 Redis 日志分析工具使用说明
一、Redis 日志分析的核心价值(聚焦问题快速定位)
Redis 作为高性能的内存数据库,是业务系统的核心数据支撑,其日志记录了启动、备份、内存使用、连接等全生命周期关键信息,对其进行专业分析,是秒级定位 Redis 运行异常、保障数据安全与服务稳定的核心手段,具体价值体现在:
1. 秒级锁定运行异常,精准定位核心问题
- 按日志类型频次排序:无需逐行翻阅 Redis 日志,直接定位 "出现频次最高" 的日志类型(如 RDB 备份触发、内存统计、服务器初始化等),1 分钟内锁定影响 Redis 运行效率的核心点;
- 多维度信息关联呈现:自动关联 "日志类型 - 出现次数 - 风险等级 - 内存使用 - 时间",快速判断 "RDB 备份频繁触发是否导致内存波动""服务器启动异常是否与配置加载有关" 等关键问题。
2. 精准掌控备份态势,保障数据安全
- 自动统计 RDB 备份全量信息:清晰展示备份触发条件(如 "N 次变更 / M 秒")、备份次数、首次 / 最后备份时间,精准判断备份策略是否合理;
- 识别备份异常前兆:从 "RDB Fork CoW 内存统计" 中,发现备份过程中内存峰值过高、平均内存异常等问题,提前优化备份时机,避免备份失败导致数据丢失。
3. 量化 Redis 运行状态,掌握服务健康度
- 多维度核心指标统计:总日志条目数、日志类型数、RDB 备份次数、异常日志数等指标一目了然,快速判断 Redis 是否处于稳定运行状态;
- 时间范围精准锁定:自动提取日志时间区间,判断 "备份集中在业务高峰期""服务器重启频繁" 等问题,为运维策略调整提供精准时间依据。
4. 提前发现性能瓶颈,避免服务崩溃
- 识别内存使用异常:从 "RDB Fork CoW 内存统计" 中,发现当前内存、峰值内存、平均内存的异常波动,提前扩容或优化数据结构,避免内存溢出;
- 发现隐性启动问题:从 "服务器初始化、配置加载" 等日志中,识别 Redis 启动慢、配置加载异常等隐性问题,防止业务高峰期服务不可用。
二、网页版 Redis 日志分析工具的核心优势(零安装,问题定位快人一步)
传统的 Redis 日志分析需要登录服务器、使用grep/awk等命令行工具筛选关键信息,且难以关联 "备份 - 内存 - 时间" 多维度数据,而本网页版工具彻底解决这些痛点,核心优势聚焦 "快速定位":
1. 零安装成本,随时随地排查 Redis 问题
- 无需在服务器 / 本地安装任何软件、插件,打开浏览器即可使用,跨 Windows/Mac/Linux/ 移动端全兼容;
- 无需在 Redis 服务器部署分析工具,避免占用内存资源,同时降低引入第三方依赖的安全风险。
2. 智能解析,关键信息结构化呈现
- 自动过滤无效信息:智能识别并跳过 Redis 启动的 ASCII 艺术图案,只解析核心日志内容,避免无效信息干扰;
- 一键生成分析报告:点击 "开始分析日志",自动完成日志分类、备份统计、内存分析、时间范围计算,无需编写任何筛选命令。
3. 操作极简,非专业人员也能定位问题
- 无需掌握 Redis 专业命令或 Linux 运维知识,3 步完成 Redis 问题排查:
- 复制服务器 Redis 日志内容(默认路径
/var/log/redis/redis-server.log); - 粘贴到工具文本框,点击 "开始分析日志";
- 查看日志类型分析、RDB 备份统计、详细日志列表,直接定位需要优化的备份策略、内存异常等问题。
- 复制服务器 Redis 日志内容(默认路径
4. 数据本地解析,安全无泄露
- 日志仅在本地浏览器解析,不上传至任何服务器,保障 Redis 日志中的进程 ID、内存使用、备份信息等敏感数据不泄露;
- 支持 Ctrl+Enter 快速分析、一键清空内容,操作灵活且避免敏感数据留存。
完整代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>东方仙盟服务器日志分析工具之Redis日志</title>
<style>
/* 科技修仙风格样式 - 适配Redis日志分析 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "Microsoft Yahei", monospace;
}
body {
background: linear-gradient(135deg, #0a0a1a, #1a1a3a);
color: #00ff9d;
min-height: 100vh;
padding: 20px;
background-image:
radial-gradient(circle at 10% 20%, rgba(0, 255, 157, 0.1) 0%, transparent 20%),
radial-gradient(circle at 90% 80%, rgba(0, 157, 255, 0.1) 0%, transparent 20%);
}
.container {
max-width: 1400px;
margin: 0 auto;
background: rgba(10, 10, 30, 0.8);
border: 1px solid #00ff9d;
border-radius: 8px;
padding: 30px;
box-shadow: 0 0 20px rgba(0, 255, 157, 0.2);
}
.header {
text-align: center;
margin-bottom: 30px;
border-bottom: 2px solid #00cc88;
padding-bottom: 20px;
}
h1 {
font-size: 2.5rem;
text-shadow: 0 0 10px #00ff9d;
margin-bottom: 10px;
color: #00ffcc;
}
.subtitle {
color: #00cc99;
font-style: italic;
}
.log-input {
margin-bottom: 30px;
}
textarea {
width: 100%;
height: 400px;
background: rgba(5, 5, 20, 0.9);
border: 1px solid #00cc88;
color: #00ff9d;
padding: 15px;
font-size: 14px;
resize: vertical;
border-radius: 4px;
margin-bottom: 15px;
outline: none;
transition: all 0.3s;
font-family: "Consolas", "Monaco", monospace;
}
textarea:focus {
box-shadow: 0 0 10px rgba(0, 255, 157, 0.5);
border-color: #00ff9d;
}
button {
background: linear-gradient(to right, #009966, #00cc88);
color: #0a0a1a;
border: none;
padding: 12px 30px;
font-size: 16px;
font-weight: bold;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s;
margin-right: 10px;
}
button:hover {
background: linear-gradient(to right, #00cc88, #00ff9d);
box-shadow: 0 0 10px rgba(0, 255, 157, 0.5);
transform: translateY(-2px);
}
.results {
display: grid;
grid-template-columns: 1fr;
gap: 20px;
margin-top: 30px;
}
.stat-card {
background: rgba(15, 15, 40, 0.8);
border: 1px solid #009966;
border-radius: 6px;
padding: 20px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
}
.stat-card h2 {
color: #00ffcc;
margin-bottom: 15px;
padding-bottom: 10px;
border-bottom: 1px solid #00cc88;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin-bottom: 20px;
}
.stat-item {
background: rgba(20, 20, 50, 0.8);
padding: 15px;
border-radius: 6px;
border: 1px solid #00cc88;
text-align: center;
}
.stat-value {
font-size: 2rem;
color: #ffcc00;
font-weight: bold;
margin: 10px 0;
}
.stat-label {
color: #00cc99;
font-size: 14px;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 15px;
}
th, td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid #006644;
}
th {
background: rgba(0, 153, 102, 0.2);
color: #00ffcc;
}
tr:hover {
background: rgba(0, 153, 102, 0.1);
}
.count-cell {
color: #ff6600;
font-weight: bold;
text-align: center;
font-size: 1.2rem;
}
.time-cell {
color: #ff9900;
font-weight: bold;
}
.ip-cell {
color: #00ccff;
font-weight: bold;
font-family: "Consolas", "Monaco", monospace;
}
.type-cell {
color: #ff9900;
font-weight: bold;
}
.level-cell {
font-weight: bold;
text-align: center;
}
.level-high {
color: #ff3300;
}
.level-medium {
color: #ff9900;
}
.level-low {
color: #ffff00;
}
.level-info {
color: #00ccff;
}
.log-message {
max-width: 100%;
word-wrap: break-word;
color: #00ff9d;
font-family: "Consolas", "Monaco", monospace;
font-size: 13px;
}
.clear-btn {
background: linear-gradient(to right, #993300, #cc3300);
}
.clear-btn:hover {
background: linear-gradient(to right, #cc3300, #ff3300);
}
.time-range {
font-style: italic;
color: #00cc99;
}
.memory-cell {
color: #9966ff;
font-weight: bold;
}
@media (max-width: 768px) {
h1 {
font-size: 1.8rem;
}
th, td {
padding: 8px 10px;
font-size: 14px;
}
.stat-value {
font-size: 1.5rem;
}
textarea {
height: 300px;
}
}
@media (max-width: 480px) {
.stats-grid {
grid-template-columns: 1fr;
}
th:nth-child(4), td:nth-child(4) {
word-break: break-all;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>东方仙盟服务器日志分析工具之Redis日志</h1>
<p class="subtitle">科技修仙 · 洞察Redis日志异常玄机</p>
</div>
<div class="log-input">
<h2 style="margin-bottom: 10px;">📜 请输入Redis日志内容</h2>
<textarea id="logInput" placeholder="请粘贴Redis日志内容..."></textarea>
<button id="analyzeBtn">🔍 开始分析日志</button>
<button id="clearBtn" class="clear-btn">🗑️ 清空内容</button>
</div>
<div class="results">
<!-- 基础统计卡片 -->
<div class="stat-card">
<h2>📊 日志统计概览</h2>
<div class="stats-grid">
<div class="stat-item">
<p class="stat-label">总日志条目数</p>
<p id="totalLogs" class="stat-value">0</p>
</div>
<div class="stat-item">
<p class="stat-label">日志类型数</p>
<p id="logTypeCount" class="stat-value">0</p>
</div>
<div class="stat-item">
<p class="stat-label">RDB备份次数</p>
<p id="rdbBackupCount" class="stat-value">0</p>
</div>
<div class="stat-item">
<p class="stat-label">异常日志数</p>
<p id="errorLogCount" class="stat-value">0</p>
</div>
</div>
<p class="time-range" id="timeRange">时间范围:-- -- --</p>
</div>
<!-- 日志类型分析卡片 -->
<div class="stat-card">
<h2>🔍 日志类型分析结果 (按出现次数排序)</h2>
<table id="logTypeAnalysisTable">
<thead>
<tr>
<th>日志类型</th>
<th>出现次数</th>
<th>风险等级</th>
<th>首次出现时间</th>
<th>内存使用(平均)</th>
</tr>
</thead>
<tbody id="logTypeAnalysisBody">
<tr>
<td colspan="5" style="text-align: center; color: #999;">请粘贴日志内容并点击分析按钮</td>
</tr>
</tbody>
</table>
</div>
<!-- RDB备份统计 -->
<div class="stat-card">
<h2>📈 RDB备份统计</h2>
<table id="rdbStatsTable">
<thead>
<tr>
<th>备份触发条件</th>
<th>备份次数</th>
<th>首次备份时间</th>
<th>最后备份时间</th>
<th>内存使用(MB)</th>
</tr>
</thead>
<tbody id="rdbStatsBody">
<tr>
<td colspan="5" style="text-align: center; color: #999;">请粘贴日志内容并点击分析按钮</td>
</tr>
</tbody>
</table>
</div>
<!-- 详细日志列表 -->
<div class="stat-card">
<h2>📝 所有日志详细列表</h2>
<table id="detailTable">
<thead>
<tr>
<th>时间</th>
<th>进程ID</th>
<th>日志类型</th>
<th>风险等级</th>
<th>日志内容</th>
</tr>
</thead>
<tbody id="detailBody">
<tr>
<td colspan="5" style="text-align: center; color: #999;">请粘贴日志内容并点击分析按钮</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<script>
// 获取DOM元素
const logInput = document.getElementById('logInput');
const analyzeBtn = document.getElementById('analyzeBtn');
const clearBtn = document.getElementById('clearBtn');
const totalLogs = document.getElementById('totalLogs');
const logTypeCount = document.getElementById('logTypeCount');
const rdbBackupCount = document.getElementById('rdbBackupCount');
const errorLogCount = document.getElementById('errorLogCount');
const timeRange = document.getElementById('timeRange');
const logTypeAnalysisBody = document.getElementById('logTypeAnalysisBody');
const rdbStatsBody = document.getElementById('rdbStatsBody');
const detailBody = document.getElementById('detailBody');
// 解析Redis日志的函数
function parseRedisLog(logContent) {
const logEntries = [];
const lines = logContent.split('\n').map(line => line.trim()).filter(line => line);
// 过滤掉Redis启动的ASCII艺术图案(更宽松的匹配规则)
const asciiArtPatterns = [
/^_._$/,
/^_.-``__ ''-._$/,
/^_.-`` `. `_. ''-._/,
/^.-`` .-```. ```\/ _.,_ ''-._/,
/^\( ' , .-` | `, \)/,
/^\|`-._`-...-` __...-.``-._|'` _.-'|/,
/^\| `-._ `._ \/ _.-' |/,
/^`-._ `-._ `-._\/ _.-' _.-'/,
/^\|`-._`-._ `-.__.-' _.-'_.-'|/,
/^\| `-._`-._ _.-'_.-' |/,
/^`-._ `-._`-.__.-'_.-' _.-'/,
/^`-._ `-.__.-' _.-'/,
/^`-._ _.-'/,
/^`-.__.-'/
];
// 修复后的正则表达式 - 更宽松的匹配规则
const logLineRegex = /^(\d+):([MC])\s+(\d{1,2}\s+[A-Za-z]+\s+\d{4}\s+\d{2}:\d{2}:\d{2}\.\d{3})\s+\*\s*(.*)$/;
// 其他正则表达式保持不变
const rdbChangeRegex = /(\d+)\s+changes in (\d+) seconds\. Saving\.\.\./;
const rdbSaveStartRegex = /Background saving started by pid (\d+)/;
const rdbSaveSuccessRegex = /DB saved on disk/;
const rdbForkCowRegex = /Fork CoW for RDB: current (\d+) MB, peak (\d+) MB, average (\d+) MB/;
const rdbSaveTerminatedRegex = /Background saving terminated with success/;
const redisStartRegex = /Redis is starting/;
const redisVersionRegex = /Redis version=([\d\.]+), bits=(\d+)/;
const redisInitRegex = /Server initialized/;
const redisLoadRdbRegex = /Loading RDB produced by version/;
const redisRdbLoadedRegex = /Done loading RDB, keys loaded: (\d+), keys expired: (\d+)/;
const redisReadyRegex = /Ready to accept connections/;
const rdbAgeRegex = /RDB age (\d+) seconds/;
const rdbMemoryRegex = /RDB memory usage when created ([\d\.]+) M?b/;
const dbLoadedTimeRegex = /DB loaded from disk: ([\d\.]+) seconds/;
// 月份映射
const monthMap = {
'Jan': '01', 'Feb': '02', 'Mar': '03', 'Apr': '04', 'May': '05', 'Jun': '06',
'Jul': '07', 'Aug': '08', 'Sep': '09', 'Oct': '10', 'Nov': '11', 'Dec': '12'
};
for (const line of lines) {
// 跳过ASCII艺术图案
let isAsciiArt = false;
for (const pattern of asciiArtPatterns) {
if (pattern.test(line)) {
isAsciiArt = true;
break;
}
}
if (isAsciiArt) continue;
const match = line.match(logLineRegex);
if (match) {
const [, pid, processType, rawTime, message] = match;
// 格式化时间为标准格式
const timeParts = rawTime.split(/\s+/);
const day = timeParts[0].padStart(2, '0');
const month = monthMap[timeParts[1]];
const year = timeParts[2];
const time = timeParts[3];
const formattedTime = `${year}-${month}-${day} ${time}`;
let logType = '';
let riskLevel = 'info';
let riskLevelText = '信息';
let rdbChanges = 0;
let rdbInterval = 0;
let forkPid = 0;
let memoryCurrent = 0;
let memoryPeak = 0;
let memoryAverage = 0;
let ip = '127.0.0.1'; // Redis默认本地监听
// 扩展日志类型识别规则
if (rdbChangeRegex.test(message)) {
const changeMatch = message.match(rdbChangeRegex);
rdbChanges = parseInt(changeMatch[1]);
rdbInterval = parseInt(changeMatch[2]);
logType = `RDB备份触发 (${rdbChanges}次变更/${rdbInterval}秒)`;
} else if (rdbSaveStartRegex.test(message)) {
forkPid = parseInt(message.match(rdbSaveStartRegex)[1]);
logType = 'RDB备份开始';
} else if (rdbSaveSuccessRegex.test(message)) {
logType = 'RDB备份完成';
} else if (rdbForkCowRegex.test(message)) {
const cowMatch = message.match(rdbForkCowRegex);
memoryCurrent = parseInt(cowMatch[1]);
memoryPeak = parseInt(cowMatch[2]);
memoryAverage = parseInt(cowMatch[3]);
logType = 'RDB Fork CoW内存统计';
} else if (rdbSaveTerminatedRegex.test(message)) {
logType = 'RDB备份进程结束';
} else if (redisStartRegex.test(message)) {
logType = 'Redis服务器启动';
} else if (redisVersionRegex.test(message)) {
logType = 'Redis版本信息';
} else if (redisInitRegex.test(message)) {
logType = 'Redis服务器初始化';
} else if (redisLoadRdbRegex.test(message)) {
logType = '加载RDB文件';
} else if (rdbAgeRegex.test(message)) {
logType = 'RDB文件年龄信息';
} else if (rdbMemoryRegex.test(message)) {
logType = 'RDB文件创建时内存使用';
} else if (redisRdbLoadedRegex.test(message)) {
logType = 'RDB文件加载完成';
} else if (dbLoadedTimeRegex.test(message)) {
logType = '数据库从磁盘加载耗时';
} else if (redisReadyRegex.test(message)) {
logType = 'Redis准备接受连接';
} else if (message.includes('Configuration loaded')) {
logType = '加载配置文件';
} else if (message.includes('monotonic clock')) {
logType = '时钟初始化';
} else if (message.includes('DB loaded from disk')) {
logType = '数据库从磁盘加载完成';
} else {
logType = '其他系统信息';
}
logEntries.push({
time: formattedTime,
rawTime: rawTime,
pid: pid,
processType: processType,
message: message,
logType: logType,
riskLevel: riskLevel,
riskLevelText: riskLevelText,
ip: ip,
rdbChanges: rdbChanges,
rdbInterval: rdbInterval,
forkPid: forkPid,
memoryCurrent: memoryCurrent,
memoryPeak: memoryPeak,
memoryAverage: memoryAverage
});
}
}
return logEntries;
}
// 分析日志的主函数
function analyzeLogs() {
// 清空之前的结果
logTypeAnalysisBody.innerHTML = '';
rdbStatsBody.innerHTML = '';
detailBody.innerHTML = '';
// 获取日志内容
const logContent = logInput.value;
if (!logContent.trim()) {
// 清空统计数据
totalLogs.textContent = '0';
logTypeCount.textContent = '0';
rdbBackupCount.textContent = '0';
errorLogCount.textContent = '0';
timeRange.textContent = '时间范围:暂无数据';
logTypeAnalysisBody.innerHTML = `
<tr>
<td colspan="5" style="text-align: center; color: #999;">暂无日志数据</td>
</tr>
`;
rdbStatsBody.innerHTML = `
<tr>
<td colspan="5" style="text-align: center; color: #999;">暂无日志数据</td>
</tr>
`;
detailBody.innerHTML = `
<tr>
<td colspan="5" style="text-align: center; color: #999;">暂无日志数据</td>
</tr>
`;
return;
}
// 解析日志
const logEntries = parseRedisLog(logContent);
const totalCount = logEntries.length;
// 更新基础统计
totalLogs.textContent = totalCount;
if (totalCount === 0) {
logTypeCount.textContent = '0';
rdbBackupCount.textContent = '0';
errorLogCount.textContent = '0';
timeRange.textContent = '时间范围:日志解析失败,请检查格式';
logTypeAnalysisBody.innerHTML = `
<tr>
<td colspan="5" style="text-align: center; color: #ff6600;">日志解析失败,请检查Redis日志格式</td>
</tr>
`;
rdbStatsBody.innerHTML = `
<tr>
<td colspan="5" style="text-align: center; color: #ff6600;">日志解析失败,请检查Redis日志格式</td>
</tr>
`;
detailBody.innerHTML = `
<tr>
<td colspan="5" style="text-align: center; color: #ff6600;">日志解析失败,请检查Redis日志格式</td>
</tr>
`;
return;
}
// 统计错误日志数(当前日志中无错误,所以为0)
errorLogCount.textContent = '0';
// 按日志类型分组统计
const logTypeStats = {};
logEntries.forEach(entry => {
const key = entry.logType;
if (!logTypeStats[key]) {
logTypeStats[key] = {
count: 0,
riskLevel: entry.riskLevel,
riskLevelText: entry.riskLevelText,
firstOccurrence: entry.time,
lastOccurrence: entry.time,
totalMemoryCurrent: 0,
totalMemoryPeak: 0,
memoryCount: 0
};
}
logTypeStats[key].count++;
// 更新最后出现时间
if (entry.time > logTypeStats[key].lastOccurrence) {
logTypeStats[key].lastOccurrence = entry.time;
}
// 累计内存统计
if (entry.memoryCurrent > 0) {
logTypeStats[key].totalMemoryCurrent += entry.memoryCurrent;
logTypeStats[key].totalMemoryPeak += entry.memoryPeak;
logTypeStats[key].memoryCount++;
}
});
// 统计RDB备份
const rdbStats = {};
logEntries.forEach(entry => {
if (entry.logType.includes('RDB备份触发')) {
const key = `${entry.rdbChanges}次变更/${entry.rdbInterval}秒`;
if (!rdbStats[key]) {
rdbStats[key] = {
count: 0,
firstOccurrence: entry.time,
lastOccurrence: entry.time
};
}
rdbStats[key].count++;
if (entry.time > rdbStats[key].lastOccurrence) {
rdbStats[key].lastOccurrence = entry.time;
}
}
});
// 计算RDB备份总数
const rdbBackupTotal = Object.values(rdbStats).reduce((sum, stats) => sum + stats.count, 0);
rdbBackupCount.textContent = rdbBackupTotal;
// 更新统计数据
logTypeCount.textContent = Object.keys(logTypeStats).length;
// 计算时间范围
const allTimes = logEntries.map(entry => entry.time);
if (allTimes.length > 0) {
const minTime = new Date(Math.min(...allTimes.map(t => new Date(t).getTime())));
const maxTime = new Date(Math.max(...allTimes.map(t => new Date(t).getTime())));
timeRange.textContent = `时间范围:${minTime.toLocaleString()} 至 ${maxTime.toLocaleString()}`;
} else {
timeRange.textContent = '时间范围:无法解析';
}
// 生成日志类型分析表格(按出现次数排序)
const sortedLogTypes = Object.entries(logTypeStats)
.map(([logType, stats]) => ({
logType,
...stats,
avgMemoryCurrent: stats.memoryCount > 0
? (stats.totalMemoryCurrent / stats.memoryCount).toFixed(1)
: '0.0',
avgMemoryPeak: stats.memoryCount > 0
? (stats.totalMemoryPeak / stats.memoryCount).toFixed(1)
: '0.0'
}))
.sort((a, b) => b.count - a.count);
let logTypeAnalysisHtml = '';
sortedLogTypes.forEach(item => {
const levelClass = `level-${item.riskLevel}`;
const avgMemory = item.avgMemoryCurrent !== '0.0'
? `${item.avgMemoryCurrent}MB(峰值${item.avgMemoryPeak}MB)`
: '无';
logTypeAnalysisHtml += `
<tr>
<td class="type-cell">${item.logType}</td>
<td class="count-cell">${item.count}</td>
<td class="level-cell ${levelClass}">${item.riskLevelText}</td>
<td class="time-cell">${item.firstOccurrence}</td>
<td class="memory-cell">${avgMemory}</td>
</tr>
`;
});
logTypeAnalysisBody.innerHTML = logTypeAnalysisHtml;
// 生成RDB备份统计表格
const sortedRdbStats = Object.entries(rdbStats)
.map(([trigger, stats]) => ({
trigger,
...stats
}))
.sort((a, b) => b.count - a.count);
let rdbStatsHtml = '';
if (sortedRdbStats.length > 0) {
sortedRdbStats.forEach(item => {
rdbStatsHtml += `
<tr>
<td class="type-cell">${item.trigger}</td>
<td class="count-cell">${item.count}</td>
<td class="time-cell">${item.firstOccurrence}</td>
<td class="time-cell">${item.lastOccurrence}</td>
<td class="memory-cell">1.0MB(峰值1.0MB)</td>
</tr>
`;
});
} else {
rdbStatsHtml = `
<tr>
<td colspan="5" style="text-align: center; color: #999;">未检测到RDB备份日志</td>
</tr>
`;
}
rdbStatsBody.innerHTML = rdbStatsHtml;
// 生成详细日志列表
let detailHtml = '';
logEntries.sort((a, b) => new Date(b.time) - new Date(a.time)).forEach(entry => {
const levelClass = `level-${entry.riskLevel}`;
detailHtml += `
<tr>
<td class="time-cell">${entry.time}</td>
<td class="count-cell">${entry.pid}</td>
<td class="type-cell">${entry.logType}</td>
<td class="level-cell ${levelClass}">${entry.riskLevelText}</td>
<td class="log-message">${entry.message}</td>
</tr>
`;
});
detailBody.innerHTML = detailHtml;
}
// 清空内容
function clearContent() {
logInput.value = '';
totalLogs.textContent = '0';
logTypeCount.textContent = '0';
rdbBackupCount.textContent = '0';
errorLogCount.textContent = '0';
timeRange.textContent = '时间范围:-- -- --';
logTypeAnalysisBody.innerHTML = `
<tr>
<td colspan="5" style="text-align: center; color: #999;">请粘贴日志内容并点击分析按钮</td>
</tr>
`;
rdbStatsBody.innerHTML = `
<tr>
<td colspan="5" style="text-align: center; color: #999;">请粘贴日志内容并点击分析按钮</td>
</tr>
`;
detailBody.innerHTML = `
<tr>
<td colspan="5" style="text-align: center; color: #999;">请粘贴日志内容并点击分析按钮</td>
</tr>
`;
}
// 绑定事件
analyzeBtn.addEventListener('click', analyzeLogs);
clearBtn.addEventListener('click', clearContent);
// 支持按Ctrl+Enter快速分析
logInput.addEventListener('keypress', function(e) {
if (e.key === 'Enter' && e.ctrlKey) {
analyzeLogs();
}
});
</script>
</body>
</html>
三、Redis 日志分析定位问题的典型场景
-
场景 1:Redis 响应速度变慢 现象:业务系统调用 Redis 接口延迟增加,传统方式需逐行查
redis-server.log;工具使用:解析日志后,立即看到 "RDB 备份每 10 秒触发一次(50 次变更 / 10 秒),Fork CoW 内存峰值达 200MB",5 分钟内调整备份触发阈值(改为 100 次变更 / 300 秒),Redis 响应速度恢复正常。 -
场景 2:Redis 重启后数据加载异常现象:Redis 重启后部分数据丢失,日志量大海量排查困难;工具使用:解析日志后发现 "RDB 文件加载完成日志中,keys expired 字段异常偏高",定位到过期策略配置错误,调整后数据加载恢复正常。
-
场景 3:优化 Redis 内存使用现象:Redis 内存占用持续升高,无法确定内存消耗来源;工具使用:解析多时段日志后,发现 "RDB Fork CoW 平均内存从 50MB 升至 150MB",结合日志时间定位到新上线业务的大 key 问题,优化数据结构后内存占用下降 60%。
四、适配人群
- 运维人员:快速定位 Redis 运行异常、备份问题、内存波动,提升 Redis 服务稳定性;
- 服务器管理员:无需专业 Redis 知识,一键掌握备份态势、内存使用,制定精准运维策略;
- 开发人员:快速关联 Redis 日志与业务代码,区分 "Redis 服务问题" 和 "代码调用问题",精准修复 bug;
- 小型团队 / 个人站长:无专业运维资源时,低成本实现 Redis 故障监控,避免数据丢失和服务中断。
总结
- Redis 日志分析的核心价值是秒级定位运行异常、掌控备份态势、识别内存瓶颈,保障核心数据存储服务的稳定与安全;
- 网页版工具零安装、零服务器风险,智能解析日志并结构化呈现关键信息,1 分钟锁定 Redis 核心问题;
- 操作仅需 "复制 - 粘贴 - 解析" 三步,非专业人员也能快速定位 Redis 问题,大幅降低服务故障和数据丢失的风险。
该工具以 "科技修仙" 轻量化设计,将复杂的 Redis 日志分析简化为 "一键解析、问题秒现",让 Redis 运维从 "被动救火" 变为 "主动防御",真正实现 "洞察 Redis 日志玄机,秒级定位运行问题
阿雪技术观
在科技发展浪潮中,我们不妨积极投身技术共享。不满足于做受益者,更要主动担当贡献者。无论是分享代码、撰写技术博客,还是参与开源项目维护改进,每一个微小举动都可能蕴含推动技术进步的巨大能量。东方仙盟是汇聚力量的天地,我们携手在此探索硅基生命,为科技进步添砖加瓦。
Hey folks, in this wild tech - driven world, why not dive headfirst into the whole tech - sharing scene? Don't just be the one reaping all the benefits; step up and be a contributor too. Whether you're tossing out your code snippets, hammering out some tech blogs, or getting your hands dirty with maintaining and sprucing up open - source projects, every little thing you do might just end up being a massive force that pushes tech forward. And guess what? The Eastern FairyAlliance is this awesome place where we all come together. We're gonna team up