public async Task LoggingToMemoryUsingCustomPathPlaceholderResolver() { const string appName = "myapp"; const string logsDirName = "Logs"; var fileProvider = new MemoryFileProvider(); var cts = new CancellationTokenSource(); var context = new TestFileLoggerContext(cts.Token, completionTimeout: Timeout.InfiniteTimeSpan); context.SetTimestamp(new DateTime(2017, 1, 1, 0, 0, 0, DateTimeKind.Utc)); var services = new ServiceCollection(); services.AddOptions(); services.AddLogging(b => b.AddFile(context, o => { o.FileAppender = new MemoryFileAppender(fileProvider); o.BasePath = logsDirName; o.Files = new[] { new LogFileOptions { Path = "<appname>-<counter:000>.log" } }; o.PathPlaceholderResolver = (placeholderName, inlineFormat, context) => placeholderName == "appname" ? appName : null; })); FileLoggerProvider[] providers; using (ServiceProvider sp = services.BuildServiceProvider()) { providers = context.GetProviders(sp).ToArray(); Assert.Equal(1, providers.Length); ILogger <LoggingTest> logger1 = sp.GetService <ILogger <LoggingTest> >(); logger1.LogInformation("This is a nice logger."); logger1.LogWarning(1, "This is a smart logger."); cts.Cancel(); // ensuring that all entries are processed await context.GetCompletion(sp); Assert.True(providers.All(provider => provider.Completion.IsCompleted)); } var logFile = (MemoryFileInfo)fileProvider.GetFileInfo($"{logsDirName}/{appName}-000.log"); Assert.True(logFile.Exists && !logFile.IsDirectory); }
public async Task FailingEntryDontGetStuck() { var logsDirName = Guid.NewGuid().ToString("D"); var tempPath = Path.Combine(Path.GetTempPath()); var logPath = Path.Combine(tempPath, logsDirName); if (Directory.Exists(logPath)) { Directory.Delete(logPath, recursive: true); } var fileProvider = new PhysicalFileProvider(tempPath); var options = new FileLoggerOptions { FileAppender = new PhysicalFileAppender(fileProvider), BasePath = logsDirName, Files = new[] { new LogFileOptions { Path = "default.log", }, }, }; var optionsMonitor = new DelegatedOptionsMonitor <FileLoggerOptions>(_ => options); var completeCts = new CancellationTokenSource(); var completionTimeoutMs = 2000; var context = new TestFileLoggerContext(completeCts.Token, TimeSpan.FromMilliseconds(completionTimeoutMs), writeRetryDelay: TimeSpan.FromMilliseconds(250)); context.SetTimestamp(new DateTime(2017, 1, 1, 0, 0, 0, DateTimeKind.Utc)); var services = new ServiceCollection(); services.AddOptions(); services.AddLogging(b => b.AddFile(context)); services.AddSingleton <IOptionsMonitor <FileLoggerOptions> >(optionsMonitor); string filePath = Path.Combine(logPath, "default.log"); try { FileLoggerProvider[] providers; using (ServiceProvider sp = services.BuildServiceProvider()) { providers = context.GetProviders(sp).ToArray(); Assert.Equal(1, providers.Length); var resetTasks = new List <Task>(); foreach (FileLoggerProvider provider in providers) { provider.Reset += (s, e) => resetTasks.Add(e); } ILoggerFactory loggerFactory = sp.GetRequiredService <ILoggerFactory>(); ILogger logger = loggerFactory.CreateLogger("X"); logger.LogInformation("This should get through."); optionsMonitor.Reload(); // ensuring that reset has been finished and the new settings are effective await Task.WhenAll(resetTasks); using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)) { logger.LogInformation("This shouldn't get through."); Task completion = context.GetCompletion(sp); Assert.False(completion.IsCompleted); completeCts.Cancel(); Assert.Equal(completion, await Task.WhenAny(completion, Task.Delay(TimeSpan.FromMilliseconds(completionTimeoutMs * 2)))); Assert.Equal(TaskStatus.RanToCompletion, completion.Status); } } IFileInfo logFile = fileProvider.GetFileInfo($"{logsDirName}/default.log"); Assert.True(logFile.Exists && !logFile.IsDirectory); var lines = logFile.ReadAllText(out Encoding encoding).Split(new[] { Environment.NewLine }, StringSplitOptions.None); Assert.Equal(Encoding.UTF8, encoding); Assert.Equal(new[] { $"info: X[0] @ {context.GetTimestamp().ToLocalTime():o}", $" This should get through.", "" }, lines); } finally { Directory.Delete(logPath, recursive: true); } }
public async Task ReloadOptionsSettings() { var configJson = $@"{{ ""{FileLoggerProvider.Alias}"": {{ ""{nameof(FileLoggerOptions.IncludeScopes)}"" : true, ""{nameof(FileLoggerOptions.Files)}"": [ {{ ""{nameof(LogFileOptions.Path)}"": ""test.log"", }}], ""{nameof(LoggerFilterRule.LogLevel)}"": {{ ""{LogFileOptions.DefaultCategoryName}"": ""{LogLevel.Trace}"" }} }} }}"; var fileProvider = new MemoryFileProvider(); fileProvider.CreateFile("config.json", configJson, Encoding.UTF8); var cb = new ConfigurationBuilder(); cb.AddJsonFile(fileProvider, "config.json", optional: false, reloadOnChange: true); IConfigurationRoot config = cb.Build(); var completeCts = new CancellationTokenSource(); var context = new TestFileLoggerContext(completeCts.Token, completionTimeout: Timeout.InfiniteTimeSpan); context.SetTimestamp(new DateTime(2017, 1, 1, 0, 0, 0, DateTimeKind.Utc)); var services = new ServiceCollection(); services.AddOptions(); services.AddLogging(b => { b.AddConfiguration(config); b.AddFile(context); }); var fileAppender = new MemoryFileAppender(fileProvider); services.Configure <FileLoggerOptions>(o => o.FileAppender ??= fileAppender); FileLoggerProvider[] providers; using (ServiceProvider sp = services.BuildServiceProvider()) { providers = context.GetProviders(sp).ToArray(); Assert.Equal(1, providers.Length); var resetTasks = new List <Task>(); foreach (FileLoggerProvider provider in providers) { provider.Reset += (s, e) => resetTasks.Add(e); } ILoggerFactory loggerFactory = sp.GetService <ILoggerFactory>(); ILogger <SettingsTest> logger1 = loggerFactory.CreateLogger <SettingsTest>(); using (logger1.BeginScope("SCOPE")) { logger1.LogTrace("This is a nice logger."); using (logger1.BeginScope("NESTED SCOPE")) { logger1.LogInformation("This is a smart logger."); // changing switch and scopes inclusion configJson = $@"{{ ""{FileLoggerProvider.Alias}"": {{ ""{nameof(FileLoggerOptions.Files)}"": [ {{ ""{nameof(LogFileOptions.Path)}"": ""test.log"", }}], ""{nameof(LoggerFilterRule.LogLevel)}"": {{ ""{LogFileOptions.DefaultCategoryName}"": ""{LogLevel.Information}"" }} }} }}"; Assert.Equal(0, resetTasks.Count); fileProvider.WriteContent("config.json", configJson); // reload is triggered twice due to a bug in the framework (https://github.com/aspnet/Logging/issues/874) Assert.Equal(1 * 2, resetTasks.Count); // ensuring that reset has been finished and the new settings are effective await Task.WhenAll(resetTasks); logger1 = loggerFactory.CreateLogger <SettingsTest>(); logger1.LogInformation("This one shouldn't include scopes."); logger1.LogTrace("This one shouldn't be included at all."); } } completeCts.Cancel(); // ensuring that all entries are processed await context.GetCompletion(sp); Assert.True(providers.All(provider => provider.Completion.IsCompleted)); } var logFile = (MemoryFileInfo)fileProvider.GetFileInfo("test.log"); Assert.True(logFile.Exists && !logFile.IsDirectory); var lines = logFile.ReadAllText(out Encoding encoding).Split(new[] { Environment.NewLine }, StringSplitOptions.None); Assert.Equal(Encoding.UTF8, encoding); Assert.Equal(new[] { $"trce: {typeof(SettingsTest).FullName}[0] @ {context.GetTimestamp().ToLocalTime():o}", $" => SCOPE", $" This is a nice logger.", $"info: {typeof(SettingsTest).FullName}[0] @ {context.GetTimestamp().ToLocalTime():o}", $" => SCOPE => NESTED SCOPE", $" This is a smart logger.", $"info: {typeof(SettingsTest).FullName}[0] @ {context.GetTimestamp().ToLocalTime():o}", $" This one shouldn't include scopes.", "" }, lines); }
private async Task LoggingToMemoryWithoutDICore(LogFileAccessMode accessMode) { const string logsDirName = "Logs"; var fileProvider = new MemoryFileProvider(); var filterOptions = new LoggerFilterOptions { MinLevel = LogLevel.Trace }; var options = new FileLoggerOptions { FileAppender = new MemoryFileAppender(fileProvider), BasePath = logsDirName, FileAccessMode = accessMode, FileEncoding = Encoding.UTF8, MaxQueueSize = 100, DateFormat = "yyMMdd", CounterFormat = "000", MaxFileSize = 10, Files = new[] { new LogFileOptions { Path = "<date>/<date:MM>/logger.log", DateFormat = "yyyy", MinLevel = new Dictionary <string, LogLevel> { ["Karambolo.Extensions.Logging.File"] = LogLevel.None, [LogFileOptions.DefaultCategoryName] = LogLevel.Information, } }, new LogFileOptions { Path = "test-<date>-<counter>.log", MinLevel = new Dictionary <string, LogLevel> { ["Karambolo.Extensions.Logging.File"] = LogLevel.Information, [LogFileOptions.DefaultCategoryName] = LogLevel.None, } }, }, TextBuilder = new CustomLogEntryTextBuilder(), IncludeScopes = true, }; var completeCts = new CancellationTokenSource(); var context = new TestFileLoggerContext(completeCts.Token, completionTimeout: Timeout.InfiniteTimeSpan); context.SetTimestamp(new DateTime(2017, 1, 1, 0, 0, 0, DateTimeKind.Utc)); var ex = new Exception(); var provider = new FileLoggerProvider(context, Options.Create(options)); try { using (var loggerFactory = new LoggerFactory(new[] { provider }, filterOptions)) { ILogger <LoggingTest> logger1 = loggerFactory.CreateLogger <LoggingTest>(); logger1.LogInformation("This is a nice logger."); using (logger1.BeginScope("SCOPE")) { logger1.LogWarning(1, "This is a smart logger."); logger1.LogTrace("This won't make it."); using (logger1.BeginScope("NESTED SCOPE")) { ILogger logger2 = loggerFactory.CreateLogger("X"); logger2.LogWarning("Some warning."); logger2.LogError(0, ex, "Some failure!"); } } } } finally { #if NETCOREAPP3_0 await provider.DisposeAsync(); #else await Task.CompletedTask; provider.Dispose(); #endif } Assert.True(provider.Completion.IsCompleted); var logFile = (MemoryFileInfo)fileProvider.GetFileInfo($"{logsDirName}/test-{context.GetTimestamp().ToLocalTime():yyMMdd}-000.log"); Assert.True(logFile.Exists && !logFile.IsDirectory); var lines = logFile.ReadAllText(out Encoding encoding).Split(new[] { Environment.NewLine }, StringSplitOptions.None); Assert.Equal(Encoding.UTF8, encoding); Assert.Equal(new[] { $"[info]: {typeof(LoggingTest)}[0] @ {context.GetTimestamp().ToLocalTime():o}", $" This is a nice logger.", "" }, lines); logFile = (MemoryFileInfo)fileProvider.GetFileInfo($"{logsDirName}/test-{context.GetTimestamp().ToLocalTime():yyMMdd}-001.log"); Assert.True(logFile.Exists && !logFile.IsDirectory); lines = logFile.ReadAllText(out encoding).Split(new[] { Environment.NewLine }, StringSplitOptions.None); Assert.Equal(Encoding.UTF8, encoding); Assert.Equal(new[] { $"[warn]: {typeof(LoggingTest)}[1] @ {context.GetTimestamp().ToLocalTime():o}", $" => SCOPE", $" This is a smart logger.", "" }, lines); logFile = (MemoryFileInfo)fileProvider.GetFileInfo( $"{logsDirName}/{context.GetTimestamp().ToLocalTime():yyyy}/{context.GetTimestamp().ToLocalTime():MM}/logger.log"); Assert.True(logFile.Exists && !logFile.IsDirectory); lines = logFile.ReadAllText(out encoding).Split(new[] { Environment.NewLine }, StringSplitOptions.None); Assert.Equal(Encoding.UTF8, encoding); Assert.Equal(new[] { $"[warn]: X[0] @ {context.GetTimestamp().ToLocalTime():o}", $" => SCOPE => NESTED SCOPE", $" Some warning.", $"[fail]: X[0] @ {context.GetTimestamp().ToLocalTime():o}", $" => SCOPE => NESTED SCOPE", $" Some failure!", } .Concat(ex.ToString().Split(new[] { Environment.NewLine }, StringSplitOptions.None)) .Append(""), lines); }
private async Task LoggingToPhysicalUsingDICore(LogFileAccessMode accessMode) { var logsDirName = Guid.NewGuid().ToString("D"); var configData = new Dictionary <string, string> { [$"{nameof(FileLoggerOptions.BasePath)}"] = logsDirName, [$"{nameof(FileLoggerOptions.FileEncodingName)}"] = "UTF-16", [$"{nameof(FileLoggerOptions.DateFormat)}"] = "yyMMdd", [$"{nameof(FileLoggerOptions.FileAccessMode)}"] = accessMode.ToString(), [$"{nameof(FileLoggerOptions.Files)}:0:{nameof(LogFileOptions.Path)}"] = "logger-<date>.log", [$"{nameof(FileLoggerOptions.Files)}:0:{nameof(LogFileOptions.MinLevel)}:Karambolo.Extensions.Logging.File"] = LogLevel.None.ToString(), [$"{nameof(FileLoggerOptions.Files)}:0:{nameof(LogFileOptions.MinLevel)}:{LogFileOptions.DefaultCategoryName}"] = LogLevel.Information.ToString(), [$"{nameof(FileLoggerOptions.Files)}:1:{nameof(LogFileOptions.Path)}"] = "test-<date>.log", [$"{nameof(FileLoggerOptions.Files)}:1:{nameof(LogFileOptions.MinLevel)}:Karambolo.Extensions.Logging.File.Test"] = LogLevel.Information.ToString(), [$"{nameof(FileLoggerOptions.Files)}:1:{nameof(LogFileOptions.MinLevel)}:{LogFileOptions.DefaultCategoryName}"] = LogLevel.None.ToString(), }; var cb = new ConfigurationBuilder(); cb.AddInMemoryCollection(configData); IConfigurationRoot config = cb.Build(); var tempPath = Path.Combine(Path.GetTempPath()); var logPath = Path.Combine(tempPath, logsDirName); var fileProvider = new PhysicalFileProvider(tempPath); var cts = new CancellationTokenSource(); var context = new TestFileLoggerContext(cts.Token, completionTimeout: Timeout.InfiniteTimeSpan); context.SetTimestamp(new DateTime(2017, 1, 1, 0, 0, 0, DateTimeKind.Utc)); var services = new ServiceCollection(); services.AddOptions(); services.AddLogging(b => b.AddFile(context, o => o.FileAppender = new PhysicalFileAppender(fileProvider))); services.Configure <FileLoggerOptions>(config); if (Directory.Exists(logPath)) { Directory.Delete(logPath, recursive: true); } try { var ex = new Exception(); FileLoggerProvider[] providers; using (ServiceProvider sp = services.BuildServiceProvider()) { providers = context.GetProviders(sp).ToArray(); Assert.Equal(1, providers.Length); ILogger <LoggingTest> logger1 = sp.GetService <ILogger <LoggingTest> >(); logger1.LogInformation("This is a nice logger."); using (logger1.BeginScope("SCOPE")) { logger1.LogWarning(1, "This is a smart logger."); logger1.LogTrace("This won't make it."); using (logger1.BeginScope("NESTED SCOPE")) { ILoggerFactory loggerFactory = sp.GetService <ILoggerFactory>(); ILogger logger2 = loggerFactory.CreateLogger("X"); logger2.LogError(0, ex, "Some failure!"); } } cts.Cancel(); // ensuring that all entries are processed await context.GetCompletion(sp); Assert.True(providers.All(provider => provider.Completion.IsCompleted)); } IFileInfo logFile = fileProvider.GetFileInfo($"{logsDirName}/test-{context.GetTimestamp().ToLocalTime():yyMMdd}.log"); Assert.True(logFile.Exists && !logFile.IsDirectory); var lines = logFile.ReadAllText(out Encoding encoding).Split(new[] { Environment.NewLine }, StringSplitOptions.None); Assert.Equal(Encoding.Unicode, encoding); Assert.Equal(new[] { $"info: {typeof(LoggingTest)}[0] @ {context.GetTimestamp().ToLocalTime():o}", $" This is a nice logger.", $"warn: {typeof(LoggingTest)}[1] @ {context.GetTimestamp().ToLocalTime():o}", $" This is a smart logger.", "" }, lines); logFile = fileProvider.GetFileInfo( $"{logsDirName}/logger-{context.GetTimestamp().ToLocalTime():yyMMdd}.log"); Assert.True(logFile.Exists && !logFile.IsDirectory); lines = logFile.ReadAllText(out encoding).Split(new[] { Environment.NewLine }, StringSplitOptions.None); Assert.Equal(Encoding.Unicode, encoding); Assert.Equal(new[] { $"fail: X[0] @ {context.GetTimestamp().ToLocalTime():o}", $" Some failure!", } .Concat(ex.ToString().Split(new[] { Environment.NewLine }, StringSplitOptions.None)) .Append(""), lines); } finally { Directory.Delete(logPath, recursive: true); } }
public void LoggingToMemoryUsingFactory() { var fileProvider = new MemoryFileProvider(); var settings = new FileLoggerSettings { FileAppender = new MemoryFileAppender(fileProvider), BasePath = LogsDirName, EnsureBasePath = true, FileEncoding = Encoding.UTF8, MaxQueueSize = 100, DateFormat = "yyyyMMdd", CounterFormat = "000", MaxFileSize = 10, Switches = new Dictionary <string, LogLevel> { { FileLoggerSettingsBase.DefaultCategoryName, LogLevel.Information } }, FileNameMappings = new Dictionary <string, string> { { "Karambolo.Extensions.Logging.File.Test", "test.log" }, { "Karambolo.Extensions.Logging.File", "logger.log" }, }, TextBuilder = new CustomLogEntryTextBuilder(), IncludeScopes = true, }; var cts = new CancellationTokenSource(); var context = new TestFileLoggerContext(cts.Token); var completionTasks = new List <Task>(); context.Complete += (s, e) => completionTasks.Add(e); context.SetTimestamp(new DateTime(2017, 1, 1, 0, 0, 0, DateTimeKind.Utc)); var ex = new Exception(); using (var loggerFactory = new LoggerFactory()) { loggerFactory.AddFile(context, settings); ILogger <LoggingTest> logger1 = loggerFactory.CreateLogger <LoggingTest>(); logger1.LogInformation("This is a nice logger."); using (logger1.BeginScope("SCOPE")) { logger1.LogWarning(1, "This is a smart logger."); logger1.LogTrace("This won't make it."); using (logger1.BeginScope("NESTED SCOPE")) { ILogger logger2 = loggerFactory.CreateLogger("X"); logger2.LogError(0, ex, "Some failure!"); } } // ensuring that all entries are processed cts.Cancel(); Assert.Single(completionTasks); Task.WhenAll(completionTasks).GetAwaiter().GetResult(); } var logFile = (MemoryFileInfo)fileProvider.GetFileInfo($@"{LogsDirName}\test-{context.GetTimestamp().ToLocalTime():yyyyMMdd}-000.log"); Assert.True(logFile.Exists && !logFile.IsDirectory); var lines = logFile.Content.Split(new[] { Environment.NewLine }, StringSplitOptions.None); Assert.Equal(Encoding.UTF8, logFile.Encoding); Assert.Equal(new[] { $"[info]: {typeof(LoggingTest).FullName}[0] @ {context.GetTimestamp().ToLocalTime():o}", $" This is a nice logger.", "" }, lines); logFile = (MemoryFileInfo)fileProvider.GetFileInfo($@"{LogsDirName}\test-{context.GetTimestamp().ToLocalTime():yyyyMMdd}-001.log"); Assert.True(logFile.Exists && !logFile.IsDirectory); lines = logFile.Content.Split(new[] { Environment.NewLine }, StringSplitOptions.None); Assert.Equal(Encoding.UTF8, logFile.Encoding); Assert.Equal(new[] { $"[warn]: {typeof(LoggingTest).FullName}[1] @ {context.GetTimestamp().ToLocalTime():o}", $" => SCOPE", $" This is a smart logger.", "" }, lines); logFile = (MemoryFileInfo)fileProvider.GetFileInfo( $@"{LogsDirName}\{Path.ChangeExtension(FallbackFileName, null)}-{context.GetTimestamp().ToLocalTime():yyyyMMdd}-000{Path.GetExtension(FallbackFileName)}"); Assert.True(logFile.Exists && !logFile.IsDirectory); lines = logFile.Content.Split(new[] { Environment.NewLine }, StringSplitOptions.None); Assert.Equal(Encoding.UTF8, logFile.Encoding); Assert.Equal(new[] { $"[fail]: X[0] @ {context.GetTimestamp().ToLocalTime():o}", $" => SCOPE => NESTED SCOPE", $" Some failure!", } .Concat(ex.ToString().Split(new[] { Environment.NewLine }, StringSplitOptions.None)) .Append(""), lines); }
public void LoggingToPhysicalUsingProvider() { var configData = new Dictionary <string, string> { [$"{nameof(FileLoggerOptions.BasePath)}"] = LogsDirName, [$"{nameof(FileLoggerOptions.EnsureBasePath)}"] = "true", [$"{nameof(FileLoggerOptions.FileEncodingName)}"] = "UTF-16", [$"{nameof(FileLoggerOptions.MaxQueueSize)}"] = "100", [$"{nameof(FileLoggerOptions.DateFormat)}"] = "yyyyMMdd", [$"{ConfigurationFileLoggerSettings.LogLevelSectionName}:{FileLoggerSettingsBase.DefaultCategoryName}"] = "Information", [$"{nameof(FileLoggerOptions.FileNameMappings)}:Karambolo.Extensions.Logging.File.Test"] = "test.log", [$"{nameof(FileLoggerOptions.FileNameMappings)}:Karambolo.Extensions.Logging.File"] = "logger.log", }; var cb = new ConfigurationBuilder(); cb.AddInMemoryCollection(configData); IConfigurationRoot config = cb.Build(); var cts = new CancellationTokenSource(); var tempPath = Path.GetTempPath(); var logPath = Path.Combine(tempPath, LogsDirName); var context = new TestFileLoggerContext(new PhysicalFileProvider(tempPath), "fallback.log", cts.Token); var completionTasks = new List <Task>(); context.Complete += (s, e) => completionTasks.Add(e); context.SetTimestamp(new DateTime(2017, 1, 1, 0, 0, 0, DateTimeKind.Utc)); var services = new ServiceCollection(); services.AddOptions(); services.AddLogging(b => b.AddFile(context)); services.Configure <FileLoggerOptions>(config); if (Directory.Exists(logPath)) { Directory.Delete(logPath, recursive: true); } try { var ex = new Exception(); ServiceProvider serviceProvider = services.BuildServiceProvider(); ILogger <LoggingTest> logger1 = serviceProvider.GetService <ILogger <LoggingTest> >(); logger1.LogInformation("This is a nice logger."); using (logger1.BeginScope("SCOPE")) { logger1.LogWarning(1, "This is a smart logger."); logger1.LogTrace("This won't make it."); using (logger1.BeginScope("NESTED SCOPE")) { ILoggerFactory loggerFactory = serviceProvider.GetService <ILoggerFactory>(); ILogger logger2 = loggerFactory.CreateLogger("X"); logger2.LogError(0, ex, "Some failure!"); } } // ensuring that all entries are processed cts.Cancel(); Assert.Single(completionTasks); Task.WhenAll(completionTasks).GetAwaiter().GetResult(); #pragma warning disable CS0618 // Type or member is obsolete IFileInfo logFile = context.FileProvider.GetFileInfo($@"{LogsDirName}\test-{context.GetTimestamp().ToLocalTime():yyyyMMdd}.log"); #pragma warning restore CS0618 // Type or member is obsolete Assert.True(logFile.Exists && !logFile.IsDirectory); var lines = ReadContent(logFile, out Encoding encoding).Split(new[] { Environment.NewLine }, StringSplitOptions.None); Assert.Equal(Encoding.Unicode, encoding); Assert.Equal(new[] { $"info: {typeof(LoggingTest).FullName}[0] @ {context.GetTimestamp().ToLocalTime():o}", $" This is a nice logger.", $"warn: {typeof(LoggingTest).FullName}[1] @ {context.GetTimestamp().ToLocalTime():o}", $" This is a smart logger.", "" }, lines); #pragma warning disable CS0618 // Type or member is obsolete logFile = context.FileProvider.GetFileInfo( $@"{LogsDirName}\{Path.ChangeExtension(context.FallbackFileName, null)}-{context.GetTimestamp().ToLocalTime():yyyyMMdd}{Path.GetExtension(context.FallbackFileName)}"); #pragma warning restore CS0618 // Type or member is obsolete Assert.True(logFile.Exists && !logFile.IsDirectory); lines = ReadContent(logFile, out encoding).Split(new[] { Environment.NewLine }, StringSplitOptions.None); Assert.Equal(Encoding.Unicode, encoding); Assert.Equal(new[] { $"fail: X[0] @ {context.GetTimestamp().ToLocalTime():o}", $" Some failure!", } .Concat(ex.ToString().Split(new[] { Environment.NewLine }, StringSplitOptions.None)) .Append(""), lines); } finally { Directory.Delete(logPath, recursive: true); } string ReadContent(IFileInfo fileInfo, out Encoding encoding) { using (Stream stream = fileInfo.CreateReadStream()) using (var reader = new StreamReader(stream)) { var result = reader.ReadToEnd(); encoding = reader.CurrentEncoding; return(result); } } }
public void FailingEntryDontGetStuck() { var tempPath = Path.GetTempPath(); var logPath = Path.Combine(tempPath, LogsDirName); if (Directory.Exists(logPath)) { Directory.Delete(logPath, recursive: true); } Directory.CreateDirectory(logPath); try { var context = new TestFileLoggerContext(new PhysicalFileProvider(tempPath), "fallback.log"); context.SetWriteRetryDelay(TimeSpan.FromMilliseconds(250)); context.SetCompletionTimeout(TimeSpan.FromMilliseconds(2000)); var completionTasks = new List <Task>(); context.Complete += (s, e) => completionTasks.Add(e); var cts = new CancellationTokenSource(); var settings = new FileLoggerSettings { BasePath = LogsDirName, FileNameMappings = new Dictionary <string, string> { { "Default", "default.log" } }, Switches = new Dictionary <string, LogLevel> { { FileLoggerSettingsBase.DefaultCategoryName, LogLevel.Information } }, ChangeToken = new CancellationChangeToken(cts.Token) }; var filePath = Path.Combine(logPath, "default.log"); using (var loggerProvider = new FileLoggerProvider(context, settings)) { ILogger logger = loggerProvider.CreateLogger("X"); logger.LogInformation("This should get through."); var newCts = new CancellationTokenSource(); settings.ChangeToken = new CancellationChangeToken(newCts.Token); cts.Cancel(); cts = newCts; Assert.Single(completionTasks); Task.WhenAll(completionTasks).GetAwaiter().GetResult(); using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)) { logger.LogInformation("This shouldn't get through."); cts.Cancel(); Assert.Equal(2, completionTasks.Count); var delayTask = Task.Delay(5000); Assert.Equal(completionTasks[1], Task.WhenAny(completionTasks[1], delayTask).GetAwaiter().GetResult()); Assert.Equal(TaskStatus.RanToCompletion, completionTasks[1].Status); } } var lines = System.IO.File.ReadAllLines(filePath); Assert.Equal(lines, new[] { $"info: X[0] @ {context.GetTimestamp().ToLocalTime():o}", $" This should get through.", }); } finally { Directory.Delete(logPath, recursive: true); } }
public void ReloadOptionsSettings() { var configJson = $@"{{ '{nameof(ConfigurationFileLoggerSettings.IncludeScopes)}' : true, '{ConfigurationFileLoggerSettings.LogLevelSectionName}': {{ '{FileLoggerSettingsBase.DefaultCategoryName}': '{LogLevel.Trace}', }} }}"; var fileProvider = new MemoryFileProvider(); fileProvider.CreateFile("config.json", configJson, Encoding.UTF8); var cb = new ConfigurationBuilder(); cb.AddJsonFile(fileProvider, "config.json", optional: false, reloadOnChange: true); var config = cb.Build(); var settings = new ConfigurationFileLoggerSettings(config); var cts = new CancellationTokenSource(); var context = new TestFileLoggerContext(cts.Token); var completionTasks = new List <Task>(); context.Complete += (s, e) => completionTasks.Add(e); context.SetTimestamp(new DateTime(2017, 1, 1, 0, 0, 0, DateTimeKind.Utc)); var services = new ServiceCollection(); services.AddOptions(); services.AddLogging(b => b.AddFile(context)); services.Configure <FileLoggerOptions>(config); using (var serviceProvider = services.BuildServiceProvider()) { var loggerFactory = serviceProvider.GetService <ILoggerFactory>(); var logger1 = loggerFactory.CreateLogger <LoggingTest>(); using (logger1.BeginScope("SCOPE")) { logger1.LogInformation("This is a nice logger."); using (logger1.BeginScope("NESTED SCOPE")) { logger1.LogInformation("This is a smart logger."); // changing switch and scopes inclusion configJson = $@"{{ '{ConfigurationFileLoggerSettings.LogLevelSectionName}': {{ '{FileLoggerSettingsBase.DefaultCategoryName}': '{LogLevel.Information}', }} }}"; fileProvider.WriteContent("config.json", configJson); Assert.Equal(1, completionTasks.Count); Task.WhenAll(completionTasks).GetAwaiter().GetResult(); logger1 = loggerFactory.CreateLogger <LoggingTest>(); logger1.LogInformation("This one shouldn't include scopes."); logger1.LogTrace("This one shouldn't be included at all."); } } // ensuring that the entry is processed completionTasks.Clear(); cts.Cancel(); Assert.Equal(1, completionTasks.Count); Task.WhenAll(completionTasks).GetAwaiter().GetResult(); } var logFile = (MemoryFileInfo)context.FileProvider.GetFileInfo($@"fallback.log"); Assert.True(logFile.Exists && !logFile.IsDirectory); var lines = logFile.Content.Split(new[] { Environment.NewLine }, StringSplitOptions.None); Assert.Equal(Encoding.UTF8, logFile.Encoding); Assert.Equal(lines, new[] { $"info: {typeof(LoggingTest).FullName}[0] @ {context.GetTimestamp().ToLocalTime():o}", $" => SCOPE", $" This is a nice logger.", $"info: {typeof(LoggingTest).FullName}[0] @ {context.GetTimestamp().ToLocalTime():o}", $" => SCOPE => NESTED SCOPE", $" This is a smart logger.", $"info: {typeof(LoggingTest).FullName}[0] @ {context.GetTimestamp().ToLocalTime():o}", $" This one shouldn't include scopes.", "" }); }
public void ReloadConfigurationSettings() { var configJson = $@"{{ '{nameof(ConfigurationFileLoggerSettings.MaxFileSize)}' : 10000, '{ConfigurationFileLoggerSettings.LogLevelSectionName}': {{ '{FileLoggerSettingsBase.DefaultCategoryName}': '{LogLevel.Information}', }} }}"; var fileProvider = new MemoryFileProvider(); fileProvider.CreateFile("config.json", configJson, Encoding.UTF8); var cb = new ConfigurationBuilder(); cb.AddJsonFile(fileProvider, "config.json", optional: false, reloadOnChange: true); var config = cb.Build(); var settings = new ConfigurationFileLoggerSettings(config); var cts = new CancellationTokenSource(); var context = new TestFileLoggerContext(cts.Token); var completionTasks = new List <Task>(); context.Complete += (s, e) => completionTasks.Add(e); context.SetTimestamp(new DateTime(2017, 1, 1, 0, 0, 0, DateTimeKind.Utc)); using (var loggerFactory = new LoggerFactory()) { loggerFactory.AddFile(context, settings); var logger1 = loggerFactory.CreateLogger <LoggingTest>(); logger1.LogInformation("This is a nice logger."); // changing date format, counter format and max file size configJson = $@"{{ '{nameof(ConfigurationFileLoggerSettings.DateFormat)}' : 'yyyyMMdd', '{nameof(ConfigurationFileLoggerSettings.MaxFileSize)}' : 1, '{nameof(ConfigurationFileLoggerSettings.CounterFormat)}' : '00', '{ConfigurationFileLoggerSettings.LogLevelSectionName}': {{ '{FileLoggerSettingsBase.DefaultCategoryName}': '{LogLevel.Information}', }} }}"; fileProvider.WriteContent("config.json", configJson); Assert.Equal(1, completionTasks.Count); Task.WhenAll(completionTasks).GetAwaiter().GetResult(); logger1.LogInformation("This is a smart logger."); // ensuring that the entry is processed completionTasks.Clear(); cts.Cancel(); Assert.Equal(1, completionTasks.Count); Task.WhenAll(completionTasks).GetAwaiter().GetResult(); } var logFile = (MemoryFileInfo)context.FileProvider.GetFileInfo($@"fallback-0.log"); Assert.True(logFile.Exists && !logFile.IsDirectory); var lines = logFile.Content.Split(new[] { Environment.NewLine }, StringSplitOptions.None); Assert.Equal(Encoding.UTF8, logFile.Encoding); Assert.Equal(lines, new[] { $"info: {typeof(LoggingTest).FullName}[0] @ {context.GetTimestamp().ToLocalTime():o}", $" This is a nice logger.", "" }); logFile = (MemoryFileInfo)context.FileProvider.GetFileInfo($@"fallback-{context.GetTimestamp().ToLocalTime():yyyyMMdd}-00.log"); Assert.True(logFile.Exists && !logFile.IsDirectory); lines = logFile.Content.Split(new[] { Environment.NewLine }, StringSplitOptions.None); Assert.Equal(Encoding.UTF8, logFile.Encoding); Assert.Equal(lines, new[] { $"info: {typeof(LoggingTest).FullName}[0] @ {context.GetTimestamp().ToLocalTime():o}", $" This is a smart logger.", "" }); }
public void ReloadSettings() { var cts = new CancellationTokenSource(); var settings = new FileLoggerSettings { Switches = new Dictionary <string, LogLevel> { { FileLoggerSettingsBase.DefaultCategoryName, LogLevel.Information } }, ChangeToken = new CancellationChangeToken(cts.Token) }; var context = new TestFileLoggerContext(CancellationToken.None); var completionTasks = new List <Task>(); context.Complete += (s, e) => completionTasks.Add(e); context.SetTimestamp(new DateTime(2017, 1, 1, 0, 0, 0, DateTimeKind.Utc)); using (var loggerFactory = new LoggerFactory()) { loggerFactory.AddFile(context, settings); var logger1 = loggerFactory.CreateLogger <LoggingTest>(); logger1.LogInformation("This is a nice logger."); // changing text format settings.TextBuilder = new CustomLogEntryTextBuilder(); var newCts = new CancellationTokenSource(); settings.ChangeToken = new CancellationChangeToken(newCts.Token); cts.Cancel(); cts = newCts; Assert.Equal(1, completionTasks.Count); Task.WhenAll(completionTasks).GetAwaiter().GetResult(); logger1.LogInformation("This is a smart logger."); // changing base path, file encoding and filename mapping settings.BasePath = "Logs"; settings.EnsureBasePath = true; settings.FileEncoding = Encoding.Unicode; settings.FileNameMappings = new Dictionary <string, string> { { typeof(LoggingTest).FullName, "test.log" } }; completionTasks.Clear(); newCts = new CancellationTokenSource(); settings.ChangeToken = new CancellationChangeToken(newCts.Token); cts.Cancel(); cts = newCts; Assert.Equal(1, completionTasks.Count); Task.WhenAll(completionTasks).GetAwaiter().GetResult(); logger1.LogWarning("This goes to another file."); // ensuring that the entry is processed completionTasks.Clear(); cts.Cancel(); Assert.Equal(1, completionTasks.Count); Task.WhenAll(completionTasks).GetAwaiter().GetResult(); } var logFile = (MemoryFileInfo)context.FileProvider.GetFileInfo($@"fallback.log"); Assert.True(logFile.Exists && !logFile.IsDirectory); var lines = logFile.Content.Split(new[] { Environment.NewLine }, StringSplitOptions.None); Assert.Equal(Encoding.UTF8, logFile.Encoding); Assert.Equal(lines, new[] { $"info: {typeof(LoggingTest).FullName}[0] @ {context.GetTimestamp().ToLocalTime():o}", $" This is a nice logger.", $"[info]: {typeof(LoggingTest).FullName}[0] @ {context.GetTimestamp().ToLocalTime():o}", $" This is a smart logger.", "" }); logFile = (MemoryFileInfo)context.FileProvider.GetFileInfo($@"Logs\test.log"); Assert.True(logFile.Exists && !logFile.IsDirectory); lines = logFile.Content.Split(new[] { Environment.NewLine }, StringSplitOptions.None); Assert.Equal(Encoding.Unicode, logFile.Encoding); Assert.Equal(lines, new[] { $"[warn]: {typeof(LoggingTest).FullName}[0] @ {context.GetTimestamp().ToLocalTime():o}", $" This goes to another file.", "" }); }
public async Task LoggingToPhysicalUsingDIAndExpectingDiagnosticEvents() { var logsDirName = Guid.NewGuid().ToString("D"); var tempPath = Path.Combine(Path.GetTempPath()); var logPath = Path.Combine(tempPath, logsDirName); var fileProvider = new PhysicalFileProvider(tempPath); var cts = new CancellationTokenSource(); var context = new TestFileLoggerContext(cts.Token, completionTimeout: Timeout.InfiniteTimeSpan); context.SetTimestamp(new DateTime(2017, 1, 1, 0, 0, 0, DateTimeKind.Utc)); var diagnosticEvents = new List <IFileLoggerDiagnosticEvent>(); context.DiagnosticEvent += diagnosticEvents.Add; var services = new ServiceCollection(); services.AddOptions(); services.AddLogging(b => b.AddFile(context, o => { o.FileAppender = new PhysicalFileAppender(fileProvider); o.BasePath = logsDirName; o.FileAccessMode = LogFileAccessMode.KeepOpen; o.Files = new[] { new LogFileOptions { Path = "<invalid_filename>.log" } }; })); if (Directory.Exists(logPath)) { Directory.Delete(logPath, recursive: true); } try { FileLoggerProvider[] providers; using (ServiceProvider sp = services.BuildServiceProvider()) { providers = context.GetProviders(sp).ToArray(); Assert.Equal(1, providers.Length); ILogger <LoggingTest> logger1 = sp.GetService <ILogger <LoggingTest> >(); logger1.LogInformation("This is a nice logger."); logger1.LogWarning(1, "This is a smart logger."); cts.Cancel(); // ensuring that all entries are processed await context.GetCompletion(sp); Assert.True(providers.All(provider => provider.Completion.IsCompleted)); } Assert.NotEmpty(diagnosticEvents); Assert.All(diagnosticEvents, e => { Assert.IsType <FileLoggerDiagnosticEvent.LogEntryWriteFailed>(e); Assert.IsType <FileLoggerProcessor>(e.Source); Assert.NotNull(e.FormattableMessage); Assert.NotNull(e.Exception); }); } finally { if (Directory.Exists(logPath)) { Directory.Delete(logPath, recursive: true); } } }
public void ReloadOptionsSettingsMultipleProviders() { var fileProvider = new MemoryFileProvider(); var fileAppender = new MemoryFileAppender(fileProvider); dynamic settings = new JObject(); dynamic globalFilters = settings[ConfigurationFileLoggerSettings.LogLevelSectionName] = new JObject(); globalFilters[FileLoggerSettingsBase.DefaultCategoryName] = LogLevel.None.ToString(); settings[FileLoggerProvider.Alias] = new JObject(); dynamic fileFilters = settings[FileLoggerProvider.Alias][ConfigurationFileLoggerSettings.LogLevelSectionName] = new JObject(); fileFilters[FileLoggerSettingsBase.DefaultCategoryName] = LogLevel.Warning.ToString(); settings[OtherFileLoggerProvider.Alias] = new JObject(); settings[OtherFileLoggerProvider.Alias][nameof(FileLoggerOptions.FallbackFileName)] = "fallback.log"; dynamic otherFileFilters = settings[OtherFileLoggerProvider.Alias][ConfigurationFileLoggerSettings.LogLevelSectionName] = new JObject(); otherFileFilters[FileLoggerSettingsBase.DefaultCategoryName] = LogLevel.Information.ToString(); var settingsJson = ((JObject)settings).ToString(); fileProvider.CreateFile("config.json", settingsJson); IConfigurationRoot config = new ConfigurationBuilder() .AddJsonFile(fileProvider, "config.json", optional: false, reloadOnChange: true) .Build(); var context = new TestFileLoggerContext(); var completionTasks = new List <Task>(); context.Complete += (s, e) => completionTasks.Add(e); var services = new ServiceCollection(); services.AddOptions(); services.AddLogging(lb => { lb.AddConfiguration(config); lb.AddFile(context, o => o.FileAppender = o.FileAppender ?? fileAppender); lb.AddFile <OtherFileLoggerProvider>(context, o => o.FileAppender = o.FileAppender ?? fileAppender); }); using (ServiceProvider sp = services.BuildServiceProvider()) { ILoggerFactory loggerFactory = sp.GetRequiredService <ILoggerFactory>(); ILogger logger = loggerFactory.CreateLogger("X"); logger.LogInformation("This is an info."); logger.LogWarning("This is a warning."); fileFilters[FileLoggerSettingsBase.DefaultCategoryName] = LogLevel.Information.ToString(); otherFileFilters[FileLoggerSettingsBase.DefaultCategoryName] = LogLevel.Warning.ToString(); settingsJson = ((JObject)settings).ToString(); fileProvider.WriteContent("config.json", settingsJson); // reload is triggered twice due to a bug in the framework (https://github.com/aspnet/Logging/issues/874) Assert.Equal(2 * 2, completionTasks.Count); Task.WhenAll(completionTasks).GetAwaiter().GetResult(); logger.LogInformation("This is another info."); logger.LogWarning("This is another warning."); } var logFile = (MemoryFileInfo)fileProvider.GetFileInfo(LoggingTest.FallbackFileName); Assert.True(logFile.Exists && !logFile.IsDirectory); var lines = logFile.Content.Split(new[] { Environment.NewLine }, StringSplitOptions.None); Assert.Equal(Encoding.UTF8, logFile.Encoding); Assert.Equal(new[] { $"warn: X[0] @ {context.GetTimestamp().ToLocalTime():o}", $" This is a warning.", $"info: X[0] @ {context.GetTimestamp().ToLocalTime():o}", $" This is another info.", $"warn: X[0] @ {context.GetTimestamp().ToLocalTime():o}", $" This is another warning.", "" }, lines); logFile = (MemoryFileInfo)fileProvider.GetFileInfo((string)settings[OtherFileLoggerProvider.Alias].FallbackFileName); Assert.True(logFile.Exists && !logFile.IsDirectory); lines = logFile.Content.Split(new[] { Environment.NewLine }, StringSplitOptions.None); Assert.Equal(Encoding.UTF8, logFile.Encoding); Assert.Equal(new[] { $"info: X[0] @ {context.GetTimestamp().ToLocalTime():o}", $" This is an info.", $"warn: X[0] @ {context.GetTimestamp().ToLocalTime():o}", $" This is a warning.", $"warn: X[0] @ {context.GetTimestamp().ToLocalTime():o}", $" This is another warning.", "" }, lines); }