diff --git a/src/NLog.Mongo/MongoTarget.cs b/src/NLog.Mongo/MongoTarget.cs
index 452f973..2d1d619 100644
--- a/src/NLog.Mongo/MongoTarget.cs
+++ b/src/NLog.Mongo/MongoTarget.cs
@@ -159,6 +159,32 @@ public string CollectionName
///
public bool IncludeEventProperties { get; set; }
+ ///
+ /// Gets or sets the suffix for the collection name in case of multi tenants application.
+ ///
+ ///
+ /// The name of the property to use as a suffix.
+ ///
+ public string CollectionSuffixProperty
+ {
+ get => (_collectionSuffixProperty as SimpleLayout)?.Text;
+ set => _collectionSuffixProperty = value ?? string.Empty;
+ }
+ private Layout _collectionSuffixProperty;
+
+ ///
+ /// Gets or sets the suffix for the database name in case of multi tenants application.
+ ///
+ ///
+ /// The name of the property to use as a suffix.
+ ///
+ public string DatabaseSuffixProperty
+ {
+ get => (_databaseSuffixProperty as SimpleLayout)?.Text;
+ set => _databaseSuffixProperty = value ?? string.Empty;
+ }
+ private Layout _databaseSuffixProperty;
+
///
/// Initializes the target. Can be used by inheriting classes
/// to initialize logging.
@@ -190,14 +216,30 @@ protected override void Write(IList logEvents)
try
{
if (_createDocumentDelegate == null)
+ {
_createDocumentDelegate = e => CreateDocument(e.LogEvent);
+ }
var documents = logEvents.Select(_createDocumentDelegate);
- var collection = GetCollection(logEvents[logEvents.Count - 1].LogEvent.TimeStamp);
- collection.InsertMany(documents);
- for (int i = 0; i < logEvents.Count; ++i)
- logEvents[i].Continuation(null);
+ var grouped = logEvents
+ .Zip(documents, (eventInfo, bsonDocument) => (EventInfo: eventInfo, Document: bsonDocument))
+ .GroupBy(z => GetSuffixOrDefault(z.Document));
+
+ foreach (var group in grouped)
+ {
+ var suffix = group.Key;
+ var items = group.ToArray();
+ var timestamp = items.Last().EventInfo.LogEvent.TimeStamp;
+
+ var collection = GetCollection(timestamp, suffix);
+ collection.InsertMany(items.Select(i => i.Document));
+
+ foreach (var (eventInfo, document) in items)
+ {
+ SetEventConfigurationToNull(eventInfo);
+ }
+ }
}
catch (Exception ex)
{
@@ -214,6 +256,32 @@ protected override void Write(IList logEvents)
}
}
+ private static AsyncLogEventInfo SetEventConfigurationToNull(AsyncLogEventInfo e)
+ {
+ e.Continuation(null);
+ return e;
+ }
+
+ private (string DatabaseSuffix, string CollectionSuffix) GetSuffixOrDefault(BsonDocument document, string defaultValue = "")
+ {
+ string databaseSuffix = GetSuffixValueOrDefault(document, defaultValue, DatabaseSuffixProperty);
+ string collectionSuffix = GetSuffixValueOrDefault(document, defaultValue, CollectionSuffixProperty);
+
+ return (databaseSuffix, collectionSuffix);
+ }
+
+ private static string GetSuffixValueOrDefault(BsonDocument document, string defaultValue, string suffixPropertyName)
+ {
+ var suffixPresent = !string.IsNullOrWhiteSpace(suffixPropertyName);
+ if (suffixPresent)
+ {
+ document.TryGetValue(suffixPropertyName, out var propertyValue);
+ return propertyValue?.ToString() ?? defaultValue;
+ }
+
+ return defaultValue;
+ }
+
///
/// Writes logging event to the log target.
/// classes.
@@ -224,7 +292,8 @@ protected override void Write(LogEventInfo logEvent)
try
{
var document = CreateDocument(logEvent);
- var collection = GetCollection(logEvent.TimeStamp);
+ var suffix = GetSuffixOrDefault(document);
+ var collection = GetCollection(logEvent.TimeStamp, suffix);
collection.InsertOne(document);
}
catch (Exception ex)
@@ -393,14 +462,17 @@ private BsonValue GetValue(MongoField field, LogEventInfo logEvent)
return bsonValue ?? new BsonString(value);
}
- private IMongoCollection GetCollection(DateTime timestamp)
+ private IMongoCollection GetCollection(DateTime timestamp, (string Database, string Collection) suffix)
{
if (_defaultLogEvent.TimeStamp < timestamp)
_defaultLogEvent.TimeStamp = timestamp;
+ Layout collectionNameWithSuffix = GetCollectionNameWithSuffix(suffix.Collection);
+ Layout databaseNameWithSuffix = GetDatabaseNameWithSuffix(suffix.Database);
+
string connectionString = _connectionString != null ? RenderLogEvent(_connectionString, _defaultLogEvent) : string.Empty;
- string collectionName = _collectionName != null ? RenderLogEvent(_collectionName, _defaultLogEvent) : string.Empty;
- string databaseName = _databaseName != null ? RenderLogEvent(_databaseName, _defaultLogEvent) : string.Empty;
+ string collectionName = collectionNameWithSuffix != null ? RenderLogEvent(collectionNameWithSuffix, _defaultLogEvent) : string.Empty;
+ string databaseName = databaseNameWithSuffix != null ? RenderLogEvent(databaseNameWithSuffix, _defaultLogEvent) : string.Empty;
if (string.IsNullOrEmpty(connectionString))
throw new NLogConfigurationException("Can not resolve MongoDB ConnectionString. Please make sure the ConnectionString property is set.");
@@ -451,6 +523,17 @@ private IMongoCollection GetCollection(DateTime timestamp)
});
}
+ private Layout GetCollectionNameWithSuffix(string suffix) => GetNameWithSuffix(_collectionName, suffix);
+
+ private Layout GetDatabaseNameWithSuffix(string suffix) => GetNameWithSuffix(_databaseName, suffix);
+
+ private static Layout GetNameWithSuffix(Layout layout, string suffix)
+ => !string.IsNullOrWhiteSpace(suffix)
+ ? layout != null
+ ? new SimpleLayout($"{layout}_{suffix}")
+ : null
+ : layout;
+
private static string GetConnectionString(string connectionName)
{
if (connectionName == null)
diff --git a/test/NLog.Mongo.Tests/LoggerTest.cs b/test/NLog.Mongo.Tests/LoggerTest.cs
index 322e933..ee78db7 100644
--- a/test/NLog.Mongo.Tests/LoggerTest.cs
+++ b/test/NLog.Mongo.Tests/LoggerTest.cs
@@ -23,6 +23,32 @@ public void Write()
.Property("Test", "Tesing properties")
.Log();
+ string t = "tenant1";
+ // The property name must match the one written in the NLog.config
+ using (ScopeContext.PushProperty("TenantId", t))
+ {
+ _logger.Trace("Sample trace message, k={0}, l={1}, t={2}", k, l, t);
+ _logger.Debug("Sample debug message, k={0}, l={1}, t={2}", k, l, t);
+ _logger.Info("Sample informational message, k={0}, l={1}, t={2}", k, l, t);
+ _logger.Warn("Sample warning message, k={0}, l={1}, t={2}", k, l, t);
+ _logger.Error("Sample error message, k={0}, l={1}, t={2}", k, l, t);
+ _logger.Fatal("Sample fatal error message, k={0}, l={1}, t={2}", k, l, t);
+ _logger.Log(LogLevel.Info, "Sample fatal error message, k={0}, l={1}, t={2}", k, l, t);
+ }
+
+ t = "tenant2";
+ // The property name must match the one written in the NLog.config
+ using (ScopeContext.PushProperty("TenantId", t))
+ {
+ _logger.Trace("Sample trace message, k={0}, l={1}, t={2}", k, l, t);
+ _logger.Debug("Sample debug message, k={0}, l={1}, t={2}", k, l, t);
+ _logger.Info("Sample informational message, k={0}, l={1}, t={2}", k, l, t);
+ _logger.Warn("Sample warning message, k={0}, l={1}, t={2}", k, l, t);
+ _logger.Error("Sample error message, k={0}, l={1}, t={2}", k, l, t);
+ _logger.Fatal("Sample fatal error message, k={0}, l={1}, t={2}", k, l, t);
+ _logger.Log(LogLevel.Info, "Sample fatal error message, k={0}, l={1}, t={2}", k, l, t);
+ }
+
string path = "blah.txt";
try
diff --git a/test/NLog.Mongo.Tests/NLog.config b/test/NLog.Mongo.Tests/NLog.config
index f23105d..1016046 100644
--- a/test/NLog.Mongo.Tests/NLog.config
+++ b/test/NLog.Mongo.Tests/NLog.config
@@ -71,7 +71,6 @@
-
+
+
+
+
+
+
+
+
+
+
+
@@ -97,5 +114,6 @@
+
-
\ No newline at end of file
+