15
\$\begingroup\$

What do you think about this way to make logging available across the application without passing log object around? Let’s say we have something which allows us to write things this way:

class Program
{
    static void Main(string[] args)
    {
        using (new Log("c:\\a.txt"))
            AsyncContext.Run(() => MainAsync());
    }

    static async Task MainAsync()
    {
        Log.Write("a1"); // Writes to a.txt
        await Task.Delay(1000);
        Log.Write("a2"); // Writes to a.txt

        using (new Log("c:\\b.txt"))
            await F();

        Log.Write("a3"); // Writes to a.txt
    }

    static async Task F()
    {
        Log.Write("b1"); // Writes to b.txt
    }
}

Here is the Log library code:

class Log : Ambient<Log>
{
    public static void Write(string line) =>
        Current?.WriteCore(line);

    public Log(string fileName)
    {
        FileName = fileName;
    }

    void WriteCore(string line) => File.AppendAllText(FileName, line);
    string FileName { get; }
}

Where Ambient<T> is defined as:

class Ambient<T> : IDisposable where T : Ambient<T>
{
    static readonly string Id = typeof(T).FullName;
    protected static T Current
    {
        get { return (T)CallContext.LogicalGetData(Id); }
        set { CallContext.LogicalSetData(Id, value); }
    }

    protected Ambient()
    {
        Previous = Current;
        Current = (T)this;
    }

    public void Dispose() => Current = Previous;
    T Previous { get; }
}

Theoretically, per-request instance of Log could come from IoC with all the dependencies injected while taking into account ASP.NET applications.

\$\endgroup\$
2
  • \$\begingroup\$ I see the CallContext for the first time and I wonder why such a useful concept is put inside the Remoting namespace and not directly under System :-| one could think it's only for remoting... \$\endgroup\$
    – t3chb0t
    Commented Sep 7, 2016 at 4:05
  • \$\begingroup\$ Historically it used for cross domain/process/machine communications… It is turned out to be useful for asynchronous code also these days. \$\endgroup\$ Commented Sep 7, 2016 at 5:28

1 Answer 1

2
\$\begingroup\$

In the example code you provided, you are just setting up where the logger is writing to, within a certain context. The reason you may consider passing around a logger object is when you need to have more complicated configuration for setting up a logger object.

It really depends on what you want. On one hand, having a static logger is nice. You don't have to worry about having to include a logger param everywhere in your code. Having to refactor your code when you have a bunch of static method calls is a huge cost if you decided to change later, to make configuration more flexible.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.