Saturday, April 10, 2010

My First Go Go

Today I decided that my first go at the Go Programming Language should be to learn how to log errors to Stdout and a log file. I thought I'd have to write a logger from scratch, but it turns out Go comes with a logging package. With more difficulty than I expected, I wrote this snippet:

package main
import (
"log"
"os"
"fmt"
)
func main(){
file, err := os.Open("./test.log", os.O_WRONLY | os.O_APPEND | os.
.O_CREAT, 0666)
if err != nil {
fmt.Print("error opening log file", err)
//TODO: bomb
}
l := log.New(os.Stdout, file, "", log.Ldate | log.Ltime | log.Lmicroseconds | log.Llongfile)
l.Log("WTF")
file.Close()
}

It logs something like the following to the console and the file ./test.log
2010/04/10 08:57:07.921499 /home/gdg/go/src/pkg/log/log.go:157: WTF

Things I learned:
  • 0666 != 666, 0666 == 438. The "0" (zero) in front of the 666 means Octal, not zero thousands, so 0666 is the same as 438 in regular base 10 (and that's a ten, not binary for 2). Somehow I forgot that in "chmod 666 foo.bar" the "666" is just understood to be in octal.
  • Even though the documentation says open accepts O_RDONLY, it doesn't. You have to do os.O_RDONLY to open a file for reading. I guess that's because O_RDONLY is part of the os package and not defined in an os.h header file like it probably would have been in C.
  • You have to do log.New(...blah...) to pass the log file handle(s) (object(s)?) to log.
  • This nonsense doesn't work: log.Lmicroseconds = true, log.Lmicroseconds=1, or log.Lmicroseconds(true). log.Lmicroseconds, as the documentation clearly states, it is a constant, not something you set. These constants can then be OR'ed together and passed to log.New(). How do very concisely change the flags being used for log.Stdout? Hell if I know.
  • When writing code documentation for public consumption, it would be a good idea to provide short examples of how to use the code. When documentation is written by the coder, the coder has been immersed in the code for so long (s)he simply will not think about all of the "obvious" details that make intuition possible. Someone who has spent all of ten minutes using the code isn't going to be able to make the same intuitive leaps. Working examples for every package would force the documentation writer to include the "obvious" details--otherwise it wouldn't compile. Yes, there are tutorials which helped me quite a bit, but they weren't enough to help me understand the log package right off.
I still need to figure out at least one more thing. Currently it prints "/home/gdg/go/src/pkg/log/log.go:157" which is a file in to Go source code. I'd prefer that it print my file, and the line number for: l.Log("WTF")