Do more in .NET
Configure POCO objects with simple, forgiving strings from configuration files or anywhere in a program.
The following sample uses an application .conf
file that looks like this:
# This is the application's .conf file. Settings in these
# files are largely case- and whitespace-insensitive. Hash
# signs may precede comments, but they aren't strictly
# necessary, as any line that does not parse to a known
# configuration setting will be ignored.
This alien will welcome our visitor
alien.Greeting = Hello
Alien.Home planet = Jupiter
Our visitor is on a tour through the solar system.
Visitor.homeplanet = Earth
visitor.tour destinations[0] = Mercury
visitor.tour destinations[1] = Venus
visitor.tour destinations[2] = Mars
visitor.ship models and makes[Tie Fighter] = Imperial
visitor.ship models and makes[Star Destroyer] = Imperial
visitor.ship models and makes[X-wing] = Rebel
internal class Sample {
private static void Main() {
/*
* Here, an instance of `Alien` is configured from
* the application's `.conf` file. Since no key is
* provided, the name of the type (Alien) is used
* as the key.
*/
var alien = Conf.Configure(new Alien());
/*
* The same thing is done for an instance of `Visitor`.
*/
var visitor = Conf.Configure(new Visitor());
/*
* After configuration, properties of each instance
* will match the values specified in the application's
* `.conf` file.
*/
Console.WriteLine($"A: {alien.Greeting}, {visitor.HomePlanet}!");
Console.WriteLine($"A: Welcome to {alien.HomePlanet}.");
Console.WriteLine($"V: Thanks! I also toured {string.Join(", ", visitor.TourDestinations)}");
Console.WriteLine($"V: on a {string.Join(", ", visitor.ShipModelsAndMakes.Select(pair => $"{pair.Value} {pair.Key}"))}");
Console.WriteLine();
/*
* Each source that was used during configuration
* is displayed here.
*/
Console.WriteLine(nameof(Conf.Sources));
Console.WriteLine("-------");
Console.WriteLine(string.Join(Environment.NewLine, Conf.Sources));
}
private class Alien {
public string Greeting { get; set; }
public string HomePlanet { get; set; }
}
private class Visitor {
public string HomePlanet { get; set; }
public IList<string> TourDestinations { get; set; } = new List<string>();
public IDictionary<string, string> ShipModelsAndMakes { get; set; } = new Dictionary<string, string>();
}
}
A lightweight, simple, and very opinionated logging library.
internal class Sample {
/*
* Typically, an instance of `ILog` is created for each type
* in code as a static member. The type may be added to logs
* to differentiate the source of each message.
*/
private static readonly ILog Log = Logging.For(typeof(Sample));
private static void Main() {
/*
* Reference `Domore.Logs.Conf` to get the extension method
* `ConfigureLogging` on the `IConfContainer` type. That
* method allows the configuration of logs at runtime with
* the exact same strings found in a `.conf` file.
*/
Conf.Contain(@"
log[console].config.default.severity = debug
log[console].config.default.format = {log} ({sev})
").ConfigureLogging();
/*
* It's a good practice to use the logging methods without
* arguments to check whether or not any logging would
* actually take place at that respective severity. It's a
* performance consideration, as this check is much less
* expensive than the call with arguments, particularly
* if the arguments are formatted strings.
*/
if (Log.Debug()) Log.Debug($"Now it's {DateTime.Now}.");
if (Log.Info()) Log.Info($"This is the logging sample.");
if (Log.Warn()) Log.Warn($"Hey! Look out!");
if (Log.Error()) Log.Error($"too late...");
if (Log.Critical()) Log.Critical($"We've got to exit.");
/*
* Logging can be reconfigured at any time.
*/
Conf.Contain(@"
log[console].service.background[debug] = cyan
log[console].service.foreground[debug] = black
log[console].service.background[info] = gray
log[console].service.foreground[info] = black
log[console].service.background[warn] = yellow
log[console].service.foreground[warn] = black
log[console].service.background[error] = red
log[console].service.foreground[error] = black
log[console].service.background[critical] = black
log[console].service.foreground[critical] = white
").ConfigureLogging();
/*
* It's not strictly necessary to check whether or not
* logging will occur before doing logging. In code
* where performance isn't critical, this may be more
* legible.
*/
Log.Debug($"Now it's {DateTime.Now}.");
Log.Info($"This is the logging sample.");
Log.Warn($"Hey! Look out!");
Log.Error($"too late...");
Log.Critical($"We've got to exit.");
Log.Critical("Changing default format and minimum severity...");
/*
* Here, the severity threshold is raised to `Info`,
* and the date and time are added to log messages.
*/
Conf.Contain(@"
log[console].config.default.severity = info
log[console].config.default.format = {dat} {tim} [{sev}]
").ConfigureLogging();
Log.Debug($"This won't show up.");
Log.Info($"But this will.");
/*
* The static method `ConfigureLogging` of class `LogConf`
* can be called as a shortcut to log configuration.
*/
LogConf.ConfigureLogging("log[console].service.background[info] = black; log[console].service.foreground[info] = gray");
/*
* Custom formats may be specified for types. That
* format will be used whenever an instance of the
* type is passed as a parameter to one of the logging
* methods. To specify a custom format for a type,
* call `Logging.Format`.
*/
Logging.Format(typeof(XY), obj => [$"{((XY)obj).X},{((XY)obj).Y}"]);
Log.Info("These XY coordinates have been formatted by a callback.", new XY { X = 1, Y = 2 }, new XY { X = 2, Y = 3 });
/*
* Custom log handlers implement `ILogService`.
* They're used by specifying the assembly qualified
* name of the type in conf.
*/
LogConf.ConfigureLogging($@"
log[queue].type = {typeof(CustomLogQueue).AssemblyQualifiedName}
log[queue].config.default.severity = info
");
Log.Info("This message will be handled by the custom log service:", typeof(CustomLogQueue).AssemblyQualifiedName);
/*
* Call `Logging.Complete` before the program exits
* to guarantee the pending log queue is emptied.
*/
Logging.Complete();
}
private class CustomLogQueue : ILogService {
public static Queue<string> Queue { get; } = new();
public void Log(string name, string data, LogSeverity severity) {
Queue.Enqueue(data);
}
public void Complete() {
}
}
private class XY {
public int X { get; set; }
public int Y { get; set; }
}
}