方案一:标准生产环境(拥有 Sudo/Root 权限)
创建专用账户
# 创建一个没有登录权限的用户 app_user
sudo useradd -r -s /bin/false app_user
# 确保该用户对 jar 包有读取权限
sudo chown app_user:app_user /opt/prod/app.jar
编写 Service 单元文件
Systemd 通过 .service 文件管理进程。创建文件 /etc/systemd/system/myapp.service。
# /etc/systemd/system/myapp-user.service
# ============================================================================
# Spring Boot 应用 Systemd 服务配置文件
# 文件位置: /etc/systemd/system/myapp-user.service
# 创建时间: $(date)
# 作者: 系统管理员
# ============================================================================
[Unit]
# 单元描述信息
Description=My SpringBoot Enterprise App - User Service
# 文档链接(可选)
Documentation=https://company.com/docs/myapp
# man:myapp-user(8) # 如果有 man page
# 定义启动顺序依赖关系
# 在以下服务/目标启动后再启动本服务
After=syslog.target network.target
# 希望这些服务也启动(弱依赖)
Wants=network-online.target
# 需要这些服务必须启动(强依赖)
Requires=network-online.target
# Before=some-other.service # 本服务在其他服务之前启动
# Conflicts=some-service.service # 与本服务冲突的服务
[Service]
# ========================= 基础配置 =========================
# 服务类型:
# simple : ExecStart 的进程是主进程(推荐用于 Spring Boot)
# forking : ExecStart 启动后会 fork 子进程,父进程退出
# notify : 服务启动完成后发送通知信号
# dbus : 通过 D-Bus 启动
Type=simple
# ========================= 用户和权限 =========================
# 运行服务的用户和组(必须提前创建)
User=app_user
Group=app_user
# 文件创建掩码(0027 = 所有者rwx,组r-x,其他---)
UMask=0027
# ========================= 工作目录和环境变量 =========================
# 服务的工作目录(应用的根目录)
WorkingDirectory=/opt/share/app/demo
# 设置环境变量
Environment="JAVA_HOME=/usr/lib/jvm/java-1.8.0"
Environment="SPRING_PROFILES_ACTIVE=prod"
Environment="LOG_PATH=/var/log/myapp-user"
# 可以使用 EnvironmentFile 替代单独的环境变量
# EnvironmentFile=/etc/default/myapp-user
# ========================= 启动命令 =========================
# ExecStart: 启动服务的主命令
# 重要:每行结尾的反斜杠(\)表示续行,确保格式正确
ExecStart=/usr/bin/java \
# JVM 模式选择
-server \
# 堆内存设置
-Xms512m `# 初始堆大小` \
-Xmx512m `# 最大堆大小` \
-Xmn256m `# 年轻代大小` \
# G1 垃圾回收器配置
-XX:+UseG1GC `# 使用 G1 垃圾回收器` \
-XX:MaxGCPauseMillis=200 `# 目标最大 GC 暂停时间` \
-XX:G1ReservePercent=25 `# G1 保留内存百分比` \
-XX:InitiatingHeapOccupancyPercent=30 `# G1 触发混合 GC 的堆占用率` \
# GC 日志配置
-verbose:gc `# 启用详细 GC 日志` \
-Xloggc:/opt/share/app/demo/logs/gc-%t.log `# GC 日志文件路径,%t 是时间戳` \
-XX:+PrintGCDetails `# 打印详细的 GC 信息` \
-XX:+PrintGCDateStamps `# 在 GC 日志中添加日期时间戳` \
-XX:+PrintGCTimeStamps `# 打印 GC 时间戳(相对于 JVM 启动)` \
-XX:+UseGCLogFileRotation `# 启用 GC 日志轮转` \
-XX:NumberOfGCLogFiles=10 `# 保留的 GC 日志文件数` \
-XX:GCLogFileSize=100M `# 每个 GC 日志文件的最大大小` \
# 内存溢出处理
-XX:+HeapDumpOnOutOfMemoryError `# 发生 OOM 时生成堆转储` \
-XX:HeapDumpPath=/opt/share/app/demo/logs/heapdump.hprof `# 堆转储文件路径` \
# 系统属性
-Djava.net.preferIPv4Stack=true `# 优先使用 IPv4 栈` \
-Dfile.encoding=UTF-8 `# 文件编码` \
-Dproject.home=/opt/share/app/demo `# 项目主目录` \
-Dspring.config.location=file:/opt/share/app/demo/config/ `# Spring 配置文件位置` \
# 应用 JAR 文件
-jar /opt/share/app/demo/myapp-user.jar \
# Spring Boot 应用参数
--server.max-http-header-size=524288 `# HTTP 请求头最大大小` \
--logging.file.path=/var/log/myapp-user `# 日志文件路径` \
--logging.level.root=INFO `# 根日志级别`
# ExecStop: 停止服务的命令(可选,不设置时使用默认信号)
# ExecStop=/bin/kill -s TERM $MAINPID
# ExecReload: 重载配置的命令(可选)
# ExecReload=/bin/kill -s HUP $MAINPID
# ========================= 进程管理 =========================
# 成功退出状态码(143 = SIGTERM 信号,Java 优雅关闭的退出码)
SuccessExitStatus=143
# 重启策略:
# no : 不重启
# always : 总是重启
# on-success : 成功退出时重启
# on-failure : 失败时重启
# on-abnormal : 异常退出时重启
# on-abort : 收到中止信号时重启
# on-watchdog : 看门狗超时时重启
Restart=always
# 重启等待时间(秒)
RestartSec=10
# 启动频率限制:60秒内最多重启5次
StartLimitInterval=60
StartLimitBurst=5
# 启动超时时间(秒)
TimeoutStartSec=300
# 停止超时时间(秒)
TimeoutStopSec=30
# 停止时发送的信号
KillSignal=SIGTERM
# 停止模式:
# control-group : 停止整个控制组(默认)
# process : 只停止主进程
# mixed : 先发 SIGTERM,一段时间后发 SIGKILL
# none : 不停止,只执行 ExecStop 命令
KillMode=process
# 是否在超时后发送 SIGKILL
SendSIGKILL=yes
# 发送 SIGKILL 前的等待时间
SendSIGKILL=30s
# ========================= 日志配置 =========================
# 标准输出重定向到 syslog(文件系统/var/log)、journal(systemd 日志系统)(可用 journalctl 查看)
# 基本查看 sudo journalctl -u myapp.service
# 实时跟踪 sudo journalctl -u myapp.service -f
StandardOutput=journal
# 标准错误重定向到 journal
StandardError=journal
# 在系统日志中标识本服务的名称
SyslogIdentifier=myapp-user
# 日志目录(自动创建)
LogsDirectory=myapp-user
# 日志目录权限
LogsDirectoryMode=0750
# 可选:将日志输出到文件
# StandardOutput=append:/var/log/myapp-user/out.log
# StandardError=append:/var/log/myapp-user/err.log
# ========================= 安全加固 =========================
# 禁止进程获取新权限
NoNewPrivileges=true
# 使用私有临时目录(/tmp 和 /var/tmp)
PrivateTmp=true
# 文件系统保护级别:
# strict : 只允许访问明确声明的目录
# full : 保护所有系统目录
# partial : 保护部分系统目录
ProtectSystem=strict
# 保护家目录(不允许访问 /home, /root, /run/user)
ProtectHome=true
# 可读写的路径(应用需要写入的目录)
ReadWritePaths=/opt/share/app/demo/logs /opt/share/app/demo/data
# 只读路径(应用的配置和库文件)
ReadOnlyPaths=/opt/share/app/demo/config /opt/share/app/demo/lib
# 更多安全选项(可选):
# PrivateDevices=true # 私有设备命名空间
# ProtectKernelTunables=true # 保护内核调优参数
# ProtectKernelModules=true # 保护内核模块
# ProtectControlGroups=true # 保护控制组
# RestrictAddressFamilies=AF_INET AF_INET6 # 限制可用的地址族
# RestrictNamespaces=true # 限制命名空间
# LockPersonality=true # 锁定 personality()
# ========================= 资源限制 =========================
# 文件描述符最大数量
LimitNOFILE=65536
# 最大进程数
LimitNPROC=4096
# 核心转储文件大小限制(infinity = 无限制)
LimitCORE=infinity
# 最大内存使用量
MemoryMax=1G
# 最大内存+交换分区使用量
MemorySwapMax=2G
# CPU 限制(可选):
# CPUQuota=200% # 最多使用 2 个 CPU 核心的 100%
# CPUWeight=100 # CPU 权重(1-10000)
[Install]
# ========================= 安装配置 =========================
# 定义在哪些目标下启用本服务
# multi-user.target : 多用户命令行模式(无图形界面)
# graphical.target : 图形界面模式
WantedBy=multi-user.target
# 也可以指定多个目标
# WantedBy=multi-user.target graphical.target
# 可选:不希望与其他服务同时运行
# Also=myapp-other.service
激活与管理
# 1. 重载配置
sudo systemctl daemon-reload
# 2. 设置开机自启 (从此告别重启焦虑)
sudo systemctl enable myapp
# 3. 启动服务
sudo systemctl start myapp
方案二:内网受限环境(无 Root 权限)
开启“驻留模式”
默认情况下,用户退出 SSH,用户下的 Systemd 进程会被杀掉。
# 检查方法,如果输出 Linger=no,则需要在服务器上执行一次开启操作。
loginctl show-user $USER --property=Linger
# 开启用户驻留,允许服务在用户注销后继续运行
sudo loginctl enable-linger <你的用户名>
配置用户级服务
配置文件位置变了,放在 ~/.config/systemd/user/ 下。
mkdir -p ~/.config/systemd/user
vim ~/.config/systemd/user/myapp.service
配置差异点:
- 不需要
User= 和Group=字段。 [Install]部分改为WantedBy=default.target。
# ~/.config/systemd/user/myapp-user.service
# 位置:用户主目录下的 .config/systemd/user/
# 说明:这是用户级服务,不需要sudo权限
[Unit]
Description=My User Space Application - User Service
Documentation=https://example.com/docs
After=network.target
# 用户服务需要显式声明
Wants=default.target
[Service]
# ========== 用户和类型配置 ==========
Type=simple
# 继承当前用户的身份,不需要指定 User=
# WorkingDirectory 使用绝对路径或家目录相对路径
WorkingDirectory=/opt/share/app/demo
# ========== 环境变量配置 ==========
# 设置 JAVA_HOME,使用系统 Java 或用户安装的 Java
Environment="JAVA_HOME=/usr/lib/jvm/java-1.8.0"
# 或者如果只有用户有 Java:
# Environment="JAVA_HOME=/home/zml/.jdks/corretto-1.8.0_412"
# Spring Boot 环境
Environment="SPRING_PROFILES_ACTIVE=dev"
Environment="SPRING_CONFIG_LOCATION=file:/opt/share/app/demo/config/"
Environment="LOG_PATH=/home/zml/logs/myapp"
# ========== 启动命令(修正版) ==========
# 使用环境变量中的 JAVA_HOME
ExecStart=${JAVA_HOME}/bin/java \
# JVM 基础参数
-Xms512m \
-Xmx512m \
-Xmn256m \
# GC 配置(简化版,移除废弃参数)
-XX:+UseG1GC \
-verbose:gc \
-Xloggc:/home/zml/logs/myapp/gc.log \
-XX:+PrintGCDetails \
-XX:+PrintGCDateStamps \
-XX:+UseGCLogFileRotation \
-XX:NumberOfGCLogFiles=10 \
-XX:GCLogFileSize=100M \
# 内存溢出处理
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/home/zml/logs/myapp/heapdump.hprof \
# 系统属性
-Dproject.home=/opt/share/app/demo \
-Dfile.encoding=UTF-8 \
# 应用 JAR
-jar /opt/share/app/demo/myapp-user.jar \
# Spring Boot 参数
--server.max-http-header-size=524288
# ========== 进程管理 ==========
SuccessExitStatus=143
Restart=always
RestartSec=10
# 用户服务需要更长的启动超时
TimeoutStartSec=180
# 用户进程退出后保持服务活动
RemainAfterExit=no
# ========== 日志配置 ==========
# 用户服务推荐输出到文件,而不是 syslog
StandardOutput=append:/home/zml/logs/myapp/out.log
StandardError=append:/home/zml/logs/myapp/err.log
# 或者使用 journal(需要启用用户级 journal)
# StandardOutput=journal
# StandardError=journal
# SyslogIdentifier=myapp-user
# ========== 限制配置(用户级) ==========
# 用户服务的资源限制
LimitNOFILE=65536
LimitNPROC=4096
[Install]
# 用户级服务使用 default.target
WantedBy=default.target
# 可以创建别名
# Also=myapp-user.socket # 如果有 socket 配置
用户级管理命令
所有命令加上 <span leaf="">--user</span> 参数即可,完全不需要 sudo。
# 用户级服务管理(注意 --user 参数)
systemctl --user daemon-reload
systemctl --user start myapp-user
systemctl --user stop myapp-user
systemctl --user restart myapp-user
systemctl --user status myapp-user
systemctl --user enable myapp-user
systemctl --user disable myapp-user
# 查看日志
journalctl --user -u myapp-user -f
journalctl --user -u myapp-user --since today
简化配置
# ~/.config/systemd/user/myapp-dev.service
# ============================================================================
# 用户级 Systemd 服务配置文件(简化版)
# 位置:~/.config/systemd/user/myapp-dev.service
# 作用:在当前用户下管理 Spring Boot 应用,无需 root 权限
# ============================================================================
[Unit]
# 服务描述,显示在 systemctl status 中
Description=MyApp Development Instance
# 定义启动顺序依赖
# network.target:等待网络服务就绪后启动
After=network.target
# 可选:可以添加更多依赖
# After=redis.service mysql.service # 如果需要数据库
# Requires=redis.service # 强依赖(必须启动)
# Wants=mysql.service # 弱依赖(希望启动)
[Service]
# ====================== 1. 基础配置 ======================
# 服务类型说明:
# simple : ExecStart 的进程是主进程(Spring Boot 推荐)
# forking : ExecStart 启动后 fork,父进程退出(传统守护进程)
# notify : 服务启动后发送通知信号
# idle : 等待其他任务完成后启动
Type=simple
# 工作目录:应用启动时的当前目录
# 所有相对路径(如配置文件)都基于此目录
WorkingDirectory=/home/zml/projects/myapp
# ====================== 2. 环境变量配置(必须在 ExecStart 之前) ======================
# 可选:设置环境变量(适用于敏感信息或配置)
# Environment="SPRING_PROFILES_ACTIVE=dev"
# Environment="DATABASE_URL=localhost:3306"
# Environment="JAVA_HOME=/usr/lib/jvm/java-11"
# 可选:从文件加载环境变量
# EnvironmentFile=/home/zml/.config/myapp/env.conf
# ====================== 3. 启动命令(使用环境变量) ======================
# ExecStart=${JAVA_HOME}/bin/java -jar app.jar
# ExecStart:服务启动的主命令
# 使用绝对路径或已在 PATH 中的命令
ExecStart=/usr/bin/java -jar target/myapp-1.0.0.jar
# 可选:如果需要更多 JVM 参数
# ExecStart=/usr/bin/java -Xms512m -Xmx1024m -jar target/myapp-1.0.0.jar
# 可选:如果需要指定配置文件
# ExecStart=/usr/bin/java -jar target/myapp-1.0.0.jar --spring.config.location=file:./config/
# 可选:端口绑定(用户服务只能用 1024 以上端口)
# ExecStart=/usr/bin/java -jar target/myapp-1.0.0.jar --server.port=8080
# ===================== 4. 其他配置(在 ExecStart 之后)=====================
# ====================== 进程管理 ======================
# 重启策略:
# no : 不重启
# on-success : 只有正常退出时重启(退出码为0)
# on-failure : 非正常退出时重启(退出码非0)
# on-abnormal: 被信号终止时重启
# on-abort : 收到中止信号时重启
# on-watchdog: 看门狗超时时重启
# always : 总是重启(推荐用于服务)
Restart=on-failure
# 重启前等待时间(秒)
# 避免频繁重启导致系统负载过高
RestartSec=10
# 可选:启动超时时间(秒)
# TimeoutStartSec=180
# 可选:停止超时时间(秒)
# TimeoutStopSec=30
# ====================== 日志配置 ======================
# 日志输出方式:
# journal : 输出到 systemd journal(推荐,便于管理)
# syslog : 输出到传统 syslog
# kmsg : 输出到内核日志
# null : 丢弃输出
# append:/path : 追加到文件
StandardOutput=journal
StandardError=journal
# 可选:在系统日志中标识服务名称
# SyslogIdentifier=myapp-dev
# 可选:输出到文件(便于调试)
# StandardOutput=append:/home/zml/logs/myapp/out.log
# StandardError=append:/home/zml/logs/myapp/err.log
[Install]
# ====================== 安装配置 ======================
# 指定服务在哪个 target 下启用
# default.target : 用户默认目标(相当于 multi-user.target 的用户级版本)
WantedBy=default.target
# 可选:同时启用到多个 target
# WantedBy=default.target graphical-session.target
# 可选:关联其他服务
# Also=myapp-dev.socket # 如果同时有 socket 服务
Journal 日志
一、Journal 日志存储位置
1. 默认存储位置
# 系统级日志(需要root权限)
/var/log/journal/ # 持久化存储(默认)
/run/log/journal/ # 临时存储(重启消失)
# 用户级日志(当前用户)
~/.local/share/journal/ # 用户持久化存储
/run/user/$UID/journal/ # 用户临时存储
2. 查看实际存储位置
# 查看 journal 配置
sudo cat /etc/systemd/journald.conf
# 查看存储方式
sudo journalctl --disk-usage
# 查看文件位置
sudo ls -lh /var/log/journal/
sudo ls -lh ~/.local/share/journal/ 2>/dev/null || echo "用户日志未启用"
# 查看详细的存储信息
sudo journalctl --list-boots
sudo journalctl --header | grep -i storage
3. 配置存储位置 (/etc/systemd/journald.conf)
[Journal]
# 存储方式:
# persistent : 持久化到磁盘(默认)
# volatile : 只存内存(重启消失)
# auto : 有磁盘就持久化,否则存内存
# none : 不存储(只转发到其他服务)
Storage=persistent
# 存储路径(默认不用改)
#SystemMaxUse= # 系统日志最大占用空间
#SystemKeepFree= # 系统保留空间
#SystemMaxFileSize= # 单个日志文件最大大小
#RuntimeMaxUse= # 运行时最大占用
#RuntimeKeepFree= # 运行时保留空间
#RuntimeMaxFileSize= # 运行时单个文件最大大小
# 压缩日志
#Compress=yes # 是否压缩旧日志
#Seal=yes # 是否加密日志
二、查看日志操作命令
1. 基础查看命令
# 查看某个服务的所有日志
sudo journalctl -u myapp.service
# 实时跟踪(类似 tail -f)
sudo journalctl -u myapp.service -f
# 查看最近日志(默认显示所有)
sudo journalctl -u myapp.service -n 50 # 最近50行
sudo journalctl -u myapp.service --since "10 minutes ago"
sudo journalctl -u myapp.service --since today
sudo journalctl -u myapp.service --since "2024-01-01" --until "2024-01-02"
# 按时间范围
sudo journalctl -u myapp.service -S "09:00" -U "17:00" # 今天9点到17点
sudo journalctl -u myapp.service --since "yesterday"
sudo journalctl -u myapp.service --since "1 hour ago"
2. 按优先级过滤
# 查看紧急程度(从0到7)
sudo journalctl -u myapp.service -p emerg # 0: 紧急
sudo journalctl -u myapp.service -p alert # 1: 警报
sudo journalctl -u myapp.service -p crit # 2: 严重
sudo journalctl -u myapp.service -p err # 3: 错误(最常用)
sudo journalctl -u myapp.service -p warning # 4: 警告
sudo journalctl -u myapp.service -p notice # 5: 通知
sudo journalctl -u myapp.service -p info # 6: 信息
sudo journalctl -u myapp.service -p debug # 7: 调试
# 查看错误及以上级别
sudo journalctl -u myapp.service -p err..emerg
3. 结构化查询(强大功能)
# 按字段查询(journal 独有的强大功能)
sudo journalctl _SYSTEMD_UNIT=myapp.service # 按服务名
sudo journalctl _PID=1234 # 按进程ID
sudo journalctl _UID=1000 # 按用户ID
sudo journalctl _COMM=java # 按命令名
sudo journalctl _EXE=/usr/bin/java # 按可执行路径
sudo journalctl _HOSTNAME=server1 # 按主机名
sudo journalctl _TRANSPORT=stdout # 按传输方式
# 组合查询
sudo journalctl _SYSTEMD_UNIT=myapp.service _PID=1234
sudo journalctl _SYSTEMD_UNIT=myapp.service + _UID=1000
4. 输出格式控制
# 不同输出格式
sudo journalctl -u myapp.service -o short # 简短格式(默认)
sudo journalctl -u myapp.service -o short-iso # ISO时间格式
sudo journalctl -u myapp.service -o short-precise # 精确时间
sudo journalctl -u myapp.service -o verbose # 详细格式(显示所有字段)
sudo journalctl -u myapp.service -o json # JSON格式
sudo journalctl -u myapp.service -o json-pretty # 格式化JSON
sudo journalctl -u myapp.service -o cat # 只显示消息内容
sudo journalctl -u myapp.service -o export # 导出格式(二进制)
# 示例:导出为JSON便于分析
sudo journalctl -u myapp.service --since today -o json > myapp-today.json
5. 跨系统查看
# 查看特定启动的日志
sudo journalctl --list-boots # 列出所有启动记录
sudo journalctl -b # 当前启动
sudo journalctl -b -1 # 上一次启动
sudo journalctl -b -2 # 上上次启动
sudo journalctl -b c1a5b2c3 # 特定启动ID
# 查看内核日志
sudo journalctl -k
sudo journalctl -k --since "1 hour ago"
# 查看所有日志(混合)
sudo journalctl
三. 查看当前日志大小
查看日志
# 查看总体使用情况
sudo journalctl --disk-usage
# 输出示例:
# Archived and active journals take up 1.2G in the file system.
# 查看每个服务的日志大小
sudo journalctl --disk-usage | grep -A 100 "Journals take up"
# 更详细的占用分析
sudo du -sh /var/log/journal/
sudo ls -lh /var/log/journal/*/*.journal
自动清理配置 (/etc/systemd/journald.conf)
[Journal]
# 限制系统日志总大小(最常用)
SystemMaxUse=1G # 系统日志最大使用1GB
#SystemMaxUse=500M # 或500MB
# 限制运行时日志大小
RuntimeMaxUse=100M
# 保留磁盘空间
SystemKeepFree=2G # 为其他文件保留2GB空间
RuntimeKeepFree=500M # 运行时保留500MB
# 限制单个日志文件大小
SystemMaxFileSize=100M # 单个文件不超过100MB
RuntimeMaxFileSize=50M # 运行时单个文件不超过50MB
# 按时间保留
MaxRetentionSec=1month # 保留1个月
#MaxRetentionSec=2weeks # 保留2周
# 压缩旧日志(节省空间)
Compress=yes
手动清理
# 按时间清理(推荐)
sudo journalctl --vacuum-time=7d # 保留最近7天
sudo journalctl --vacuum-time=2weeks # 保留最近2周
sudo journalctl --vacuum-time=1month # 保留最近1个月
# 按大小清理
sudo journalctl --vacuum-size=500M # 保留最近500MB
sudo journalctl --vacuum-size=1G # 保留最近1GB
# 按文件数量清理
sudo journalctl --vacuum-files=10 # 保留最近10个日志文件
# 组合清理
sudo journalctl --vacuum-time=7d --vacuum-size=500M
# 清理后查看效果
sudo journalctl --disk-usage
日志轮转
# 使用 logrotate 配合 journal(如果需要)
sudo tee /etc/logrotate.d/journal << 'EOF'
/var/log/journal/*/*.journal {
monthly
rotate 12
compress
delaycompress
missingok
notifempty
sharedscripts
postrotate
systemctl kill --kill-who=main --signal=SIGUSR2 systemd-journald
endscript
}
EOF
实际运维
场景1:服务突然崩溃,查找原因
#!/bin/bash
# diagnose-crash.sh
SERVICE="myapp.service"
LOG_FILE="crash-report-$(date +%Y%m%d_%H%M%S).txt"
echo "=== 服务崩溃诊断报告 ===" > $LOG_FILE
echo "诊断时间: $(date)" >> $LOG_FILE
echo "服务名称: $SERVICE" >> $LOG_FILE
echo "" >> $LOG_FILE
# 1. 查看服务状态
echo "=== 服务状态 ===" >> $LOG_FILE
systemctl status $SERVICE --no-pager >> $LOG_FILE 2>&1
echo "" >> $LOG_FILE
# 2. 查看最后100行日志
echo "=== 最后100行日志 ===" >> $LOG_FILE
journalctl -u $SERVICE -n 100 --no-pager >> $LOG_FILE
echo "" >> $LOG_FILE
# 3. 查看今天的错误日志
echo "=== 今天的所有错误 ===" >> $LOG_FILE
journalctl -u $SERVICE --since today -p err..alert --no-pager >> $LOG_FILE
echo "" >> $LOG_FILE
# 4. 查看崩溃时间点
echo "=== 服务生命周期事件 ===" >> $LOG_FILE
journalctl -u $SERVICE -o cat --no-pager | grep -E "(Started|Stopping|Failed)" | tail -20 >> $LOG_FILE
echo "" >> $LOG_FILE
# 5. 导出为JSON供进一步分析
journalctl -u $SERVICE --since "1 hour ago" -o json-pretty > crash-analysis.json
echo "诊断完成,报告保存在: $LOG_FILE"
echo "详细分析数据: crash-analysis.json"
场景2:监控日志大小并报警
#!/bin/bash
# monitor-journal-size.sh
# 阈值配置(单位:字节)
WARNING_THRESHOLD=800000000 # 800MB
CRITICAL_THRESHOLD=1000000000 # 1GB
# 获取当前使用量
USAGE=$(journalctl --disk-usage | awk '{print $7}' | sed 's/[^0-9]*//g')
# 检查并报警
if [ "$USAGE" -ge "$CRITICAL_THRESHOLD" ]; then
echo "CRITICAL: Journal 日志超过 ${CRITICAL_THRESHOLD} 字节,当前: ${USAGE} 字节"
# 自动清理
journalctl --vacuum-size=500M
# 发送报警
send-alert.sh "Journal 日志过大,已自动清理"
elif [ "$USAGE" -ge "$WARNING_THRESHOLD" ]; then
echo "WARNING: Journal 日志超过 ${WARNING_THRESHOLD} 字节,当前: ${USAGE} 字节"
send-alert.sh "Journal 日志即将满,请关注"
else
echo "OK: Journal 日志使用正常,当前: ${USAGE} 字节"
fi
# 输出详细信息
echo "=== Journal 使用详情 ==="
journalctl --disk-usage
echo ""
echo "=== 各服务日志大小排名 ==="
journalctl --disk-usage --no-pager | tail -n +2 | sort -k2 -hr | head -10
场景3:日志分析和统计
#!/bin/bash
# log-analysis.sh
SERVICE="myapp.service"
DAYS=7
echo "=== $SERVICE 近${DAYS}天日志分析 ==="
echo ""
# 1. 总日志行数
TOTAL=$(journalctl -u $SERVICE --since "${DAYS} days ago" | wc -l)
echo "总日志行数: $TOTAL"
# 2. 错误统计
ERRORS=$(journalctl -u $SERVICE --since "${DAYS} days ago" -p err | wc -l)
WARNINGS=$(journalctl -u $SERVICE --since "${DAYS} days ago" -p warning | wc -l)
echo "错误数: $ERRORS"
echo "警告数: $WARNINGS"
# 3. 最频繁的错误信息
echo ""
echo "=== 最频繁的错误 ==="
journalctl -u $SERVICE --since "${DAYS} days ago" -p err -o cat | \
sort | uniq -c | sort -rn | head -10
# 4. 按小时统计错误
echo ""
echo "=== 按小时错误分布 ==="
journalctl -u $SERVICE --since "${DAYS} days ago" -p err -o short-iso | \
awk '{print $1}' | cut -d: -f1,2 | sort | uniq -c
# 5. 导出到文件
journalctl -u $SERVICE --since "${DAYS} days ago" -o json-pretty > analysis-$(date +%Y%m%d).json