-- Singleton object to deal with log messages class log log: (message, tags, level) -> tags = tags or {} if not level error("Level is required") tag_rev = {tag,true for tag in *tags} chunk = level: level time: os.clock! message: message tags: tag_rev stream_pos = log.messages + 1 log.stream[stream_pos] = chunk for tag in *tags log.by_tags[tag] = log.by_tags[tag] or {} table.insert(log.by_tags[tag], stream_pos) log.by_level[level] = log.by_level[level] or {} table.insert(log.by_level[level], stream_pos) log.messages += 1 for observer in *log.observers observer(chunk) reset: -> log.stream = {} log.messages = 0 log.by_tags = {} log.by_level = {} log.observers = {} info: (message, tags) -> log.log(message, tags, "info") warn: (message, tags) -> log.log(message, tags, "warn") error: (message, tags) -> log.log(message, tags, "error") panic: (message, tags) -> log.log(message, tags, "panic") observe: (callback) -> table.insert(log.observers, callback) of_level: (level, callback) -> if not log.by_level[level] return for message_idx in *log.by_level[level] callback(log.stream[message_idx]) of_tags: (tags, callback) -> if type(tags) ~= "table" tags = {tags} first_tag = tags[1] assert(type(first_tag) == "string", "tag was not a string, was a " .. type(first_tag)) if not first_tag error("Must pass a tag") if not log.by_tags[first_tag] return for message_idx in *log.by_tags[first_tag] message = log.stream[message_idx] suitable = true for tag in *tags if not message.tags[tag] suitable = false break if suitable callback(message) log.reset() log