What's so bad about stdlib's log package?

1987 views
Skip to first unread message

Nate Finch

unread,
Nov 4, 2015, 10:40:52 PM11/4/15
to golang-nuts

I see a lot of hate directed at the stdlib's log package, and of course, there are a medley of third party logging packages. What are people's problems with the standard log package? I've seen accusations that it doesn't have leveled logging, which seems like an exercise in laziness, because that's easy to write:

package log
import (
    "io/ioutil"
    "log"
    "os"
)

var (
    Trace = log.New(ioutil.Discard, "TRACE ", log.LstdFlags)
    Debug = log.New(os.Stdout, "DEBUG ", log.LstdFlags)
    // etc
)

With the above trivial package, you can now do log.Debug.Printf("foo!") just like any of the third party loggers.

Some loggers are structured loggers, which again seems like not a big deal, you can pass a map[string]interface{} into log.Printf and it'll print out the map in a fairly easily parse-able way...

The only thing I can think of is that the time formatting is kinda ugly. I guess the other feature is being able to change levels of logs for individual packages, though honestly, in my ~10 years of working on projects that use such a feature, I think I've used it all of once or twice.

What am I missing?


Shawn Milochik

unread,
Nov 4, 2015, 10:55:23 PM11/4/15
to golang-nuts
I like it as-is, because it's simple to use. Much simpler than the logging package in Python, for example. Remember that the complainers are the ones who speak up. Those of us who like it don't bother to shout about it because otherwise we'd never shut up about "the good parts" (which is most of the language).

Some people may have legitimate needs for something more complicated, and they're free to build it. The Go core developers never set out to provide everything -- not even all the stuff they knew we (and they) would need. They set out to make just enough that we could build the rest ourselves.

If any package in the standard library isn't hefty enough for someone, they're free (and encouraged) to go and build or adapt their own. That doesn't mean that there was ever anything wrong with the standard library package, though. I think when people criticize something just because it isn't built for their use-case, they don't quite "get it." Generally I just keep quiet and let it pass. But, since you asked...



Axel Wagner

unread,
Nov 5, 2015, 2:38:53 AM11/5/15
to Nate Finch, golang-nuts
Hi,

I am mostly indifferent towards the log package, but just as one note:

Nate Finch <nate....@gmail.com> writes:
> Trace = log.New(ioutil.Discard, "TRACE ", log.LstdFlags)

This may *seem* like a good idea, but it still means, that the log
package has to do all the formatting (just to not use it). If log.Logger
had been an interface, you could replace this with a Logger that truly
has NOPs.

> With the above trivial package, you can now do log.Debug.Printf("foo!") just
> like any of the third party loggers.

But, how is this different from a "third party logger"? It's a trivial
third party logger, but it's still a package that I have to write or
import to wrap the stdlib package. It's functionally equivalent.

> I guess the other feature is being able to change levels of logs for
> individual packages, though honestly, in my ~10 years of working on
> projects that use such a feature, I think I've used it all of once or
> twice.

I believe it would be usefull if this existed (e.g. if every package
would expose, as a convention or some way of technically requiring it) a
Debug, Trace, Error… *log.Logger that you could set. I get mightily
annoyed if third-party packages spam the stderr of my interactive
program (because that's just bad user experience), but at the same time
I often encounter errors that I'd like to debug where tracing would be
very helpfull. xgb is an example of a program that used to use the
log-package without exposing a knob.

Another usefull thing (though that's kind of hart to do from the
language perspective) would be dynamically scoped logging. I would like,
during tests, to really crank up the verbosity of all packages, but have
them log to the (*testing.T).Log functions, so that I only see it, if
something fails. At the same time, this would need to be dynamically
scoped, so it can't be done via global variables, because that would
prevent parallel tests which would slow down tests considerably. There
are ways to cheat your way out of it, though.


Overall: What is the problem with third party loggers? Why not use a
package that gives you more power than the stdlib, that's what packages
are for, after all :)

Best,

Axel

Dave Cheney

unread,
Nov 5, 2015, 2:51:02 AM11/5/15
to golang-nuts, nate....@gmail.com
Overall: What is the problem with third party loggers? Why not use a
package that gives you more power than the stdlib, that's what packages
are for, after all :)


IMO the problem with third party loggers is, well, their from a third party.

