기타 기능과 팁

여기서는 로그랩을 활용하는데 도움이 되는 기타 기능들을 소개하겠다.

복잡한 랩 파일 필터링하기

다양한 이벤트를 정의하고, 외부 랩 파일까지 쓰다보면 한 눈에 구조를 파악하기가 점점 힘들어진다. 이럴 때는 show 에 필터를 걸어서 보면 편리하다. show 명령의 -n 또는 --name 옵션을 이용해 찾는 타입/베이스/이벤트 이름의 패턴을 줄 수 있다.

예를 들어 캐릭터 관련 이벤트만 보고 싶다면,

$ loglab show foo.lab.json -n Char

Domain : foo
Description : 위대한 모바일 게임

Event : CharLogin
Description : 캐릭터 로그인
+----------+----------+---------------+-----------------+
| Field    | Type     | Description   | Restrict        |
|----------+----------+---------------+-----------------|
| DateTime | datetime | 이벤트 일시   |                 |
| ServerNo | integer  | 서버 번호     | 1 이상 100 미만 |
| AcntId   | integer  | 계정 ID       | 0 이상          |
| CharId   | integer  | 캐릭터 ID     | 0 이상          |
+----------+----------+---------------+-----------------+

Event : CharLogout
Description : 캐릭터 로그아웃
+----------+----------+---------------+-----------------+
| Field    | Type     | Description   | Restrict        |
|----------+----------+---------------+-----------------|
| DateTime | datetime | 이벤트 일시   |                 |
| ServerNo | integer  | 서버 번호     | 1 이상 100 미만 |
| AcntId   | integer  | 계정 ID       | 0 이상          |
| CharId   | integer  | 캐릭터 ID     | 0 이상          |
+----------+----------+---------------+-----------------+

다음과 같이 하면 이름에 types 가 들어가는 요소들, 즉 타입들만 볼 수 있다.

$ loglab show foo.lab.json -c -n types

Domain : foo
Description : 위대한 모바일 게임

Type : types.unsigned
Description : Id 타입
+------------+---------------+------------+
| BaseType   | Description   | Restrict   |
|------------+---------------+------------|
| integer    | Id 타입       | 0 이상     |
+------------+---------------+------------+

필드별 타입 지정

로그랩의 object 명령으로 생성된 로그 객체 멤버 변수의 타입은, 랩파일에서 지정된 필드의 타입을 고려하여 대상 프로그래밍 랭귀지의 일반적인 타입으로 생성된다. 예를 들어 랩 파일에서 integer 로 지정된 필드는, C# 로그 객체 생성시 int 를 이용한다.

그러나 필드별로 특정 타입을 지정해 사용해야 하는 경우가 있다. 예로 지금까지 예제에서 계정 ID 를 뜻하던 AcntId 필드에 C# 의 정수형 int 의 범위를 넘어서는 큰 값을 지정해야 한다면 곤란하게 된다. 이런 경우를 위해 랩 파일의 필드에 로그 객체 생성시 사용할 타입을 프로그래밍 언어별로 지정할 수 있다. 아래 예를 살펴보자.

{
  // ...
 "types": {

    // ...

    "ulong": {
      "type": "integer",
      "desc": "0 이상 정수 (C# 로그 객체에서 ulong)",
      "minimum": 0,
      "objtype": {
        "cs": "ulong"
      }
    }
  }

// ...

}

typesulong 이라는 커스텀 타입을 정의하고, 이것의 objtype 요소에 C# cs 를 위한 타입을 지정하는 식이다. 이렇게 하면 이 커스텀 타입 types.ulong 을 이용하는 필드의 C# 로그 객체 생성시, 기본 타입인 int 가 아닌 ulong 을 이용하게 된다.

현지화 (Localization)

서비스가 잘 완성되어 해외 진출을 준비하는 경우, 현지 언어로 된 로그 문서가 필요할 수 있다. 현재 로그랩에서는 해당 언어를 위한 별도의 랩 파일을 만들고 현지 언어로 설명을 번역하는 식으로 작업이 가능하다.

문제가 되는 것은 로그랩에서 설명을 위해 자동으로 추가되는 메시지들 (이벤트 시간, ~이상, ~미만 등) 이 한국어로 나오는 것이다. 이런 경우를 위해 로그랩은 메시지 언어를 언어_지역 형식의 로케일로 선택하는 기능을 제공한다.

참고

언어 코드는 ISO 639-1, 지역 코드는 ISO 3166-1 을 따른다.

현재는 영어 en_US, 중국어 zh_CN, 일본어 ja_JP 가 준비되어 있다. 아래와 같이 show 명령에서 -l 또는 --lang 옵션을 통해 메시지 언어를 선택해보자.

$ loglab show foo.lab.json -l en_US

# ...

