안녕하세요.
웹훅 API를 지원하는 도구 중 채널기반 메시징 도구인 메타모스트(Mattermost)를 이용해 C# 프로그램에서 발생하는 로그를 특정 채널로 수신할 수 있는 방법을 소개하겠습니다.
프로그램을 배포해서 어딘가에 구축하면 구축정보가 개발자에게 제대로 전달이 잘 안되는 경우가 있습니다. 제가 직접 배포 부터 구축까지 관리를 하는 상황이라면 체계를 만들어서 시키겠는데 그런 상황이 아니면 꽤나 답답하게 환경정보 없이 버그를 수정해야 하는 난감한 상황이 발생합니다.
Mattermost로 소개하고 있지만 대신 로그 수신도구를 통해 통계나 대시보드로 활용할 수도 있고 별도 API서버를 통해 체계적으로 관리할 수도 있을 것 같네요.
Gitlab과 Mattermost 연동하면서 갑자기 C#로그를 띄어보면 어떨까 하는 생각에 두서없이 진행하였습니다.
자 시작해볼까요?
Nuget에서 Mattermost로 검색해보면 몇 개 튀어 나옵니다.
그러나 약간씩 용도가 달라요. 그러니 "Matterhook.NET.MatterhookClient"를 설치해줍니다.
(저는 Github에 있는 소스를 내려 받았어요)
그리고 아래 클래스를 적당한 곳에 추가합니다.
using Matterhook.NET.MatterhookClient;
using System;
using System.Text;
using System.Threading.Tasks;
namespace EnjoyDev
{
public class MattermostHelper
{
MatterhookClient Error;
string _AppName { get; set; }
string _InstitutionName { get; set; }
string _AppVersion { get; set; }
public MattermostHelper(string ErrorWebhookUri, string AppName = "Spider", string AppVer = "1.0.0.0", string InstitutionName = "HNT Medical")
{
if (string.IsNullOrEmpty(ErrorWebhookUri)) Error = new MatterhookClient(ErrorWebhookUri);
this._AppName = AppName;
this._InstitutionName = InstitutionName;
this._AppVersion = AppVer;
}
// 시스템 정보 찍어주기
private string MakeMessage(string Message)
{
SpiderSystemInfo sysInfo = new SpiderSystemInfo();
string strSys = "";
strSys += sysInfo.getOperatingSystemInfo();
strSys += sysInfo.getProcessorInfo();
strSys += sysInfo.getMemoryInfo();
StringBuilder sb = new StringBuilder();
sb.AppendLine("## " + _InstitutionName);
sb.AppendLine("");
sb.AppendLine(string.Format("### {0} ({1})", _AppName, _AppVersion));
sb.AppendLine("");
sb.AppendLine("### System Information");
sb.AppendLine("");
sb.AppendLine(" + IP Address: " + SpiderNetwork.GetIPAddresses());
sb.AppendLine("");
sb.AppendLine("```");
sb.AppendLine(strSys);
sb.AppendLine("```");
sb.AppendLine("");
sb.AppendLine(Message);
if (sb.ToString().Length > 4000)
{
string str = sb.ToString().Substring(0, 4000);
return str;
}
return sb.ToString();
}
// 에러로그 - 아래와 다른점은 Message를 코드블록(```메시지```)으로 감싸줍니다.
public void ErrorRemoteLog(string Message)
{
try
{
if (Error == null) return;
Message = "```" + Environment.NewLine + Message + Environment.NewLine + "```";
MattermostMessage message = new MattermostMessage()
{
Text = MakeMessage(Message),
Username = _AppName,
IconUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/0/05/Robot_icon.svg/2000px-Robot_icon.svg.png",
};
Task.WaitAll(Error.PostAsync(message));
}
catch { }
}
// 실패로그
public void FailRemoteLog(string Message)
{
try
{
if (Error == null) return;
MattermostMessage message = new MattermostMessage()
{
Text = MakeMessage(Message),
Username = _AppName,
IconUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/0/05/Robot_icon.svg/2000px-Robot_icon.svg.png",
};
Task.WaitAll(Error.PostAsync(message));
}
catch { }
}
}
public class SpiderSystemInfo
{
public string getOperatingSystemInfo()
{
StringBuilder sb = new StringBuilder();
sb.AppendLine();
sb.AppendLine("Displaying operating system info....");
//Create an object of ManagementObjectSearcher class and pass query as parameter.
ManagementObjectSearcher mos = new ManagementObjectSearcher("select * from Win32_OperatingSystem");
foreach (ManagementObject managementObject in mos.Get())
{
if (managementObject["Caption"] != null)
{
sb.AppendLine("Operating System Name : " + managementObject["Caption"].ToString()); //Display operating system caption
}
if (managementObject["OSArchitecture"] != null)
{
sb.AppendLine("Operating System Architecture : " + managementObject["OSArchitecture"].ToString()); //Display operating system architecture.
}
if (managementObject["CSDVersion"] != null)
{
sb.AppendLine("Operating System Service Pack : " + managementObject["CSDVersion"].ToString()); //Display operating system version.
}
}
return sb.ToString();
}
public string getMemoryInfo()
{
var wmi = new ManagementObjectSearcher("select * from Win32_OperatingSystem");
ResourceUsageInfo info = wmi.Get().Cast<ManagementObject>().Select(mo => new ResourceUsageInfo()
{
TotalSize = ulong.Parse(mo["TotalVisibleMemorySize"].ToString()),
FreeSize = ulong.Parse(mo["FreePhysicalMemory"].ToString()),
}).FirstOrDefault();
string MemoryInfo = string.Format("\n\rTotal Memory Information : {0} Mbyte", info.TotalSize / 1024);
return MemoryInfo;
}
public string getProcessorInfo()
{
StringBuilder sb = new StringBuilder();
sb.AppendLine();
sb.AppendLine("Displaying Processor Name....");
RegistryKey processor_name = Registry.LocalMachine.OpenSubKey(@"Hardware\Description\System\CentralProcessor\0", RegistryKeyPermissionCheck.ReadSubTree); //This registry entry contains entry for processor info.
if (processor_name != null)
{
if (processor_name.GetValue("ProcessorNameString") != null)
{
sb.AppendLine(processor_name.GetValue("ProcessorNameString").ToString()); //Display processor ingo.
}
}
return sb.ToString();
}
}
}
우선 여기까지 C#은 세팅해놓고, 메타모스트 Webhook API를 가져오는 방법을 설명할께요.
당연히 관리권한이 있어야 하며, 저는 메타모스트를 직접 서버에 구축했습니다.
먼저 수신 받을 팀과 채널을 먼저 생성해주셔야 해요.
그리고나서 메타모스트 왼쪽 상단 아이콘을 클릭하면 나오는 메뉴에서 통합을 클릭합니다.
요런 화면이 뜨는데 우리는 수신을 받아야 하기 때문에 Incoming Webhook를 클릭합니다.
그 화면에서 "Incoming Webhook 추가하기"를 눌러주세요.
제목, 설명은 적당하게 이름을 지어줍니다.
채널은 수신받을 채널을 선택해주세요. "이 채널로 고정"은 무조건 선택해주세요. 고정을 하지 않을 경우 API로 채널을 직접 넣는 방식이 가능할 것으로 보는데, 저는 그렇게 테스트하지는 않았어요.
사용자 이름은 채널에 메시지가 추가될 때 사용하는 이름입니다. 적당하게 넣어주세요.
프로필 사진은 넣지 않아도 됩니다.
다 끝났어요.
이제 다시 C# 프로그램으로 돌아가서 Logging 사용법을 알려드릴께요.
저는 전역으로 사용하기 위해서 Static으로 클래스를 하나 설정해서 사용합니다.
public static MattermostHelper mattermost;
그리고 프로그램 초기화 하는 곳에서 저 변수를 초기화 해줍니다.
mattermost = new MattermostHelper({위에서 추가한 Webhook API를 넣어주세요.},
Application.ProductName,
string.Format("{0}", Application.ProductVersion),
"사이트명");
API, 현재 프로그램 이름, 버전정보, 사이트 정보를 넣어줍니다.
실제 로깅하는 예제입니다.
// Try~Catch문에서 찍어준 로그입니다.
GlobalClass.mattermost.ErrorRemoteLog(ex.Message +
Environment.NewLine +
Environment.NewLine +
ex.StackTrace);
아래는 메타모스트에 수신된 모습이에요. 지워진 거는 사이트 정보 및 제품이름/버전입니다.
이것의 단점은 인터넷이 안되면 안된다는 것. 망분리 된 곳은 안에서 나가는 것은 보통 허용하기 때문에 뚫어주면 되지 않을까 예상해봅니다.
'개발 > C#' 카테고리의 다른 글
[2022.09] 비프 음을 Stop시킬 때까지 내게 하기 (1) | 2022.09.21 |
---|---|
[2022.09] 맥 어드레스를 이용한 라이선스 기법 (0) | 2022.09.20 |
[2022.08] C# Dapper 불특정 테이블을 조회할 때 (0) | 2022.08.12 |
[2022.02] MSMQ를 이용해 프로세스 간 메시지 송/수신 처리하기 (0) | 2022.02.22 |
[2021.09] fo-DICOM Log4Net 사용방법 (0) | 2021.09.06 |