#15 Add basic verbosity scoping

parent c26f6e59
Pipeline #103 passed with stages
in 8 minutes and 23 seconds
using Microsoft.VisualStudio.TestTools.UnitTesting;
using KSharp.NChronicle.Core.Model;
using System.Collections.Generic;
namespace KSharp.NChronicle.Core.Tests.ForChronicle
{
public partial class WhenUsingAChronicle
{
[TestClass]
public class AndScoping : WhenUsingAChronicleTestBase
{
private new static IEnumerable<object[]> _chronicleLevel => WhenUsingAChronicleTestBase._chronicleLevel;
[TestMethod]
[DynamicData(nameof(_chronicleLevel))]
public void ThenTheDefaultVerbositoryIsZero(ChronicleLevel level)
{
// Act
CallAction(level, _message);
// Assert
Assert.AreEqual(0, this._receivedRecord.Verbosity);
}
[TestMethod]
[DynamicData(nameof(_chronicleLevel))]
public void ThenScopingInWillGiveOneVerbosity(ChronicleLevel level)
{
using (this._chronicle.ScopeIn())
{
// Act
CallAction(level, _message);
}
// Assert
Assert.AreEqual(1, this._receivedRecord.Verbosity);
}
[TestMethod]
[DynamicData(nameof(_chronicleLevel))]
public void ThenScopingInMoreWillGiveMoreVerbosity(ChronicleLevel level)
{
using (this._chronicle.ScopeIn())
using (this._chronicle.ScopeIn())
{
// Act
CallAction(level, _message);
}
// Assert
Assert.AreEqual(2, this._receivedRecord.Verbosity);
}
[TestMethod]
[DynamicData(nameof(_chronicleLevel))]
public void ThenScopingOutWillDropVerbosity(ChronicleLevel level)
{
using (this._chronicle.ScopeIn())
using (this._chronicle.ScopeIn())
{
// Act
CallAction(level, _message);
}
CallAction(level, _message);
// Assert
Assert.AreEqual(0, this._receivedRecord.Verbosity);
}
[TestMethod]
[DynamicData(nameof(_chronicleLevel))]
public void ThenScopingOutDirectlyWillDropVerbosity(ChronicleLevel level)
{
this._chronicle.ScopeIn();
this._chronicle.ScopeIn();
// Act
CallAction(level, _message);
this._chronicle.ScopeOut();
CallAction(level, _message);
// Assert
Assert.AreEqual(1, this._receivedRecord.Verbosity);
}
}
}
}
......@@ -36,7 +36,7 @@ namespace KSharp.NChronicle.Core.Tests.ForChronicleLibrary
{
this._exception = e;
}
this._record = new ChronicleRecord(ChronicleLevel.Critical, "This is a test message", this._exception, "Tag1", "Tag2");
this._record = new ChronicleRecord(ChronicleLevel.Critical, "This is a test message", this._exception, 3, "Tag1", "Tag2");
}
[TestMethod]
......
......@@ -22,6 +22,7 @@ namespace KSharp.NChronicle.Core.Tests.ForChronicleRecord
private Exception _exception;
private string[] _tags;
private ChronicleLevel _level;
private int _verbosity;
[TestInitialize]
public void Init()
......@@ -30,7 +31,8 @@ namespace KSharp.NChronicle.Core.Tests.ForChronicleRecord
this._exception = new IOException();
this._tags = new[] { "Tag1", "Tag2" };
this._level = ChronicleLevel.Critical;
this._chronicleRecord = new ChronicleRecord(this._level, this._message, this._exception, this._tags);
this._verbosity = 3;
this._chronicleRecord = new ChronicleRecord(this._level, this._message, this._exception, this._verbosity, this._tags);
}
[TestMethod]
......@@ -51,6 +53,13 @@ namespace KSharp.NChronicle.Core.Tests.ForChronicleRecord
Assert.AreEqual(this._message, this._chronicleRecord.Message, "The message is not as given.");
}
[TestMethod]
public void ThenTheVerbosityIsAsGiven()
{
Assert.AreEqual(this._verbosity, this._chronicleRecord.Verbosity, "The verbosity is not as given.");
}
[TestMethod]
public void ThenTheTagsAreAsGiven()
{
......@@ -66,7 +75,7 @@ namespace KSharp.NChronicle.Core.Tests.ForChronicleRecord
[TestMethod]
public void ThenTheTagsAreEmptyIfGivenNull()
{
this._chronicleRecord = new ChronicleRecord(this._level, this._message, this._exception, null);
this._chronicleRecord = new ChronicleRecord(this._level, this._message, this._exception, 0, null);
Assert.IsFalse(this._chronicleRecord.Tags.Any(), "The tags are not empty.");
}
......@@ -74,7 +83,7 @@ namespace KSharp.NChronicle.Core.Tests.ForChronicleRecord
[TestMethod]
public void ThenTheMessageIsNullIfGivenNull()
{
this._chronicleRecord = new ChronicleRecord(this._level, null, this._exception, this._tags);
this._chronicleRecord = new ChronicleRecord(this._level, null, this._exception, 0, this._tags);
Assert.IsNull(this._chronicleRecord.Message, "The message is not null.");
}
......@@ -82,7 +91,7 @@ namespace KSharp.NChronicle.Core.Tests.ForChronicleRecord
[TestMethod]
public void ThenTheExceptionIsNullIfGivenNull()
{
this._chronicleRecord = new ChronicleRecord(this._level, this._message, null, this._tags);
this._chronicleRecord = new ChronicleRecord(this._level, this._message, null, 0, this._tags);
Assert.IsNull(this._chronicleRecord.Exception, "The exception is not null.");
}
......
......@@ -34,6 +34,12 @@ namespace KSharp.NChronicle.Core.Tests.ForChronicleRecord
Assert.AreEqual(ChronicleLevel.Info, this._chronicleRecord.Level, "The level is not Info");
}
[TestMethod]
public void ThenTheVerbosityIsZero()
{
Assert.AreEqual(0, this._chronicleRecord.Verbosity, "The verbosity is not 0");
}
[TestMethod]
public void ThenTheMessageIsNull()
{
......
......@@ -51,6 +51,7 @@ namespace KSharp.NChronicle.Core.Tests.ForChronicleRecord
Assert.IsNotNull(deserializedRecord.Exception, "The exception was not deserialized.");
Assert.IsNotNull(deserializedRecord.Tags, "The tags were not deserialized.");
Assert.AreEqual(originalRecord.Verbosity, deserializedRecord.Verbosity, "The message was deserialized but incorrectly; the verbosity was incorrect.");
Assert.AreEqual(originalRecord.ThreadId, deserializedRecord.ThreadId, "The message was deserialized but incorrectly; the ThreadId was incorrect.");
Assert.AreEqual(originalRecord.Message, deserializedRecord.Message, "The message was deserialized but incorrectly; the message was incorrect.");
Assert.AreEqual(originalRecord.UtcTime, deserializedRecord.UtcTime, "The time was not deserialized correctly; the time was incorrect.");
......@@ -106,7 +107,7 @@ namespace KSharp.NChronicle.Core.Tests.ForChronicleRecord
string[] tags = new[] { "Tag1", "Tag2" };
ChronicleLevel level = ChronicleLevel.Critical;
ChronicleRecord chronicleRecord = new ChronicleRecord(level, message, exception, tags);
ChronicleRecord chronicleRecord = new ChronicleRecord(level, message, exception, 2, tags);
return (JsonConvert.SerializeObject(chronicleRecord), chronicleRecord);
}
......
......@@ -11,6 +11,11 @@ namespace KSharp.NChronicle.Core.Abstractions
public interface IChronicleRecord
{
/// <summary>
/// The scope depth from which this record was created.
/// </summary>
int Verbosity { get; }
/// <summary>
/// The managed thread Id for the thread on which this record was created.
/// </summary>
......
......@@ -2,6 +2,7 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using KSharp.NChronicle.Core.Abstractions;
using KSharp.NChronicle.Core.Model;
......@@ -16,6 +17,7 @@ namespace KSharp.NChronicle.Core
private ChronicleConfiguration _configuration;
private ConcurrentBag<string> _tags;
private int _currentVerbosity;
/// <summary>
/// Create Chronicle configured with the NChronicle base configuration.
......@@ -27,9 +29,16 @@ namespace KSharp.NChronicle.Core
this._tags = new ConcurrentBag<string>();
}
private void ConfigurationChangedHandler()
public IDisposable ScopeIn()
{
this._configuration = Chronicle.GetConfiguration();
Interlocked.Increment(ref this._currentVerbosity);
return new ChronicleScope(this);
}
public void ScopeOut()
{
if (this._currentVerbosity > 0)
Interlocked.Decrement(ref this._currentVerbosity);
}
#region Tags
......@@ -335,7 +344,7 @@ namespace KSharp.NChronicle.Core
private IChronicleRecord BuildRecord(ChronicleLevel level, string message, Exception exception, IEnumerable<string> tags)
{
IEnumerable<string> allTags = tags.Concat(this._tags);
return new ChronicleRecord(level, message, exception, allTags.ToArray());
return new ChronicleRecord(level, message, exception, this._currentVerbosity, allTags.ToArray());
}
private void SendToLibraries(IChronicleRecord record)
......@@ -346,6 +355,11 @@ namespace KSharp.NChronicle.Core
}
}
private void ConfigurationChangedHandler()
{
this._configuration = Chronicle.GetConfiguration();
}
/// <summary>
/// Destructor for this <see cref="Chronicle"/> instance.
/// </summary>
......
......@@ -21,6 +21,7 @@ namespace KSharp.NChronicle.Core.Converters
var exception = record["exception"];
var level = record["level"];
var message = record["message"];
var verbosity = record["verbosity"];
var tags = record["tags"];
if (threadId != null)
......@@ -33,6 +34,8 @@ namespace KSharp.NChronicle.Core.Converters
chronicleRecord.Level = level.ToObject<ChronicleLevel>();
if (message != null)
chronicleRecord.Message = message.Value<string>();
if (verbosity != null)
chronicleRecord.Verbosity = verbosity.Value<int>();
if (tags != null)
chronicleRecord.Tags = tags.ToObject<ReadOnlyCollection<string>>();
......@@ -46,6 +49,7 @@ namespace KSharp.NChronicle.Core.Converters
json.Add("threadId", JToken.FromObject(record.ThreadId));
json.Add("utcTime", JToken.FromObject(record.UtcTime));
json.Add("verbosity", JToken.FromObject(record.Verbosity));
json.Add("level", JToken.FromObject(record.Level));
if (record.Exception != null)
json.Add("exception", JToken.FromObject(record.Exception));
......
......@@ -35,7 +35,7 @@ namespace KSharp.NChronicle.Core.Model
/// <param name="message">Developer message for this record. Optional.</param>
/// <param name="exception">Related <see cref="System.Exception"/> for this record. Optional.</param>
/// <param name="tags">Tags to append to this record. Optional.</param>
public ChronicleRecord(ChronicleLevel level, string message = null, Exception exception = null, params string[] tags)
public ChronicleRecord(ChronicleLevel level, string message = null, Exception exception = null, int verbosity = 0, params string[] tags)
{
this.ThreadId = Thread.CurrentThread.ManagedThreadId;
this.UtcTime = DateTime.UtcNow;
......@@ -43,8 +43,14 @@ namespace KSharp.NChronicle.Core.Model
this.Tags = tags?.ToList().AsReadOnly() ?? new List<string>().AsReadOnly();
this.Exception = exception == null ? null : new ChronicleException(exception);
this.Level = level;
this.Verbosity = verbosity;
}
/// <summary>
/// The scope depth from which this record was created.
/// </summary>
public int Verbosity { get; internal set; }
/// <summary>
/// The managed thread Id for the thread on which this record was created.
/// </summary>
......
using System;
using KSharp.NChronicle.Core.Abstractions;
namespace KSharp.NChronicle.Core.Model
{
public class ChronicleScope : IChronicle, IDisposable
{
private bool _disposed;
private Chronicle _chronicle;
internal ChronicleScope(Chronicle chronicle)
{
this._chronicle = chronicle;
}
public void Critical(string message, params string[] tags)
=> this._chronicle.Critical(message, tags);
public void Critical(string message, Exception exception, params string[] tags)
=> this._chronicle.Critical(message, exception, tags);
public void Critical(Exception exception, params string[] tags)
=> this._chronicle.Critical(exception, tags);
public void Debug(string message, params string[] tags)
=> this._chronicle.Debug(message, tags);
public void Debug(string message, Exception exception, params string[] tags)
=> this._chronicle.Debug(message, exception, tags);
public void Debug(Exception exception, params string[] tags)
=> this._chronicle.Debug(exception, tags);
public void Emergency(string message, params string[] tags)
=> this._chronicle.Emergency(message, tags);
public void Emergency(string message, Exception exception, params string[] tags)
=> this._chronicle.Emergency(message, exception, tags);
public void Emergency(Exception exception, params string[] tags)
=> this._chronicle.Emergency(exception, tags);
public void Fatal(string message, params string[] tags)
=> this._chronicle.Fatal(message, tags);
public void Fatal(string message, Exception exception, params string[] tags)
=> this._chronicle.Fatal(message, exception, tags);
public void Fatal(Exception exception, params string[] tags)
=> this._chronicle.Fatal(exception, tags);
public void Info(string message, params string[] tags)
=> this._chronicle.Info(message, tags);
public void Info(string message, Exception exception, params string[] tags)
=> this._chronicle.Info(message, exception, tags);
public void Info(Exception exception, params string[] tags)
=> this._chronicle.Info(exception, tags);
public void Success(string message, params string[] tags)
=> this._chronicle.Success(message, tags);
public void Success(string message, Exception exception, params string[] tags)
=> this._chronicle.Success(message, exception, tags);
public void Success(Exception exception, params string[] tags)
=> this._chronicle.Success(exception, tags);
public void Trace(string message, params string[] tags)
=> this._chronicle.Trace(message, tags);
public void Trace(string message, Exception exception, params string[] tags)
=> this._chronicle.Trace(message, exception, tags);
public void Trace(Exception exception, params string[] tags)
=> this._chronicle.Trace(exception, tags);
public void Warning(string message, params string[] tags)
=> this._chronicle.Warning(message, tags);
public void Warning(string message, Exception exception, params string[] tags)
=> this._chronicle.Warning(message, exception, tags);
public void Warning(Exception exception, params string[] tags)
=> this._chronicle.Warning(exception, tags);
#region IDisposable Support
public void Dispose()
{
if (this._disposed) return;
this._disposed = true;
this._chronicle.ScopeOut();
}
#endregion
}
}
\ No newline at end of file
......@@ -69,6 +69,7 @@ Further Libraries planned for the NChronicle logging framework include NChronicl
<Compile Include="Abstractions\IChronicle.cs" />
<Compile Include="Abstractions\IChronicleLibrary.cs" />
<Compile Include="Model\ChronicleRecord.cs" />
<Compile Include="Model\ChronicleScope.cs" />
<Compile Include="StaticChronicle.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment