はじめに
プリザンターの1.4.11.0からログをデータベースのSysLogs
テーブルだけではなく、テキストファイルに出力する機能が追加されました。この追加機能を応用して、テキストファイル以外へのログ出力機能を拡張してみましょう。
実装をみてみる
C#のコード側を見てみる
今回の追加機能、実はNLogで実装されています。ロガーの実装部分を見てみましょう。プリザンターのロギング機能は、Implem.Pleasanter.Models.SysLogModel
にひとまとめで実装されています。
private static readonly Logger logger = LogManager.GetLogger("syslogs");
この部分が NLogロガーの定義部分です。ここを見るとsyslogs
という名前のNLogロガーが定義されていることが分かります。
このNLogロガーに対してログ出力が実装されています。では実際に出力の実装を見てみましょう。
if (Parameters.SysLog.EnableLoggingToFile)
{
logger.ForLogEvent(SysLogType != SysLogTypes.Info ? LogLevel.Error : LogLevel.Info)
.Message("UpdateSysLog")
.Property("syslog", ToLogModel(this))
.Log();
}
if (Parameters.SysLog.EnableLoggingToFile)
{
logger.ForLogEvent(sysLogType != SysLogTypes.Info ? LogLevel.Error : LogLevel.Info)
.Message("WriteSysLog")
.Property("syslog", ToLogModel(this))
.Log();
}
パラメータのSysLogs
のEnableLoggingToFile
がtrue
の時のみ、ロガーが使われるようになっています。
いずれも出力されるメッセージが異なるのみで、カスタムプロパティsyslog
に対して、プリザンター内部のログモデルSysLogModel
がセットされています。今回はSysLogModel
の中でこの処理が呼ばれているため、SysLogModel
をthis
で指しています。ToLogModel
メソッドでは渡されたSysLogModel
をSysLogLogModel
に変換しているだけです。
public static SysLogLogModel ToLogModel(SysLogModel s)
{
return new SysLogLogModel
{
CreatedTime = s.StartTime,
SysLogId = s.SysLogId,
Ver = s.Ver,
SysLogType = s.SysLogType.ToInt(),
OnAzure = s.OnAzure,
MachineName = s.MachineName,
ServiceName = s.ServiceName,
TenantName = s.TenantName,
Application = s.Application,
Class = s.Class,
Method = s.Method,
RequestData = s.RequestData,
HttpMethod = s.HttpMethod,
RequestSize = s.RequestSize,
ResponseSize = s.ResponseSize,
Elapsed = s.Elapsed,
ApplicationAge = s.ApplicationAge,
ApplicationRequestInterval = s.ApplicationRequestInterval,
SessionAge = s.SessionAge,
SessionRequestInterval = s.SessionRequestInterval,
WorkingSet64 = s.WorkingSet64,
VirtualMemorySize64 = s.VirtualMemorySize64,
ProcessId = s.ProcessId,
ProcessName = s.ProcessName,
BasePriority = s.BasePriority,
Url = s.Url,
UrlReferer = s.UrlReferer,
UserHostName = s.UserHostName,
UserHostAddress = s.UserHostAddress,
UserLanguage = s.UserLanguage,
UserAgent = s.UserAgent,
SessionGuid = s.SessionGuid,
ErrMessage = s.ErrMessage,
ErrStackTrace = s.ErrStackTrace,
InDebug = s.InDebug,
AssemblyVersion = s.AssemblyVersion,
Comments = s.Comments.ToJson(),
Creator = s.Creator.Id,
Updator = s.Updator.Id,
UpdatedTime = s.EndTime.Equals(0.ToDateTime()) ? s.StartTime : s.EndTime
};
}
設定ファイルを見てみる
NLogでは実際の出力に関する設定群は外部の設定ファイルに切り出されているのが一般的です。プリザンターでも外部ファイルに設定が保持されています。それがappsettings.json
になります。
これの中身を見てみましょう。
{
"type": null,
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"NLog": {
"throwConfigExceptions": true,
"targets": {
"async": true,
"jsonfile": {
"type": "File",
"fileName": "logs/${date:format=yyyy}/${date:format=MM}/${date:format=dd}/syslogs.json",
"keepFileOpen": false,
"concurrentWrites": true,
"archiveFileName": "logs/${date:format=yyyy}/${date:format=MM}/${date:format=dd}/syslog.{#####}.json",
"archiveAboveSize": 10240,
"archiveNumbering": "Sequence",
"layout": {
"type": "JsonLayout",
"Attributes": [
{
"name": "timestamp",
"layout": "${date:format=O}"
},
{
"name": "level",
"layout": "${level:upperCase=true}"
},
{
"name": "message",
"layout": "${message}"
},
{
"name": "syslog",
"encode": false,
"layout": {
"type": "JsonLayout",
"includeEventProperties": "true"
}
},
{
"name": "exception",
"encode": false,
"layout": {
"type": "jsonLayout",
"Attributes": [
{
"name": "type",
"layout": "${exception:format=type}"
},
{
"name": "message",
"layout": "${exception:format=message}"
},
{
"name": "stacktrace",
"layout": "${exception:format=tostring}"
}
]
}
}
]
}
},
"csvfile": {
"type": "File",
"fileName": "logs/${date:format=yyyy}/${date:format=MM}/${date:format=dd}/syslogs.csv",
"keepFileOpen": false,
"concurrentWrites": true,
"archiveFileName": "logs/${date:format=yyyy}/${date:format=MM}/${date:format=dd}/syslog.{#####}.csv",
"archiveAboveSize": 10240,
"archiveNumbering": "Sequence",
"layout": {
"type": "CSVLayout",
"quoting": "All",
"columns": [
{
"name": "timestamp",
"layout": "${date:format=O}"
},
{
"name": "level",
"layout": "${level:upperCase=true}"
},
{
"name": "message",
"layout": "${message}"
},
{
"name": "CreatedTime",
"layout": "${event-properties:syslog:format=o:objectpath=CreatedTime}"
},
{
"name": "SysLogId",
"layout": "${event-properties:syslog:objectpath=SysLogId}"
},
{
"name": "Ver",
"layout": "${event-properties:syslog:objectpath=Ver}"
},
{
"name": "SysLogType",
"layout": "${event-properties:syslog:objectpath=SysLogType}"
},
{
"name": "OnAzure",
"layout": "${event-properties:syslog:objectpath=OnAzure}"
},
{
"name": "MachineName",
"layout": "${event-properties:syslog:objectpath=MachineName}"
},
{
"name": "ServiceName",
"layout": "${event-properties:syslog:objectpath=ServiceName}"
},
{
"name": "TenantName",
"layout": "${event-properties:syslog:objectpath=TenantName}"
},
{
"name": "Application",
"layout": "${event-properties:syslog:objectpath=Application}"
},
{
"name": "Class",
"layout": "${event-properties:syslog:objectpath=Class}"
},
{
"name": "Method",
"layout": "${event-properties:syslog:objectpath=Method}"
},
{
"name": "Api",
"layout": "${event-properties:syslog:objectpath=Api}"
},
{
"name": "SiteId",
"layout": "${event-properties:syslog:objectpath=SiteId}"
},
{
"name": "ReferenceId",
"layout": "${event-properties:syslog:objectpath=ReferenceId}"
},
{
"name": "ReferenceType",
"layout": "${event-properties:syslog:objectpath=ReferenceType}"
},
{
"name": "Status",
"layout": "${event-properties:syslog:objectpath=Status}"
},
{
"name": "Description",
"layout": "${event-properties:syslog:objectpath=Description}"
},
{
"name": "RequestData",
"layout": "${event-properties:syslog:objectpath=RequestData}"
},
{
"name": "HttpMethod",
"layout": "${event-properties:syslog:objectpath=HttpMethod}"
},
{
"name": "RequestSize",
"layout": "${event-properties:syslog:objectpath=RequestSize}"
},
{
"name": "ResponseSize",
"layout": "${event-properties:syslog:objectpath=ResponseSize}"
},
{
"name": "Elapsed",
"layout": "${event-properties:syslog:objectpath=Elapsed}"
},
{
"name": "ApplicationAge",
"layout": "${event-properties:syslog:objectpath=ApplicationAge}"
},
{
"name": "ApplicationRequestInterval",
"layout": "${event-properties:syslog:objectpath=ApplicationRequestInterval}"
},
{
"name": "SessionAge",
"layout": "${event-properties:syslog:objectpath=SessionAge}"
},
{
"name": "SessionRequestInterval",
"layout": "${event-properties:syslog:objectpath=SessionRequestInterval}"
},
{
"name": "WorkingSet64",
"layout": "${event-properties:syslog:objectpath=WorkingSet64}"
},
{
"name": "VirtualMemorySize64",
"layout": "${event-properties:syslog:objectpath=VirtualMemorySize64}"
},
{
"name": "ProcessId",
"layout": "${event-properties:syslog:objectpath=ProcessId}"
},
{
"name": "ProcessName",
"layout": "${event-properties:syslog:objectpath=ProcessName}"
},
{
"name": "BasePriority",
"layout": "${event-properties:syslog:objectpath=BasePriority}"
},
{
"name": "Url",
"layout": "${event-properties:syslog:objectpath=Url}"
},
{
"name": "UrlReferer",
"layout": "${event-properties:syslog:objectpath=UrlReferer}"
},
{
"name": "UserHostName",
"layout": "${event-properties:syslog:objectpath=UserHostName}"
},
{
"name": "UserHostAddress",
"layout": "${event-properties:syslog:objectpath=UserHostAddress}"
},
{
"name": "UserLanguage",
"layout": "${event-properties:syslog:objectpath=UserLanguage}"
},
{
"name": "UserAgent",
"layout": "${event-properties:syslog:objectpath=UserAgent}"
},
{
"name": "SessionGuid",
"layout": "${event-properties:syslog:objectpath=SessionGuid}"
},
{
"name": "ErrMessage",
"layout": "${event-properties:syslog:objectpath=ErrMessage}",
"quoting": "All"
},
{
"name": "ErrStackTrace",
"layout": "${replace-newlines:replacement=|:${event-properties:syslog:objectpath=ErrStackTrace}}",
"quoting": "All"
},
{
"name": "InDebug",
"layout": "${event-properties:syslog:objectpath=InDebug}"
},
{
"name": "AssemblyVersion",
"layout": "${event-properties:syslog:objectpath=AssemblyVersion}"
},
{
"name": "Comments",
"layout": "${event-properties:syslog:objectpath=Comments}"
},
{
"name": "Creator",
"layout": "${event-properties:syslog:objectpath=Creator}"
},
{
"name": "Updator",
"layout": "${event-properties:syslog:objectpath=Updator}"
},
{
"name": "UpdatedTime",
"layout": "${event-properties:syslog:format=o:objectpath=UpdatedTime}"
},
{
"name": "exception",
"layout": "${replace-newlines:replacement=|:${exception:format=ToString}}",
"quoting": "All"
}
]
}
},
"logconsole": {
"type": "AsyncWrapper",
"target": {
"type": "Console",
"detectConsoleAvailable": true,
"writeBuffer": true
}
}
},
"rules": [
{
"logger": "console",
"minLevel": "Info",
"writeTo": "logconsole"
},
{
"logger": "syslogs",
"minLevel": "Info",
"writeTo": "csvfile"
}
]
}
}
中身の解説は公式マニュアル(システムログをテキスト出力できるようにする)にも記載があるので詳しい説明はそちらに譲ります。
ここで着目すべき点は、layout
プロパティの${{event-properties:syslog:objectpath=
で始まる設定値の部分です。これはさきほどのToSysLogModel
メソッドで生成されたSysLogLogModel
のプロパティです。 実はこれ、データベースのSysLogs
テーブルのカラム名と1対1で対比しています。この対比しているという部分、後々出てきますので、覚えておいてください。
設定をしてみる
では、実際に設定してみます。NLogでは出力先(ファイルなど)のことをターゲットと言います。
このターゲットですが、NLogの公式サイトに使用可能なものが列挙されていたり(Configuration options/Targets)、GitHubあたりでNLog Custom Targetなどで検索するといろいろとライブラリが出てきます。注意が必要なのは、プリザンターの1.4.x系は.NET 8で作られているので、それに互換性のあるものしか使えないということです。基本的には.NETStandard 2.0以降、または.NET Core 1.0以降や.NET 5以降に対応したものしかつかえません。ライフサイクルの都合があるので現時点では、.NETStandard 2.0や.NETStandard 2.1、.NET 8あたりに対応したものから選択するのが無難かと思います。
今回は、NLog.Slackというライブラリを使ってSlackにメッセージを投げてみます。
カスタムターゲットのDLLを配置する
カスタムターゲットのDLLをNLog.dll
と同じ階層に格納します。マニュアル通りのセットアップだとImplem.Pleasanter
の中です。Enterprise EditionのLicense.dll
と同じところになります。
DLLの取得方法ですが、nuget.orgあたりから探してくるのが楽です。フレームワークのバージョンを指定して検索出来るからです。もちらん自分でソースコードを取得してビルドしてもOKです。
今回はnuget.orgからダウンロードしてきます。NLog.Slackからパッケージをダウンロードしてきます。
拡張子がnupkg
になっているのでzip
に変更します。
>ren nlog.slack.2.0.0.nupkg nlog.slack.2.0.0.zip
そして、このZIPファイルを解凍して
>mkdir nlog.slack.2.0.0
>tar -xf nlog.slack.2.0.0.zip -C nlog.slack.2.0.0
>tree /f nlog.slack.2.0.0
中身を見てみます。
C:\USERS\VEHICLEVISION\DOWNLOADS\NLOG.SLACK.2.0.0
│ .signature.p7s
│ NLog.Slack.nuspec
│ [Content_Types].xml
│
├─lib
│ ├─net45
│ │ NLog.Slack.dll
│ │
│ └─netstandard2.0
│ NLog.Slack.dll
│
├─package
│ └─services
│ └─metadata
│ └─core-properties
│ 51baff12b5f44b8d8b84fb5e392c9a23.psmdcp
│
└─_rels
.rels
NLog.Slack.dll
が2つあります。前述の通り、プリザンターの1.4.x系は.NET 8での実装になるのでnetstandard2.0
の中のDLLを使用します。
と、ここまでは簡単なのですが次が結構大変な作業です。それは依存関係の追加です。依存関係は追加したDLLが読み出しているDLLのことで、追加したDLLが動くために必要なDLLとなります。nuget.orgだとDependenciesタブから参照することが出来ます。
今回選定したNLog.Slack
では依存するものがないので、代わりにNLogでSyslogへ出力を使用するときによく使われるNLog.Targets.Syslog
で見てみましょう。次の様な依存関係があります。
この例だとPolly.Contrib.WaitAndRetry
が依存関係にあります。これに対しても前述と同じような方法でDLLを取り出して同じフォルダに格納します。依存関係にさらに依存関係がある場合はそれをさらに繰り返して、全ての依存関係を解消させる必要があります。
プリザンターが標準で持っているライブラリに対して依存関係が設定されている場合があるので、その場合は、標準で持っているライブラリのバージョンを確認して依存関係を満たすかどうかの確認が必要になります。(バージョン関係が満たせないときは、バージョンアップ方向だと上書きで対応出来る場合がありますが、そのライブラリへの依存している/されているの関係が満たせるかどうかは分からないため厳密にやるにはぷのソースコードをダウンロードしてきて、ライブラリの依存関係をnugetで調べる必要があります)
SlackのWebhook用のURLを取得する
SlackのWebhook用のURLを取得してください。取得方法はここでは割愛します。
SysLog.jsonの書き換え
まずは、公式マニュアルに従って、SysLog.json
のEnableLoggingToFile
をtrue
に書き換えます。
appsettings.jsonの書き換え
次にappsettings.jsonを下記の様に書き換えます。
extensions
が先ほど配置したDLLを指すところになります。slack.layout
が出力したいものを設定する部分になります。データベースのSysLogs
テーブルのカラムと1対1になるよというところがここで生きてきます。出力したいlayoutをcsvfile.Attributes.layout
を参考に書き上げてみてください。
また、ログの出力レベルについては、"writeTo": "slack"
の1行上のminLevel
で設定します。この設定ではInfo異常のレベルのものが出力される(プリザンターの現状の設定はInfoとErrorのみ)ようになりますが、Errorのみで良い場合は"level":"Error",
となります。
細かいところの設定はNLogのWikiなどを各自参照してください。
{
"type": null,
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"NLog": {
"throwConfigExceptions": true,
+ "extensions": [
+ { "assemblyFile": "NLog.Slack.dll" }
+ ],
"targets": {
"async": true,
"jsonfile": {
"type": "File",
"fileName": "logs/${date:format=yyyy}/${date:format=MM}/${date:format=dd}/syslogs.json",
"keepFileOpen": false,
"concurrentWrites": true,
"archiveFileName": "logs/${date:format=yyyy}/${date:format=MM}/${date:format=dd}/syslog.{#####}.json",
"archiveAboveSize": 10240,
"archiveNumbering": "Sequence",
"layout": {
"type": "JsonLayout",
"Attributes": [
{
"name": "timestamp",
"layout": "${date:format=O}"
},
{
"name": "level",
"layout": "${level:upperCase=true}"
},
{
"name": "message",
"layout": "${message}"
},
{
"name": "syslog",
"encode": false,
"layout": {
"type": "JsonLayout",
"includeEventProperties": "true"
}
},
{
"name": "exception",
"encode": false,
"layout": {
"type": "jsonLayout",
"Attributes": [
{
"name": "type",
"layout": "${exception:format=type}"
},
{
"name": "message",
"layout": "${exception:format=message}"
},
{
"name": "stacktrace",
"layout": "${exception:format=tostring}"
}
]
}
}
]
}
},
"csvfile": {
"type": "File",
"fileName": "logs/${date:format=yyyy}/${date:format=MM}/${date:format=dd}/syslogs.csv",
"keepFileOpen": false,
"concurrentWrites": true,
"archiveFileName": "logs/${date:format=yyyy}/${date:format=MM}/${date:format=dd}/syslog.{#####}.csv",
"archiveAboveSize": 10240,
"archiveNumbering": "Sequence",
"layout": {
"type": "CSVLayout",
"quoting": "All",
"columns": [
{
"name": "timestamp",
"layout": "${date:format=O}"
},
{
"name": "level",
"layout": "${level:upperCase=true}"
},
{
"name": "message",
"layout": "${message}"
},
{
"name": "CreatedTime",
"layout": "${event-properties:syslog:format=o:objectpath=CreatedTime}"
},
{
"name": "SysLogId",
"layout": "${event-properties:syslog:objectpath=SysLogId}"
},
{
"name": "Ver",
"layout": "${event-properties:syslog:objectpath=Ver}"
},
{
"name": "SysLogType",
"layout": "${event-properties:syslog:objectpath=SysLogType}"
},
{
"name": "OnAzure",
"layout": "${event-properties:syslog:objectpath=OnAzure}"
},
{
"name": "MachineName",
"layout": "${event-properties:syslog:objectpath=MachineName}"
},
{
"name": "ServiceName",
"layout": "${event-properties:syslog:objectpath=ServiceName}"
},
{
"name": "TenantName",
"layout": "${event-properties:syslog:objectpath=TenantName}"
},
{
"name": "Application",
"layout": "${event-properties:syslog:objectpath=Application}"
},
{
"name": "Class",
"layout": "${event-properties:syslog:objectpath=Class}"
},
{
"name": "Method",
"layout": "${event-properties:syslog:objectpath=Method}"
},
{
"name": "Api",
"layout": "${event-properties:syslog:objectpath=Api}"
},
{
"name": "SiteId",
"layout": "${event-properties:syslog:objectpath=SiteId}"
},
{
"name": "ReferenceId",
"layout": "${event-properties:syslog:objectpath=ReferenceId}"
},
{
"name": "ReferenceType",
"layout": "${event-properties:syslog:objectpath=ReferenceType}"
},
{
"name": "Status",
"layout": "${event-properties:syslog:objectpath=Status}"
},
{
"name": "Description",
"layout": "${event-properties:syslog:objectpath=Description}"
},
{
"name": "RequestData",
"layout": "${event-properties:syslog:objectpath=RequestData}"
},
{
"name": "HttpMethod",
"layout": "${event-properties:syslog:objectpath=HttpMethod}"
},
{
"name": "RequestSize",
"layout": "${event-properties:syslog:objectpath=RequestSize}"
},
{
"name": "ResponseSize",
"layout": "${event-properties:syslog:objectpath=ResponseSize}"
},
{
"name": "Elapsed",
"layout": "${event-properties:syslog:objectpath=Elapsed}"
},
{
"name": "ApplicationAge",
"layout": "${event-properties:syslog:objectpath=ApplicationAge}"
},
{
"name": "ApplicationRequestInterval",
"layout": "${event-properties:syslog:objectpath=ApplicationRequestInterval}"
},
{
"name": "SessionAge",
"layout": "${event-properties:syslog:objectpath=SessionAge}"
},
{
"name": "SessionRequestInterval",
"layout": "${event-properties:syslog:objectpath=SessionRequestInterval}"
},
{
"name": "WorkingSet64",
"layout": "${event-properties:syslog:objectpath=WorkingSet64}"
},
{
"name": "VirtualMemorySize64",
"layout": "${event-properties:syslog:objectpath=VirtualMemorySize64}"
},
{
"name": "ProcessId",
"layout": "${event-properties:syslog:objectpath=ProcessId}"
},
{
"name": "ProcessName",
"layout": "${event-properties:syslog:objectpath=ProcessName}"
},
{
"name": "BasePriority",
"layout": "${event-properties:syslog:objectpath=BasePriority}"
},
{
"name": "Url",
"layout": "${event-properties:syslog:objectpath=Url}"
},
{
"name": "UrlReferer",
"layout": "${event-properties:syslog:objectpath=UrlReferer}"
},
{
"name": "UserHostName",
"layout": "${event-properties:syslog:objectpath=UserHostName}"
},
{
"name": "UserHostAddress",
"layout": "${event-properties:syslog:objectpath=UserHostAddress}"
},
{
"name": "UserLanguage",
"layout": "${event-properties:syslog:objectpath=UserLanguage}"
},
{
"name": "UserAgent",
"layout": "${event-properties:syslog:objectpath=UserAgent}"
},
{
"name": "SessionGuid",
"layout": "${event-properties:syslog:objectpath=SessionGuid}"
},
{
"name": "ErrMessage",
"layout": "${event-properties:syslog:objectpath=ErrMessage}",
"quoting": "All"
},
{
"name": "ErrStackTrace",
"layout": "${replace-newlines:replacement=|:${event-properties:syslog:objectpath=ErrStackTrace}}",
"quoting": "All"
},
{
"name": "InDebug",
"layout": "${event-properties:syslog:objectpath=InDebug}"
},
{
"name": "AssemblyVersion",
"layout": "${event-properties:syslog:objectpath=AssemblyVersion}"
},
{
"name": "Comments",
"layout": "${event-properties:syslog:objectpath=Comments}"
},
{
"name": "Creator",
"layout": "${event-properties:syslog:objectpath=Creator}"
},
{
"name": "Updator",
"layout": "${event-properties:syslog:objectpath=Updator}"
},
{
"name": "UpdatedTime",
"layout": "${event-properties:syslog:format=o:objectpath=UpdatedTime}"
},
{
"name": "exception",
"layout": "${replace-newlines:replacement=|:${exception:format=ToString}}",
"quoting": "All"
}
]
}
},
"logconsole": {
"type": "AsyncWrapper",
"target": {
"type": "Console",
"detectConsoleAvailable": true,
"writeBuffer": true
}
- }
+ },
+ "slack": {
+ "type": "Slack",
+ "layout": "${longdate}|${level}|${message} |${event-properties:syslog:objectpath=ErrMessage} |${replace-newlines:replacement=|:${event-properties:syslog:objectpath=ErrStackTrace}}",
+ "webHookUrl": "**SlackのWebhook用URL**"
+ }
},
"rules": [
{
"logger": "console",
"minLevel": "Info",
"writeTo": "logconsole"
},
{
"logger": "syslogs",
"minLevel": "Info",
"writeTo": "csvfile"
- }
+ },
+ {
+ "logger": "syslogs",
+ "minLevel": "Info",
+ "writeTo": "slack"
+ }
]
}
}
動かしてみる
ここまでできたら後は動かすだけです。プリザンターのプロセスを再起動させると動き始めます。
Slackにプリザンターからのログが飛んでいることが確認出来ました。
まとめ
プリザンターにNLogが組み込まれたことによって、いろいろなターゲットに対してログを投げることができるようになりました。Azureで運用している場合はApplicationInsightsに、AWSで運用している場合はCloudWatchに、はたまたエラーの時のは管理者にメールでと活用の方法は様々です。
依存関係の解消の部分や、使用している.NETバージョンの選定が難しい、NLogの設定回りもC#やVB.NETなどの.NET系の言語開発になれていないととっつきにくいなどと、いろいろと難易度が高い点がありますが、使いこなせるようになると強い武器になります。皆さんも是非試して見てください。