今年はプログラミングを再開した。
もともとワテのブログはプログラミングの話題を中心に行く予定でスタートしたが、最近では趣味のDIYや電子工作の記事が多かった。
と言う事で、今回はASP.NET Core 8.0で作成したMVCのウェブサイトにおいて、ログファイルにデータを書き出す手法を調査した。
最近のプログラミングの世界は日進月歩で物凄い勢いで進化している。
完全に時代に取り残されてしまったワテはChatGPTの力を借りてログファイル出力に成功したので、その手法を備忘録としてまとめる事にしたのだ。
ワテは過去にNLogなどのフリーなツールを使ってログファイル出力した事もあるが、今回はASP.NET Core やC#の標準的な機能のみでログ出力機能を実装した。
もし何か間違いや勘違いが有れば、それはワテの責任ではなくてChatGPTに文句を言ってくれ。
では、本題に入ろう。
ASP.NET Core MVCのサイトを開発する
ASP.NET Core MVCのサイトを開発するにはVisual Studioを使うの良い。
Visual Studio CodeでもASP.NET Core MVCのサイトを開発出来るようだが、ワテはやったことがない。
Visual Studio 2022を起動して「新しいプロジェクトの作成」を開くと各種のテンプレートが表示される。
以下のようにセレクターを設定する。
プラットフォーム:すべてのプラットフォーム
種類:Web
そうするとすべてのプラットフォーム、つまりWindows、Linux、macOSの三種類のOSに全対応したテンプレートに絞り込まれる。
その中に「ASP.NET Core Webアプリ」と言うテンプレートあるので、それを選ぶ。
場所
ソリューション名
などを指定して「次へ」進む。
以下のように設定して「作成」ボタンをクリックすればこの設定でテンプレートが読み込まれる。
認証の種類:なし(他の選択肢:個別のアカウント、Microsoft ID、Windows)
HTTSP用の構成 にチェック
読み込まれたテンプレートには以下のフォルダやProgram.csファイルがある。
wwwroot
Controllers
Models
Views
Program.cs
Program.csの中身
上記のように設定して読み込まれたASP.NET MVC(Core 8.0)のテンプレートでは、Program.csの初期状態の中身は以下の通りだ。
var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllersWithViews(); var app = builder.Build(); // Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Home/Error"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); app.Run();
C#コード ASP.NET Core8.0 MVCのProgram.csの中身
最近のC#8.0にはMain関数が無い。Mainを書いても良いが省略可能らしい。
さらに昔はStartup.csというファイルから起動処理が開始したが、それが無くなりProgram.csが最初に実行されるようだ。
そんなにコロコロ変えるなよ!と言いたいが、まあマイクロソフトの事情があるのだろう。
さて、上のProgram.csの中身はワテは10パーセントも理解出来ていない。
それにも関わらず、このProgram.csに改良を加えてログファイル出力機能を追加するのだ。
具体的にはウェブサイトの訪問者数をカウントしてログファイルに書き出し機能を作ってみる。
もちろん、ログファイルにはカウント数だけでなく、自分で好きな情報を書き出しても良い。
Windows VPSはエックスサーバー社がお勧め
現在ワテはXserver社のWindowsVPS(下表左)と共用サーバー(下表右)の2種類のレンタルサーバーを契約している。
タイプ | Xserver VPS for Windows Server | エックスサーバーのVPSサーバー | エックスサーバー |
種別 |
WindowsVPS |
LinuxVPS |
共用サーバー |
料金 |
プラン2GB(ワテ契約) 月額830円(36ヶ月一括前払い時) |
月額580円~
|
月額990円(36ヶ月一括前払い時) |
ドメイン |
wareko.netで運用 |
|
wareko.jpで運用 |
表 ワテが契約中のレンタルサーバー(二種類)
今回のASP.NET Core8.0 MVCの実験はXserver社のWindows VPS(wareko.netドメイン)で行っている。
訪問者数カウントしログファイルに書き出す
「ウェブサイトの訪問者数をカウントしてログファイルに書き出したい」とChatGPTに質問をしまくって、最終的に以下のようにProgram.csを変更した。
//ログファイルをここで指定する方式。 using CounterTestCoreMVC.MyCode; var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllersWithViews(); builder.Services.AddSingleton<IVisitorCounterService, VisitorCounterService>(); // ここでサービスを追加 // FileLoggerをDIコンテナーに登録 //builder.Services.AddSingleton<FileLogger>(new FileLogger("Logs/visitor.log")); builder.Services.AddSingleton<FileLogger>(new FileLogger("wwwroot/Logs/visitorA.log")); var app = builder.Build(); // Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Home/Error"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); app.Run();
C#コード Program.csにAddSingletonで自前のサービスを追加する
上記コードに於いてbuilder.Services.AddSingletonと言う行が2つあるがこれを追加した。
ChatGPTによると、このような手法をDI(Dependency Injection、依存性の注入)と言うらしい。
ワテの場合はDIとは何かは全く理解出来ていない。
兎に角、ChatGPTに教えてもらったワテの理解ではこのようにすると、そのサービス(具体的には自作の関数かな?)をASP.NET MVCのControllerクラスの中で使えるのだ。
まあファイルに書き出すだけの処理なら、こんな事をやらなくてもControllerクラスの中のIndexアクションでSystem.IO.File.AppendAllText(・・・)を実行すれば可能だ。
いずれにしてもChatGPTの指示に従ってDIの手法でログファイル書き出しを実装するのだ。
ログファイルは “wwwroot/Logs/visitorA.log” に出力されるので事前にwwwroot/Logsフォルダは自分で作っておく必要がある。
MyCodeフォルダには以下の2つのファイルを作成した。
FileLogger.cs
using System; using System.IO; public class FileLogger { private string? _logFilePath; public FileLogger(string logFilePath) { _logFilePath = logFilePath; } public FileLogger(IConfiguration configuration) { _logFilePath = configuration["Logging:File:Path"]; } public void Log(string message) { try { if (_logFilePath != null) { using (StreamWriter writer = File.AppendText(_logFilePath)) { writer.WriteLine($"{DateTime.Now}: {message}"); } } else { Console.WriteLine($"Error: Log file path is null."); } } catch (Exception ex) { Console.WriteLine($"Error writing to log file: {ex.Message}"); } } }
C#コード ログファイルに書き出すサービスで使う関数
IVisitorCounterService.cs
namespace CounterTestCoreMVC.MyCode { // IVisitorCounterService.cs public interface IVisitorCounterService { void IncrementVisitorCount(); int GetVisitorCount(); } // VisitorCounterService.cs public class VisitorCounterService : IVisitorCounterService { private static int _visitorCount = 0; public void IncrementVisitorCount() { _visitorCount++; } public int GetVisitorCount() { return _visitorCount; } } }
C#コード 訪問者数カウントサービスで使う関数
どちらの関数もChatGPTさんのアドバイスに従ってワテが作成したものだ。
コードの中身は詳しくは見ていない。ChatGPTさんが教えてくれたコードをほぼ丸ごとコピペしただけだ。そんなやり方でプログラミングをしているとテストでカンニングしているような物凄く後ろめたい気持ちになるが、まあここは妥協する。前に進むことが重要だ。詳細はあとから復習して理解すれば良い。
namespaceは付けたり付けなかったりと一貫性が無いが、どっちでも良いだろう。
interfaceと言うやつも使っているが、これもChatGPTさんにその意味や使うメリットなどを色々教えてもらったのだが、すっかり忘れた。あかんがな。
HomeController.csファイル
HomeController.csファイルは以下のようにした。
using System; using System.Diagnostics; using CounterTestCoreMVC.Models; using CounterTestCoreMVC.MyCode; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.WebUtilities; using System.IO; namespace CounterTestCoreMVC.Controllers { public class HomeController : Controller { private readonly ILogger<HomeController> _logger; private readonly IVisitorCounterService _visitorCounterService; private readonly FileLogger _fileLogger; private readonly IWebHostEnvironment _environment; public HomeController(ILogger<HomeController> logger, IVisitorCounterService visitorCounterService, FileLogger fileLogger, IWebHostEnvironment environment) { _logger = logger; _visitorCounterService = visitorCounterService; _fileLogger = fileLogger; _environment = environment; } public IActionResult Index() { _visitorCounterService.IncrementVisitorCount(); int visitorCount = _visitorCounterService.GetVisitorCount(); var path = Directory.GetCurrentDirectory(); // ここで visitorCount をログに記録することもできます _logger.LogInformation($"Visitor count: {visitorCount} by ①logger"); _fileLogger.Log($"Visitor count: {visitorCount} by ②fileLogger"); LogFileWrite($"Visitor count: {visitorCount} by ③LogFileWrite"); return View(); } public IActionResult Privacy() { return View(); } [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] public IActionResult Error() { return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); } public void LogFileWrite(string logText) { /* 書き込みフォルダの存在確認は当関数内ではやらないので、そのフォルダが存在しない場合には事前に手動などで作成しておく必要あり。 */ var logFolder = Path.Combine(_environment.WebRootPath, "MyLogFolder");//wwwroot/MyLogFolder var logFName = "ログファイル.txt"; var pathFNameLog = Path.Combine(logFolder, logFName); // 現在の日付と時刻を取得 string currentDateTime = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"); // 追記するテキスト string newText = $"{currentDateTime}: {logText}"; // ファイルにテキストを追記 System.IO.File.AppendAllText(pathFNameLog, newText + Environment.NewLine); } } }
C#コード ログファイルに書き出すサービスを使うHomeController.csファイルの例
このHomeController.csファイルの要点は2つある。
一つ目の要点は、以下に示すようにHomeControllerクラスのコンストラクタで、Program.csでDI(Dependency Injection、依存性の注入)した2つのサービス(IVisitorCounterService、FileLogger)をprivate変数に代入している。
private readonly ILogger<HomeController> _logger; private readonly IVisitorCounterService _visitorCounterService; private readonly FileLogger _fileLogger; private readonly IWebHostEnvironment _environment; public HomeController(ILogger<HomeController> logger, IVisitorCounterService visitorCounterService, FileLogger fileLogger, IWebHostEnvironment environment) { _logger = logger; _visitorCounterService = visitorCounterService; _fileLogger = fileLogger; _environment = environment; }
C#コード HomeControllerクラスのコンストラクタ
ILoggerはデフォルトで準備されているログ機能だ。
この機能を使うとコンソール画面にログを表示させる事が出来る。
ところがそれはローカルのパソコンで開発している時には便利なのだが、ウェブサイトをWebDeployしてサーバーに発行するとコンソール画面などは表示されない。
そこでログファイルに出力する手法をChatGPTに教えてもらいながら実装したのだ。
IWebHostEnvironment は wwwrootフォルダの名前を取得する為にこのあとで必要になるので使っている。
二つ目の要点は、Index関数の中で訪問者数をカウントして三通りの手法でログを書き出している。
public IActionResult Index() { _visitorCounterService.IncrementVisitorCount(); int visitorCount = _visitorCounterService.GetVisitorCount(); var path = Directory.GetCurrentDirectory(); // ここで visitorCount をログに記録することもできます _logger.LogInformation($"Visitor count: {visitorCount} by ①logger"); _fileLogger.Log($"Visitor count: {visitorCount} by ②fileLogger"); LogFileWrite($"Visitor count: {visitorCount} by ③LogFileWrite"); return View(); }
C#コード HomeControllerクラスのIndexアクション内で訪問者数をカウントしてログ出力
前半部分では訪問者数をカウントする処理を実行している。
その訪問者数を標準のロガー(_logger)を使ってコンソールに書き出すのが①だ。
②はProgram.csでDIした自前のログサービスを使ってファイルに出力する。
③はDIの手法を使わずに単純にテキストファイル出力用の自前の関数LogFileWrite(string logText)を実行してログファイルに書き出している。
こんなふうにすれば、WebDeployしてサーバーに発行したウェブサイトをデバッグする場合に、必要に応じてログファイルに情報を書き出せるので開発作業が大幅にやり易くなった。
なお、これくらいシンプルなASP.NET Core MVCのサイトなら、Program.csでDIする手法②のメリットが感じられないと思う。
そんな事をやらなくても③のようにテキストファイル出力用の関数を作れば良いからだ。
DIするメリットはChatGPTさんに何度か教えてもらったのだが、ワテの場合は未だに理解出来ていない。
誰か教えてくれw
ログファイルをProgram.csで指定するのではなくてappsettings.jsonで指定したい
さて、Program.csでDIする手法でログファイルに書き出す手法だが、ログファイル名やパスもProgram.csで指定している。
まあそれでも問題は無いが、コードに書き込むのではなくてappsettings.jsonで指定することも可能だ。
まず、Program.csは以下のように変更する。
//ログファイルをappsettings.jsonで指定する方式 using Microsoft.Extensions.Configuration; using CounterTestCoreMVC.MyCode; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Configuration; var builder = WebApplication.CreateBuilder(args); var configuration = new ConfigurationBuilder() .SetBasePath(builder.Environment.ContentRootPath) .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) .Build(); // Add IConfiguration to DI container builder.Services.AddSingleton<IConfiguration>(builder.Configuration); // Add services to the container. builder.Services.AddControllersWithViews(); builder.Services.AddSingleton<IVisitorCounterService, VisitorCounterService>(); // ここでサービスを追加 // FileLoggerをDIコンテナーに登録 //builder.Services.AddSingleton<FileLogger>(new FileLogger("Logs/visitor.log")); builder.Services.AddSingleton<FileLogger>(); var app = builder.Build(); // Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Home/Error"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); app.Run();
C#コード Program.csでログファイル名を指定するのではなくてappsettings.json設定を使う場合
言うまでもなく上記コードもChatGPTさんの回答をほぼコピペなので、ワテは良く分かっていない。
そしてappsettings.jsonは以下のように作成する。
{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information", "Microsoft.AspNetCore": "Warning" }, "Console": { "LogLevel": { "Default": "Information" } }, "File": { "Path": "wwwroot/Logs2/log.txt", "IncludeScopes": false, "Append": true } }, "AllowedHosts": "*" }
C#でログファイルのパスや出力条件を設定したappsettings.jsonの例
詳しいことは皆さん各自で勉強して下さい。
まとめ
ChatGPTに頼るとプログラミングはどんどん進むのだが、内容を理解出来ないままでもコードをコピペして書き進めれば出来てしまうのは良いのか悪いのか?
まあ、それは動くプログラムを完成させたあとで考えよう。
プログラムは動くものを最短時間で完成させることに意味がある。
当記事では最新のASP.NET Core8.0 MVCのウェブサイト開発に於いて、ログファイルを出力する手法を2つ紹介した。
一つ目はChatGPTさんに教えてもらったDI(Dependency Injection、依存性の注入)の手法だ。
このDIの手法を使うと、Controllerクラスに自前のサービスを追加出来るのだ。
今回は訪問者数をカウントするサービスとログファイルに書き出すサービスの2つをDIした。
ログファイルの出力ファイル名はProgram.cs内でコードに文字列として書き込む手法と、appsettings.jsonファイルで指定する手法の2つを紹介した。
ASP.NET開発ではログファイルに書き出すならこれらのDIの手法を使うと良いだろう。
DIの手法は色んな応用が効くようなので、ワテとしても今後もDIに付いて勉強したいと思っている。
二つ目は単純にテキストファイルに書き出す手法も紹介した。
現状ではワテはDIのメリットを全く理解出来ていないので、本当のところはDIなんかやらずに単純にテキストファイル書き出すほうが分かり易い。
まあ兎に角、引き続きASP.NET Core MVCを勉強するぞ。
(続く)
コメント