Shellの基礎知識(実践編)

【Shellの基礎知識】簡単なログ出力ロジックを作ってみました。

商用プロジェクトは1から100に至るまで、すべてエビデンスの世界です。システムに障害は付きものですので、障害報告を行う際には、必ず「1次報告」「中間報告」「最終報告」をエビデンス付きで提出しなければなりません。

シェルスクリプトを作成する場合、予め障害が発生することを念頭に入れて設計を進めていきます。基盤として挙げるなら「CPU」や「メモリ」、「ディスク」など、リソース周りの障害は約束されていることです。では、そのエビデンスとして必要になるものは何でしょう?

エビデンスとして必ず必要になるものとは、ずばり「ログ」です。

実行から障害に至るまでの経緯を時系列にログとして記録していなければ、対象方法が見つかりません。「ログ出力なくしてシステム開発にあらず!」システムの成功は、すべて「ログの出力の有無」にかかっているといっても過言ではありません!

そこで、ちょっとしたログ出力のロジックを作成してみることにしました。

共通ログ出力クラスの概要と役割

ログ管理は、運用現場の効率化やトラブル解決において間接的ながらも大きな役割を果たします。しかし、現場ではその重要性が認識されにくく、属人的な対応や管理の手間が課題となることが多々あります。共通ログ出力クラスは、こうした非効率を解消し、ログ管理を標準化することで、システムの運用をスムーズにするために設計されています。

共通ログ出力クラスの目的

共通ログ出力クラスは、シェルスクリプト内で発生するイベントやエラーを記録し、運用効率を向上させるための仕組みを提供します。
このクラスを導入することで以下の目的を達成できます:

  • ログ記録の統一化:
    ログフォーマットを統一し、複数のスクリプト間で一貫性を保つ。
  • デバッグの効率化:
    スクリプト内で発生したエラーや処理結果を記録し、問題発生時の迅速な原因特定を可能にする。
  • 運用保守性の向上:
    手動でのログ管理や記録ミスを防ぎ、運用の手間を削減する。

例えば、複数のスクリプトが同一のログフォーマットで出力することで、エラー解析や動作確認が格段にスムーズになります。

ログ管理の重要性と課題解決のポイント

ログ管理はシステムの信頼性を支える重要な要素ですが、直接的な利益を生まないことから、多くの現場で軽視される傾向があります。その結果、エラー発生時に原因の特定が困難になり、トラブルシューティングに余計な時間と労力を費やすなど、非効率な状況が発生することが少なくありません。

ログ管理の重要性

システム運用においてログ管理は、障害発生時の原因追及や運用状況の可視化に欠かせない要素です。
特にシェルスクリプトでは、簡易的な処理が多い反面、エラーが発生しても原因が記録されないことが頻繁に起こります。これにより、以下のような問題が発生します:

  • エラーの再現が困難になる。
  • 複数のスクリプト間でログフォーマットが異なり、解析に手間がかかる。
  • 障害発生時に必要な情報が欠落する。

課題解決のポイント

共通ログ出力クラスを使用することで、以下の課題を解決します:

  • ログフォーマットの統一:
    日時、ログレベル、メッセージを標準化し、可読性を向上させます。
  • エラーの記録自動化:
    スクリプトのエラーや成功状態を自動で記録し、トラブル対応の時間を短縮します。
  • ログのローテーション:
    古いログを自動削除し、ディスク容量の枯渇を防ぎます。

共通ログ出力クラスの設計の基本方針

共通ログ出力クラスは、以下の設計方針を基に構築されています:

基本方針

  1. 汎用性を重視
    • どのスクリプトからでも利用できるよう、関数形式で実装。
    • ログ出力先やフォーマットを環境変数でカスタマイズ可能に設計。
  2. 簡単な導入手順
    • スクリプトに数行のコードを追加するだけで利用可能。
    • 必要な設定を最小限に抑え、即時使用を実現。
  3. 可読性と保守性の向上
    • ログメッセージには「日時」「ログレベル」「メッセージ本文」を含め、誰が見ても理解しやすい構造。
    • ログの出力先は標準出力、ファイル、syslog など複数選択肢を提供。
  4. エラーへの耐性
    • ログ出力に失敗してもスクリプトの動作に影響を与えないよう設計。

共通ログ出力クラスを活用することで、シェルスクリプトの運用効率を格段に向上させるだけでなく、運用トラブルに迅速に対応できる体制を整えることが可能です。次回の記事では、具体的な実装方法とサンプルコードについて解説します。

設計書の構成と設計要素

