macOSを長期間運用していると、「昨日エラーが出た原因を後から調べたい」「ネットワーク障害が発生した時間帯のMac側ログを確認したい」といった場面があります。
しかし、必要なときにログを取り始めても、すでに問題が発生した後では遅いことがあります。
そこで今回は、macOSのUnified Logから「error」「fault」を定期的に収集し、gzip圧縮してSynology NASへ自動保存する仕組みを作ります。
この方法ではNASが一時的に切断されていても問題ありません。いったんMac内部へ保存し、NASが再接続されたときに未転送分を自動的にまとめて送ります。
今回の構成は次のようになります。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
macOS Unified Log ↓ error / fault を抽出 ↓ gzip圧縮 ↓ Mac内部のpendingフォルダへ保存 ↓ NAS接続確認 ├─ 接続中 → NASへ転送 └─ 未接続 → Mac内に保持 ↓ NAS復帰後に自動転送 |
今回作る仕組み
今回のスクリプトは、24時間ごとにmacOSのエラー系ログを収集します。
実際の処理は5分ごとにlaunchdから起動されますが、毎回ログを作るわけではありません。前回収集から24時間経過している場合だけ新しいログを作成します。
NASへの転送は5分ごとに確認されるため、ログ作成時にNASが切断されていても、その後NASがマウントされれば自動的に転送されます。
保存されるファイル名は次のようになります。
|
1 |
M2ProMacmini_2026-08-08_19-06-23.log.gz |
NAS側では一定期間が経過したログを自動削除するようにし、今回は90日保存としています。
保存先フォルダを作成
まずNAS側にログ保存用フォルダを作成しておきます。
今回の例では、
|
1 |
/Volumes/FileServer/バックアップ/M2ProMacmini_log |
を使用します。
Mac側では未転送ログを次の場所へ保持します。
|
1 |
~/Library/Application Support/M2ProMacminiLog/pending |
へ保存します。
|
1 |
mkdir -p "$HOME/Library/Scripts" |
続いて、
|
1 |
nano "$HOME/Library/Scripts/m2promacmini-log.sh" |
を実行します。
以下を貼り付けます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
#!/bin/zsh set -u setopt NULL_GLOB BASE="$HOME/Library/Application Support/M2ProMacminiLog" PENDING="$BASE/pending" STATE="$BASE/last_collect_epoch" AUTOLOG="$BASE/automation.log" NAS_ROOT="/Volumes/FileServer" DEST="/Volumes/FileServer/バックアップ/M2ProMacmini_log" COLLECT_INTERVAL=$((24 * 60 * 60)) RETENTION_DAYS=90 mkdir -p "$PENDING" write_log() { printf '%s %s\n' \ "$(date '+%Y-%m-%d %H:%M:%S')" \ "$*" >> "$AUTOLOG" } nas_mounted() { /sbin/mount | \ /usr/bin/grep -F " on ${NAS_ROOT} (smbfs," \ >/dev/null 2>&1 } transfer_one() { local src="$1" local name="${src##*/}" local partial nas_mounted || return 1 mkdir -p "$DEST" || return 1 # 重要: # 先頭を "." にするとSMB経由で隠し属性が残る場合があるため、 # 通常ファイル名 + .partial を使用する partial="$DEST/${name}.partial" rm -f "$partial" if ! cp -p "$src" "$partial"; then write_log "NASコピー失敗: $name" rm -f "$partial" return 1 fi if ! cmp -s "$src" "$partial"; then write_log "NASコピー検証失敗: $name" rm -f "$partial" return 1 fi if ! mv -f "$partial" "$DEST/$name"; then write_log "NASリネーム失敗: $name" rm -f "$partial" return 1 fi rm -f "$src" write_log "NAS転送完了: $name" return 0 } flush_pending() { nas_mounted || return 0 mkdir -p "$DEST" || return 0 local src for src in "$PENDING"/*.log.gz; do transfer_one "$src" done # NAS上の90日より古いログを削除 find "$DEST" \ -type f \ -name 'M2ProMacmini_*.log.gz' \ -mtime +"$RETENTION_DAYS" \ -delete 2>/dev/null } last_collect_epoch() { if [[ -f "$STATE" ]]; then cat "$STATE" else echo 0 fi } collect_logs() { local force="${1:-0}" local now local last local start local start_text local end_text local stamp local name local raw local output now="$(date +%s)" last="$(last_collect_epoch)" if [[ "$force" != "1" && "$last" -gt 0 ]]; then if (( now - last < COLLECT_INTERVAL )); then return 0 fi fi if (( last > 0 )); then start="$last" else start=$((now - COLLECT_INTERVAL)) fi start_text="$(date -r "$start" '+%Y-%m-%d %H:%M:%S')" end_text="$(date -r "$now" '+%Y-%m-%d %H:%M:%S')" stamp="$(date '+%Y-%m-%d_%H-%M-%S')" name="M2ProMacmini_${stamp}.log.gz" raw="$BASE/M2ProMacmini_${stamp}.log" output="$PENDING/$name" write_log "ログ収集開始: ${start_text} ~ ${end_text}" if ! /usr/bin/log show \ --start "$start_text" \ --end "$end_text" \ --style syslog \ --predicate 'messageType == error OR messageType == fault' \ > "$raw"; then write_log "macOSログ収集失敗" rm -f "$raw" return 1 fi if ! gzip -c "$raw" > "$output"; then write_log "gzip圧縮失敗" rm -f "$raw" "$output" return 1 fi rm -f "$raw" printf '%s\n' "$now" > "$STATE" write_log "ローカル保存完了: $name" flush_pending } show_status() { echo "=== M2ProMacmini Log Status ===" if nas_mounted; then echo "NAS: mounted" else echo "NAS: not mounted" fi local files=("$PENDING"/*.log.gz) echo "Pending logs: ${#files[@]}" if [[ -f "$STATE" ]]; then local last last="$(cat "$STATE")" echo "Last collection: $(date -r "$last" '+%Y-%m-%d %H:%M:%S')" else echo "Last collection: none" fi echo "Automation log:" echo "$AUTOLOG" } case "${1:-auto}" in auto) flush_pending collect_logs 0 ;; collect) collect_logs 0 ;; collect-force) collect_logs 1 ;; flush) flush_pending ;; status) show_status ;; *) echo "Usage:" echo " $0 auto" echo " $0 collect" echo " $0 collect-force" echo " $0 flush" echo " $0 status" exit 1 ;; esac |
保存は、
|
1 2 3 |
Control + O Enter Control + X |
です。
その後、実行権限を付けます。
|
1 |
chmod +x "$HOME/Library/Scripts/m2promacmini-log.sh" |
なお、NASのフォルダ名に日本語を使用している場合、macOSでは濁点・半濁点のUnicode表現が見た目と内部で異なることがあります。
NASパスは手入力するより、Finderやターミナル上で実際のパスを確認して指定するのがおすすめです。
修正版で重要なポイント
最初に作ったバージョンでは、NASへコピーするときに、
|
1 |
partial="$DEST/.${name}.partial" |
としていました。
つまりNAS上では一時的に、
|
1 |
.M2ProMacmini_2026-08-08_19-06-23.log.gz.partial |
というファイルを作っていました。
先頭が「.」なのでUnix系OSでは隠しファイルとして扱われます。
コピー完了後に通常のファイル名へ変更していたのですが、Synology NASへSMB経由で保存した場合、隠し属性がリネーム後にも残るケースがありました。
その結果、
|
1 2 3 |
Synology File Station → 見える ターミナルのls → 見える macOS Finder → 見えない |
という少し分かりにくい状態になりました。
今回の修正版では、
|
1 |
partial="$DEST/${name}.partial" |
としています。
これならコピー途中は、
|
1 |
M2ProMacmini_2026-08-08_19-06-23.log.gz.partial |
となり、隠しファイルにはなりません。
コピー後にcmpで元ファイルとNAS側の一時ファイルを比較し、正常であれば正式な.log.gzへリネームします。
途中でNAS接続が切れた場合も、完成していないファイルを正常なログとして扱う可能性を抑えられます。
手動で動作確認する
まず現在の状態を確認します。
|
1 |
"$HOME/Library/Scripts/m2promacmini-log.sh" status |
強制的にログを1回作成する場合は、
|
1 |
"$HOME/Library/Scripts/m2promacmini-log.sh" collect-force |
を実行します。
正常であれば、
|
1 |
M2ProMacmini_2026-08-08_19-06-23.log.gz |
のようなファイルがNAS側へ作成されます。
スクリプト自身の動作履歴は、
|
1 |
tail -30 "$HOME/Library/Application Support/M2ProMacminiLog/automation.log" |
で確認できます。
正常時は例えば、
|
1 2 3 |
2026-08-08 19:06:23 ログ収集開始: 2026-08-07 19:06:23 ~ 2026-08-08 19:06:23 2026-08-08 19:07:33 ローカル保存完了: M2ProMacmini_2026-08-08_19-06-23.log.gz 2026-08-08 19:07:33 NAS転送完了: M2ProMacmini_2026-08-08_19-06-23.log.gz |
のようになります。
NASを切断した状態でもテストできる
この仕組みのポイントは、NASが使えない状態でもログ収集そのものは継続できることです。
NAS未接続時には、
|
1 |
~/Library/Application Support/M2ProMacminiLog/pending |
へ.log.gzが残ります。
確認するには、
|
1 |
ls -lah "$HOME/Library/Application Support/M2ProMacminiLog/pending" |
を実行します。
NASを再接続したあと、
|
1 |
"$HOME/Library/Scripts/m2promacmini-log.sh" flush |
とすれば、未転送分だけNASへ転送できます。
launchdを設定すれば、このflushも手動で行う必要はありません。
launchdで完全自動化する
macOSにはWindowsのタスクスケジューラに相当するlaunchdがあります。
LaunchAgentを作ります。
|
1 2 |
mkdir -p "$HOME/Library/LaunchAgents" nano "$HOME/Library/LaunchAgents/com.sybit.m2promacmini-log.plist" |
以下を貼り付けます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.sybit.m2promacmini-log</string> <key>ProgramArguments</key> <array> <string>/bin/zsh</string> <string>-c</string> <string>exec "$HOME/Library/Scripts/m2promacmini-log.sh" auto</string> </array> <key>RunAtLoad</key> <true/> <key>StartInterval</key> <integer>300</integer> <key>ProcessType</key> <string>Background</string> </dict> </plist> |
文法をチェックします。
|
1 |
plutil -lint "$HOME/Library/LaunchAgents/com.sybit.m2promacmini-log.plist" |
正常なら、
|
1 |
OK |
と表示されます。
登録します。
|
1 2 3 |
launchctl bootstrap \ gui/$(id -u) \ "$HOME/Library/LaunchAgents/com.sybit.m2promacmini-log.plist" |
すでに登録済みの設定を更新するときは、
|
1 2 3 4 5 6 7 |
launchctl bootout \ gui/$(id -u) \ "$HOME/Library/LaunchAgents/com.sybit.m2promacmini-log.plist" launchctl bootstrap \ gui/$(id -u) \ "$HOME/Library/LaunchAgents/com.sybit.m2promacmini-log.plist" |
とします。
登録状態は、
|
1 |
launchctl print gui/$(id -u)/com.sybit.m2promacmini-log |
で確認できます。
5分間隔でもログを5分ごとに作るわけではない
StartIntervalを300秒にしています。
|
1 |
<integer>300</integer> |
そのためlaunchdからスクリプト自体は約5分間隔で起動します。
ただしログ収集部分には、
|
1 |
COLLECT_INTERVAL=$((24 * 60 * 60)) |
という判定が入っています。
そのため実際の動作は、
|
1 2 3 4 5 6 7 8 9 |
5分ごと ↓ NASに未転送ログがある? ↓ あれば転送 ↓ 前回ログ収集から24時間経過した? ↓ 経過していれば新しいログを収集 |
となります。
NAS復帰を比較的早く検出しつつ、ログファイル自体は1日1個程度に抑えられます。
NASへ本当に保存されているか確認する
macOSの/Volumes配下には、NAS未接続時に同名フォルダが残ってしまうケースも考えられます。
そのため、保存先が本当にSMBマウントなのか確認するには、
|
1 |
mount | grep "/Volumes/FileServer" |
を実行します。
例えば、
|
1 |
//user@NAS/FileServer on /Volumes/FileServer (smbfs, ...) |
のように(smbfs, ...)と表示されれば、Synology NASへSMB接続されています。
さらに、
|
1 |
df -h "/Volumes/FileServer" |
でも確認できます。
Finderにログが表示されない場合
NAS上ではログが存在するのにFinderだけ表示されない場合、隠し属性が付いている可能性があります。
今回、旧バージョンのスクリプトで実際にこの状態になりました。
確認は、
|
1 |
ls -lO@ "/Volumes/FileServer/バックアップ/M2ProMacmini_log" |
です。
隠し属性を解除する場合は、
|
1 2 |
chflags nohidden \ "/Volumes/FileServer/バックアップ/M2ProMacmini_log/"*.log.gz |
を実行します。
Finderを再起動する場合は、
|
1 |
killall Finder |
です。
ただし、今回掲載している修正版では一時ファイルを先頭ドット付きで作らないため、新規ログについては同じ問題が発生しにくくなっています。
ログを見る方法
.log.gzなので、ダブルクリックして展開しても構いません。
ターミナルから内容だけ確認するなら、
|
1 |
gzcat M2ProMacmini_2026-08-08_19-06-23.log.gz | less |
でも確認できます。
キーワードを絞るなら、
|
1 2 |
gzcat M2ProMacmini_2026-08-08_19-06-23.log.gz \ | grep -i "error" |
なども使えます。
まとめ
この方法なら、macOSで発生したerror・faultログを毎日自動的に蓄積できます。
NASが停止している場合でもログ収集自体は止まらず、Mac内部のpendingフォルダへ待避します。NASが再びマウントされた後はlaunchdが自動的に未転送ログを検出して送信します。
特に、自宅サーバーやNAS、ルーターなどを含めてネットワーク障害の原因を後から調べたい場合、Mac側にも継続的なログが残っているとかなり役立ちます。
今回のようにSynology NAS側でルーターのsyslogも保存しておけば、
|
1 2 3 |
ルーター側ログ + macOS側ログ |
を同じ時間軸で比較できるため、「インターネット側の問題なのか」「LAN側なのか」「Mac側なのか」といった切り分けもしやすくなります。
なお、macOSのログにはユーザー名、ファイルパス、アクセス先などが含まれる場合があります。
取得した生ログをブログやSNSへそのまま公開する場合は、個人情報が含まれていないか確認してから掲載した方が安全です。
