티스토리 뷰

C#/C# 강좌 Winform

Doridori C# 강의 3) 65. Logging (log4net)

꼬꼬마도리도리 2022. 11. 24. 22:28

======================================================

안녕하세요 Doridori 입니다.

으아아아아!!!!!

회사 일이 너무너무너무너무 많습니다.

정신이 하나도 없는 하루하루를 보내고 있습니다. 

(그 와중에 블로그에 술리뷰는 왜 계속 올라오냐 라고 하시면 할말은 없습니다만 . . .)

원래 준비하던 내용은 데이터 들을 수집하는 부분에 크롤링을 섞어 보려고 하였는데 내용 취합 하기도 쉽지 않아,

고민하다가 갑자기 logging이 눈에 띄여서 정리하게 되었습니다.

개인적으로는 사용 할 곳이 많을 것 같아서 만족스럽긴 하네요.

이번 연말까지는 계속 바쁠것 같기는 한데 이것도 하고 저것도 하고 다 해보겠습니다.

모두 화이팅 하십시오!!

======================================================

 

65. Logging (log4net)

UI)

65. Logging (log4net) (UI).zip
4.31MB

 

Soruce)

65. Logging (log4net).zip
4.32MB

 

교재)

65. Logging (log4net).pdf
0.52MB

Config)

log4net.config
0.01MB

 

 

 

프로그램을 하면서 로그 관련된 이야기만하고 화면에 보여지는 부분을 위한 Logging Function 정도만 만들어서 사용 했는데 많이 사용되는 Open-Source Lib가 있어서 가져왔습니다. 

보통 각 회의사의 솔루션에는 Logging을 담당하는 Lib들이 이미 존재 하기 때문에 별도로 구성 할 필요가 없는 경우가 많지만 개인이 처음부터 Logging 관련 된 Logic을 구현 하려고 하면 까다로운 구간이 많을 것입니다. 

해당 구간에 대한 경험치를 가지고 있는 분들이 미리 좋은 길을 만들어 놓았는데 굳이 사용하지 않을 이유가 없다 라는게 제 생각 입니다.

사용해보면서 Logging에 대한 노하우를 터득해보는것도 많은 도움이 될것이라고 생각 합니다. 

Source 자체는 몇줄 안되기 때문에 Setting 하는 순서를 익히는 것이 중요 할 듯 합니다.

UI)

간단하게 TextBox에 Log 내용을 쓰고 Log Level에 따라 필요한 방법으로 Log를 남기는 프로그램을 만들어 봤습니다. 

Log가 어떻게 남는지에 대한 Sample 입니다.

 

강의)

위에서도 적어 놨지만 Source 보다는 Setting 순서를 아는 것이 중요하다고 생각 됩니다. 

강의를 하면서 어버버 한구간들이 있기 때문에 강의 뒤 쪽에 세팅 하는 부분만 빠르게 한번 더 정리하고 넘어 갔습니다. 

(끝까지 보시면 도움이 됩니다. ^^v)

 

 

Source)

Main Form)

Main Logic은 별다른 내용이 없습니다. Setting 하는 것이 거의 다 입니다.

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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using log4net;
 
namespace DATA_COLLECTOR
{
    public partial class frmDataCollector : Form
    {
        #region 전역
        private ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);  // Logger 선언
        #endregion
 
        public frmDataCollector()
        {
            InitializeComponent();
        }
 
        #region Form Event
 
        /// <summary>
        /// Form Load Event
        /// Form Loading 시 Control 초기 속성 Setting
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void frmDataCollector_Load(object sender, EventArgs e)
        {
            log.Info("---------------- Start App ----------------");
 
            // Richtextbox 속성 값 Setting
            rtbLogging.BackColor = System.Drawing.Color.White;
            rtbLogging.BorderStyle = BorderStyle.None;
            rtbLogging.HideSelection = false;
            rtbLogging.ReadOnly = true;
            rtbLogging.Text = "";
            rtbLogging.WordWrap = false;
            rtbLogging.Font = new Font(FontFamily.GenericMonospace, 9);
 
        }
 
        /// <summary>
        /// TextBox Log 입력 후 Enter Key로 입력 시 TextBox 입력 값 Logging
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tboxLog_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                fnclogging(tboxLog.Text);
            }
        }
 
        /// <summary>
        /// Logging Button Click 시 TextBox 입력 값 Logging
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnLogging_Click(object sender, EventArgs e)
        {
            fnclogging(tboxLog.Text);
        }
 
        #endregion
 
 
        #region Inner Function
 
        /// <summary>
        /// Radio Button에 선택 된 Level과 log4net setting 값을 기준으로 Logging 하는 부분
        /// </summary>
        /// <param name="strLoggingData"></param>
        private void fnclogging(string strLoggingData)
        {
            if (rdoInfo.Checked)
            {
                log.Info(strLoggingData);
            }
            else if (rdoDebug.Checked)
            {
                log.Debug(strLoggingData);
            }
            else if (rdoWarn.Checked)
            {
                log.Warn(strLoggingData);
            }
            else if (rdoError.Checked)
            {
                log.Error(strLoggingData);
            }
            else if (rdoFatal.Checked)
            {
                log.Fatal(strLoggingData);
            }
        }
 
        #endregion
 
    }
}
 