共通ログ出力クラスを効率的に設計・実装するためには、動作環境、使用する変数、実装された関数の詳細を体系的に把握することが重要です。ここでは、これらの要素を表形式でまとめ、各要素の内容を簡潔に比較・理解しやすいようにしました。

クラス設計の前提条件

共通ログ出力クラスの動作環境や設計時の注意点を表にまとめました。この情報は、スクリプトを異なる環境に導入する際の参考になります。

項目内容
対応OSRHEL系Linuxメイン。必要に応じてDebian系やWSLも可。
対応シェルBash(4.x以上を推奨)。Bourne Shell(sh)でも動作可能。
依存関係標準コマンド( echo, date, catなど)を使用し、外部ツールへの依存なし。
ディレクトリ構成 /log ディレクトリにログファイルを保存(出力先のカスタマイズ可能)。

使用する変数一覧

ここでは、共通ログ出力クラスで使用されるグローバル変数を種類ごとに整理しています。この表は、グローバル変数の役割を明確にし、スクリプトのカスタマイズやトラブルシューティングを効率化することを目的としています。

変数名説明初期値
BASE_PATHベースディレクトリのパスを指定。 /work/scripts/
COM_PATH共通関数やクラスを保存するディレクトリを指定。 ${BASE_PATH}/com
DEFAULT_LOG_MODEログの出力先を指定(例: CONSOLE, FILE, SYSLOG)。 CONSOLE
ETC_PATH設定ファイルを保存するディレクトリを指定。 ${BASE_PATH}/etc
INFRA_MSG_CONFメッセージ設定ファイルのパスを指定。 ${ETC_PATH}/infraMessage.cfg
JOB_OK正常終了コードを定義。
JOB_WR警告終了コードを定義。 1
JOB_ERエラー終了コードを定義。 2
LOG_FORMATログのフォーマットを指定(例: %s [ %-5s] %s\n)。 %s [ %-5s] %s\n
LOG_LEVELログ出力のレベルを指定(例: DEBUG, INFO, WARNING, ERROR)。 INFO<span class="hutoaka"></span>1)
LOG_PATHログファイルの保存ディレクトリを指定。 ${BASE_PATH}/log
REP_PATHレポート出力用ディレクトリを指定。 ${BASE_PATH}/rep
SCRIPT_NAME実行中のスクリプト名を取得。 $(basename "$0")
TIMER_STARTタイマーの開始時刻を記録。
TMP_PATH一時ファイルを保存するディレクトリを指定。 ${BASE_PATH}/tmp

1:INFO 設定時に DEBUG は記録されません。

実装されている関数の詳細

以下は、共通ログ出力クラスに実装されている主要な関数とその目的をまとめた表です。この表を利用することで、関数の役割を短時間で理解でき、実際のコードに素早く適用できます。

関数名目的説明
divider区切り線の表示メッセージを区切り線とともに出力します。
startLogログ出力の初期化ログモードに応じて出力先を設定し、ログの開始メッセージを記録します。
exitLogログ出力の終了スクリプトの終了コードに応じた終了メッセージを記録します。
logOutログメッセージの記録指定されたログレベルとメッセージをフォーマットして出力します。
getMsgメッセージの取得メッセージ設定ファイルから指定されたメッセージIDに対応する文言を取得します。
startTimerタイマーの開始現在時刻を記録し、後続処理の実行時間を計測します。
endTimerタイマーの終了開始時刻からの経過時間を計算し、ログに記録します。
parseArgsコマンドライン引数の解析ログレベルやログモードなどの引数を解析して設定します。

 共通ログ出力クラスの設定手順

共通ログ出力クラスを正しく利用するためには、クラスの設置場所や設定方法を適切に行う必要があります。以下では、設置場所の選定から設定手順、ログ保存先のカスタマイズまで、具体的な手順を説明します。

共通ログ出力クラスは、他のスクリプトから容易に利用できるよう、以下のディレクトリ構造を推奨します。

前提となる実行環境

Beエンジニアでシェルスクリプトを実行する環境は下記の通りとします。

実行環境

BASE_DIR(任意のディレクトリ)

  • scripts
    • bin(実行スクリプト格納領域)
      • <<各種実行スクリプト>>.sh (実行ファイル)
    • com(共通スクリプト格納領域)
      • logger.shrc(共通ログ出力ファイル)
      • utils.shrc(共通関数定義ファイル)
    • etc(設定ファイル等の格納領域)
      • infraMessage.conf(メッセージ定義ファイル)
    • log(スクリプト実行ログの格納領域)
      • スクリプト名.log 
    • tmp(テンポラリ領域)
    • rep(レポート出力領域)