Logging does not compose, at least not at the level of sophistication that the Go package ecosystem is at right now. And I'm not sure anyone would argue java's commons.logging "meta framework" was the thing they had in mind when they said sophistication.

Different projects and packages use different logging packages, so you end up having to configure multiple loggers via multiple methods. I think this more fundamental than saying log.Logger is not an interface. 

Axel Wagner

unread,
Nov 5, 2015, 2:58:46 AM11/5/15
to Dave Cheney, golang-nuts, nate....@gmail.com
Dave Cheney <da...@cheney.net> writes:
> Different projects and packages use different logging packages, so you end
> up having to configure multiple loggers via multiple methods. I think this
> more fundamental than saying log.Logger is not an interface.

I agree that that's problem. It's a problem that can only solved by
one-logging-package-to-rule-them-all™, though, at least if we agree
*that* the stdlib log package is deficient. And the more I think about
it, the more I think it is.

A thing I forgot to mention btw, is: The log package exposes both a type
*log.Logger and a set of functions that use a default. But this default
is not exposed, so I as main can not even change how everyone is logging
(because, of course, everyone is just using the functions instead of
exposing their own logger).

James Bardin

unread,
Nov 5, 2015, 8:51:11 AM11/5/15
to golang-nuts, da...@cheney.net, nate....@gmail.com


On Thursday, November 5, 2015 at 2:58:46 AM UTC-5, Axel Wagner wrote:

A thing I forgot to mention btw, is: The log package exposes both a type
*log.Logger and a set of functions that use a default. But this default
is not exposed, so I as main can not even change how everyone is logging
(because, of course, everyone is just using the functions instead of
exposing their own logger).

You can SetOutput, SetFlags, and SetPrefix at the package level. 

Caleb Spare

unread,
Nov 5, 2015, 10:19:36 AM11/5/15
to Axel Wagner, golang-nuts, Nate Finch


On Nov 4, 2015 11:38 PM, "'Axel Wagner' via golang-nuts" <golan...@googlegroups.com> wrote:
>
> Hi,
>
> I am mostly indifferent towards the log package, but just as one note:
>
> Nate Finch <nate....@gmail.com> writes:
> >     Trace = log.New(ioutil.Discard, "TRACE ", log.LstdFlags)
>
> This may *seem* like a good idea, but it still means, that the log
> package has to do all the formatting (just to not use it). If log.Logger
> had been an interface, you could replace this with a Logger that truly
> has NOPs.

Even then, if you put that no op logging in a tight loop, you'll see convT2E eating your lunch in profiles. For really efficient noop logging, you need an if statement (see the glog approach). (IMO this is one place where c-style macros can be quite useful -- they would allow you to completely omit code.)

> --
> You received this message because you are subscribed to the Google Groups "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

Axel Wagner

unread,
Nov 5, 2015, 2:08:25 PM11/5/15
to James Bardin, golang-nuts, da...@cheney.net, nate....@gmail.com
James Bardin <j.ba...@gmail.com> writes:
> You can SetOutput, SetFlags, and SetPrefix at the package level.

But *I* want to SetOutput, SetFlags and SetPrefix. In main. Every main
has different requirements. For example, I don't mind a daemon spamming
(on the contrary), it's just going to the log-aggregator for later
consumption. My interactive binaries shouldn't output anything at all,
unless something goes wrong.

