Custom implementation of ILogger

Personally, I think It's not a good way to do that, "prefix" will be duplicated a lot. Why don't you use Log Scopes instead?

public IActionResult GetById(string id)
{
    TodoItem item;
    using (_logger.BeginScope("Message attached to logs created in the using block"))
    {
        _logger.LogInformation(LoggingEvents.GetItem, "Getting item {ID}", id);
        item = _todoRepository.Find(id);
        if (item == null)
        {
            _logger.LogWarning(LoggingEvents.GetItemNotFound, "GetById({ID}) NOT FOUND", id);
            return NotFound();
        }
    }
    return new ObjectResult(item);
}

Output

info: TodoApi.Controllers.TodoController[1002]
      => RequestId:0HKV9C49II9CK RequestPath:/api/todo/0 => TodoApi.Controllers.TodoController.GetById (TodoApi) => Message attached to logs created in the using block
      Getting item 0
warn: TodoApi.Controllers.TodoController[4000]
      => RequestId:0HKV9C49II9CK RequestPath:/api/todo/0 => TodoApi.Controllers.TodoController.GetById (TodoApi) => Message attached to logs created in the using block
      GetById(0) NOT FOUND

Currently, you can't change the logging template, it's the limitation of built-in basic logging in Asp.net Core. For more powerful one, you can try Serilog, keep using ILogger interface and change some line of code in program.cs class

You should also look at this Benefits of Structured Logging vs basic logging


I think you should not call a _logger in a custom logger.

It would be a circular call on runtime and the result would be "prefix: prefix: prefix: prefix: prefix: prefix: prefix: prefix: ..."

Simply, you can create a simple logger and implement a log writter such as Console, database writter, log4net, ...

Now first, you should change your custom logger like below:

    public class CustomLogger : ILogger
    {
        private readonly string CategoryName;
        private readonly string _logPrefix;

        public CustomLogger(string categoryName, string logPrefix)
        {
            CategoryName = categoryName;
            _logPrefix = logPrefix;
        }

        public IDisposable BeginScope<TState>(TState state)
        {
            return new NoopDisposable();
        }

        public bool IsEnabled(LogLevel logLevel)
        {
            return true;
        }

        public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
        {
            string message = _logPrefix;
            if (formatter != null)
            {
                message += formatter(state, exception);
            }
            // Implement log writter as you want. I am using Console
            Console.WriteLine($"{logLevel.ToString()} - {eventId.Id} - {CategoryName} - {message}");
        }

        private class NoopDisposable : IDisposable
        {
            public void Dispose()
            {
            }
        }
    }

The second step, create a logger provider:

     public class LoggerProvider : ILoggerProvider
        {     
            public ILogger CreateLogger(string categoryName)
            {                
                return new CustomLogger(categoryName, "This is prefix: ");
            }

            public void Dispose()
            {
            }
        }

The third step, in Configure from Startup.cs:

    loggerFactory.AddProvider(new MicroserviceLoggerProvider());

Implement an extensions for adding prefix to log records.

    public static class LogExtensions
    {
        public static void PrefixLogDebug(this ILogger logger, string message, string prefix = "Edward", params object[] args)
        {
            logger.LogDebug($"{prefix} {message}");
        }
    }

Useage:

        _log.PrefixLogDebug("Log From Prefix extension");
        _log.PrefixLogDebug("Log From Prefix extension", "New Prefix");