11

StackExchange.Redis writes log messages to a TextWriter. It does not use the ILogger interface for logging.

I would like to turn messages written to a TextWriter, into Serilog debug level messages.

Thoughts?

Richard Collette
  • 5,462
  • 4
  • 53
  • 79

1 Answers1

3

Something like the following (only smoke tested, but seems to work):

public class TextWriterLogger : TextWriter
{
    private ILogger logger;
    private StringBuilder builder = new StringBuilder();
    private bool terminatorStarted = false;

    public TextWriterLogger(ILoggerFactory log)
    {
        logger = log.CreateLogger("RedisTrace");
    }

    public override void Write(string value)
    {
        logger.LogDebug(value);
    }

    public override void Write(char value)
    {
        builder.Append(value);
        if (value == NewLine[0])
            if (NewLine.Length == 1)
                Flush2Log();
            else
                terminatorStarted = true;
        else if (terminatorStarted)
            if (terminatorStarted = NewLine[1] == value)
                Flush2Log();
    }

    private void Flush2Log()
    {
        if (builder.Length > NewLine.Length)
            logger.LogDebug(builder.ToString());
        builder.Clear();
        terminatorStarted = false;
    }


    public override Encoding Encoding
    {
        get { return Encoding.Default; }
    }
}
d_f
  • 4,599
  • 2
  • 23
  • 34
  • `if (terminatorStarted = NewLine[1] == value)` - don't do this, you're assigning terminatorStarted to the condition NewLine[1] == value being true. It's confusing to read, and breaks SOLID principle L: https://en.wikipedia.org/wiki/SOLID. I presume you actually meant to write something like `if (terminatorStarted == (NewLine[1] == value))` or even `else if (terminatorStarted && NewLine[1] == value)`. If you provide to your buffer as chars "\", then "\" and "n" your buffer will essentially escape the newline, you can overflow your buffer with providing constant backslashes... – NotoriousPyro Mar 06 '21 at 15:00
  • Instead you should check that adding to StringBuilder would not exceed its maximum implementation capacity. Flush the buffer if it would exceed the capacity of StringBuilder or a reasonable capacity for a string... Otherwise your application will easily overflow. – NotoriousPyro Mar 06 '21 at 15:01
  • 1
    "you're assigning terminatorStarted to the condition `NewLine[1] == value`" -- something like that. that's a one-liner for: `if (NewLine[1] == value){Flush2Log();}else{terminatorStarted = false;}` when the second character of a possible terminator is not `NewLine[1]` -- it's not a terminator, so I clear the flag. The implementation is not generic, but it works fine with the Redis's constructor -- my only goal here – d_f Mar 07 '21 at 19:48
  • Note for those looking to make this slightly more generic: if you don't necessarily want to use `LogDebug`, you can add a `LogLevel logLevel` field to the class, fill it in the constructor, and then call `logger.Log(logLevel, value)` in the Write function instead. – Ben Seymour Mar 11 '22 at 14:15