It's not a decision the package author can make, it's a decision the
main-author must make. And unless a package exports a *log.Logger that
it uses (which no one does, that's my point), you can't.

Jakob Borg

unread,
Nov 5, 2015, 2:54:51 PM11/5/15
to Axel Wagner, James Bardin, golang-nuts, da...@cheney.net, nate....@gmail.com

> On 5 nov. 2015, at 20:08, 'Axel Wagner' via golang-nuts <golan...@googlegroups.com> wrote:
>
> James Bardin <j.ba...@gmail.com> writes:
>> You can SetOutput, SetFlags, and SetPrefix at the package level.
>
> But *I* want to SetOutput, SetFlags and SetPrefix. In main. Every main
> has different requirements.

What James probably means is that you can call log.SetOutput and so on from your main to affect the default logger.

However I'd be suspicious in general about a reusable non-main package doing any kind of logging. In my opinion it should communicate by returning an error.

(A program consisting of several internal packages is different of course.)

//jb

James Bardin

unread,
Nov 5, 2015, 2:54:57 PM11/5/15
to Axel Wagner, golang-nuts, Dave Cheney, nate....@gmail.com
On Thu, Nov 5, 2015 at 2:08 PM, Axel Wagner
<axel.wa...@googlemail.com> wrote:
> But *I* want to SetOutput, SetFlags and SetPrefix. In main.

I'm not saying it fixes every case, I was only responding the the
comment about not being able to control logging when a package using
the log package-level function.

That's actually the easiest case to control, because anything you set
in main overrides whatever the packages use (unless they later change
the logging output again).

setting log.SetOutput(ioutil.Discard) will silence most packages that
try to log something.

Axel Wagner

unread,
Nov 5, 2015, 3:23:56 PM11/5/15
to Jakob Borg, James Bardin, golang-nuts, da...@cheney.net, nate....@gmail.com
Jakob Borg <ja...@nym.se> writes:
> What James probably means is that you can call log.SetOutput and so on from your main to affect the default logger.
>
> However I'd be suspicious in general about a reusable non-main package
> doing any kind of logging. In my opinion it should communicate by
> returning an error.

In a perfect world without bugs in third-party packages that may be
defensible (I would still disagree, to be honest). But we don't live in
a perfect world, packages have bugs and I need to debug them and
unterstand what they are doing. An error value can never gives you the
granularity that a verbose debug log can give you.

Dave Cheney

unread,
Nov 5, 2015, 5:20:22 PM11/5/15
to Axel Wagner, Jakob Borg, James Bardin, golang-nuts, nate....@gmail.com

I don't believe this is true.

You can embed line numbers in errors, create a chain of errors with causes, and embed stack traces into errors ?

Have you seen Roger Peppe's errgo package, or juju/errors package which do this (albeit with slightly different philosophies) ?

Axel Wagner

unread,
Nov 5, 2015, 7:54:13 PM11/5/15
to Dave Cheney, Jakob Borg, James Bardin, golang-nuts, nate....@gmail.com
Dave Cheney <da...@cheney.net> writes:
> I don't believe this is true.

I concede that.

But you are still taking away the info log-level from non-main packages
(because returning a non-nil error is considered an error condition, so
I can't log things that aren't errors). It also seems like a strange
replacement to logging to me: You are trying to work around the lack of
good, per-package logging facilities in the stdlib by abusing errors to
in-band signal the logs. Because what's the difference between returning
an ErrorWithLoggingInfo and just log to a custom logger, that main can
choose to replace with a nop-logger if it's not required (or the other
way around)? From the outside, what happens is, that you get the log
printed to stderr (or not). And the code for both (from main) will look
mostly the same too (in regards to activating/deactivating/redirecting/whatever
the logs).

Dave Cheney

unread,
Nov 5, 2015, 8:03:03 PM11/5/15
to Axel Wagner, Jakob Borg, James Bardin, golang-nuts, Nate Finch
I'm totally with you that given the two positions of

1. nobody gets to use any logging package in a library because you
have applications constructed of n logging libraries operating
independently.
2. everyone uses the built in log package.

Neither is acceptable.

However, I want to counter with the question of what purpose does it
serve to log an error inside a package? For example

err := something()
if err != nil {
log.Printf("error: something failed: %v", err)
// what do we do here ?
}

So, an error has occurred, the function cannot continue, but we must
return some value to the caller. What value should we return ? It
cannot be nil, because that would indicate success, tf. we must return
an error value ... but we just logged the error value, will the caller
also log it, and their caller, and theirs ?

In this situation I do not see the value in the library logging the
error, instead it must instead pass it up to the caller, potentially
wrapped in some additional information in the way the juju/errors
package does.

Thanks

Dave

Nate Finch

unread,
Nov 5, 2015, 11:11:58 PM11/5/15
to golang-nuts, da...@cheney.net, ja...@nym.se, j.ba...@gmail.com, nate....@gmail.com
I think the confusion is between packages internal to your application, and reusable packages which are intended for general consumption including by third parties.

Packages that you never intend for anyone outside your application to use may log all they want... they're aware of the rules of the application and can follow them.

A reusable package (i.e. something for use with multiple applications, some of which may belong to third parties) should never ever log.  Imagine if encoding/json decided to start logging failed decodes rather than just returning an error...

In these cases, the error is absolutely the right place to return information.  And yes, it may take some thought for some of the more complicated packages out there.... but it's still the only acceptable way to communicate.