cs

 

RichTextBoxAppender)

RichTextBox Control에 Logging을 하기 위해 추가 한 Appender 입니다.

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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
using log4net.Appender;
using log4net.Core;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
 
namespace DATA_COLLECTOR
{
    //RechTextBox와 Log4net Config를 연결 하기 위한 Appender
    class RichTextBoxAppender : AppenderSkeleton
    {
        private RichTextBox _textBox;
        public RichTextBox AppenderTextBox { get { return _textBox; } set { _textBox = value; } }
        public string FormName { get; set; }
        public string TextBoxName { get; set; }
 
        private static Control FindControlRecursive(Control root, string textBoxName)
        {
            if (root.Name == textBoxName) return root;
            foreach (Control c in root.Controls)
            {
                var t = FindControlRecursive(c, textBoxName);
                if (t != nullreturn t;
            }
            return null;
        }
 
        protected override void Append(LoggingEvent loggingEvent)
        {
            if (_textBox == null)
            {
                if (string.IsNullOrEmpty(FormName) || string.IsNullOrEmpty(TextBoxName)) return;
 
                var form = Application.OpenForms[FormName];
                if (form == nullreturn;
 
                _textBox = (RichTextBox)FindControlRecursive(form, TextBoxName);
                if (_textBox == nullreturn;
 
                form.FormClosing += (s, e) => _textBox = null;
            }
            _textBox.BeginInvoke((MethodInvoker)delegate
            {
                if (loggingEvent.Level == Level.Debug)
                {
                    _textBox.SelectionStart = _textBox.TextLength;
                    _textBox.SelectionLength = 0;
                    _textBox.SelectionColor = Color.RoyalBlue;
                    _textBox.AppendText(RenderLoggingEvent(loggingEvent));
                    _textBox.SelectionColor = _textBox.ForeColor;
                }
                else if (loggingEvent.Level == Level.Info)
                {
                    _textBox.SelectionStart = _textBox.TextLength;
                    _textBox.SelectionLength = 0;
                    _textBox.SelectionColor = Color.ForestGreen;
                    _textBox.AppendText(RenderLoggingEvent(loggingEvent));
                    _textBox.SelectionColor = _textBox.ForeColor;
                }
                else if (loggingEvent.Level == Level.Warn)
                {
                    _textBox.SelectionStart = _textBox.TextLength;
                    _textBox.SelectionLength = 0;
                    _textBox.SelectionColor = Color.DarkOrange;
                    _textBox.AppendText(RenderLoggingEvent(loggingEvent));
                    _textBox.SelectionColor = _textBox.ForeColor;
                }
                else if (loggingEvent.Level == Level.Error)
                {
                    _textBox.SelectionStart = _textBox.TextLength;
                    _textBox.SelectionLength = 0;
                    _textBox.SelectionColor = Color.DarkRed;
                    _textBox.AppendText(RenderLoggingEvent(loggingEvent));
                    _textBox.SelectionColor = _textBox.ForeColor;
                }
                else if (loggingEvent.Level == Level.Fatal)
                {
                    _textBox.SelectionStart = _textBox.TextLength;
                    _textBox.SelectionLength = 0;
                    _textBox.SelectionColor = Color.Crimson;
                    _textBox.AppendText(RenderLoggingEvent(loggingEvent));
                    _textBox.SelectionColor = _textBox.ForeColor;
                }
                else
                {
                    _textBox.AppendText(RenderLoggingEvent(loggingEvent));
                }
            });
        }
    }
}
 
cs

 

log4net Config)

가장 중요한 log4net 설정 파일 입니다.

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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <log4net>
 
    <!-- 컨트롤 + K + F 코드정렬 -->
    
    <!-- Console 화면에 Log 출력 -->
    <appender name="console" type="log4net.Appender.ConsoleAppender">
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%date %level %logger - %message%newline" />
      </layout>
    </appender>
 
    <!-- 지정한 Folder에 Log 파일을 기록 -->
    <appender name="file_app" type="log4net.Appender.RollingFileAppender">
      <file value=".\FileLog\" />
      <appendToFile value="true" />
      <datePattern value="yyyy-MM-dd'_app.log'" />
      <rollingStyle value="Date" />
      <staticLogFileName value="false" />
      <filter type="log4net.Filter.LevelRangeFilter">
        <param name="LevelMin" value="DEBUG" />
        <param name="LevelMax" value="FATAL" />
      </filter>
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%date [%thread] %level %logger - %message%newline" />
      </layout>
    </appender>
 
