方案一:标准生产环境(拥有 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