Skip to content

Log to Spans #767

Description

@enzian

I there an idiomatic way of getting Logs written to a Span? This would accomplish two things at once - eliminate the need for collecting running console outputs and on the other hand it would enable the user to decouple the code from direcly writing to the logs of the current span... The issue I'm facing here is that I am not really clear on the best method to get something like this done. One solution I cam up with was to write an ILogger and ILoggerFactory that look something like this:

    internal class Factory : ILoggerProvider
    {
        public ILogger CreateLogger(string categoryName)
        {
            return new ToSpanLogger(categoryName);
        }

        public void Dispose()
        {
            // nothing to dispose here...
        }
    }

    internal class ToSpanLogger : ILogger
    {
        private readonly string category;

        public ToSpanLogger(string category)
        {
            this.category = category;
        }

        public TelemetrySpan Span { get; set; }

        public IDisposable BeginScope<TState>(TState state)
        {
            if (state is TelemetrySpan span)
            {
                return new SpanScope(this, span);
            }

            return null;
        }

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

        public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
        {
            if (this.Span != null)
            {
                if (logLevel == LogLevel.Error)
                {
                    Span.SetAttribute("error", true);
                }
                Span.AddEvent(formatter(state, exception));
            }
        }
    }

    internal class SpanScope : IDisposable
    {
        public SpanScope(ToSpanLogger logger, TelemetrySpan span)
        {
            Logger = logger;
            Logger.Span = span;
        }

        public ToSpanLogger Logger { get; }

        public void Dispose()
        {
            Logger.Span = null;
        }
    }
}

which could then be used like this:

var span = tracer.StartRootSpan("test", SpanKind.Client, new SpanCreationOptions {});
var logger = scope.ServiceProvider.GetRequiredService<ILogger<Type>>();
using (var logscope = logger.BeginScope(span))
{
    logger.LogInformation("starting here");
    DoSomeWork();
    logger.LogError("ended in desaster");
}
span.End();

Metadata

Metadata

Assignees

No one assigned

    Labels

    questionFurther information is requested

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions