392 lines
12 KiB
C#
392 lines
12 KiB
C#
// Copyright (c) .NET Foundation. All rights reserved.
|
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using Microsoft.AspNetCore.Diagnostics.Elm;
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Options;
|
|
using Xunit;
|
|
|
|
namespace Microsoft.AspNetCore.Diagnostics.Tests
|
|
{
|
|
public class ElmLoggerTest
|
|
{
|
|
private const string _name = "test";
|
|
private const string _state = "This is a test";
|
|
private static Func<string, LogLevel, bool> _filter = (_, __) => true;
|
|
|
|
private static Tuple<ElmLogger, ElmStore> SetUp(Func<string, LogLevel, bool> filter = null, string name = null)
|
|
{
|
|
// Arrange
|
|
var store = new ElmStore();
|
|
var options = new ElmOptions() { Filter = filter ?? _filter };
|
|
var provider = new ElmLoggerProvider(store, Options.Create(options));
|
|
var logger = (ElmLogger)provider.CreateLogger(name ?? _name);
|
|
|
|
return new Tuple<ElmLogger, ElmStore>(logger, store);
|
|
}
|
|
|
|
[Fact]
|
|
public void LogsWhenNullFormatterGiven()
|
|
{
|
|
// Arrange
|
|
var t = SetUp();
|
|
var logger = t.Item1;
|
|
var store = t.Item2;
|
|
|
|
// Act
|
|
logger.Log(LogLevel.Information, 0, _state, null, null);
|
|
|
|
// Assert
|
|
Assert.Single(store.GetActivities());
|
|
}
|
|
|
|
[Fact]
|
|
public void DoesNotLogWithEmptyStateAndException()
|
|
{
|
|
// Arrange
|
|
var t = SetUp();
|
|
var logger = t.Item1;
|
|
var store = t.Item2;
|
|
|
|
// Act
|
|
logger.Log<object>(LogLevel.Information, 0, null, null, null);
|
|
|
|
// Assert
|
|
Assert.Empty(store.GetActivities());
|
|
}
|
|
|
|
[Fact]
|
|
public void DefaultLogsForAllLogLevels()
|
|
{
|
|
// Arrange
|
|
var t = SetUp();
|
|
var logger = t.Item1;
|
|
var store = t.Item2;
|
|
|
|
// Act
|
|
logger.Log(LogLevel.Trace, 0, _state, null, null);
|
|
logger.Log(LogLevel.Debug, 0, _state, null, null);
|
|
logger.Log(LogLevel.Information, 0, _state, null, null);
|
|
logger.Log(LogLevel.Warning, 0, _state, null, null);
|
|
logger.Log(LogLevel.Error, 0, _state, null, null);
|
|
logger.Log(LogLevel.Critical, 0, _state, null, null);
|
|
|
|
// Assert
|
|
Assert.Equal(6, (store.GetActivities().SelectMany(a => NodeLogs(a.Root, new List<LogInfo>()))).ToList().Count);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(LogLevel.Warning, "", 3)]
|
|
[InlineData(LogLevel.Warning, "te", 3)]
|
|
[InlineData(LogLevel.Warning, "bad", 0)]
|
|
[InlineData(LogLevel.Critical, "", 1)]
|
|
[InlineData(LogLevel.Critical, "test", 1)]
|
|
[InlineData(LogLevel.Trace, "t", 6)]
|
|
public void Filter_LogsWhenAppropriate(LogLevel minLevel, string prefix, int count)
|
|
{
|
|
// Arrange
|
|
var t = SetUp((name, level) => (name.StartsWith(prefix, StringComparison.Ordinal) && level >= minLevel), _name);
|
|
var logger = t.Item1;
|
|
var store = t.Item2;
|
|
|
|
// Act
|
|
logger.Log(LogLevel.Trace, 0, _state, null, null);
|
|
logger.Log(LogLevel.Debug, 0, _state, null, null);
|
|
logger.Log(LogLevel.Information, 0, _state, null, null);
|
|
logger.Log(LogLevel.Warning, 0, _state, null, null);
|
|
logger.Log(LogLevel.Error, 0, _state, null, null);
|
|
logger.Log(LogLevel.Critical, 0, _state, null, null);
|
|
|
|
// Assert
|
|
Assert.Equal(count, (store.GetActivities().SelectMany(a => NodeLogs(a.Root, new List<LogInfo>()))).ToList().Count);
|
|
}
|
|
|
|
[Fact]
|
|
public void CountReturnsCorrectNumber()
|
|
{
|
|
// Arrange
|
|
var t = SetUp();
|
|
var logger = t.Item1;
|
|
var store = t.Item2;
|
|
|
|
// Act
|
|
using (logger.BeginScope("test14"))
|
|
{
|
|
for (var i = 0; i < 25; i++)
|
|
{
|
|
logger.LogWarning("hello world");
|
|
}
|
|
using (logger.BeginScope("test15"))
|
|
{
|
|
for (var i = 0; i < 25; i++)
|
|
{
|
|
logger.LogCritical("goodbye world");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Assert
|
|
Assert.Equal(50, store.Count());
|
|
}
|
|
|
|
[Fact]
|
|
public void ThreadsHaveSeparateActivityContexts()
|
|
{
|
|
// Arrange
|
|
var t = SetUp();
|
|
var logger = t.Item1;
|
|
var store = t.Item2;
|
|
|
|
var testThread = new TestThread(logger);
|
|
Thread workerThread = new Thread(testThread.work);
|
|
|
|
// Act
|
|
workerThread.Start();
|
|
using (logger.BeginScope("test1"))
|
|
{
|
|
logger.LogWarning("hello world");
|
|
Thread.Sleep(1000);
|
|
logger.LogCritical("goodbye world");
|
|
}
|
|
workerThread.Join();
|
|
|
|
// Assert
|
|
Assert.Equal(17, (store.GetActivities().SelectMany(a => NodeLogs(a.Root, new List<LogInfo>()))).ToList().Count);
|
|
Assert.Equal(2, store.GetActivities().ToList().Count);
|
|
}
|
|
|
|
[Fact]
|
|
public void ScopesHaveProperTreeStructure()
|
|
{
|
|
// Arrange
|
|
var t = SetUp();
|
|
var logger = t.Item1;
|
|
var store = t.Item2;
|
|
|
|
var testThread = new TestThread(logger);
|
|
Thread workerThread = new Thread(testThread.work);
|
|
|
|
// Act
|
|
workerThread.Start();
|
|
using (logger.BeginScope("test2"))
|
|
{
|
|
logger.LogWarning("hello world");
|
|
Thread.Sleep(1000);
|
|
logger.LogCritical("goodbye world");
|
|
}
|
|
workerThread.Join();
|
|
|
|
// Assert
|
|
// get the root of the activity for scope "test2"
|
|
var root1 = (store.GetActivities())
|
|
.Where(a => string.Equals(a.Root.State?.ToString(), "test2"))?
|
|
.FirstOrDefault()?
|
|
.Root;
|
|
Assert.NotNull(root1);
|
|
var root2 = (store.GetActivities())
|
|
.Where(a => string.Equals(a.Root.State?.ToString(), "test12"))?
|
|
.FirstOrDefault()?
|
|
.Root;
|
|
Assert.NotNull(root2);
|
|
|
|
Assert.Equal(0, root1.Children.Count);
|
|
Assert.Equal(2, root1.Messages.Count);
|
|
Assert.Equal(1, root2.Children.Count);
|
|
Assert.Equal(12, root2.Messages.Count);
|
|
Assert.Equal(0, root2.Children.First().Children.Count);
|
|
Assert.Equal(3, root2.Children.First().Messages.Count);
|
|
}
|
|
|
|
[Fact]
|
|
public void CollapseTree_CollapsesWhenNoLogsInSingleScope()
|
|
{
|
|
// Arrange
|
|
var t = SetUp();
|
|
var logger = t.Item1;
|
|
var store = t.Item2;
|
|
|
|
// Act
|
|
using (logger.BeginScope("test3"))
|
|
{
|
|
}
|
|
|
|
// Assert
|
|
Assert.Empty(store.GetActivities());
|
|
}
|
|
|
|
[Fact]
|
|
public void CollapseTree_CollapsesWhenNoLogsInNestedScope()
|
|
{
|
|
// Arrange
|
|
var t = SetUp();
|
|
var logger = t.Item1;
|
|
var store = t.Item2;
|
|
|
|
// Act
|
|
using (logger.BeginScope("test4"))
|
|
{
|
|
using (logger.BeginScope("test5"))
|
|
{
|
|
}
|
|
}
|
|
|
|
// Assert
|
|
Assert.Empty(store.GetActivities());
|
|
}
|
|
|
|
[Fact]
|
|
public void CollapseTree_DoesNotCollapseWhenLogsExist()
|
|
{
|
|
// Arrange
|
|
var t = SetUp();
|
|
var logger = t.Item1;
|
|
var store = t.Item2;
|
|
|
|
// Act
|
|
using (logger.BeginScope("test6"))
|
|
{
|
|
using (logger.BeginScope("test7"))
|
|
{
|
|
logger.LogTrace("hi");
|
|
}
|
|
}
|
|
|
|
// Assert
|
|
Assert.Single(store.GetActivities());
|
|
}
|
|
|
|
[Fact]
|
|
public void CollapseTree_CollapsesAppropriateNodes()
|
|
{
|
|
// Arrange
|
|
var t = SetUp();
|
|
var logger = t.Item1;
|
|
var store = t.Item2;
|
|
|
|
// Act
|
|
using (logger.BeginScope("test8"))
|
|
{
|
|
logger.LogDebug("hi");
|
|
using (logger.BeginScope("test9"))
|
|
{
|
|
}
|
|
}
|
|
|
|
// Assert
|
|
Assert.Single(store.GetActivities());
|
|
var context = store.GetActivities()
|
|
.Where(a => string.Equals(a.Root.State?.ToString(), "test8"))
|
|
.First();
|
|
Assert.Empty(context.Root.Children);
|
|
}
|
|
|
|
[Fact]
|
|
public void CollapseTree_WorksWithFilter()
|
|
{
|
|
// Arrange
|
|
var t = SetUp((_, level) => level >= LogLevel.Warning, null);
|
|
var logger = t.Item1;
|
|
var store = t.Item2;
|
|
|
|
// Act
|
|
using (logger.BeginScope("test10"))
|
|
{
|
|
using (logger.BeginScope("test11"))
|
|
{
|
|
logger.LogInformation("hi");
|
|
}
|
|
}
|
|
|
|
// Assert
|
|
Assert.Empty(store.GetActivities());
|
|
}
|
|
|
|
#if NET46
|
|
private static void DomainFunc()
|
|
{
|
|
var t = SetUp();
|
|
var logger = t.Item1;
|
|
using (logger.BeginScope("newDomain scope"))
|
|
{
|
|
logger.LogInformation("Test");
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void ScopeWithChangingAppDomains_DoesNotAccessUnloadedAppDomain()
|
|
{
|
|
// Arrange
|
|
var t = SetUp();
|
|
var logger = t.Item1;
|
|
var store = t.Item2;
|
|
var setupInfo = new AppDomainSetup
|
|
{
|
|
ApplicationBase = AppDomain.CurrentDomain.BaseDirectory
|
|
};
|
|
var domain = AppDomain.CreateDomain("newDomain", null, setupInfo);
|
|
|
|
// Act
|
|
domain.DoCallBack(DomainFunc);
|
|
AppDomain.Unload(domain);
|
|
using (logger.BeginScope("Scope1"))
|
|
{
|
|
logger.LogInformation("Testing");
|
|
}
|
|
|
|
Assert.Equal(1, store.Count());
|
|
}
|
|
#endif
|
|
|
|
private List<LogInfo> NodeLogs(ScopeNode node, List<LogInfo> logs)
|
|
{
|
|
if (node != null)
|
|
{
|
|
logs.AddRange(node.Messages);
|
|
foreach (var child in node.Children)
|
|
{
|
|
NodeLogs(child, logs);
|
|
}
|
|
}
|
|
return logs;
|
|
}
|
|
|
|
private class TestThread
|
|
{
|
|
private ILogger _logger;
|
|
|
|
public TestThread(ILogger logger)
|
|
{
|
|
_logger = logger;
|
|
}
|
|
|
|
public void work()
|
|
{
|
|
using (_logger.BeginScope("test12"))
|
|
{
|
|
for (var i = 0; i < 5; i++)
|
|
{
|
|
_logger.LogDebug(string.Format("xxx {0}", i));
|
|
Thread.Sleep(5);
|
|
}
|
|
using (_logger.BeginScope("test13"))
|
|
{
|
|
for (var i = 0; i < 3; i++)
|
|
{
|
|
_logger.LogDebug(string.Format("yyy {0}", i));
|
|
Thread.Sleep(200);
|
|
}
|
|
}
|
|
for (var i = 0; i < 7; i++)
|
|
{
|
|
_logger.LogDebug(string.Format("zzz {0}", i));
|
|
Thread.Sleep(40);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |