XenoAtom.Logging supports two complementary routing mechanisms:
LogManagerConfig.RootLogger + LogManagerConfig.Loggers)AcceptFilters / RejectFilters)Use hierarchy first for coarse routing and filters for precise predicates.
Every logger inherits from RootLogger, then applies matching named logger configs in order of specificity.
using XenoAtom.Logging;
using XenoAtom.Logging.Writers;
var fileWriter = new FileLogWriter("logs/app.log");
var jsonWriter = new JsonFileLogWriter("logs/app.jsonl");
var config = new LogManagerConfig
{
RootLogger =
{
MinimumLevel = LogLevel.Info,
Writers = { fileWriter }
}
};
// "App.Http" and children become Debug and fan out to both writers.
config.Loggers.Add("App.Http", LogLevel.Debug, [fileWriter, jsonWriter], includeParents: true);
includeParents controls whether parent/root writers are inherited.
true: append writers from parent and current config.false: replace inherited writers with current config writers.config.Loggers.Add("App.Audit", LogLevel.Info, [jsonWriter], includeParents: false);
In this example, App.Audit.* writes only to jsonWriter.
Each LogWriter has:
RejectFilters: applied first, any match rejects immediately.AcceptFilters: if non-empty, at least one filter must match.fileWriter.RejectFilters.Add(static m =>
m.Logger.Name.StartsWith("App.Noisy", StringComparison.Ordinal));
fileWriter.AcceptFilters.Add(static m =>
m.Level >= LogLevel.Warn);
Configuration collections are mutable. Update them from one thread, then call:
config.ApplyChanges();
Do not mutate writer filter collections concurrently with high logging traffic.
MinimumLevel = LogLevel.Info.See also: