GIS Converter Logging -Code

Dani_S 4,946 Reputation points
2025-12-16T18:46:48.4766667+00:00

Hi Michel,

1.Can you please look on my Logger implementations and tests ?

2.If you find something missing , can you give me the fixes ?

3.Files

Console.App -Client

GitConverter ConsoleApp README.txt

Program.cs.txt

Logging code

README.md.txt

IAppLogger.cs.txt

Log.cs.txt

LogHelper.cs.txt

LogHelper.cs.txt

NullLogger.cs.txt

Developer technologies | C#
Developer technologies | C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
{count} votes

1 answer

Sort by: Most helpful
  1. Michael Le (WICLOUD CORPORATION) 6,925 Reputation points Microsoft External Staff Moderator
    2025-12-17T10:11:20.6833333+00:00

    Hello @Dani_S ,

    I've reviewed your logging implementation. Overall, the architecture is solid.

    A few issues and suggestions for improvement:

    Unreachable code

    In Program.cs, you have this problem:

    private static void Main(string[] args)
    {
        ShowHelp();
        return;
    
        // everything below is unreachable
        Console.OutputEncoding = System.Text.Encoding.UTF8;
        args = EnsureDebugArgs(args);
        Environment.Exit(Run(args));
    }
    

    return; should be removed so the rest of the method can execute. Or, if you intend to only show help and exit, remove the rest of the code.

    The SetFile method does too many things

    Your Log.SetFile method is about 80 lines long and handles multiple responsibilities.

    You should break it into smaller, focused methods:

    public static void SetFile(string logFilePath, string logLevel = "All")
    {
        if (string.IsNullOrWhiteSpace(logFilePath))
            throw new ArgumentException("logFilePath cannot be null or empty.", nameof(logFilePath));
    
        var fullPath = ResolvePath(logFilePath);
        EnsureDirectoryExists(fullPath);
        ProbeWriteAccess(fullPath);
    
        var appender = CreateRollingAppender(fullPath);
        var level = ParseLogLevel(logLevel);
    
        ConfigureRepository(appender, level);
        Current = new Log4NetAdapter(LogManager.GetLogger(typeof(Log)));
    }
    
    private static string ResolvePath(string logFilePath)
    {
        return Path.GetFullPath(logFilePath);
    }
    
    private static void EnsureDirectoryExists(string fullPath)
    {
        var directory = Path.GetDirectoryName(fullPath);
        if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
        {
            Directory.CreateDirectory(directory);
        }
    }
    
    private static void ProbeWriteAccess(string fullPath)
    {
        try
        {
            using (var fs = new FileStream(fullPath, FileMode.Append, FileAccess.Write, FileShare. Read))
            {
                // just testing write access
            }
        }
        catch (Exception ex)
        {
            throw new IOException($"Cannot write to log file path '{fullPath}'. Check permissions and that the path is valid.", ex);
        }
    }
    
    private static RollingFileAppender CreateRollingAppender(string fullPath)
    {
        var layout = new PatternLayout
        {
            ConversionPattern = "%date %-5level %logger - %message%newline%exception"
        };
        layout.ActivateOptions();
    
        var roller = new RollingFileAppender
        {
            AppendToFile = true,
            File = fullPath,
            Layout = layout,
            MaxSizeRollBackups = 5,
            MaximumFileSize = "10MB",
            RollingStyle = RollingFileAppender.RollingMode. Size,
            StaticLogFileName = true,
            LockingModel = new FileAppender.MinimalLock(),
            Encoding = System.Text.Encoding. UTF8
        };
        roller.ActivateOptions();
    
        return roller;
    }
    
    private static log4net.Core. Level ParseLogLevel(string logLevel)
    {
        var levelMap = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
        {
            ["ALL"] = "ALL",
            ["DEBUG"] = "DEBUG",
            ["INFO"] = "INFO",
            ["WARN"] = "WARN",
            ["ERROR"] = "ERROR",
            ["FATAL"] = "FATAL",
            ["OFF"] = "OFF"
        };
    
        var normalizedLevel = (logLevel ?? "ALL").Trim().ToUpperInvariant();
        var levelName = levelMap. ContainsKey(normalizedLevel) ? levelMap[normalizedLevel] : "ALL";
    
        var entry = Assembly.GetEntryAssembly();
        var repoAssembly = (Assembly)(entry ?? Assembly.GetExecutingAssembly());
        var repo = LogManager.GetRepository(repoAssembly);
        var hierarchy = repo as log4net.Repository. Hierarchy.Hierarchy;
    
        return hierarchy?. LevelMap[levelName] ??  hierarchy?.LevelMap["ALL"];
    }
    
    private static void ConfigureRepository(RollingFileAppender appender, log4net.Core.Level level)
    {
        var entry = Assembly.GetEntryAssembly();
        var repoAssembly = (Assembly)(entry ?? Assembly.GetExecutingAssembly());
        var repo = LogManager.GetRepository(repoAssembly);
    
        var hierarchy = repo as log4net.Repository.Hierarchy.Hierarchy;
        if (hierarchy != null)
        {
            hierarchy. ResetConfiguration();
        }
    
        log4net.Config.BasicConfigurator.Configure(repo, appender);
    
        if (hierarchy != null)
        {
            hierarchy.Root.Level = level;
            hierarchy.Configured = true;
        }
    }
    

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.