ディレクトリ説明
scripts/bin/実行可能なスクリプト(メインロジック)を配置するディレクトリ。
scripts/com/共通関数やクラス(例: logger.shrc)を格納するディレクトリ。
scripts/etc/設定ファイル(環境変数やコンフィグ)を配置するディレクトリ。
scripts/log/ログファイルを保存するディレクトリ。
scripts/tmp/一時ファイルや中間データを保存するディレクトリ。
scripts/rep/レポート出力ディレクトリ。集計用の元データなどを格納する。

設置例: 共通ログ出力クラス(例: logger.shrc)は scripts/com/ に配置します。以下は、scripts/com/logger.shrc に配置する共通ログ出力クラスのソースコード例です。

本スクリプト利用により発生した利用者の損害全てに対し、いかなる責任をも負わないものとし、損害賠償をする一切の義務はないものとします。

共通ログ出力クラスの設定方法

クラスを利用するためには、実行スクリプト内でクラスを読み込む設定を行います。以下に設定手順を示します。

  1. スクリプト内でクラスを読み込む
    実行スクリプト内で source コマンドを使って共通ログ出力クラスを読み込みます。

    source ./com/logger.shrc

  2. ログファイルの初期設定
    ログファイルの保存先やログレベルを環境変数で設定します。

    LOG_FILE="./log/my_script.log"
    LOG_LEVEL="INFO"

  3. 必要な関数を呼び出す
    スクリプト内で関数を順序立てて呼び出し、ログ出力を有効化します。

    startLog
    logOut "INFO" "スクリプト開始"
    # 処理コード closeLog

設置例: ログ出力テストクラス(例: testLog.sh)は scripts/bin/ に配置します。以下は logger.shrc を使ってログ出力を行うソースコードの例です。

本スクリプト利用により発生した利用者の損害全てに対し、いかなる責任をも負わないものとし、損害賠償をする一切の義務はないものとします。

便宜的に「infraMessage.cfg」をプログラム内で作成していますが、実際の運用では、想定されるエラーメッセージや通知メッセージをあらかじめ集約・管理しておくための設定ファイルとして利用します。このファイルは運用前に手動で準備し、必要に応じて内容を更新していくものです。プログラムで動的に作成されるファイルではない点にご注意ください。

実行結果

ログ保存先ディレクトリの設定

共通ログ出力クラスでは、ログファイルの保存先を柔軟に設定できます。

設定方法説明
デフォルト設定ディレクトリ /log にログを保存します。
環境変数による設定環境変数 LOG_FILE を設定することで、任意の保存先を指定可能です。
動的ディレクトリ作成保存先ディレクトリが存在しない場合、自動で作成されます(例: mkdir -p コマンドを使用)。

保存先ディレクトリに書き込み権限がない場合、ログの記録に失敗する可能性があります。保存先が適切に設定されているかを事前に確認することを推奨します。

 実践的な利用方法とサンプルコード

共通ログ出力クラスを実際のスクリプトに適用することで、ログ管理を効率化し、運用のトラブル対応を迅速化することができます。このセクションでは、基本的な利用方法と具体的なサンプルコードを紹介します。また、よくある利用ケースについても解説します。

基本的な使い方

共通ログ出力クラスを使用すると、ログの出力先やログレベルを柔軟に設定できます。ログ出力先(CONSOLE または FILE)の設定により、メッセージを標準出力に表示するか、ログファイルに記録するかを切り替えることができます。

ログ出力先の設定

モード説明設定方法
CONSOLE標準出力(コンソール)に表示 DEFAULT_LOG_MODE="CONSOLE"
FILE指定したログファイルに記録 DEFAULT_LOG_MODE="FILE"
使い分けのポイント:
  • CONSOLE: デバッグ時や短期間の確認用に便利です。
  • FILE: 長期間のログ記録や、後で分析が必要な場合に適しています。
FILEモードの追加設定:

出力先をファイルにする場合、以下の設定を追加してください。

LOG_PATH="/work/scripts/log"
LOG_FILE="${LOG_PATH}/script.log"
mkdir -p "${LOG_PATH}"

ログレベルの設定

共通ログ出力クラスでは、記録するログメッセージの重要度を制御するためにログレベルを設定します。

レベル説明設定方法
DEBUG詳細なデバッグ情報を記録 LOG_LEVEL="DEBUG"
INFO情報メッセージを記録(デフォルト) LOG_LEVEL="INFO"
WARNING警告メッセージを記録 LOG_LEVEL="WARNING"
ERRORエラーメッセージのみ記録 LOG_LEVEL="ERROR"
ログレベルの動作例:

