diff --git a/README.md b/README.md index 06fd15c..69a0ec9 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,17 @@ # ![mongo icon](https://raw.githubusercontent.com/ChangemakerStudios/serilog-sinks-mongodb/dev/assets/mongo-icon.png) Serilog.Sinks.MongoDB -![Build status](https://github.com/ChangemakerStudios/serilog-sinks-mongodb/actions/workflows/deploy.yml/badge.svg) +[![NuGet version](https://badge.fury.io/nu/Serilog.Sinks.MongoDB.svg)](https://badge.fury.io/nu/Serilog.Sinks.MongoDB) +[![Downloads](https://img.shields.io/nuget/dt/Serilog.Sinks.MongoDB.svg?logo=nuget&color=purple)](https://www.nuget.org/packages/Serilog.Sinks.MongoDB) +[![Build status](https://github.com/ChangemakerStudios/serilog-sinks-mongodb/actions/workflows/deploy.yml/badge.svg)](https://github.com/ChangemakerStudios/serilog-sinks-mongodb/actions) A Serilog sink that writes events as documents to [MongoDB](http://mongodb.org). **Package** - [Serilog.Sinks.MongoDB](http://nuget.org/packages/serilog.sinks.mongodb) | **Platforms** - .NET 4.7.2, .NET Standard 2.0,, .NET Standard 2.1 +### New in v6.x +* Upgraded to MongoDb v2.28 -- fixing breaking changes. + ### New in v5.x * Output structured MongoDB Bson logs by switching to the .MongoDBBson() extensions. Existing the .MongoDB() extensions will continue to work converting logs to Json and then to Bson. * Rolling Log Collection Naming (Thanks to [Revazashvili](https://github.com/Revazashvili) for the PR!). MongoDBBson sink only. diff --git a/src/Serilog.Sinks.MongoDB/Helpers/MongoDbDocumentHelpers.cs b/src/Serilog.Sinks.MongoDB/Helpers/MongoDbDocumentHelpers.cs index ce63276..cb31eee 100644 --- a/src/Serilog.Sinks.MongoDB/Helpers/MongoDbDocumentHelpers.cs +++ b/src/Serilog.Sinks.MongoDB/Helpers/MongoDbDocumentHelpers.cs @@ -1,4 +1,4 @@ -// Copyright 2014-2022 Serilog Contributors +// Copyright 2014-2024 Serilog Contributors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/src/Serilog.Sinks.MongoDB/Helpers/MongoDbHelpers.cs b/src/Serilog.Sinks.MongoDB/Helpers/MongoDbHelpers.cs index 67625a7..af2a360 100644 --- a/src/Serilog.Sinks.MongoDB/Helpers/MongoDbHelpers.cs +++ b/src/Serilog.Sinks.MongoDB/Helpers/MongoDbHelpers.cs @@ -1,4 +1,4 @@ -// Copyright 2014-2022 Serilog Contributors +// Copyright 2014-2024 Serilog Contributors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/src/Serilog.Sinks.MongoDB/Helpers/RollingIntervalHelper.cs b/src/Serilog.Sinks.MongoDB/Helpers/RollingIntervalHelper.cs index f5ea6e8..5f70593 100644 --- a/src/Serilog.Sinks.MongoDB/Helpers/RollingIntervalHelper.cs +++ b/src/Serilog.Sinks.MongoDB/Helpers/RollingIntervalHelper.cs @@ -1,4 +1,4 @@ -// Copyright 2014-2022 Serilog Contributors +// Copyright 2014-2024 Serilog Contributors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/src/Serilog.Sinks.MongoDB/Helpers/StringHelpers.cs b/src/Serilog.Sinks.MongoDB/Helpers/StringHelpers.cs index d00a4e5..6b1207c 100644 --- a/src/Serilog.Sinks.MongoDB/Helpers/StringHelpers.cs +++ b/src/Serilog.Sinks.MongoDB/Helpers/StringHelpers.cs @@ -1,4 +1,4 @@ -// Copyright 2014-2022 Serilog Contributors +// Copyright 2014-2024 Serilog Contributors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/src/Serilog.Sinks.MongoDB/LoggerConfigurationMongoDBExtensions.cs b/src/Serilog.Sinks.MongoDB/LoggerConfigurationMongoDBExtensions.cs index 09d65d3..96bf3ef 100644 --- a/src/Serilog.Sinks.MongoDB/LoggerConfigurationMongoDBExtensions.cs +++ b/src/Serilog.Sinks.MongoDB/LoggerConfigurationMongoDBExtensions.cs @@ -1,4 +1,4 @@ -// Copyright 2014-2022 Serilog Contributors +// Copyright 2014-2024 Serilog Contributors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/src/Serilog.Sinks.MongoDB/Serilog.Sinks.MongoDB.csproj b/src/Serilog.Sinks.MongoDB/Serilog.Sinks.MongoDB.csproj index abb446f..e21fc07 100644 --- a/src/Serilog.Sinks.MongoDB/Serilog.Sinks.MongoDB.csproj +++ b/src/Serilog.Sinks.MongoDB/Serilog.Sinks.MongoDB.csproj @@ -9,29 +9,38 @@ - 5.4.0 + 6.0.0 Kiran Makkapati, Jaben Cargman, Serilog Contributors Copyright © Serilog Contributors 2014-2022 The MongoDB sink for Serilog - http://serilog.net/images/serilog-sink-nuget.png + Apache-2.0 Serilog.Sinks.MongoDB http://serilog.net - https://github.com/ChangemakerStudios/serilog-sinks-mongodb - git + serilog-sink-nuget.png serilog, mongodb README.md - v5.4 - Upgraded to MongoDB.Driver to version 2.19 due to vulnerabilities. + v6.0 - Upgraded to MongoDB.Driver to version 2.28 due to incompatibilities. + https://github.com/ChangemakerStudios/serilog-sinks-mongodb + git true true true snupkg + + true + + + + + + @@ -41,10 +50,10 @@ - + - + diff --git a/src/Serilog.Sinks.MongoDB/Sinks/MongoDB/LogEntry.cs b/src/Serilog.Sinks.MongoDB/Sinks/MongoDB/LogEntry.cs index e78458d..72cea66 100644 --- a/src/Serilog.Sinks.MongoDB/Sinks/MongoDB/LogEntry.cs +++ b/src/Serilog.Sinks.MongoDB/Sinks/MongoDB/LogEntry.cs @@ -1,4 +1,4 @@ -// Copyright 2014-2022 Serilog Contributors +// Copyright 2014-2024 Serilog Contributors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ // limitations under the License. using System; +using System.Diagnostics; using System.Linq; using MongoDB.Bson; @@ -33,6 +34,7 @@ public class LogEntry public DateTime UtcTimeStamp { get; set; } + [BsonIgnoreIfNull] public MessageTemplate? MessageTemplate { get; set; } public string? RenderedMessage { get; set; } @@ -40,22 +42,34 @@ public class LogEntry public BsonDocument? Properties { get; set; } public BsonDocument? Exception { get; set; } + [BsonIgnoreIfNull] + public string? TraceId { get; set; } + [BsonIgnoreIfNull] + public string? SpanId { get; set; } - public static LogEntry MapFrom(LogEvent logEvent) + public static LogEntry MapFrom(LogEvent logEvent, bool includeMessageTemplate) { if (logEvent == null) throw new ArgumentNullException(nameof(logEvent)); - return new LogEntry + var logEntry = new LogEntry + { + RenderedMessage = logEvent.RenderMessage(), + Level = logEvent.Level, + UtcTimeStamp = logEvent.Timestamp.ToUniversalTime().UtcDateTime, + TraceId = logEvent.TraceId?.ToString(), + SpanId = logEvent.SpanId?.ToString(), + Exception = logEvent.Exception?.ToBsonDocument().SanitizeDocumentRecursive(), + Properties = BsonDocument.Create( + logEvent.Properties.ToDictionary( + s => s.Key.SanitizedElementName(), + s => s.Value.ToBsonValue())) + }; + + if (includeMessageTemplate) { - MessageTemplate = logEvent.MessageTemplate, - RenderedMessage = logEvent.RenderMessage(), - Level = logEvent.Level, - UtcTimeStamp = logEvent.Timestamp.ToUniversalTime().UtcDateTime, - Exception = logEvent.Exception?.ToBsonDocument().SanitizeDocumentRecursive(), - Properties = BsonDocument.Create( - logEvent.Properties.ToDictionary( - s => s.Key.SanitizedElementName(), - s => s.Value.ToBsonValue())) - }; + logEntry.MessageTemplate = logEvent.MessageTemplate; + } + + return logEntry; } } \ No newline at end of file diff --git a/src/Serilog.Sinks.MongoDB/Sinks/MongoDB/MongoDBJsonFormatter.cs b/src/Serilog.Sinks.MongoDB/Sinks/MongoDB/MongoDBJsonFormatter.cs index 619e86a..d7276cb 100644 --- a/src/Serilog.Sinks.MongoDB/Sinks/MongoDB/MongoDBJsonFormatter.cs +++ b/src/Serilog.Sinks.MongoDB/Sinks/MongoDB/MongoDBJsonFormatter.cs @@ -1,4 +1,4 @@ -// Copyright 2014-2022 Serilog Contributors +// Copyright 2014-2024 Serilog Contributors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -84,7 +84,7 @@ protected override void WriteJsonProperty( } else { - base.WriteJsonProperty(name, value, ref precedingDelimiter, output); + base.WriteJsonProperty(name, value!, ref precedingDelimiter, output); } } diff --git a/src/Serilog.Sinks.MongoDB/Sinks/MongoDB/MongoDBSink.cs b/src/Serilog.Sinks.MongoDB/Sinks/MongoDB/MongoDBSink.cs index 91d4d70..725dc4c 100644 --- a/src/Serilog.Sinks.MongoDB/Sinks/MongoDB/MongoDBSink.cs +++ b/src/Serilog.Sinks.MongoDB/Sinks/MongoDB/MongoDBSink.cs @@ -1,4 +1,4 @@ -// Copyright 2014-2022 Serilog Contributors +// Copyright 2014-2024 Serilog Contributors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -14,9 +14,11 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading.Tasks; +using MongoDB.Bson; using MongoDB.Bson.Serialization; using Serilog.Events; @@ -37,6 +39,10 @@ static MongoDBSink() cm.MapProperty(s => s.StackTrace); cm.MapProperty(s => s.Data); }); + + // fixes https://github.com/serilog/serilog/issues/2101 + BsonTypeMapper.RegisterCustomTypeMapper(typeof(IntPtr), new CustomIntPtrMapper()); + BsonTypeMapper.RegisterCustomTypeMapper(typeof(UIntPtr), new CustomIntPtrMapper()); } public MongoDBSink(MongoDBSinkConfiguration configuration) @@ -46,6 +52,26 @@ public MongoDBSink(MongoDBSinkConfiguration configuration) public override Task EmitBatchAsync(IEnumerable events) { - return this.InsertMany(events.Select(LogEntry.MapFrom)); + return this.InsertMany( + events.Select(@event => LogEntry.MapFrom(@event, this.IncludeMessageTemplate))); + } + + private class CustomIntPtrMapper : ICustomBsonTypeMapper + { + public bool TryMapToBsonValue(object value, [UnscopedRef] out BsonValue? bsonValue) + { + switch (value) + { + case IntPtr intPtr: + bsonValue = intPtr.ToInt32(); + return true; + case UIntPtr uIntPtr: + bsonValue = uIntPtr.ToUInt32(); + return true; + default: + bsonValue = null; + return false; + } + } } } \ No newline at end of file diff --git a/src/Serilog.Sinks.MongoDB/Sinks/MongoDB/MongoDBSinkBase.cs b/src/Serilog.Sinks.MongoDB/Sinks/MongoDB/MongoDBSinkBase.cs index a7fe84f..38f2eec 100644 --- a/src/Serilog.Sinks.MongoDB/Sinks/MongoDB/MongoDBSinkBase.cs +++ b/src/Serilog.Sinks.MongoDB/Sinks/MongoDB/MongoDBSinkBase.cs @@ -1,4 +1,4 @@ -// Copyright 2014-2022 Serilog Contributors +// Copyright 2014-2024 Serilog Contributors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -39,7 +39,7 @@ public abstract class MongoDBSinkBase : IBatchedLogEventSink /// protected MongoDBSinkBase(MongoDBSinkConfiguration configuration) { - if (configuration == null) throw new ArgumentNullException(nameof(configuration)); + if (configuration! == null) throw new ArgumentNullException(nameof(configuration)); this._configuration = configuration; @@ -51,6 +51,8 @@ protected MongoDBSinkBase(MongoDBSinkConfiguration configuration) LazyThreadSafetyMode.ExecutionAndPublication); } + protected bool IncludeMessageTemplate => !this._configuration.ExcludeMessageTemplate; + protected string CollectionName => this._configuration.CollectionName; protected RollingInterval RollingInterval => this._configuration.RollingInterval; diff --git a/src/Serilog.Sinks.MongoDB/Sinks/MongoDB/MongoDBSinkConfiguration.cs b/src/Serilog.Sinks.MongoDB/Sinks/MongoDB/MongoDBSinkConfiguration.cs index b5b5b6a..8bf4b46 100644 --- a/src/Serilog.Sinks.MongoDB/Sinks/MongoDB/MongoDBSinkConfiguration.cs +++ b/src/Serilog.Sinks.MongoDB/Sinks/MongoDB/MongoDBSinkConfiguration.cs @@ -1,4 +1,4 @@ -// Copyright 2014-2022 Serilog Contributors +// Copyright 2014-2024 Serilog Contributors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -38,6 +38,8 @@ public class MongoDBSinkConfiguration public bool Legacy { get; internal set; } + public bool ExcludeMessageTemplate { get; internal set; } + public InsertManyOptions? InsertManyOptions { get; internal set; } public RollingInterval RollingInterval { get; private set; } = RollingInterval.Infinite; @@ -58,6 +60,11 @@ public void Validate() throw new ArgumentNullException( nameof(this.ExpireTTL), "Expiration TTL is only supported on the MongoDBBson Sink"); + + if (this.ExcludeMessageTemplate && this.Legacy) + throw new ArgumentNullException( + nameof(this.ExcludeMessageTemplate), + "Exclude Message Template is only supported on the MongoDBBson Sink"); } /// @@ -88,6 +95,17 @@ public void SetExpireTTL(TimeSpan? timeToLive) this.ExpireTTL = timeToLive; } + /// + /// Sets if the log should include the "MessageTemplate" field, + /// as the RenderedMessage field is already included the "MessageTemplate" + /// may be unnecessary/redundant. + /// + /// + public void SetExcludeMessageTemplate(bool excludeMessageTemplate) + { + this.ExcludeMessageTemplate = excludeMessageTemplate; + } + /// /// Allows configuring "InsertManyOptions" in MongoDb. /// diff --git a/src/Serilog.Sinks.MongoDB/Sinks/MongoDB/MongoDBSinkDefaults.cs b/src/Serilog.Sinks.MongoDB/Sinks/MongoDB/MongoDBSinkDefaults.cs index a2717aa..ee56d82 100644 --- a/src/Serilog.Sinks.MongoDB/Sinks/MongoDB/MongoDBSinkDefaults.cs +++ b/src/Serilog.Sinks.MongoDB/Sinks/MongoDB/MongoDBSinkDefaults.cs @@ -1,4 +1,4 @@ -// Copyright 2014-2022 Serilog Contributors +// Copyright 2014-2024 Serilog Contributors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/src/Serilog.Sinks.MongoDB/Sinks/MongoDB/MongoDBSinkLegacy.cs b/src/Serilog.Sinks.MongoDB/Sinks/MongoDB/MongoDBSinkLegacy.cs index 7fc63a6..9a0491f 100644 --- a/src/Serilog.Sinks.MongoDB/Sinks/MongoDB/MongoDBSinkLegacy.cs +++ b/src/Serilog.Sinks.MongoDB/Sinks/MongoDB/MongoDBSinkLegacy.cs @@ -1,4 +1,4 @@ -// Copyright 2014-2022 Serilog Contributors +// Copyright 2014-2024 Serilog Contributors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/src/Serilog.Sinks.MongoDB/Sinks/MongoDB/RollingInterval.cs b/src/Serilog.Sinks.MongoDB/Sinks/MongoDB/RollingInterval.cs index 7c1422f..8dd3eb6 100644 --- a/src/Serilog.Sinks.MongoDB/Sinks/MongoDB/RollingInterval.cs +++ b/src/Serilog.Sinks.MongoDB/Sinks/MongoDB/RollingInterval.cs @@ -1,4 +1,4 @@ -// Copyright 2014-2022 Serilog Contributors +// Copyright 2014-2024 Serilog Contributors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/src/Serilog.Sinks.MongoDB/serilog-sink-nuget.png b/src/Serilog.Sinks.MongoDB/serilog-sink-nuget.png new file mode 100644 index 0000000..a77d65c Binary files /dev/null and b/src/Serilog.Sinks.MongoDB/serilog-sink-nuget.png differ diff --git a/test/Serilog.Sinks.MongoDB.Tests/LoggerWithTraceIdTests.cs b/test/Serilog.Sinks.MongoDB.Tests/LoggerWithTraceIdTests.cs new file mode 100644 index 0000000..ad65844 --- /dev/null +++ b/test/Serilog.Sinks.MongoDB.Tests/LoggerWithTraceIdTests.cs @@ -0,0 +1,108 @@ +using FluentAssertions; + +using Microsoft.Extensions.Configuration; + +using MongoDB.Driver; + +using Serilog.Helpers; + +namespace Serilog.Sinks.MongoDB.Tests; + +using System.Diagnostics; +using NUnit.Framework; + +[TestFixture] +public class LoggerWithTraceIdTests +{ + private const string MongoConnectionString = "mongodb://localhost:27017"; + + private const string MongoDatabaseName = "mongodb-sink"; + + private static (MongoClient, IMongoDatabase) GetDatabase() + { + var mongoClient = new MongoClient(MongoConnectionString); + return (mongoClient, mongoClient.GetDatabase(MongoDatabaseName)); + } + + [Test] + public void Log_Without_Activity_Should_Have_TraceId_And_SpanId_Null() + { + var configuration = new ConfigurationBuilder() + .AddJsonFile("serilog.json") + .Build(); + + var collectionName = RollingInterval.Month.GetCollectionName("test"); + + const string Message = "some message logged into mongodb without activity"; + + using (var logger = new LoggerConfiguration() + .ReadFrom.Configuration(configuration) + .CreateLogger()) + { + logger.Information(Message); + } + + var (mongoClient, mongoDatabase) = GetDatabase(); + var collectionExists = mongoDatabase.CollectionExists(collectionName); + + collectionExists.Should().BeTrue(); + + var mongoCollection = mongoDatabase.GetCollection(collectionName); + var document = mongoCollection.Find(x => x.RenderedMessage == Message).FirstOrDefault(); + + document.Should().NotBeNull(); + document.TraceId.Should().BeNull(); + document.SpanId.Should().BeNull(); + + mongoClient.DropDatabase(MongoDatabaseName); + } + + [Test] + public void Log_Within_Activity_Should_Have_TraceId_And_SpanId_Not_Null() + { + ActivityTraceId traceId; + ActivitySpanId spanId; + + var configuration = new ConfigurationBuilder() + .AddJsonFile("serilog.json") + .Build(); + + var collectionName = RollingInterval.Month.GetCollectionName("test"); + + const string Message = "some message logged into mongodb within an activity"; + + using (var logger = new LoggerConfiguration() + .ReadFrom.Configuration(configuration) + .CreateLogger()) + { + var activitySource = new ActivitySource("Serilog.Sinks.MongoDB.Tests"); + var activityListener = new ActivityListener + { + ShouldListenTo = s => true, + SampleUsingParentId = (ref ActivityCreationOptions activityOptions) => ActivitySamplingResult.AllData, + Sample = (ref ActivityCreationOptions activityOptions) => ActivitySamplingResult.AllData + }; + ActivitySource.AddActivityListener(activityListener); + using (var activity = activitySource.StartActivity("LogTest")) + { + traceId = activity.TraceId; + spanId = activity.SpanId; + logger.Information(Message); + } + } + + var (mongoClient, mongoDatabase) = GetDatabase(); + var collectionExists = mongoDatabase.CollectionExists(collectionName); + + collectionExists.Should().BeTrue(); + + var mongoCollection = mongoDatabase.GetCollection(collectionName); + var document = mongoCollection.Find(x => x.RenderedMessage == Message).FirstOrDefault(); + + document.Should().NotBeNull(); + document.TraceId.Should().Be(traceId.ToString()); + document.SpanId.Should().Be(spanId.ToString()); + + mongoClient.DropDatabase(MongoDatabaseName); + } +} \ No newline at end of file diff --git a/test/Serilog.Sinks.MongoDB.Tests/Serilog.Sinks.MongoDB.Tests.csproj b/test/Serilog.Sinks.MongoDB.Tests/Serilog.Sinks.MongoDB.Tests.csproj index f7147d0..9586d50 100644 --- a/test/Serilog.Sinks.MongoDB.Tests/Serilog.Sinks.MongoDB.Tests.csproj +++ b/test/Serilog.Sinks.MongoDB.Tests/Serilog.Sinks.MongoDB.Tests.csproj @@ -6,12 +6,12 @@ - + - - - + + +