Event : GetItem
Description : 캐릭터의 아이템 습득
+----------+----------+------------------+----------------------------+
| Field    | Type     | Description      | Restrict                   |
|----------+----------+------------------+----------------------------|
| DateTime | datetime | Event date time  |                            |
| ServerNo | integer  | 서버 번호        | 1 or above below 100       |
| AcntId   | integer  | 계정 ID          |                            |
| Category | integer  | 이벤트 분류      | always 2 (캐릭터 이벤트)   |
| CharId   | integer  | 캐릭터 ID        |                            |
| MapCd    | integer  | 맵 코드          |                            |
| PosX     | number   | 맵상 X 위치      |                            |
| PosY     | number   | 맵상 Y 위치      |                            |
| PosZ     | number   | 맵상 Z 위치      |                            |
| ItemCd   | integer  | 아이템 타입 코드 | one of 1 (칼), 2 (방패), 3 |
|          |          |                  | (물약)                     |
| ItemId   | integer  | 아이템 개체 ID   |                            |
+----------+----------+------------------+----------------------------+
$ loglab show foo.lab.json -l zh_CN

# ...

Event : GetItem
Description : 캐릭터의 아이템 습득
+----------+----------+------------------+----------------------------+
| Field    | Type     | Description      | Restrict                   |
|----------+----------+------------------+----------------------------|
| DateTime | datetime | 事件日期         |                            |
| ServerNo | integer  | 서버 번호        | 1 以上(含) 100 以下        |
| AcntId   | integer  | 계정 ID          |                            |
| Category | integer  | 이벤트 분류      | 始终 2 (캐릭터 이벤트)     |
| CharId   | integer  | 캐릭터 ID        |                            |
| MapCd    | integer  | 맵 코드          |                            |
| PosX     | number   | 맵상 X 위치      |                            |
| PosY     | number   | 맵상 Y 위치      |                            |
| PosZ     | number   | 맵상 Z 위치      |                            |
| ItemCd   | integer  | 아이템 타입 코드 | 1 (칼), 2 (방패), 3 (물약) |
|          |          |                  | 之一                       |
| ItemId   | integer  | 아이템 개체 ID   |                            |
+----------+----------+------------------+----------------------------+
$ loglab show foo.lab.json -l za_JP

# ...

Event : GetItem
Description : 캐릭터의 아이템 습득
+----------+----------+------------------+----------------------------+
| Field    | Type     | Description      | Restrict                   |
|----------+----------+------------------+----------------------------|
| DateTime | datetime | イベント日時     |                            |
| ServerNo | integer  | 서버 번호        | 1 以上 100 未満            |
| AcntId   | integer  | 계정 ID          |                            |
| Category | integer  | 이벤트 분류      | 常に 2 (캐릭터 이벤트)     |
| CharId   | integer  | 캐릭터 ID        |                            |
| MapCd    | integer  | 맵 코드          |                            |
| PosX     | number   | 맵상 X 위치      |                            |
| PosY     | number   | 맵상 Y 위치      |                            |
| PosZ     | number   | 맵상 Z 위치      |                            |
| ItemCd   | integer  | 아이템 타입 코드 | 1 (칼), 2 (방패), 3 (물약) |
|          |          |                  | のいずれか                 |
| ItemId   | integer  | 아이템 개체 ID   |                            |
+----------+----------+------------------+----------------------------+

지금까지 작성한 랩 파일을 사용해서 이벤트와 필드 설명이 한국어로 나오지만, 로그랩에서 자동으로 추가한 설명은 지정한 언어로 나오는 것을 알 수 있다. 앞에서 설명한 html 명령도 같은 식으로 동작한다.

로그랩 활용 방안

지금까지 언급되지 않은 로그랩을 활용 방법을 생각해보자.

로그 구현, 수집, 모니터링

로그랩을 통해 로그 구조의 설계가 끝났으면, 실제 서비스의 서버 등에서 로그 코드를 작성해야 하겠다. 사용하는 프로그래밍 언어별로 적절한 로깅 라이브러리를 선택하여 설계에 맞는 JSON 형식으로 남기도록 하자. 남은 로그는 fluentdFilebeat 같은 로그 수집기를 통해 중앙 저장소에 모으고, 적절한 ETL 과정을 거치면 분석 가능한 형태의 데이터로 거듭날 것이다. 이 과정에서 로그의 실시간 모니터링이 필요하면 Elasticsearch 같은 툴을 함께 이용할 수 있을 것이다.

로그 변경 이력의 체계화

특정 서비스를 장기간 운용하다보면 버전별 로그 변경 내용을 문서화하고 공유하는 것도 큰 일이다. 로그랩을 통해 설계/운용되는 로그는 텍스트 형식인 랩 파일 안에 로그 구조의 모든 것이 표현되기에, 텍스트 파일의 차이를 비교하는 diff 등의 툴로 랩 파일을 비교하면 버전별 로그 구조의 차이를 간단히 표현할 수 있다. 로그 변경 이력을 수작업으로 기록할 필요가 사라지는 것이다.

추가적으로, 랩 파일의 domain 요소 아래 version 을 선택적으로 기술할 수 있는데, 이것을 이용하면 html 명령으로 생성하는 HTML 파일의 타이틀에 버전 정보가 추가되기에 문서 구분에 도움이 될 수 있다.