Axel Wagner

unread,
Nov 6, 2015, 2:27:39 AM11/6/15
to golang-nuts
Dave Cheney <da...@cheney.net> writes:
> In this situation I do not see the value in the library logging the
> error, instead it must instead pass it up to the caller, potentially
> wrapped in some additional information in the way the juju/errors
> package does.

I am with you on that :) But libraries might not only log errors. They
might also want to log debug or info. And debug in particular is very
useful imho. So something should be done to make logging possible. And I
would be fine (though it still wouldn't be perfect, but it's something I
could work with) if we made it best practice to have

var (
Debug = log.NewLogger(ioutil.Discard, …)
Info = log.NewLogger(os.Stdout, …)
)

in every package (or whatever logging levels we find to be the "correct"
ones) so that you, as an author of main, get package-level granularity
of how logging should happen. It still wouldn't be perfect, but
package-granularity logging control is the minimum I would expect for
decent logging and *at the very least* the Debug level is important,
imho. If I would want to improve on that, I would then have as
additional steps

a) Create a canonical interface (which *could* be provided by the
stdlib, but might also be provided by a canonical third-party package)
that will be used instead of *log.Logger
b) Provide a magical implementation of that interface for tests that
figures out which testing.T to write to (e.g. by scanning the stack) and
use that.

If I had all of that, I would be completely happy with logging in go. b
is completely optional, because you wouldn't need cooperation from the
ecosystem for it. But package-level logging is the bare minimum.

Anyway, I think I'm out of the debate :) It's not *that* important to
me, just wanted to give my thoughts :)

Best,

Axel

Axel Wagner

unread,
Nov 6, 2015, 2:36:27 AM11/6/15
to golang-nuts
Nate Finch <nate....@gmail.com> writes:
> I think the confusion is between packages internal to your application, and
> reusable packages which are intended for general consumption including by
> third parties.

Not for me :) I'd treat them the same (by applying the higher standards
to "internal" packages too).

> A reusable package (i.e. something for use with multiple applications, some
> of which may belong to third parties) should never ever log. Imagine if
> encoding/json decided to start logging failed decodes rather than just
> returning an error...

Imagine encoding/json creating weird output and the only way you have to
troubleshoot the package or your usage of it, was to clone the source
and single-step through the debugger. Now contrast that with setting
json.Debug = log.NewLogger(os.Stderr), rerunning your code and getting a
detailed view of what encoding/json *thinks* it should do.

That's my basic point: If I have no way of letting a package output
debug information, then the only way I have of debugging it or my usage
of it, is reading the sourcecode, single-stepping a debugger, stracing
it… I have to resort to black-box debugging, instead of white-box
debugging. I find that completely annoying.

I am with you, that maybe no third-party package should log *by
default*. But I want packages to give me the option of both a coarse "I
am doing approximately this at he moment, so it's completely fine that
it takes a while" and a completely verbose "here is a complete trace of
what I think you wanted me to do". Because depending on the application
and what I'm currently doing, both might be useful and needed even.

Peter Mogensen

unread,
Nov 6, 2015, 2:51:02 AM11/6/15
to golan...@googlegroups.com


On 2015-11-06 02:02, Dave Cheney wrote:
> However, I want to counter with the question of what purpose does it
> serve to log an error inside a package?

Probably none... however it does make sense to enable lower level (INFO,
DEBUG) messages from a library. - especially if log levels can be
dynamically tuned at runtime.

/Peter

roger peppe

unread,
Nov 6, 2015, 3:06:38 AM11/6/15
to golang-nuts
My main problem with the log package is that SetOutput is too low
level - you can't plug the standard logging into some other logging
system without parsing the log messages written to the Writer, which
feels very fragile.

Peter Mogensen

unread,
Nov 6, 2015, 4:09:09 AM11/6/15
to golan...@googlegroups.com


On 2015-11-06 09:06, roger peppe wrote:
> My main problem with the log package is that SetOutput is too low
> level - you can't plug the standard logging into some other logging
> system without parsing the log messages written to the Writer, which
> feels very fragile.

Yes...
I guess my main problem is that it discourages debug-level logging since
you only have the Print*() functions.
You tend to only log rare error-states to avoid flooding logs.

We've actually stopped using the std lib logger and written our own.

/Peter

Hariharan Srinath

