본문 바로가기
C++ 200제/코딩 IT 정보

[디자인패턴] Qt 싱글톤 패턴, 로그 생성 프로그램

by vicddory 2017. 11. 20.

[디자인패턴] Qt 싱글톤 패턴, 로그 생성 프로그램


Qt에서 디자인패턴 중 싱글톤 패턴 기반으로 로그 파일을 생성하는 프로그램입니다. Qt도 C++을 기초로 제작된 언어라 싱글톤 패턴 소스는 C++의 것을 그대로 사용했습니다. 특별한 경우가 아닌 이상, C++ 디자인패턴 소스는 Qt에서도 구동됩니다.


이 포스트의 전체 프로젝트 파일은 아래 링크를 누르시면 받을 수 있습니다.


[디자인패턴] Qt 싱글톤패턴 프로젝트 - SingletonFileIO.zip [링크]


[디자인패턴] Qt 싱글톤 패턴, 로그 생성 프로그램[Singleton design pattern Qt]


우선, 실행 화면부터 보시죠. 메인 화면에는 메시지 레벨 3단계가 표시됩니다. UI는 꽤 단순하죠? 로그를 생성할 때, 메시지 레벨이 존재합니다. 위의 버튼에 보이는 ERROR, WARNING, INFORMATION으로 총 3개입니다.


디자인패턴 프로그램 코드상에서 레벨을 지정해 Qt Creator의 디버그 창에서만 메시지를 찍을 것인지, 해당 내용을 로그 파일에 남길 것인지 결정할 수 있습니다.


그리고 레벨 3단계는 로그 파일에도 그대로 찍히게 됩니다.


디자인패턴 Qt강좌 싱글톤[Singleton design pattern Qt]

위의 그림 중 왼쪽은 Qt Creator의 디버그 창(Application Output, 편하게 디버그 창으로 부름)입니다.


우측은 실제 프로그램이고요. 버튼을 누를 때마다 좌측처럼 메시지가 출력됩니다. 저 출력되는 메시지가 파일에도 쓰이죠. 물론, 위에서 설명했다시피, 설정에 따라 파일에 쓰이는 메시지는 매번 다를 수 있습니다.


싱글톤 Qt 강좌 디자인패턴[Singleton design pattern Qt]


디자인패턴 중 싱글톤 패턴으로 생성한 파일입니다. 파일 내용을 알려드리겠습니다.


15 - 시時

23 - 분分

36 - 초秒

135 - 밀리 세컨드 3자리


[ERROR] - 로그 레벨


print - 그냥 찍어본거


153 - 인자1

55 - 인자2

asdfasdf - 인자3


간단하죠? 이젠 소스를 조금 살펴보겠습니다.


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
#include "mainwindow.h"
#include "ui_mainwindow.h"
 
// [디자인패턴] 싱글턴 패턴 사용을 위한 전방 선언
bool LogSingleton::destroyed = false;
LogSingleton* LogSingleton::pInst = 0;
 
// 메인 시작
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
 
    LogSingleton::getInstance().SetDir("C:/");
    LogSingleton::getInstance().SetLevel(LEVEL_WARNING);
    LogSingleton::getInstance().SetName("test");
    LogSingleton::getInstance().SetDate("yyyyMMdd");
    LogSingleton::getInstance().SetTime("hhmmsszzz");
 
 
    LogSingleton::getInstance().Print(LEVEL_ERROR, "print %d %d %s"15355"asdfasdf");
 
    // 임시 버튼 이벤트, 싱글턴 패턴
    connect(ui->btn_err_, &QPushButton::clicked, this, [=]() {
        LogSingleton::getInstance().Print(LEVEL_ERROR, "print %d %d %s"15355"asdfasdf");
    });
 
    connect(ui->btn_war_, &QPushButton::clicked, this, [=]() {
        LogSingleton::getInstance().Print(LEVEL_WARNING, "print %d %d %s"15355"asdfasdf");
    });
 
    connect(ui->btn_info_, &QPushButton::clicked, this, [=]() {
        LogSingleton::getInstance().Print(LEVEL_INFORMATION, "print %d %d %s"15355"asdfasdf");
    });
}
 
MainWindow::~MainWindow()
{
    delete ui;
}
cs


뭐.. 특별한 건 없어요. 핵심적인 건 LogSingleton의 Set() 함수들입니다.

파일을 생성한 디렉터리와 로그 파일에 기록할 로그 레벨을 설정해야 합니다.


1
2
3
4
5
6
7
8
#ifndef COMMON_H
#define COMMON_H
 
enum WriteLevel {
    LEVEL_ERROR = 0,
    LEVEL_WARNING,
    LEVEL_INFORMATION
};
cs


저는 로그 레벨을 경고 단계까지 주었습니다.


그러면 INFORMATION에 해당하는 메시지는 디버그 창에만 뜨고, 실제 파일엔 쓰이지 않습니다. 나머진... 뭐 ... 그냥저냥 설정하면 되고요.


1
2
3
4
5
LogSingleton::getInstance().SetDir("C:/");
LogSingleton::getInstance().SetLevel(LEVEL_WARNING);
LogSingleton::getInstance().SetName("test");
LogSingleton::getInstance().SetDate("yyyyMMdd");
LogSingleton::getInstance().SetTime("hhmmsszzz");
cs


로그 파일은 LogFile 클래스에 존재하는데 아래처럼 되어 있어요.


실제로 이 코드를 사용하실 분들은 여기서 로그 파일의 스타일을 꾸미시면 됩니다.


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
#ifndef LOGFILE_H
#define LOGFILE_H
 