    <!-- 지정한 Folder에 Log 파일을 기록 (Error 관련 Log를 별도로 기록 하기 위해 File을 별도 생성) -->
    <appender name="file_fatal" type="log4net.Appender.RollingFileAppender">
      <file value=".\FileLog\" />
      <appendToFile value="true" />
      <datePattern value="yyyy-MM-dd'_fatal.log'" />
      <rollingStyle value="Date" />
      <staticLogFileName value="false" />
      <filter type="log4net.Filter.LevelRangeFilter">
        <param name="LevelMin" value="ERROR" />
        <param name="LevelMax" value="FATAL" />
      </filter>
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%date [%thread] %level %logger - %message%newline" />
      </layout>
    </appender>
 
    <!-- 지정 된 Access DB의 Table에 Log를 기록 -->
    <appender name="db_access" type="log4net.Appender.AdoNetAppender">
      <connectionString value="Provider=Microsoft.ACE.OLEDB.12.0;Data Source=.\DBLog\log.mdb;"/>
      <!--  <connectionString value="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\log\access.mdb;User Id=;Password=;" />     -->
      <commandText value="INSERT INTO Log ([Date],[Thread],[Level],[Logger],[Message]) VALUES (@log_date, @thread, @log_level, @logger, @message)" />
      <parameter>
        <parameterName value="@log_date" />
        <dbType value="String" />
        <size value="255" />
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%date" />
        </layout>
      </parameter>
      <parameter>
        <parameterName value="@thread" />
        <dbType value="String" />
        <size value="255" />
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%thread" />
        </layout>
      </parameter>
      <parameter>
        <parameterName value="@log_level" />
        <dbType value="String" />
        <size value="50" />
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%level" />
        </layout>
      </parameter>
      <parameter>
        <parameterName value="@logger" />
        <dbType value="String" />
        <size value="255" />
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%logger" />
        </layout>
      </parameter>
      <parameter>
        <parameterName value="@message" />
        <dbType value="String" />
        <size value="1024" />
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%message" />
        </layout>
      </parameter>
    </appender>
 
    <!-- 지정 된 Mail로 Log를 전송 -->
    <appender name="email_send" type="log4net.Appender.SmtpAppender">
      <to value="minidoridori@naver.com" />
      <from value="minidoridori@naver.com" />
      <subject value="test logging message" />
      <smtpHost value="smtp.naver.com" />
      <authentication value="Basic" />
      <port value="587" />
      <username value="사용자ID" />
      <password value="비밀번호" />
      <bufferSize value="10" />
      <EnableSsl value="true"/>
      <lossy value="true" />
      <threshold value="DEBUG" />
      <filter type="log4net.Filter.LevelRangeFilter">
        <param name="LevelMin" value="ERROR" />
        <param name="LevelMax" value="FATAL" />
      </filter>
      <evaluator type="log4net.Core.LevelEvaluator">
        <threshold value="WARN"/>
      </evaluator>
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%newline%date [%thread] %-5level %logger [%property{NDC}] - %message%newline%newline%newline" />
      </layout>
    </appender>
 
    <!-- 화면의 Control으로 Log를 전송 (Class를 생성 후 Appender Class를 호출하여 사용) -->
    <appender name="rtb_log" type="DATA_COLLECTOR.RichTextBoxAppender, DATA_COLLECTOR">
      <formName value="frmDataCollector"/>
      <textBoxName value="rtbLogging"/>
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%date %-5level - %message%newline" />
      </layout>
    </appender>
 
    <!-- Appender Setting Name을 연결 하는 부분 -->
    <root>
      <!--  OFF - nothing gets logged (cannot be called) > FATAL > ERROR > WARN > INFO > DEBUG > ALL - everything gets logged (cannot be called)  -->
      <level value="ALL"/>
      <appender-ref ref="console"/>
      <appender-ref ref="file_app"/>
      <appender-ref ref="file_fatal"/>
      <appender-ref ref="db_access"/>
      <appender-ref ref="email_send"/>
      <appender-ref ref="rtb_log"/>
    </root>
 
 
    <!-- 지정한 Folder에 Log 파일을 기록 시 날짜 외 File 용량 기준으로 개별 파일을 생성 하기 위한 부분 -->
    <!--
    <appender name="fatal_file" type="log4net.Appender.RollingFileAppender"> 
      <file value="log\" />
      <appendToFile value="true" />
      <datePattern value="yyyy-MM-dd'_fatal.log'" />
      <rollingStyle value="Size" />
      <maxSizeRollBackups value="5" />
      <maximumFileSize value="100MB" />
      <staticLogFileName value="true" />
      <filter type="log4net.Filter.LevelRangeFilter">
        <param name="LevelMin" value="FATAL" />
        <param name="LevelMax" value="FATAL" />
      </filter>
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%date [%thread] %level %logger - %message%newline" />
      </layout>
    </appender>
    -->
 
  </log4net>
</configuration>
cs
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
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
글 보관함