Philip Withnall
philip.withnall@collabora.co.uk 2015
라이브러리 및 프로그램의 디버깅 및 정보 출력을 기록합니다
조성호
shcho@gnome.org 2016-2018.

# 기록

#

        요약

        라이브러리 및 프로그램의 디버깅 출력 내지는 정보 출력 문제는 널리 알려진 문제이며, 개별 표준 입출력 및 표준 오류 스트림을 대상으로, 로그 출력을 다중 스트림으로 변환하는 다양한 방법이 있습니다. 몇가지 로그 처리 구현 방식을 제안해드리겠습니다. 그러나 가장 중요한건 로깅이 일관된 모양새를 지니도록 하여, 최소한의 노력으로 로그 데이터에 접근하고 검색하는게 목적대로 할 수 있어야합니다. 프로젝트별로 다른 로깅 기법 및 형식을 활용하는건 바람직한 접근 방식이 아닙니다.

          표준 오류 스트림 및 표준 출력 스트림으로 로그를 바로 내보내는 대신, GLib 로깅 프레임워크를 활용하십시오().
          systemd를 프로젝트에 붙일 수 있다면 journal로 직접 로깅해보십시오().
          로그 순환 및 삭제를 (따로) 구현하지 마시고 시스템 서비스가 알아서 하게 두십시오().

# GLib 기록 프레임워크

GLib에서는 `g_log()` 함수를 기반으로 `g_debug()`, `g_message()`, `g_warning()`, `g_error()` 편의 래퍼 함수로 이루어진 기록 프레임워크를 제공합니다. 기록 프레임워크에는 몇가지 쓸 만한 기능이 있습니다:

`g_log_set_handler()` 함수를 활용한 프로그램에서의 기록 메시지 처리 대상 전환.

따로 처리할 수 있는 다중 기록 도메인.

따로 처리할 수 있는 다중 기록 단계. 실행 시간 도중에 디버깅 메시지를 켜거나 끌 수 있습니다.

‘fatal’ 메시지 출력시 자동 중단 지원.

유연성 개선을 목적으로 `printf()`,`g_print()`, `g_printerr()`와 같은 함수를 취향에 따라 사용해야합니다. 로깅 처리 함수는 단순화 목적으로 외부 쉘 스크립트에서 보다는, 코드 자체에서 로그를 처리할 수 있습니다.

로깅 프레임워크를 사용하는 주된 이유는 GLib와 기타 관련 라이브러리에서 이미 사용하기 때문입니다. 로깅 프레임워크를 사용하면, 모든 로깅 메시지를 동일한 체계로 넘기며, 비슷하게 처리할 수 있습니다.

GLib 로깅 프레임워크를 활용하려면 모든 기타 프로젝트와 분명히 구분할 수 있도록 프로젝트에 `G_LOG_DOMAIN`를 정의하십시오. 디버깅 메시지 기록을 남기려면 `g_debug("Message")` 함수 코드를 호출하십시오.

예를 들어 로그 메시지를 남길 때 자체 형식이 필요하거나 journald 통합이 필요하여 기본 GLib 로그 처리자로 충분히 만족스럽지 않으면, 다음 코드로 로그 처리자를 설정하십시오:

static const gchar * log_level_to_string (GLogLevelFlags level) { switch (level) { case G_LOG_LEVEL_ERROR: return "ERROR"; case G_LOG_LEVEL_CRITICAL: return "CRITICAL"; case G_LOG_LEVEL_WARNING: return "WARNING"; case G_LOG_LEVEL_MESSAGE: return "MESSAGE"; case G_LOG_LEVEL_INFO: return "INFO"; case G_LOG_LEVEL_DEBUG: return "DEBUG"; default: return "UNKNOWN"; } }

static void log_handler_cb (const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data) { const gchar *log_level_str;

/* Ignore debug messages if disabled. */ if (!debug_enabled && (log_level & G_LOG_LEVEL_DEBUG)) { return; }

log_level_str = log_level_to_string (log_level & G_LOG_LEVEL_MASK);

/* Use g_printerr() for warnings and g_print() otherwise. */ if (flags <= G_LOG_LEVEL_WARNING) { g_printerr ("%s: %s: %s\n", log_domain, log_level_str, message); } else { g_print ("%s: %s: %s\n", log_domain, log_level_str, message); } }

g_log_set_handler ("log-domain", G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION, log_handler_cb, NULL);

## 예외

일반 코드에 `g_message()` 함수를 출력 용도로 사용하지 마십시오. 내용 출력은 명령행 프로그램과 같이 프로그램의 최상단 출력에 한해서만 `g_print()`로 드물게 처리해야합니다.

라이브러리 코드에서 `g_warning()` 함수를 사용하지 마십시오. `GError`s를 대신 활용하십시오.

이와 비슷하게, 라이브러리 코드에서 로그 처리자를 설정하지 마십시오. 로그 메시지는 라이브러리 코드에서 전달하고 로그 처리자가 프로그램의 최상단으로 전달해야 합니다.

# journald 통합

기존의 syslog 방식 로그에 비해, journald에서는 로그의 이후 분석을 쉽게 해주는 구조적 로그 데이터 저장소를 지원합니다. 프로젝트의 의존 로그 처리자로 `systemd-journal`를 추가할 수 있다면, `g_print()` 함수와 `g_printerr()` 함수 대신 `sd_journal_print()` 함수와 `sd_journal_send()` 함수로 프로젝트의 로그 처리 함수 활용 범위를 확장할 수 있습니다.

자세한 내용은 저널 기록 방법 링크를 참고하십시오.

# 기록 전환

기록 파일 전환은 GLib 리고 시스템의 범위를 벗어나는 기능 입니다. logrotate 또는 systemd-journald와 같은 일반 시스템 기록 매커니즘으로 처리합니다.