Transcript: the misunderstood global

In this blog post, I will discuss why using Transcript can be really badly used. I will present that with some simple care we can develop modular solutions that are flexible and can take advantages of using Transcript without the inconvenients. 

As a general remark, if you want to log better use a real system logging PLAIN real objects and not just dead strings. Because you can do a lot more with objects than mere strings. You can use Beacon (whose core is available in Pharo by default)http://www.humane-assessment.com/blog/beacon or other logging frameworks as the one developed by Cyril Ferlicot and based on dynamic variables https://github.com/jecisc/TinyLogger.

Now let us imagine that you still want to log strings.

Transcript: A misunderstood object

Transcript is a kind of stdout on which you can write some strings outputs. It is cheap. The class exposes a stream-based API (and this is a really important design point as we will see in the future).

Here is a typical bad use of Transcript

myMethod
    Transcript show: 'foo' ; cr  

It is bad because it hard codes a reference to transcript while Pharo proposes some helpers methods such as traceCr:

myMethod
   self traceCr: 'foo'

Some developers may think that this is not important but it can help you if one day you want to control the logging and for example use an object with the same API but to do something else. So avoid hardcoding globals. But there is more. 

The real concern

The problem amongst others is that Transcript is a singleton and in fact an UGLY global variable. Once you use it for real in your code, you basically killed the modularity of your program and the only thing that you can do is to hope that nothing bad can happen.

Let us look at a concrete simple case. The microdown Parser came (yes we removed this) with a simple method named closeMe:

MicAbstractBlock >> closeMe
	
    Transcript << 'Closing ' << self class name; cr; endEntry

So this method is producing a little trace so that the parser developer could understand what was happening. So you can think that this is ok.

There are two main problems:

  • First what if you want to deploy your application in a system where you do not want at all to get Transcript its class and all its family. I’m thinking for example about people producing minimal images.
  • Second, when Pharo is built on Jenkins all the tests are executed because we love tests. And this Transcript expression produces dirt on the build log. You do not want to have read such trace when you are trying to understand why the build is not working.
Closing MicCodeBlock
Closing MicCodeBlock
Closing MicCodeBlock
Closing MicCodeBlock
Closing MicCodeBlock
Closing MicCodeBlock
Closing MicCodeBlock
Closing MicCodeBlock
Closing MicCodeBlock
Closing MicCodeBlock
Closing MicCodeBlock
Closing MicCodeBlock
Closing MicHeaderBlock
Closing MicHeaderBlock
Closing MicHeaderBlock
Closing MicHeaderBlock
Closing MicHeaderBlock
Closing MicHeaderBlock
Closing MicHeaderBlock
Closing MicListItemBlock
Closing MicListItemBlock
Closing MicOrderedListBlock

You can say that I’m exaggerating. But let us see the good way to have a log and be able to unplug it.

Encapsulation to the rescue

The solution is really simple. Just use object-oriented programming and encapsulation. To support the Parser developer, we can simply add a stream to the class.

For example we define a new variable to and initialize it to a write stream.

MicAbstractBlock >>initialize
   super initialize.
   logStream := WriteStream on: (String new: 1000)

Then we can rewrite the method closeMe as follows

MicAbstractBlock >>closeMe
logStream << 'Closing ' << self class name; cr

Then we can provide a simple setter method so that the developer can set for example the Transcript as a stream to write to. 

MicAbstractBlock >>logStream:  aStream 
logStream := aStream

If we do not control the creation of instances of the class using the stream, we will have difficulties to configure it. So if want to be able to configure the class to use a different logger, we can define a class variable so that we can send message to the class and at initialization time, we can take the value from the class variable instead of hardcoding the WriteStream.

The net result is that we have the control and we can decide what is happening. In addition we can write tests to make sure that the logging is correct. Because using Transcript makes this a brittle exercise since someone else may write to the Transcript when you do not expect it. 

Conclusion

Transcript is not bad per se. It promotes bad coding practices. Developers should stop listening to the sirens of easy and cheap global variables. With a little bit of care and a limited infrastructure is to possible to get the best of both worlds: modular objects and taking advantages of the existing infrastructure whose Transcript belongs to. 

Published by Stéphane Ducasse

I'm one of the leader of the Pharo project, co-founder of Synectique, author of several books and far too many other things. http://stephane.ducasse.free.fr I wish you fun and business with Pharo

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: