← Назад к вопросам

Как вывести ошибку в лог в bash скрипте

1.3 Junior🔥 202 комментариев
#Скриптинг и программирование

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Логирование ошибок в bash-скриптах: полное руководство

В bash-скриптах выводить ошибки в логи можно несколькими способами, в зависимости от требуемого уровня детализации, формата логов и целей мониторинга. Вот основные подходы, которые я использую в production-среде.

Базовые методы вывода ошибок

1. Стандартные потоки вывода и перенаправление

Каждый процесс в Linux имеет три стандартных потока:

  • stdout (дескриптор 1) - для обычного вывода
  • stderr (дескриптор 2) - для сообщений об ошибках
  • stdin (дескриптор 0) - для ввода данных
#!/bin/bash
# Простой вывод ошибки в stderr
echo "Ошибка: файл не найден" >&2

# Перенаправление stderr в файл
command_that_might_fail 2>>/var/log/myapp/errors.log

# Перенаправление обоих потоков в один файл
some_command >>/var/log/myapp/all.log 2>&1
# Или более современная запись:
some_command &>>/var/log/myapp/all.log

2. Функция для логирования с временными метками

#!/bin/bash

LOG_FILE="/var/log/myapp/script.log"
ERROR_LOG="/var/log/myapp/errors.log"

# Функция для логирования с временной меткой
log_message() {
    local level=$1
    local message=$2
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    echo "[${timestamp}] [${level}] ${message}" | tee -a "$LOG_FILE"
}

# Функция для логирования ошибок
log_error() {
    log_message "ERROR" "$1" >&2
    # Дополнительно пишем в отдельный файл ошибок
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: $1" >> "$ERROR_LOG"
}

# Использование
if [[ ! -f "/required/file.txt" ]]; then
    log_error "Файл /required/file.txt не существует"
    exit 1
fi

Продвинутые методы для production-среды

3. Использование trap для перехвата ошибок и завершения работы

#!/bin/bash

set -euo pipefail  # Строгий режим: завершать при ошибках

# Настройка обработчиков ошибок
cleanup() {
    local exit_code=$?
    local error_line="$1"
    
    if [[ $exit_code -ne 0 ]]; then
        echo "[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] Скрипт завершился с ошибкой $exit_code на строке $error_line" >&2
        echo "[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] Скрипт завершился с ошибкой $exit_code на строке $error_line" >> "/var/log/myapp/critical.log"
    fi
    
    # Дополнительная очистка ресурсов при необходимости
    exit $exit_code
}

# Устанавливаем trap для обработки ошибок
trap 'cleanup ${LINENO}' ERR EXIT

# Пример операции, которая может завершиться ошибкой
important_operation() {
    if ! mount /dev/sdb1 /mnt/data; then
        echo "Ошибка монтирования" >&2
        return 1
    fi
}

4. Структурированное логирование в JSON (для интеграции с ELK/Grafana)

#!/bin/bash

json_log() {
    local level=$1
    local message=$2
    local script_name=$(basename "$0")
    local timestamp=$(date --iso-8601=seconds)
    
    cat <<EOF
{
  "timestamp": "${timestamp}",
  "level": "${level}",
  "script": "${script_name}",
  "pid": "$$",
  "message": "${message}",
  "hostname": "$(hostname)"
}
EOF
}

# Использование
json_log "ERROR" "Не удалось подключиться к базе данных" >> /var/log/myapp/structured.json

# Для отправки напрямую в Logstash или syslog
json_log "ERROR" "Критическая ошибка конфигурации" | logger -t "myapp-script" -p user.err

Практические рекомендации для DevOps

  1. Используйте систему управления логами:

    • Настройте logrotate для ротации логов
    • Интегрируйте с rsyslog или systemd-journald
    • Для контейнеров используйте json-file драйвер или отправляйте напрямую в Loki/Fluentd
  2. Уровни логирования:

declare -A LOG_LEVELS=([DEBUG]=0 [INFO]=1 [WARN]=2 [ERROR]=3 [FATAL]=4)
CURRENT_LOG_LEVEL="INFO"

log() {
    local level=$1
    local message=$2
    
    if [[ ${LOG_LEVELS[$level]} -ge ${LOG_LEVELS[$CURRENT_LOG_LEVEL]} ]]; then
        echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $message" >&2
    fi
}
  1. Контекст и трассировка:
# Добавление stack trace для ошибок
log_error_with_trace() {
    log_error "$1"
    log_error "Stack trace:"
    local i=0
    while caller $i; do
        ((i++))
    done | head -10 >&2
}
  1. Метрики и алертинг:
    • Логируйте ошибки с уникальными кодами для мониторинга
    • Интегрируйте с Prometheus через текстовые файлы метрик
    • Используйте exit codes для автоматического алертинга

Пример комплексного решения

#!/bin/bash
set -euo pipefail

# Конфигурация
readonly LOG_DIR="/var/log/myapp"
readonly SCRIPT_NAME=$(basename "$0")
readonly HOSTNAME=$(hostname)

# Инициализация логгера
init_logger() {
    mkdir -p "$LOG_DIR"
    exec 3>>"$LOG_DIR/${SCRIPT_NAME}.log"
    exec 4>>"$LOG_DIR/${SCRIPT_NAME}.error.log"
}

# Функция логирования
log() {
    local severity=$1
    local message=$2
    local timestamp=$(date --iso-8601=ns)
    
    # В человекочитаемый лог
    echo "[${timestamp:0:23}] [${severity}] [${HOSTNAME}] ${message}" >&3
    
    # В лог ошибок (если это ошибка)
    if [[ $severity == "ERROR" || $severity == "FATAL" ]]; then
        echo "[${timestamp:0:23}] [${severity}] ${message}" >&4
        
        # Отправка алерта (пример)
        send_alert "$severity" "$message"
    fi
    
    # В консоль (только ошибки)
    if [[ $severity == "ERROR" || $severity == "FATAL" ]]; then
        echo "[${severity}] ${message}" >&2
    fi
}

# Обработчик ошибок
error_handler() {
    local exit_code=$?
    local line_no=$1
    log "ERROR" "Скрипт завершился с кодом ${exit_code} на строке ${line_no}"
    exit $exit_code
}

trap 'error_handler ${LINENO}' ERR

# Основная логика
init_logger
log "INFO" "Скрипт запущен"

if ! perform_critical_operation; then
    log "FATAL" "Критическая операция не удалась"
    exit 1
fi

log "INFO" "Скрипт успешно завершен"

Ключевые принципы, которые я применяю:

  • Всегда разделяйте stdout и stderr
  • Добавляйте временные метки и контекст (PID, hostname, script name)
  • Используйте структурированный формат для машинной обработки
  • Настройте ротацию логов для предотвращения заполнения диска
  • Интегрируйте с централизованной системой сбора логов
  • Используйте exit codes для автоматического определения статуса выполнения

В production-среде рекомендую использовать специализированные системы логирования, такие как vector, fluent-bit или logstash для агрегации и обработки логов из bash-скриптов.

Как вывести ошибку в лог в bash скрипте | PrepBro