<pre class="notranslate">% go get labix.org/v2/mgo
% go test -i -race labix.org/v2/mgo

This goes into an infinite loop and eats
up arbitrary amounts of memory when doing so.

I instrumented genlist to print out the
list nodes if it had traversed more than a million:

    print("genlist %p; node %J\n", l, l-&gt;n);

A sample output is this:

genlist 7ffff4338a30; node  u(100) l(5503) tc(1)
genlist 7ffff441cf30; node  u(100) l(5503) tc(1)
genlist 7ffff4338a30; node  u(100) l(5503) tc(1)
genlist 7ffff441cf30; node  u(100) l(5503) tc(1)
genlist 7ffff4338a30; node  u(100) l(5503) tc(1)

The command line that failed in this way was:
/home/rog/go/pkg/tool/linux_amd64/6g -o $WORK/labix.org/v2/mgo/_obj/_go_.6 -race -p
labix.org/v2/mgo -complete -D _/home/rog/src/go/src/labix.org/v2/mgo -I $WORK -I
/home/rog/src/go/pkg/linux_amd64_race ./auth.go ./cluster.go ./doc.go ./gridfs.go
./log.go ./queue.go ./server.go ./session.go ./socket.go ./stats.go

I've verified that this behaviour occurs both on tip (a6a9792f94ac)
and go1.1.1.</pre>
<h2 dir="auto">Motivation</h2>
<p dir="auto">Go stdlib provides a <code class="notranslate">log.Logger</code> that is widely used in both Go programs and Go libraries (eg. most notably in <code class="notranslate">http.Server</code>) as a standardized logging component.</p>
<p dir="auto">However, the current implementation of the <code class="notranslate">log.Logger</code> enforces a specific logging format with limited flexibility which requires text parsing to allow it to be connected to any other logging system implementation through the swappable <code class="notranslate">io.Writer</code> interface. This adds complexity and overhead. The other alternative of directly using third party logging libraries is not very scalable both because existing libraries including components like <code class="notranslate">http.Server</code> in the stdlib expect <code class="notranslate">log.Logger</code> and because of network effects of stdlib.</p>
<h2 dir="auto">Proposal</h2>
<p dir="auto">This proposes a minor change to the log package to make <code class="notranslate">log.Logger</code> more extensible in a fully backwards compatible by allowing <code class="notranslate">Logger.Output()</code> to write logs using custom logging implementations.</p>
<p dir="auto">We do this by defining a function type called <code class="notranslate">OutputFn</code> with the same function signature as the current <code class="notranslate">Logger.Output()</code> and introducing an unexported field <code class="notranslate">outputfn</code> in <code class="notranslate">Logger</code> to hold the function that should be used for output much like we use the <code class="notranslate">out</code> field to hold the <code class="notranslate">io.Writer</code> currently. By default, <code class="notranslate">log.New()</code> will set the <code class="notranslate">outputfn</code> to the  current implementation which is moved to <code class="notranslate">Logger.DefOutputFn()</code>. This can be swapped to any other <code class="notranslate">OutputFn</code> implementation by calling <code class="notranslate">Logger.SetOutputFn()</code> in the same way as we currently can swap <code class="notranslate">io.Writer</code> by calling <code class="notranslate">Logger.SetOutput()</code></p>
<p dir="auto">For the top level <code class="notranslate">log</code> package, we provide <code class="notranslate">log.SetOutputFn()</code> and <code class="notranslate">log.SetDefOutputFn()</code> to set the <code class="notranslate">OutputFn</code> of the <code class="notranslate">log.std</code> logger and reset it to default.</p>
<h2 dir="auto">Proof of Concept</h2>
<p dir="auto">I have implemented a <a href="https://github.com/srinathh/log">proof of concept</a> for this proposed change and added <a href="https://github.com/srinathh/log/blob/master/customlogger_test.go">an example</a> in the test suite that demonstrates logging to a Tab Separated Variable format. While in the example i am merely writing to a Writer, the Output could be to anything including over the network etc.</p>
<p dir="auto">If this approach looks ok, I can package this into a CL incorporating any feedback.</p>
<h2 dir="auto">Rationale for the Approach</h2>
<p dir="auto">Most of the actual work of logging happens in <code class="notranslate">Logger.Output()</code> (and in the unexported <code class="notranslate">Logger.formatHeader()</code> called by it) which makes it a prime candidate for flexibility. We cannot change <code class="notranslate">log.Logger</code> into an interface due to Go1 compatibility promise and this approach therefore allows a lot of flexibility with a very minor change.</p>