diff --git a/src/Serilog.Sinks.ElmahIo/LoggerConfigurationElmahIOExtensions.cs b/src/Serilog.Sinks.ElmahIo/LoggerConfigurationElmahIOExtensions.cs index 2d54cda..53cab51 100644 --- a/src/Serilog.Sinks.ElmahIo/LoggerConfigurationElmahIOExtensions.cs +++ b/src/Serilog.Sinks.ElmahIo/LoggerConfigurationElmahIOExtensions.cs @@ -49,6 +49,8 @@ public static LoggerConfiguration ElmahIo( var batchingSink = new PeriodicBatchingSink(elmahIoSink, batchingOptions); + elmahIoSink.CreateInstallation(); + return loggerConfiguration.Sink( batchingSink, restrictedToMinimumLevel: options.MinimumLogEventLevel ?? LevelAlias.Minimum, diff --git a/src/Serilog.Sinks.ElmahIo/Sinks/ElmahIo/ElmahIOSink.cs b/src/Serilog.Sinks.ElmahIo/Sinks/ElmahIo/ElmahIOSink.cs index f317815..95d140d 100644 --- a/src/Serilog.Sinks.ElmahIo/Sinks/ElmahIo/ElmahIOSink.cs +++ b/src/Serilog.Sinks.ElmahIo/Sinks/ElmahIo/ElmahIOSink.cs @@ -14,6 +14,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Net.Http.Headers; using System.Reflection; @@ -21,6 +22,7 @@ using System.Text; using System.Threading.Tasks; using Elmah.Io.Client; +using Newtonsoft.Json.Linq; using Serilog.Events; using Serilog.Sinks.PeriodicBatching; @@ -35,11 +37,12 @@ public class ElmahIoSink : IBatchedLogEventSink private static readonly string _serilogAssemblyVersion = typeof(Log).GetTypeInfo().Assembly.GetCustomAttribute().Version; readonly ElmahIoSinkOptions _options; - readonly IElmahioAPI _client; + private IElmahioAPI _client; /// /// Construct a sink that saves logs to the specified storage account. /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0290:Use primary constructor", Justification = "Don't want to use primary constructors when there are more than one.")] public ElmahIoSink(ElmahIoSinkOptions options) { _options = options; @@ -61,34 +64,6 @@ public async Task EmitBatchAsync(IEnumerable batch) if (batch == null || !batch.Any()) return; - var client = _client; - if (_client == null) - { - var api = ElmahioAPI.Create(_options.ApiKey, new ElmahIoOptions - { - Timeout = new TimeSpan(0, 0, 30), - UserAgent = UserAgent(), - }); - - api.Messages.OnMessageFilter += (sender, args) => - { - var filter = _options.OnFilter?.Invoke(args.Message); - if (filter.HasValue && filter.Value) - { - args.Filter = true; - } - }; - api.Messages.OnMessage += (sender, args) => - { - _options.OnMessage?.Invoke(args.Message); - }; - api.Messages.OnMessageFail += (sender, args) => - { - _options.OnError?.Invoke(args.Message, args.Error); - }; - client = api; - } - var messages = new List(); foreach (var logEvent in batch) @@ -121,9 +96,11 @@ public async Task EmitBatchAsync(IEnumerable batch) messages.Add(message); } + EnsureClient(); + try { - await client + await _client .Messages .CreateBulkAndNotifyAsync(_options.LogId, messages) .ConfigureAwait(false); @@ -389,5 +366,89 @@ private static List Items(LogEvent logEvent, string keyName) .Select(element => element.ToItem()) .ToList(); } + + internal void CreateInstallation() + { + try + { + var logger = new LoggerInfo + { + Type = "Serilog.Sinks.ElmahIo", + Properties = [], + ConfigFiles = [], + Assemblies = + [ + new AssemblyInfo { Name = "Serilog.Sinks.ElmahIo", Version = _assemblyVersion }, + new AssemblyInfo { Name = "Elmah.Io.Client", Version = typeof(IElmahioAPI).GetTypeInfo().Assembly.GetCustomAttribute().Version }, + new AssemblyInfo { Name = "Serilog", Version = _serilogAssemblyVersion } + ], + }; + + var installation = new CreateInstallation + { + Type = ApplicationInfoHelper.GetApplicationType(), + Name = _options.Application, + Loggers = [logger] + }; + + var location = GetType().Assembly.Location; + var currentDirectory = Path.GetDirectoryName(location); + + var appsettingsFilePath = Path.Combine(currentDirectory, "appsettings.json"); + if (File.Exists(appsettingsFilePath)) + { + var appsettingsContent = File.ReadAllText(appsettingsFilePath); + var appsettingsObject = JObject.Parse(appsettingsContent); + if (appsettingsObject.TryGetValue("Serilog", out JToken serilogSection)) + { + logger.ConfigFiles.Add(new ConfigFile + { + Name = Path.GetFileName(appsettingsFilePath), + Content = new JObject { { "Serilog", serilogSection.DeepClone() } }.ToString(), + ContentType = "application/json" + }); + } + } + + EnsureClient(); + + _client.Installations.Create(_options.LogId.ToString(), installation); + } + catch (Exception e) + { + Debugging.SelfLog.WriteLine("Caught exception while creating installation: {0}", e); + // We don't want to crash the entire application if the installation fails. Carry on. + } + } + + private void EnsureClient() + { + if (_client == null) + { + var api = ElmahioAPI.Create(_options.ApiKey, new ElmahIoOptions + { + Timeout = new TimeSpan(0, 0, 30), + UserAgent = UserAgent(), + }); + + api.Messages.OnMessageFilter += (sender, args) => + { + var filter = _options.OnFilter?.Invoke(args.Message); + if (filter.HasValue && filter.Value) + { + args.Filter = true; + } + }; + api.Messages.OnMessage += (sender, args) => + { + _options.OnMessage?.Invoke(args.Message); + }; + api.Messages.OnMessageFail += (sender, args) => + { + _options.OnError?.Invoke(args.Message, args.Error); + }; + _client = api; + } + } } }