例えば、 LOG_LEVEL="INFO" を設定した場合、 INFOWARNINGERROR のメッセージは記録されますが、 DEBUG メッセージは無視されます。ログレベルは「重要度の低いメッセージを除外するフィルター」として機能します。

コード例

以下は、ログ出力先を FILE に設定し、ログレベルを INFO に設定した場合のスクリプト例です。

#!/bin/bash

# グローバル変数の設定
BASE_PATH="/work/scripts"
LOG_PATH="${BASE_PATH}/log"
LOG_FILE="${LOG_PATH}/script.log"
DEFAULT_LOG_MODE="FILE" # ログ出力先をファイルに設定
LOG_LEVEL="INFO" # 記録するログレベルを設定

# 必要なディレクトリ作成
mkdir -p "${LOG_PATH}"

# 共通ログ出力クラスの読み込み
source "${BASE_PATH}/com/logger.shrc"

# ログ出力
startLog
logOut "DEBUG" "This is a debug message." # 記録されない
logOut "INFO" "This is an informational message." # 記録される
logOut "WARNING" "This is a warning message." # 記録される
logOut "ERROR" "This is an error message." # 記録される
closeLog

出力結果例

ログファイル( /work/scripts/log/script.log)には、以下の内容が記録されます:

==== ログ開始: 2025-01-28 12:00:00 ====
[INFO] This is an informational message.
[WARNING] This is a warning message.
[ERROR] This is an error message.
==== ログ終了: 2025-01-28 12:00:02 ====

よくある利用ケース

共通ログ出力クラスは、さまざまなシナリオで活用できます。以下に代表的なケースを示します。

スクリプトのデバッグログの記録

デバッグログを活用すると、スクリプトの実行状況や変数の状態を詳細に追跡できます。以下は変数の値を記録する例です。

logOut "DEBUG" "変数 value の値は: $value"

プロセスのエラー追跡ログの活用

エラーが発生したプロセスやコマンドを追跡する場合、以下のように logOut を活用します。

if ! cp source.txt destination.txt; then
  logOut "ERROR" "ファイルのコピーに失敗しました"
fi

共通ログ出力クラスを導入するメリット

共通ログ出力クラスを導入することで、スクリプトの運用効率や保守性が飛躍的に向上します。ここでは、主なメリットを3つの観点から解説します。

5.1 ログ管理の効率化

ログ管理が効率化されることで、以下のような利点があります:

  • 一貫性のあるログフォーマット: ログ出力が標準化されるため、複数スクリプト間でフォーマットの違いを気にする必要がなくなります。これにより、ログ解析やエラーの追跡が簡単になります。
  • 自動的なログディレクトリ作成: 保存先ディレクトリが存在しない場合でも、動的に作成する機能を備えており、手動での事前準備が不要です。
  • 柔軟な出力先の指定: 環境変数を利用して、ログの保存先を簡単にカスタマイズできます。これにより、ローカル環境や本番環境の要件に応じた運用が可能です。

5.2 スクリプト保守性の向上

スクリプト保守の手間を削減し、長期的な運用に適した状態を実現します。

  • コードの再利用性向上: ログ出力の仕組みを共通クラスとして切り出すことで、複数のスクリプト間で同じロジックを使い回すことができます。これにより、重複したコードの記述を減らし、保守性が向上します。
  • 可読性の向上: ログに日時やログレベルが付与されるため、スクリプトの実行内容を明確に把握できます。これにより、第三者がコードをレビューする際の負担が軽減されます。
  • 変更時の影響範囲を最小化: ログ管理に関する処理を共通クラスに集約しているため、変更が必要な場合でもクラス内の修正だけで対応可能です。

5.3 トラブルシューティングの迅速化

エラーや問題が発生した際の対応速度を格段に向上させます。

よく読まれている記事

1

IT入門シリーズ 🟢 STEP 1: ITの基礎を知る(ITとは何か?) 📌 IT初心者が最初に学ぶべき基本知識。ITの概念、ネットワーク、OS、クラウドの仕組みを学ぶ ...

2

「私たちが日々利用しているスマートフォンやインターネット、そしてスーパーコンピュータやクラウドサービス――これらの多くがLinuxの力で動いていることをご存じですか?無料で使えるだけでなく、高い柔軟性 ...

3

この記事は、Linuxについて勉強している初心者の方向けに「Shellスクリプト」について解説します。最後まで読んで頂けましたら、Shellスクリプトはどのような役割を担っているのか?を理解出来るよう ...

-Shellの基礎知識(実践編)