Wednesday, January 12, 2011

When all magic goes wrong: std::vector of incomplete type

I have recently been working on an API. I put great effort into separating the implementation from the interface, which in this case means that the header file of the API strictly contains declarations. No executable code at all. This makes it easier to hide implementation details, which is something we should always aim for, especially for APIs.

In C++ there are several ways to hide implementation. One way is to forward declare types and simply use pointers and references to those types in header files. However, when you need to use a type by-value it is not possible to use a forward declared. For example:
class CompleteType { };
class IncompleteType;
class HoldsTheAboveTypes {
  CompleteType value0;     // Ok.
  IncompleteType* pointer; // Ok.
  IncompleteType value1;   // Compilation error!
};
In my experience, there are usually ways to avoid having types by-value that are implementation details. Usually its a matter of thinking hard about the life-time or ownership of an object. However, when I implemented the API mentioned above I ran into a problem that seemed to be unsolvable.

I needed to have a class with a field of type std::vector of an incomplete type, that is:
class StdVectorOfIncompleteType {
  std::vector<IncompleteType> value;
};
This code fails to compile, though, giving some error message about "invalid use of incomplete type" (just as the code above). However, IncompleteType isn't used anywhere! So it should compile, shouldn't it?

(Well, I guess you could argue that it should compile if C++ would be designed properly, but it not so let's not go into that...)

The reason the above code doesn't compile is because the following special methods are automagically generated by the compiler:
  • zero-argument constructor
  • copy constructor
  • destructor
  • assignment operator
The default implementations of these special methods are nice to have in most cases. However, in the example with std::vector<IncompleteType> above these default implementation doesn't work at all. It is these default implementation that causes the compilation error, which is very much non-obvious. All (auto-)magic goes wrong.

So to fix the compilation error given above, we simply need to declare these special methods, and provide an implementation to them in a separate .cc-file, where the declaration of IncompleteType is available.

I've been fiddeling with programming for more 15 years (professionally much shorter, though) and I've run into this problem several times before but never tried to understand the cause for it. Today I did.

Sunday, January 9, 2011

Design patterns are to software development what McDonald's is to cooking

I remember reading the GoF design patterns book and thinking gosh, this is really good stuff. Now I can write program like a real master! I liked the whole idea so much that I went on reading the xUnit Patterns book, and a few more like Refactoring to Patterns.

Looking back on these books now and what I learned from them, I realize that it's not the patterns described in the books that I value the most. It's the reason for their existents; the motivation for using them. For example, the Factory pattern exists because it's often desirable to separate object construction from domain logic. Why? Because it reduces coupling, which means code are easier to enhance, reuse, extend, and test. So when you understand why a pattern exists, then you know when to use and when not to use it.

The problem is that you don't need to understand why a design pattern is needed in order to use a design pattern in you code. Code with a misused design pattern is worse than code without that pattern. As an example, here is some code taken basically directly from an application I worked with:

Thing t = new ThingFactory().create(arg);
with ThingFactory defined as

class ThingFactory {
  Thing create(int arg) { return new Thing(arg); }
}
This is a prime example of code that misuses a design pattern. Clearly, (s)he who wrote this code did not understanding why and when a Factory should be used, (s)he simply used used a Factory without thinking. Probably because (s)he just read some fancy-named design-pattern book.

This is one big problem I see with design patterns. It makes it easy to write code that looks good and professional, when in fact it's horribly bad and convoluted. The Design Patterns book is the software equivalent to MacDonald's Big Book of Burgers: you need to be a good cook/developer already in order to learn anything that will actually make you burgers/software skills better. A less-than-good cook/developer will only learn how to make burgers/software that look good on the surface.

I recently read Object-Oriented Design Heuristics by Arthur J. Riel, and I must say that this book is much better than the Design Patterns book. First of all, it more than just a dictionary of patterns, it's actually a proper book you can read (without being bored to death). Second, the design rules (what the author calls "heuristics") are much more deep and applicable than design patterns. These rules are like Maxwell's equations for good software design. Understand and apply them, and your software will be well designed.

Let me illustrate with an example how I think Riel is different from GoF. Where GoF say "this is a hammer, use it on nails", Riel says "to attach to wooden objects you can either use hammer+nails or screwdriver+screws, both has pros and cons." Sure GoF is easier to read and you'll learn some fancy word you can say if you running out of buzz-words, but Riel actually makes you understand. Understanding is underrated.


But let's give the GoF book et. al, some slack. To be honest I actually did learn something useful and important from those books, and I couldn't do my daily programming work properly without that knowledge:
  • favor composition over inheritance,
  • separating construction logic from domain logic, and
  • code towards an interface, not an implementation.
To me, these three ideas are the essence of most design pattern that are worth knowing and if you keep those three ideas in you head all the time you won't screw up to much.

Oh, there is actually one more idea you should keep in your head or you will definitely screw up big time (literately).
    But of course there is one more important thing about knowing design patterns. It helps you communicating with your colleagues. Design patterns defines a pattern language, so instead of saying "...you know that class that instantiates a bunch of classes an connects them..."you can say "...you know that builder class...". Honestly, for me this language is way more important than the patterns themselves.

    Actually, there is one thing that I learned from all those design-patterns books (not including Riel). They taught me something important that I could only have learned from few other places: I learned that if an author tries hard enough, (s)he can write a 500 pages book consisting of some common sense and two or three good ideas repeated over and over again. The xUnit Patterns book is the prime example of this. Don't read it. Read Riel.

    Thursday, January 6, 2011

    Global contracts: mutexes, new/delete, singeltons

    I had lunch with a former collegue some time ago. We discussed several things, I would like to write about one idea in particular: global contracts.

    A global contract is an agreement that every part of your code needs to adhere to. A good example of a global contract is locks. Locks are used in multi-threaded applications to synchronize threads. The problem is that there is no way to enforce that threads take the locks when they should, nor to release the lock when they should. Application code is free to access memory/object with or with out locks. Correct application code, though, have to take locks, access the memory/object, and the release the lock.

    A similar but more common global contract is Singletons. Every part of the application must know how to get the singleton instance, and if the singleton has mutable state every part of the application must also know how to mutate that state correctly.

    An even more common global contract is memory management in application without garbage collection. In such applications, the knowledge of how to allocate and deallocate instances of objects are distributed. There are ways to design your application such that the global contract is encapsulated, but in general the detailed knowledge of the contract is spread out all over the application.

    Ok, global contracts are bad for the application design, but how do we get rid of them? We can't simply ignore locks or memory management, so how do we handle them? I see the following possibilities:
    1. Encapsulate the details of the global contract, or
    2. Make the global contract into a local contract.

    The first alternative means that you accept that contract is global, but you make sure that it's simple to adhere to it. An example of this is to use smart pointers, or to hide that there is a singleton within a single class, which also holds state, e.g.,
    class SingletonHider {
      Singleton singleton = Singleton::instance();
      int intState;
      void changeState(int i) { intState = i; }
      void doit() { singleton.doit(i); }
    }
    And every time some part of the application needs to use the Singleton class it instantiates a SingletonHider, which holds all the state needed. The precise behavior (or any state that absolutely needs to be global) is handled by the Singleton class.

    The second alternative is to make the global contract into a local contract. How is this possible? Well, whether or not a contract is global or local is entirely a question of perspective. If you look a memory management from a operating system's perspective, the details of new and delete are definitely a local contract of that application; it's not a operating system-global contract. So the idea for making global contract local is to implement a class that acts like a overseer of the rest of the code. This class encapsulates all the details of the contract and provides a simple interface. This is exactly what a garbage collector is. Let's take Java's garbage collector as an example:
    • it oversees: inspects the running application and determines when a piece of memory is garbage
    • simple interface: every piece of memory is allocated by new. The details of where the memory is allocated (constant pool, generation 1, stack, etc) is encapsulated.
    I'm sure there are more examples and more ways how to make a global contract local, but the approaches outlined here should give a few ideas the next time you which to refactor away a singleton, or fix new-delete coupling.

    Sunday, November 21, 2010

    Integrating invisible tools with the shell

    I've been working on a build system called Mim for some time. It has some interesting features, the most interesting being that files/artifacts are implicitly built when they are read. In other words, there is never a file on disk that is out of date.

    That's pretty neat, but what happens if a file cannot be built for some reason? What happens it there is a syntax error in a .c file, for instance? Since Mim runs in the background, how should such errors be reported? Simply writing them to a log file might suffice, but the experience for the end-user is not very nice. Imagine having to open a file every time a file fails to compile... Horrible!

    This is not a unique problem for Mim. All tools that runs in the background have problem with reporting errors, and most solve this by writing to a log. However, for Mim I chose a totally other way of doing it.

    Most shell utilities, e.g. cat, uses functions in the standard library for printing, open files, etc, but more importantly (for this post) for reporting errors. The error function is used to report errors.

    That means that we can control how errors are printed, by LD_PRELOADing a .so-file with another implementation of the error function. This is what I did to implement error reporting for Mim.

    This is how Mim works now; if you try to open a file that Mim cannot build because of a build error (e.g., compile error), you'll get the following printed to the console:
    $ cat generated-file.txt
    cat: Mim: error 512 when building artifact
    which is much better than the old error message which just was a generic message indicating that the file could not be opened.

    But there is still room for improvement! It's nice to get a error message that says that the file couldn't be built, but it would be even nicer if the message told us why the artifact couldn't be built. I haven't implemented that yet, but I'm working on it. I imagine something like this:
    $ cat generated-file.txt
    cat: Mim: error 512 when building artifact generated-file.txt, because:
      input-file.cc failed to build, because:
        input-file.cc:53: Syntax error.
    $ emacs input-file.cc
       
    That will require some more work though as I said, but it's definitely possible and it's technically not hard.

    Saturday, September 4, 2010

    The firge, the door and the fire alarm

    Today when I prepared breakfast I realized that the fridge wasn't properly closed. It had been a small opening the whole night. That's not particularly good.

    But what's worse is that the fridge did not make any noise indicating that the door was open. In fact, it did the opposite: it turned off the only indication there was that the door wasn't properly closed -- it turned of the light inside the fridge. If that light wasn't turned off it would be easier to spot that the door was open.

    Turning the light off would not be a big problem if there was some way the fridge alarmed when the door was open. Let's say making a noise when it had been open for more than 1 minute.

    I'm sure that it's a feature that the fridge turns off the light by it self when it has been on for too long. But I can't the use-case when it would be a useful. There must be a timer somewhere that turns off the light. That timer should instead trigger an alarm.

    Actually there is an noise-making-device in the fridge. But that only used when the temperature in the fridge is above a certain temperature. That noise-making-devices should be triggered by that timer. Why didn't it?

    I don't know.

    Recently I've noticed more and more weird design choices in everyday things. Like having the handle of a door be shaped as a pipe when you should push the door, and having the handle be shaped like a flat surface when you should pull the door (think about it, a surface is easy to push and a pipe is easy to pull).

    Even worse, I've experienced that fire alarms sounds very very much like the break-in alarm.

    Friday, September 3, 2010

    Mim: the build system you always wanted

    The word mim means any of the following:
    • an incorrect way of writing 1999 in roman numbers (as in "we gonna party like it's 1999"),
    • the Madame Mim from the Disney movie Sword in the Stone,
    • Swedish for "mime" meaning "imitating"
    • a figure in Norse mythology renowned for his wisdom
    • an acronym for "Mim Isn't Make"
    it's also the name of the build system I've been thinking and working of for a while. This post is about what Mim is now and what it can become. Text in red describes stuff that isn't implemented yet, text in yellow is things that are kind-of implemented, and text in black describes implemented features. First, I'll describe how Mim is different from other build systems and why it's better.

    Problem
    The problems with traditional build system are:
    • make sure the dependencies are correct and built in the right order (think: make depend && make && make install),
    • be sure of that every built file you see is up to date,
    • hard understand how a software project is built (what are the artifacts? where are they stored? what are the dependencies?),
    • hard to understand which variables a build have (e.g., make DEBUG=1)
    Solution
    The solution to these problem is (of course) Mim. With Mim you clearly see all artifact the build system produces, how they are built, and when they need to be built. In fact, you can see the artifacts, e.g., by doing ls, before they are built. In other words, using your normal command line tools you can browse the project file tree to find the artifact you need (to run, to view, to copy, etc.) without building anything. Sounds like magic? Keep reading...

    When when you found the program you need to get built you simple execute it. No need to issue any command yourself to build it. The program will be automatically built for you and then executed. Similarly, if you have a tool that generates a text file as part of the build, you can do emacs file.txt. The file will be automatically generated and then opened in emacs.

    If you need to get more information about a file you can do mim ls filename. You'll get something like this written to the console:
    -r-xr-xr-x you users filename (g++ filename.cc -o filename)
    that is, you can easily discover to the access rights, the owner, the group, and how the file is built, of any artifact simply by locating it in the file system.

    This is especially powerful when you work with project of which source code generation is part of the building process. Understanding what files are generated, how they are generated, can be very hard. With Mim, however, this is very easy, because every file (generated or not) looks the same to you when you browse the file tree. Additionally, you can ask Mim for additional information about a certain artifact file, e.g., get the command for building the file by doing mim cmd file.

    Furthermore, getting the dependencies right when the build involves several steps (generating .d files, generating source code, generating .d files for the generated code, compiling the source code, linking the source code against libraries (which themselves has to be built, including its generated source code)) is also very very hard. And understanding it a few month later is even harder. Mim solves all these problems.


    Let's take an example. Let's assume we have a directory called hello with a file hello.cc contaning an implementation of Hello, World. There is also a file called Mimfile that tells Mim how to build this program. Doing mim ls in hello gives you:
    greeting.txt
    hello.cc     codegen -lang en greeting.txt -o hello.cc
    hello-en     g++ hello.cc -o hello-en
    Mimfile
    which tells you that there is only one physical file, greetings.txt, in this directory (except the obligatory Mimfile) and two artifact files, hello.cc and hello-en, of which the latter is an executable. You can also see that there is some code generation involved by looking at the command line for hello.cc. You can tell all this by issuing one simple command, mim ls, without building anything at all.

    Let's say you wish to take a peek at the hello.cc file. All you need to do then is to do cat hello.cc. No need to worry that the code you see is out of date because Mim makes sure it's up to date before it's opened.

    Being lazy
    Being lazy is a good attribute; as long as you're not too lazy. Being lazy in the context of a build system means that you only rebuild a file if is inputs have changed. How do Mim achieve this?

    Mim woks by intercepting all read and write accesses to the file system. This means that Mim has perfect knowledge of 1) which files are read when a given artifact if built, and 2) which files that have been updated since the artifact was last built. This means that artifacts are only rebuilt when needed.

    This information makes it possible for Mim to easily construct a graph dependencies and what needs to be built. This implies that Mim start building the leaves of the graph as early as possible to reduce the build time.

    Instant messaging
    In large project there is usually variables that controls how the project is built. Mim supports such variables in a nice way. To list the variables simply do mim conf, you'll get something like this printed to the console:
    lang      en   en|fr|swe  Controls in which language "Hello, World" is printed.
    debug     off  on|off     Compile with or without debug information.
    optimize  0    1|2|3      Optimization level as defined by gcc:s -O flag.
    
    The first column is the the name of the variable, second column is its current value, third is its possible value, and last column is a textual description of it.

    Changing the value of the variable lang to swe is done by doing mim conf lang=swe. Setting a variable have instant effect. For example, if a variable affects which files are built, you can see the updated list of files immediately by doing ls without building anything. Example:
    $ ls hello*
    hello.cc  hello-en
    $ mim lang=swe
    $ ls
    hello.cc  hello-swe
    
    That is, by changing the variable lang from en to swe we get an artifact file called hello-swe instead of hello-en like we got before.

    To aid discoverability even more, you can do mim ls hello* lang==* to list all files matching hello* for all values of lang. For the hello example this outputs:
    hello-en   lang=en   g++ hello.cc -o hello-en
    hello-fr   lang=fr   g++ hello.cc -o hello-fr
    hello-swe  lang=swe  g++ hello.cc -o hello-swe
    
    All these features helps you in learning how an a project is built, how to use its build system, how to change it, how those changes affect the build system, and more.


    The Tracker
    Since Mim intercepts all accesses to the file system, it can help you when you do potentially bad things, for example, if you delete a file that is needed to build an artifact. Mim also updates your Mimfiles automatically when you do non-destructive changes to your source tree. For instance, when you rename an input file to gcc as the following example illustrates this:
    $ mim ls hello*
    hello.cc  codegen -lang en greeting.txt -o hello.cc
    hello-en  g++ hello.cc -o hello-en
    $ mv hello.cc hi.cc
    $ mim ls hello*
    hi.cc     codegen -lang en greeting.txt -o hi.cc
    hello-en  g++ hi.cc -o hello-en

    Mim can do this automatic update of Mimfiles correctly in many cases, but it's impossible to get it right every time since that would require deep knowledge of all possible build tools (e.g., gcc, ld, m4, etc). However, Mim makes certain (reasonable) assumption on how the build tools behave, and as long as the build tools' conform to these assumptions Mim gets it right most of the time. Anyway, to check that Mim got it right, you simple build of application so this isn't a big problem in practice.

    Getting general
    Mim is built around the concepts of events and actions triggered by event. So far we have implicitly discussed the event open artifact for reading and the action was update artifact. The possible events that can trigger actions are:
    • open physical of artifact file for reading (before-read),
    • closing a physical or artifact file after write (after-write),
    • deleting physical or artifact file or directory (delete-file),
    • creating a physical file or directory (create-file), and
    • moving a physical or artifact file (move-file).
    Since arbitrary commands can be executed as the result of an event, you can do what ever you heart desire when a file is moved, delete, created, etc. However, actions that interact with Mim would be hard to implement ourself and are thus built into Mim:
    • update an artifact (without reading the artifact file)
    • log to the Mim log (e.g., an error message)
    • reject event (e.g., reject opening a file).
    The reject event action is useful, e.g., if you need to verify the consistency of a save file: if the file is consistent it cannot be saved.

    Playing nice
    So far so good. But what about integrating with other non-miming tools like, say, make. Since all Mim does to build an artifact files is to execute a command line in the shell, Mim can easily be used as a front-end to make. Here is an example of doing mim ls in a directory of a project that does just that:
    main.cc
    hello     make hello
    Makefile
    Mimfile
    Furthermore, any program can use Mim for building files, because all the program need to do to build a file is to (try to) open it for reading. Mim will then kick in and build the file, without the other program ever nothing anything special. This is very powerful, because every program you have on you computer, cat, emacs, Mozilla, etc, can build any file using Mim.

    Saving the environment
    Do you have ./configure && make && sudo make install aliased to nike on you system? If you do you're not using Mim.

    One reason to use autoconf (i.e., the ./configure script above) is to find the paths to all the necessary tools, libraries, headers, etc, needed to build your project. With autoconf this involves generating Makefiles that are specifically targeted at your system. The problem with autoconf and the generated Makefiles is that you have to be a genius to understand what the heck is going on.

    So what do Mim do instead? Easy, you have an artifact file for each tool, library, header, etc, that is needed by your build. Those artifacts are configured (e.g., using autoconf) to link to a specific file on your system. These tools (or, ) are normally placed in a directory called ext (for "external"); thus, to list the dependencies to external tools simply do find ext or ls -R ext. Every file listed is an external dependency. Example:
    
    $ cd my-project/ext
    $ ls -R *
    ext/bin:
    g++  ld  python
    
    ext/include:
    3rdparty.h
    
    ext/lib:
    3rdparty.so
    

    In this example we have configured the ext directory to contain a compiler, linker, and a python interpreter. So, when we need to compile a .cc file we have the following command line in the Mimfile ext/bin/g++ file.cc. The first time this command line is executed, the artifacts (ext/bin/g++, etc) are not yet configured, so Mim launches the configuration tool (autoconf). After that, the artifacts in the ext directory point to the correct files on the local system and can be executed easily.

    A way of thinking of this is that Mim creates a virtual environment (directory structure) that is ideal(ized) for you specific project. All the tools and libraries you need are right there in the ext directory.


    Deep understanding
    Mim really, really, understands mimfiles. If a mimfile is updated, artifacts potentially need to be rebuilt. However, Mim will only rebuild artifact which command lines has changed or if their dependencies' command lines are changed. Have you ever experienced that your entire project needs to be rebuilt just because you added an a comment to a makefile? That will never happen with Mim.

    Mim has even support for refactoring mimfiles via your filesystem! In the hidden directory .mimfile the artifact files' mimfile-data is represented as files. So, if you want to change the name of an artifact file simply do mv .mimfile/old-name .mimfile/new-name; to create a new artifact file that is identical to an existing one do cp .mimfile/original .mimfile/copy; to delete an entry from the mimfile do rm .mimfile/remove-me. Finally, to edit the command line for an artifact you simply edit the corresponding file under .mimfile, e.g., echo 'gcc input-file.c -o %output' > .mimfile/file-to-be-edited.

    As mentioned earlier, Mim also has variables that can be used to configure how your project is built. These variables are also represented as files under the directory .mimvars. Here you can easily browse the variables using ls, find, grep, etc, or even your graphical file browser. The get the current value of a variable, simply do cat .mimvars/the-variable and to set the value do something like echo 'new value' > .mimvars/the-variable.


    When thing go wrong
    Ok, so Mim is nice, I think I've communicated that by now. But sometimes the world is very very unnice. Sometimes the world don't want to compile your code. Then you need to either 1) fix the world, or 2) fix your code. To my experience the latter is slightly easer than the former, although your mileage may vary.

    To fix your code you need to know what the compiler complains about, but how do you do this when the files is compiled behind the scenes? Simply do mim log errors to print the error messages to the console, fix the error, and run the program again.

    Doing mim log error after every build error gets annoying very fast. Thus there is a variant, mim log errors -f, that is useful when you wish to get continuous feedback. Given the -f flag Mim will run in the background and print the error message to the console as soon as they appear. So, assuming hello.cc contains an error, trying to execute hello will print the flowing:
    $ mim errors -f
    $ ./hello
    hello/hello.cc: In function ‘int main()’:
    hello/hello.cc:3: error: ‘printf’ was not declared in this scope
    which is is just what gcc outputs, thus, any tool that parses the output from gcc will understand the error output from Mim as well. This is of course also applicable for other tools like make.

    Summing up
    I've described the ideas behind Mim, its current state, and possible future features (that, by that way should be very reasonable to implement :)). You can find Mim here. You'll need Intel Threading Building Blocks and Lua 5.1. I've developed using Ubuntu and also compiled it on Suse 11.2. The instructions for how to build Mim itself is currently horribly missing, so please contact me if you like to try Mim out.

      Friday, July 9, 2010

      DSL: Domain Specific Logging

      A few years ago, I was asked to to design a logging framework for our new Product X 2.0. Yeah, I know, "design a logging framework?". That's what I thought too. So I didn't really do much design; all I did was to say "let's use java.util.logging", and then I was done designing. Well kind of anyway...

      I started to think back on my first impression of the logging framework in Product X 1.0. I remembered that I had a hard time understanding how it was (supposed) to be used from a developer's point-of-view. That is, when faced with a situation where I had to log, I could not easily answer:
      • to which severity category does the entry belong (FINEST, FINER, FINE, INFO, WARNING, SEVERE, ERROR, etc)
      • what data should be provided in the entry and how it should be formatted, and
      • how log entries normally is expressed textually (common phrases, and other conventions used).
      In other words, it was too general (i.e., complex) with too much freedom to define your own logging levels, priorities, etc.

      That's why I proposed to make logging easier, thus, the logging framework in Product X 2.0 has very few severity levels. This is implemented by simply wrapping a java.util.logging.Logger in a new class that only has the most necessary methods.

      This simplification reduced the code noise in the logger interface, which made it a lot easier to choose log-level when writing a new log message in the source. This had an important implication (that we originally didn't think of): given that a certain message had to be logged, we (the developers) agreed more on which level that message should be logged. No more, "oh, this is a log message written by Robert -- he always use high log level for unimportant messages".

      Of course, you could still here comments like "oh, this log message is written by John -- he always use ':' instead of '=' when printing variables". This isn't much of an issue if an somewhat intelligent being is reading the logs, e.g., a human. However, if the logs are read by a computer program, this can be a big problem -- this was the case for Product X 2.0.

      This variable-printing business could be solved quite easily; we simply added a method to the logger class (MyLogger) called MyLogger variable(final String name, final Object value) that logged a variable. Example:
      logger.variable("result", compResult).low("Computation completed."); 
      which would result in the following log message:
      
      2008-apr-12 11:49:30 LOW: Compuation completed. [result = 37289.32];
      When I did this, I began to think differently about log messages. It put me into a new mind-set -- the pattern matching mind-set. Patterns in log messages started to appear almost everywhere. Actually, most of our logs followed one of the following patterns:
      • Started X
      • Completed X
      • Trying to X
      • Failed to X
      • Succeeded to X
      Here is an example:
      try {
        myLogger.tryingTo("connect to remote host").variable(
                          "address", remoteAddress).low();
        // Code for connecting.
        myLogger.succeededTo("connect to remote host").variable(
                             "address", remoteAddress).low();
      } catch (final IOException e) {
        myLogger.failedTo("connect to remote host").cause(e).medium();
      }
      
      To take this even further, it is possible to only allow certain combinations of succeededTo, tryingTo, cause, etc, by declaring them in different interfaces. Let's assume that it should only be possible to use cause if the failedTo method have been called. Here's the code for doing that:
      
      interface MyLogger {
        // ... other logging methods.
        CauseLogger failedTo(String msg);
      } 
       
      interface CauseLogger {
        LevelChooser cause(Throwable cause);
        LevelChooser cause(String message);
      }
      
      interface LevelChooser {
        void low();
        void medium();
        void high();
      } 
      This is essentially what's called fluent interfaces. It complicates the design of the logging framework, but also makes it impossible to misuse it. Good or bad? It's a matter of taste, I think, but it's a good design strategy to have up your sleeve.