#include <QDebug>
#include <QString>
#include <QDateTime>
 
#include <QFile>
#include <QTextStream>
 
#include "Common.h"
 
class LogFile // 디자인패턴, 싱글턴 패턴
{
    // Log 타입
    struct status {
        QString dir;
        int level;
        QString name;
        QString date;
        QString time;
    };
 
public:
    LogFile();
 
private:
    typedef status Status;
    Status status_;
 
public// setter
    void Dir(const QString dir) {
        status_.dir.clear();
        status_.dir.append(dir);
    }
 
    void Level(const int level) {
        status_.level = level;
    }
 
    void Name(const QString name) {
        status_.name.clear();
        status_.name.append(name);
    }
 
    void Date(const QString date) {
        status_.date.clear();
        status_.date.append(date);
    }
 
    void Time(const QString time) {
        status_.time.clear();
        status_.time.append(time);
    }
 
public:
    void Print(const int level, const QString msg);
};
 
#endif // LOGFILE_H
cs


typedef 키워드는 정말 마음에 들어요. 내 마음대로 주무를 수 있다는 것이 이토록 좋을 줄이야 ㅎㅎㅎ.


싱글톤 싱글턴 패턴 Qt[Singleton design pattern Qt]


근데, 한 가지 문제가 있습니다. Qt 5.3.2를 기준으로 C++11에서 지원하는 가변 인자 템플릿이 적용되질 않습니다. 인자의 수를 정해놓고 사용하는 데엔 문제가 없지만, 인자의 수를 정해놓지 않으면 에러가 발생하네요. (이거 때문에 디자인패턴 구성이 조금 망가짐)


아마도 5.3.2에서는 지원이 안 되나 봅니다. 저는 이런저런 시도를 해보았으나 자꾸 안되길래 접었어요. 이 부분에 대해서 아는 분들은 댓글이나 다른 포스트로 알려주시면 감사하겠습니다.

그래서, 어쩔 수 없이 C++에 C 코드를 넣었습니다. 아래처럼요.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdlib.h>        /* for malloc, NULL, size_t */
#include <stdarg.h>        /* for va_ stuff */
#include <string.h>        /* for strcat et al. */
 
.....
.....
 
public// 출력 메시지 전달
    void Print(const int level, char* err, ... ) {
        char log_content[1024= {0,};
 
        va_list ap;
        va_start(ap, err);
        vsprintf_s(log_content + strlen(log_content), 1024, err, ap);
        va_end(ap);
 
        log_file_->Print(level, log_content);
    }
cs


C언어에서는 가변인자를 이용할 수 있도록 va_list란 함수를 제공합니다. 소스는 간단해요.


필요에 따라 2개, 또는 3개의 헤더 파일을 인클루드하고, 위에 나온 소스를 그대로 사용하시면 됩니다. 이렇게 사용하시면, 인자의 개수에 상관없이 Print() 함수를 이용할 수 있어요. 개발자에도 유용한 함수죠.


Singleton 싱글톤 싱글턴 패턴 Qt 강좌[[Singleton design pattern Qt] 디자인패턴] 싱글톤 패턴 개념


마지막으로 싱글톤 패턴, 디자인패턴 구현 소스.


저는 여기서 참조 했습니다(C++에서 싱글톤 패턴 구현하기 [링크]). 이글루스 gimmesilver란 분이 작성해 놓은 코드가 좋아서 그대로 사용했으니, 다른 분들도 참조해 보세요.


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
#ifndef LOGSINGLETON_H /* 디자인패턴 클래스 */
#define LOGSINGLETON_H
 
#include <QDebug>
#include <stdio.h>
#include <stdlib.h>        /* for malloc, NULL, size_t */
#include <stdarg.h>        /* for va_ stuff */
#include <string.h>        /* for strcat et al. */
 
#include "logfile.h"
#include "Common.h"
 
class LogSingleton { // 싱글턴 패턴
public:
    static LogSingleton &getInstance() {
        if (destroyed) {
            new(pInst) LogSingleton; // 2)
            atexit(killSingleton);
 
            destroyed = false;
        } else if (pInst == 0) {
            create();
        }
 
        return *pInst;
    }
 
private:
    LogSingleton() { log_file_ = new LogFile(); }
    LogSingleton(const LogSingleton & other);
 
    ~LogSingleton() {
        delete log_file_;
 
        destroyed = true;  // 1)
    }
 
    static void create() {
        static LogSingleton inst;
 
        pInst = &inst;
    }
 
    static void killSingleton() {
        pInst->~LogSingleton();  // 3)
    }
 
    static bool destroyed;
    static LogSingleton *pInst;
 
    LogFile *log_file_;
 
public// 로그를 찍는 상태를 설정
    void SetDir(const QString dir) { log_file_->Dir(dir);}
    void SetLevel(const int level) { log_file_->Level(level); }
    void SetName(const QString name) { log_file_->Name(name); }
    void SetDate(const QString date) { log_file_->Date(date); }
    void SetTime(const QString time) {log_file_->Time(time); }
 
public// 출력 메시지 전달
    void Print(const int level, char* err, ... ) {
        char log_content[1024= {0,};
 
        va_list ap;
        va_start(ap, err);
        vsprintf_s(log_content + strlen(log_content), 1024, err, ap);
        va_end(ap);
 
        log_file_->Print(level, log_content);
    }
};
 
#endif // LOGSINGLETON_H
cs


잘 이해가 안 되는 부분은 댓글로 적어주세요.


[디자인패턴] Qt 싱글톤 패턴, 로그 생성 프로그램

댓글