Skip to content
Commit 56a0ea12 authored by Ned Burns's avatar Ned Burns
Browse files

Hyper-lightweight logging update

Update to our system for high-volume logging with the goal of having any
logging call result in *zero* allocations, not even strings.

The core pieces are LogBuffer and LogMessage. LogBuffer is a simple
ring buffer that holds instances of LogMessage. LogMessages store
data related the thing they're logging, but in a generic way that
allows them to be reused across ALL log types.

In order to log a message, the caller must supply two things:

- The _initializer_, a function that stores data on a LogMessage
- The _printer_, a function that converts a LogMessage containing the
stored data into a human-readable string.

When a message is logged, the initializer is called on a newly-obtained
instance of LogMessage. The printer is also stored on the message and
the message itself is inserted into the buffer. Later, when the message
needs to be dumped, the printer is called on the message instance to
create a human-readable string.

Using features of Kotlin, we can inline calls to the initializer so the
function is effectively erased. The printers cannot be erased (we need
to maintain a reference to them) and so each printer requires a (small)
classdef. However, because the printers are stateless, we only ever need
one instance copy per printer.

Advantages of this system:
- Lightweight: no-allocation logging (still some overhead, but it's
very slight).
- Ease of use: easy to add new logs. Log-generating code is easy to
read.
- Closer match to logcat: system uses TAG and logcat levels (VERBOSE,
etc)
- Finer-grained control over debugging: Can control which tags and/or
buffers log to logcat and at what logging level (VERBOSE, etc).

Disadvantages:
- Each log type requires the creation of an anonymous class def (the
printer function). With a ton of such class defs, our code might get
bloated (it's unlikely for us to reach this point, however).
- Easy to accidentally write a log message that triggers an allocation
(if your printer function captures any outside scope, such as one of the
original log parameters, then the system will need to instantiate a new
instance of the printer function for each call instead of reusing a
singleton static instance).

Test: manual
Change-Id: I2c7c272cda4ce61d56427ab1d6eb270d9365b325
parent 617b3b71
Loading
Loading
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment