https://new.jameshunt.us

rlog & errno - Little Tools

I wrote two little tools today, rlog and errno.

errno - Looking up Error Numbers

The fact that this utility doesn't really exist had always bothered me, so I wrote it myself after grepping through /usr/include looking for ESOMETHING constants and their numeric definitions.

It works like a little something like this:

$ errno 2
ENOENT           2 No such file or directory

$ errno EAFNOSUPPORT EAGAIN EWOULDBLOCK EINVAL
EAFNOSUPPORT     47 Address family not supported by protocol family
EAGAIN           35 Resource temporarily unavailable
EAGAIN           35 Resource temporarily unavailable
EINVAL           22 Invalid argument

$ errno | grep Bad
EBADF             9 Bad file descriptor
EFAULT           14 Bad address
EPROCUNAVAIL     76 Bad procedure for program
EBADEXEC         85 Bad executable (or shared library)
EBADARCH         86 Bad CPU type in executable
EBADMSG          94 Bad message

This can be quite handy when all you get from a program is the error message (i.e. 'Invalid argument') and you're trying to track down which system call is failing. Most kernels ship with pretty good syscall man pages, and they tend to list all of the possible values of errno that can result, and why.

For example:

ERRORS
     The socket() system call fails if:

     [EACCES]           Permission to create a socket of the specified
                        type and/or protocol is denied.

     [EAFNOSUPPORT]     The specified address family is not supported.

     [EMFILE]           The per-process descriptor table is full.

     [ENFILE]           The system file table is full.

     [ENOBUFS]          Insufficient buffer space is available.
                        The socket cannot be created until sufficient
                        resources are freed.

     [ENOMEM]           Insufficient memory was available to fulfill
                        the request.

     [EPROTONOSUPPORT]  The protocol type or the specified protocol is
                        not supported within this domain.

     [EPROTOTYPE]       The socket type is not supported by the protocol.

     If a new protocol family is defined, the socreate process is free
     to return any desired error code.  The socket() system call will
     pass this error code along (even if it is undefined).

Now, if you get ENOMEM, and print a message via strerror(3), you'll get something like this in your output stream:

Cannot allocate memory

With errno, you can reverse that process and get back the ENOMEM constant, with ease:

errno | grep 'Cannot allocate memory'
ENOMEM           12 Cannot allocate memory

Hey, look at that! It's ENOMEM. Incidentally, on my Macbook, that maps to the numeric error code 12. Neat.

The errno utility also lets you "preview" the error message a given code will produce, which can be helpful in supporting lots of different platforms.

The code for errno is over here, on Github

rlog - Ring Buffer Logging

A ring buffer, or circular buffer is a constant-space data structure that prioritizes newer things over older things. rlog is a Ring Buffer Logging utility that brings an innovative twist to the collection, retrieval and longterm obligation of debug logging.

Ah, debugging, that glorious panacea of the operations world.

If only I had debugging mode turned on, I bet I could get to the bottom of
this weird problem! But turning it on means restarting the daemon, and I
may not be able to reproduce the problem once that happens!

Oh what's a cloud engineer to do!?

One solution, popular with people who like to pay for lots and lots of storage and then never read the logs, is to just log everything, all the time. They set up centralized syslog, Hadoop, Elastic Search, Cassandra, or even -gasp- MongoDD. After a few weeks, there is so much data to look through, it's pointless to even try!

Rather than do that, I wrote rlog.

rlog reads every line from standard input, and datestamps each. It allocates enough space to retain 2048 messages. When the 2049th message is received, the 1st messages is dropped. The 2050th messages pushes out the 2nd message, and so on.

When a client connects to rlog (usually via 127.0.0.1:1040), they receive the most recent 2048 messages immediately (hence the datestamps), and then receive future messages as they are received by rlog itself.

It's like tail -f, except (a) there is no file and (b) it takes up no disk space.

I'll be using rlog to provide high-resolution debugging output from live production systems. When I need the diagnostics, I hook up a nc process to the port. When I don't, it's chewing a (small) constant amount of RAM. Win-win!

rlog is also over at Github

Little Tools = The Best Tools

They can be sharp. They don't take a lot of time to build. The low time investment makes them more malleable; easier to fix; improve; expand.

Happy Hacking!

James (@iamjameshunt) works on the Internet, spends his weekends developing new and interesting bits of software and his nights trying to make sense of research papers.

Currently exploring Kubernetes, as both a floor wax and a dessert topping.