unread,
Nov 6, 2015, 7:03:04 AM11/6/15
to golang-nuts
I've run into similar issues in the past and i'd like to share a proposal to make the current Go Logging package much more extensible in a fully backwards compatible by allowing Logger.Output() to write logs using a custom logging implementation. 

The proposal is here: https://github.com/srinathh/log

The implementation is a minor modification to log package to define a function type called OutputFn with the same signature as the current Logger.Ouptut(). The current implementation of Output is moved into a function called Logger.DefaultOutputFn(). We introduce a mutex protected variable Logger.outputfn in the Logger which points to the default implementation but can be swapped for any other implementation by calling Logger.SetOutputFn(). For the standard logger, we introduce log.SetOutputFn() and log.SetDefOutputFn() to set and reset custom loggers.

The example in customlogger_test.go shows how we can use this approach to swap a logger that outputs logs in a tab separated variable file format but this approach can be easily used to plugin the Go logging system into any other system without text parsing etc.

Regards
Srinath

Eric Johnson

unread,
Nov 6, 2015, 1:09:36 PM11/6/15
to golang-nuts
I really like this thread.

This strikes me as a great problem for the community to solve. In simple cases, so far, I've taken one of the suggestions of this thread, and use error returns as an opportunity for the main application to log messages. That's a relatively trivial case, though. There are cases where I want to log "info" level messages, but continue the normal application flow.

Ideally, a library (that is, not "main") package will not control its own logging, and not dictate a logging API. Otherwise, the community starts having to pick choices, and that's unfortunate - I've seen this play out in the Java world between commons logging, JRE logging, log4j, slf4j, and the compatibility issues that you run into when you have to support multiple solutions in one application.

In Go, this could be straightforward - a package should use an interface for the logging that it wants, not have it dictated by a third party. Since, in Go, this is a relatively trivial exercise to define an interface, how about having every package choose its own logging interface?

This leaves two problems - how should the package define its interface, and how does the main application provide an implementation of that interface?

I think the package can define its interface in one of two ways - either a wide interface or a narrow one.

The wide interface would need methods like "Debug()", "Error()", "Info()", "Warn()", and possibly also "IsDebugging()", "IsErroring()", "IsInfoing()", and "IsWarning()" - the latter set of functions so that package authors can bracket possibly expensive logging operations with an if block to avoid them if not needed. Invocations look like:

if log.IsDebugging() {
  log.Debug(...)
}

The narrow interface might be just a pair of methods - "Log()", and "IsActive()", and then the library gets a separate instance for each of the levels of logging it wants.

In this case, the log calls look like:
if debug.IsActive() {
  debug.Log(...)
}

A package level configuration of the logging for the latter pattern look like this:

func SetLogging(debug, info, warn error Log) {
 ...
}

I sort of like the latter, narrower interface more - seems more idiomatic. But I think it works either way, because the package author doesn't need to choose!

The next challenge is this - how does the main application provide a logger for the library package to use? Three options that I see:
1) Explicit parameters to objects. Good because main package has full control. Bad, because it widens the API, and if the package is buried several levels deep in dependencies, that could be awkward. This might depend on whether the package defines objects that will be instantiated with different logging configurations for different parts of the application. Seems like a rare case.

2) Package level method to set logging configuration (see the example above). Even better if the package sets a reasonable default - perhaps using the standard logging package. Now the main package can override the default logging if it wants, or it can just let the package do what it wants.

3) Package registers with a logging API. The main application defines how each logging API is satisfied. I'm waving my hands a little, but the up-side of central registration is that once the Go ecosystem settles on a few patterns, this registration library could deal with loading configuration on behalf of the main package. It could use reflect to identify the types of the interfaces being requested, and map that to the actual logging library being used.

Seems like #2 is the natural place for the community to start.

And, having kicked the problem to the "main" package, now the community can build infrastructure around dynamically changing the provider loggers based on a configuration file.

Eric.

Chris Hines

unread,
Nov 6, 2015, 7:25:34 PM11/6/15
to golang-nuts, da...@cheney.net, ja...@nym.se, j.ba...@gmail.com, nate....@gmail.com
There are a lot of good things being said in this thread. As a contributor to both log15 and the Go kit logging packages I've spent a fair amount of time thinking about many of these issues. I hope the list doesn't mind if I provide a few links to my thoughts rather than copy and pasting them.

