0

I'm trying to make Serilog log framework available to a VB6 application to write to Logentries.com via TCP Token input. I've used JaminSehic Com Logger as a base to create a wrapper to Serilog . When i try to call the SetupWithLogEntries method i get an error stating Serilog or one of it's dependencies cannot be found or loaded. I have found articles such as AnyCPU that describe to make sure that the wrapper is compiled in .net as x86. But i still run into this problem.

Now the dll is visible by the vb6 application. It can instantiate the object. It's when the call is made to SetupWithLogEntries that this dependency error occurs.

using System.IO;
using System.Linq;
using System.Runtime.InteropServices;

using Serilog;
using Serilog.Core;
using Serilog.Enrichers;
using Serilog.Events;

namespace Com.Logger
{
    /// <summary>
    /// A wrapper around Serilog logging API, used for writing log events.
    /// </summary>
    /// <remarks>
    /// Contains wrapper methods for templated methods with implict log event level only
    /// ParamArray (params) cannot be passed from VB6 as it is ByRef and in .NET ParamArray (params) as ByVal so had to pass optional object parameters which works well enough
    /// Maximum of 10 parameters are allowed and only the primitive data types from VB6 are supported. 
    /// VB6 classes will show up as System.__ComObject
    /// </remarks>
    [Guid("293E7B05-9557-44D5-BF9B-B5F60B25574B")]
    [ComVisible(true)]
    [ProgId("Com Logger")]
    [ClassInterface(ClassInterfaceType.AutoDual)]
    public class LoggerWrapper
    {
        private ILogger _logger;

        public LoggerWrapper()
        {
            _logger = Log.Logger;
        }

        [DispId(0)]
        public void Setup(string serviceName, string serviceVersion, string machineName, string correlationId,
            short restrictedToMinimumLevel = (short)LogEventLevel.Verbose,
            //string outputTemplate = "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level}] {Message}{NewLine}{Exception}",
            string outputTemplate = "{Timestamp:O} {MachineName} {Service} {Version} {CorrelationId} [{Level}]  ({Exception}) {Message}{NewLine}{Exception}",
            int fileSizeLimitBytes = 1073741824, int retainedFileCountLimit = 31)
        {
            var ServiceDetailsEnricher = new ServiceDetailsEnricher
            {
                ServiceName = serviceName,
                ServiceVersion = serviceVersion,
                MachineName = machineName,
                CorrelationId = correlationId
            };
            _logger = new LoggerConfiguration()
               .MinimumLevel.Debug()
               .Enrich.With(ServiceDetailsEnricher)
               .WriteTo.ColoredConsole( outputTemplate: outputTemplate)
               .WriteTo.RollingFile(pathFormat: GetLogFileName(serviceName), 
                   restrictedToMinimumLevel: (LogEventLevel)restrictedToMinimumLevel,
                   outputTemplate: outputTemplate, fileSizeLimitBytes: fileSizeLimitBytes,
                   retainedFileCountLimit: retainedFileCountLimit)
               .CreateLogger();
        }

        [DispId(1)]
        public void SetupWithLogEntries(string serviceName, string serviceVersion, string machineName, string correlationId, string tokenId,
            short restrictedToMinimumLevel = (short)LogEventLevel.Verbose,
            string outputTemplate = "{Timestamp:O} {MachineName} {Service} {Version} {CorrelationId} [{Level}]  ({Exception}) {Message}{NewLine}{Exception}",
            int fileSizeLimitBytes = 1073741824, int retainedFileCountLimit = 31)
        {
            var ServiceDetailsEnricher = new ServiceDetailsEnricher
            {
                ServiceName = serviceName,
                ServiceVersion = serviceVersion,
                MachineName = machineName,
                CorrelationId = correlationId
            };

            _logger = new LoggerConfiguration()
               .MinimumLevel.Debug()
               .Enrich.With(ServiceDetailsEnricher)
               .Enrich.FromLogContext()
               .WriteTo.RollingFile(pathFormat: GetLogFileName(serviceName),
                   restrictedToMinimumLevel: (LogEventLevel)restrictedToMinimumLevel,
                   outputTemplate: outputTemplate, fileSizeLimitBytes: fileSizeLimitBytes,
                   retainedFileCountLimit: retainedFileCountLimit)               
               .WriteTo.Console(Serilog.Events.LogEventLevel.Debug)
               .WriteTo.Logentries(
                    token: tokenId,
                    outputTemplate: outputTemplate)
               .CreateLogger();
        }


        [DispId(2)]
        public void Verbose(string messageTemplate, object arg0 = null, object arg1 = null, object arg2 = null, object arg3 = null, object arg4 = null, object arg5 = null, object arg6 = null, object arg7 = null, object arg8 = null, object arg9 = null)
        {
            _logger.Verbose(messageTemplate, RemoveMissingParams(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9));
        }

        [DispId(3)]
        public void Debug(string messageTemplate, object arg0 = null, object arg1 = null, object arg2 = null, object arg3 = null, object arg4 = null, object arg5 = null, object arg6 = null, object arg7 = null, object arg8 = null, object arg9 = null)
        {
            _logger.Debug(messageTemplate, RemoveMissingParams(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9));
        }