디버그 로그는 어디에?

게임 업계에서는 분석의 대상이 되는 주요 이벤트의 로그를 운영 (Operation) 로그 라 하고, 개발자가 디버깅을 위해 남기는 로그를 디버그 (Debug) 로그 로 구분하여 부르는 경우가 많다. 디버그 로그에는 많은 필드가 필요하지 않으며, 개발자가 자유롭게 기술할 수 있는 문자열 필드 하나가 중심이 된다.

로그의 용량 및 용도 측면에서는 운영 로그와 디버그 로그를 별도의 파일에 남기는 것이 맞다고 볼 수 있지만, 두 종류의 로그가 하나의 파일에 있으면 디버깅에는 더 유리할 수 있어 같은 파일에 남기는 것을 선호할 수도 있겠다.

이 선택은 서비스 특성에 맞게 결정하면 되겠다.

참고

만약 운영 로그와 디버그 로그를 하나의 파일에 기록하려면, 랩 파일에 디버그 로그용 이벤트를 하나 추가해 사용하면 되겠다. 아래에 소개하는 MMORPG 예제의 Debug 이벤트를 참고하자.

MMORPG 위한 예제

로그랩을 큰 프로젝트에 사용할 때 참고할 만한 예제가 있으면 도움이 될 것이다. 아래는 MMORPG 게임의 주요 이벤트들을 로그랩으로 기술한 것이다 (로그랩 코드의 example 디렉토리에서 확인할 수 있다).

랩 파일 : https://raw.githubusercontent.com/haje01/loglab/master/example/rpg.lab.json

HTML 보기 : https://raw.githack.com/haje01/loglab/master/example/rpg.html

몇 가지 구성 측면의 특징을 설명하면,

증가/감소는 하나의 이벤트로

기본 필드는 변하지 않고 수량만 증가 또는 감소하는 이벤트들이 있다. 예를 들어 아이템의 경우 증가하거나 감소할 수 있는데 이것을 각각 별도 이벤트로 만들지 않고, 아이템 변화 ItemChg 이벤트 하나를 만들고 변화량 Change 에 +/- 값을 주는 식으로 구현하였다.

ID 와 코드의 구분

앞에서도 언급했지만, 개별 개체를 구분할 때는 아이디 Id 를, 미리 정의된 특정 범주값을 나타낼 때는 코드 Cd 를 필드의 접미어로 사용했다. 예로 특정 아이템에 대해 ItemId 는 그 아이템 개체를 식별하기 위한 값이고, ItemCd 는 그 아이템이 어떤 종류인지 분류하기 위한 값이다. Id 는 임의값으로 유니크하면 되고, Cd 는 미리 정의된 값으로 문서화된 설명이 있어야 한다.

맵 코드와 좌표

게임내 특정 지역에서 발생하는 이벤트를 위해 맵 코드와 위치 좌표 필드를 포함하였다. 예제에서는 계정이나 시스템 등 맵상에서가 아닌 이벤트들도 함께 다루기 위해 옵션으로 설정하였으나, 가능한 경우 꼭 기록하는 것이 분석에 도움이 된다.

링크 ID 이용

하나의 사건에서 여러 로그 이벤트가 발생하는 경우가 있다. 예를 들어 거래소에서 아이템을 구매하는 경우 아이템은 들어오고 돈은 빠져나가야 한다. 로그 측면에서는 아이템 증가 로그와 돈 감소 로그가 함께 남아야 하는 것이다.

이런 경우 분석을 위해서는 그 사건의 연관 로그들을 찾아볼 수 있어야 하는데, 필자는 링크 ID 방식을 추천한다. 링크 ID 는 사건 발생 시점에서 랜덤 정수 하나를 만들고 (트랜잭션 ID 등도 가능하겠다), 그것을 연관된 로그들의 같은 필드 (예제에서는 LinkRd) 에 기입하는 방식이다. 랜덤한 정수는 웬만해서는 일치하기 힘들기에, 비슷한 시간대에 발생한 연관 로그들을 찾기에는 충분한 식별력을 가진다.

참고

일부 서비스들은 이런 경우 다양한 관련 이벤트 정보를 하나의 필드에 뭉쳐서 넣는 방식을 취하는데, 파싱이 힘들고 확장이 어려워 좋은 방법은 아닌 것 같다.

캐릭터 싱크 로그

싱크 로그는 서버에서 정기적 (예: 5 분) 으로 캐릭터의 정보를 로그로 출력하는 것이다 (일종의 스냅샷). 예제에서는 캐릭터 상태 CharSync 와 캐릭터 머니 상태 CharMoneySync 이벤트로 구성하였다. 머니는 종류에 따라 다양할 수 있기에 별도 이벤트로 분리하였고 LinkRd 로 연결해서 보도록 하였다. 싱크 로그는 게임내 이상현상이나 어뷰징 탐지에 활용될 수 있다.

이 예제의 방식이 절대적인 것은 아니며, 어디까지나 로그랩의 활용에 참고가 되었으면 한다.