With regards to package scoped logging and packages with different sharing scopes: https://github.com/go-kit/kit/issues/42.

On the evolution of the Go kit logging package: http://go-talks.appspot.com/github.com/ChrisHines/talks/structured-logging/structured-logging.slide. The slides also compare the APIs of several popular logging packages and try to make the case for structured logging over Printf based logging.

Of note, the central interface of Go kit package log is a nicely composable one method interface:

type Logger interface {
Log(keyvals ...interface{}) error
}


Chris

Nate Finch

unread,
Nov 9, 2015, 10:46:32 PM11/9/15
to golang-nuts, da...@cheney.net, ja...@nym.se, j.ba...@gmail.com, nate....@gmail.com
I loved the slides, Chris.  Achieving a tiny interface is truly a worthy goal in Go.  

I guess I don't see a huge difference between using gokit's key/value interface and just passing a map[string]interface{} to log.Println.  

Can you sell me on the difference?  

Chris Hines

unread,
Nov 10, 2015, 12:22:35 AM11/10/15
to golang-nuts, da...@cheney.net, ja...@nym.se, j.ba...@gmail.com, nate....@gmail.com
The primary benefit is the ability to write composable log formats and middle-ware that can operate on typed log event data rather than a byte stream. One of my personal favorites from log15 is gopkg.in/inconshreveable/log15.v2/ext.EscalateErrHandler.

A secondary benefit is having the option not to format the data into a []byte immediately.

Giovanni Bajo

unread,
Nov 11, 2015, 2:47:10 PM11/11/15
to golang-nuts
I feel like the problem is the opposite; I would like all third-party loggers to go through the standard library as final output (or middle stage...) to have a central convenient way for:

 * Turning on/off logging for each library
 * Setting logging level for each library

This way, people could use logging in all libraries, and they might have be turned off by default; then, in my application, I could do something like log.Enable("github.com/spf13/cobra", log.DEBUG) when I need it, or something like that.

This would bring us in the same heaven of net/http, where third-party web frameworks and middlewares can evolve and experiment, but all of them piggyback on the same basic structure so they're fully composable.

Giovanni

Peter Mogensen

unread,
Nov 11, 2015, 3:07:05 PM11/11/15
to golan...@googlegroups.com


On 2015-11-11 20:47, Giovanni Bajo wrote:
> I feel like the problem is the opposite; I would like all third-party
> loggers to go through the standard library as final output

That just have lousy performance. - and sacrifices a lot of features.

I wrote a custom log library (with heavy inspiration from a lot of
others) and tried every possible way to allow that. But it's simply not
possible without taking the Logger Mutex way to many times.

/Peter

Peter Mogensen

unread,
Nov 25, 2015, 7:22:08 AM11/25/15
to golan...@googlegroups.com


On 2015-11-06 10:08, Peter Mogensen wrote:
> We've actually stopped using the std lib logger and written our own.

... We decided to publish it:

https://github.com/One-com/gonelog

Now ... I'm aware that there's already way too many log libraries for Go
around. (as the author of log15 nicely points out).

The main reason for writing this library is not the NIH syndrome, but
the wish to have a library which would be source code compatible with
code written for the stdlib log package. - to allow smooth transition
paths for code.

I felt that goal would run counter to the design of every other log
library out there.

Also ... once the decision to write a new library was taken there was a
lot of feature request which feasibility would be nice to test out.
Those were:

* Leveled logging with syslog levels.
* Structured key/value logging
* Hierarchical contextable logging to have k/v data in context logged
automatically.
* Low resource usage to allow more (debug) log-statements even if they
don't result in output.
* Light syntax to encourage logging on INFO/DEBUG level. (and low cost
of doing so)
* Explore compatibility with http://golang.org/x/net/context to make the
context object a Logger.
* Flexibility in how log events are output.
* A fast simple lightweight default in systemd newstyle daemon only
outputting <level>message to standard output.


Regard the result as brainstorm of a lot of different features which
usefulness are not really decided - but which just happens to compile,
run and work. There's still rough edges - especially regarding formatters.

The code has went through some cleanup and refactoring in order to
publish, and it'll will probably go through more in response to
comments, so the original test was dropped in refactoring.

It does pass the stdlib test though - and seems to log a lot faster when
just doing the most minimal logging.

I would very much like to hear your comments.

regards,
Peter Mogensen
Reply all
Reply to author
Forward
0 new messages