        [DispId(4)]
        public void Information(string messageTemplate, object arg0 = null, object arg1 = null, object arg2 = null, object arg3 = null, object arg4 = null, object arg5 = null, object arg6 = null, object arg7 = null, object arg8 = null, object arg9 = null)
        {
            _logger.Information(messageTemplate, RemoveMissingParams(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9));
        }

        [DispId(5)]
        public void Warning(string messageTemplate, object arg0 = null, object arg1 = null, object arg2 = null, object arg3 = null, object arg4 = null, object arg5 = null, object arg6 = null, object arg7 = null, object arg8 = null, object arg9 = null)
        {
            _logger.Warning(messageTemplate, RemoveMissingParams(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9));
        }

        [DispId(6)]
        public void Error(string messageTemplate, object arg0 = null, object arg1 = null, object arg2 = null, object arg3 = null, object arg4 = null, object arg5 = null, object arg6 = null, object arg7 = null, object arg8 = null, object arg9 = null)
        {
            _logger.Error(messageTemplate, RemoveMissingParams(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9));
        }

        [DispId(7)]
        public void Fatal(string messageTemplate, object arg0 = null, object arg1 = null, object arg2 = null, object arg3 = null, object arg4 = null, object arg5 = null, object arg6 = null, object arg7 = null, object arg8 = null, object arg9 = null)
        {
            _logger.Fatal(messageTemplate, RemoveMissingParams(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9));
        }

        // legacy method helper
        [DispId(8)]
        public void WriteDebug(string message)
        {
            _logger.Debug(message);
        }


        private static object[] RemoveMissingParams(object arg0, object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9)
        {
            return new[] { arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9 }.Where(a => a != null).ToArray();
        }

        private static string GetLogFileName(string serviceName)
        {
            var logFileName = Path.Combine(GetLogFilePath(), serviceName);
            return string.Format("{0}_{1}{2}", logFileName, "{Date}", ".txt");
        }

        private static string GetBufferFileName(string serviceName)
        {
            return Path.Combine(GetLogFilePath(), serviceName);
        }

        private static string GetLogFilePath()
        {
            const string logSubFolder = "Logs";
            var pathRoot = System.AppDomain.CurrentDomain.BaseDirectory; //Directory.GetCurrentDirectory();
            return Path.Combine(pathRoot, logSubFolder);
        }
    }

    internal class ServiceDetailsEnricher : ILogEventEnricher
    {
        public string ServiceName { get; set; }
        public string ServiceVersion { get; set; }
        public string MachineName { get; set; }
        public string CorrelationId { get; set; }

        public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
        {
            logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("Service", ServiceName));
            logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("Version", ServiceVersion));
            logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("MachineName", MachineName));
            logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("CorrelationId", CorrelationId));

        }
    }
}

When building make sure to sign the assembly and compile it to x86 target cpu. regasm the dll and create a tlb.

then in vb application: reference the tlb

Place the code below in a vb6 application. The Log.SetupWithLogEntries will throw an error of a Serilog dependency that is missing. If I call Log.Setup then no error is thrown and the log file is created. (so seems to be an issue with the LogEntires Sink.)

    Dim Log As Com_Logger.LoggerWrapper
    Set Log = New Com_Logger.LoggerWrapper

    'Log.Setup "VB6LoggerWrapperClient", "1.0.1.23", "test", "abc", LogEventLevel.Debug

    Log.SetupWithLogEntries "VB6LoggerWrapperClient", "1.0.1.23", "test", "abc", "tokenABC", LogEventLevel.Debug

    Dim x As Integer
    Dim y As Currency
    Dim z As Date
    Dim a As String
    Dim b As Boolean
    Dim c As Long
    Dim d As Double
    Dim s As Single
    Dim f As Byte

    x = 23
    y = 99.5654
    z = Now()
    a = "I am a STRING!"
    b = True
    c = 345345435
    d = 343.343432
    s = 1.2
    f = 255

    Call Log.Debug("This is debug {Integer} {Currency} {Date} {Stringy} {Boolean} {Long} {Double} {Single} {Byte}", x, y, z, a, b, c, d, s, f)
    Call Log.Information("This is Info {Integer} {Currency} {Date} {Stringy} {Boolean} {Long} {Double} {Single} {Byte}", x, y, z, a, b, c, d, s, f)
Community
  • 1
  • 1
user1161137
  • 1,067
  • 1
  • 11
  • 31

1 Answers1

0

Ok so I discovered what the problem was. From using the original project from JaminSec, it needed to be upgraded since it was using old components. So i upgraded Serilog, to the current v1.5x, and included the current version of Serilog.Sinks.Logentries v1.5.8. COM-Interop would consistently fail due to dependency issues. Yet Logentries stated it was compatible.. well it does work fine in .NET but not as a COM!!!

Answer was to downgrade Serilog to v1.4.x!!! and it works correctly as COM-INTEROP!

user1161137
  • 1,067
  • 1
  • 11
  • 31