Skip to content

xcode-actions/json-logger

Repository files navigation

📄 JSONLogger

Compatible from Swift 5.1 to 6. Compatible with macOS, iOS, visionOS, tvOS and watchOS. Compatible with Linux, Windows, WASI and Android.

A simple swift-log-compatible logger which logs in JSON, one entry per line.

Usage 🤓

Bootstrap the LoggingSystem with JSONLogger before creating any logger:

LoggingSystem.bootstrap(JSONLogger.init, metadataProvider: nil/* or whatever you want */)

To stream JSONSeq (RFC 7464), you can use a convenience initializer:

LoggingSystem.bootstrap(JSONLogger.initForJSONSeq, metadataProvider: nil/* or whatever you want */)

Log Format 📖

The JSONs generated by JSONLogger have the following fields:

  • level: The level of the log. Value is a Logger.Level (trace, debug, info, notice, warning, error or critical).
  • message: The message of the log (String value).
  • metadata: The structured metadata of the logs (JSON object value). The structure of the metadata values are kept as much as possible.
  • date or date-1970: The date of the log. If the log can be encoded properly using a JSONEncoder the date field is present and the value comes from the encoder. If there is an error encoding the log, the date-1970 fallback is used with the value being a Double representing the time interval since January 1, 1970 (UNIX time).
  • label: The label of the logger (String value).
  • source: The source of the log (String value).
  • file: The file from which the log has been generated (String value).
  • function: The function from which the log has been generated (String value).
  • line: The line from which the log has been generated (UInt value).

The JSONs should not have any newlines, however there are no actual guarantees from JSONEncoder. In particular you can configure the encoder to pretty print the JSONs…

Configuration 🛠️

Output FileHandle

By default JSONLogger logs on stdout. You can configure it to log on any file descriptor using the fileHandle parameter at init time.

Line Prefix, Suffix and Separator

JSONLogger allows you to choose:

  • A suffix for the JSON payload (the “newline separator;” defaults to [0x0a], aka. a single UNIX newline);
  • A prefix (defaults to []);
  • An inter-message separator (defaults to []).

The inter-message separator is logged after a JSON message, but only when a new message arrives, as opposed to the suffix which is logged after every JSON message.

As an example, if you log two messages, you’ll get the following output:

prefix JSON1 suffix  separator  prefix JSON2 suffix

An interesting configuration is to set the prefix to [0x1e] and the suffix to [0x0a], which generates a JSONSeq stream.

Another interesting configuration is to set the inter-JSON separator to [0xff] or [0xfe] (or both), and set the prefix and suffix to an empty array. The 0xff and 0xfe bytes should never appear in valid UTF-8 strings and can be used to separate JSON payloads (JSON requires UTF-8 encoding).

I’m not sure why JSONSeq does not do that but there must be a good reason (probably because the resulting output would not be valid UTF-8 anymore).

JSON Encoder

In order to log messages, JSONLogger will create a LogLine struct for every message, then encode this struct using a JSONEncoder.

This encoder is customizable.

All JSON messages generated by JSONLogger should be decodable by a JSONDecoder matching the configuration of the encoder.

When the Encoder Fails

When there is a problem encoding a log line (e.g. a date formatter fails), JSONLogger will fallback to manually encoding the log message.

The manually encoded JSON differs slightly from the normal output:

  • The metadata are stripped and replaced by:
{
   "JSONLogger.LogInfo": "Original metadata removed (see JSONLogger doc)",
   "JSONLogger.LogError": ERROR_MESSAGE
}
  • The log message is prefixed by MANGLED LOG MESSAGE (see JSONLogger doc) -- ;
  • Instead of a date field, there is a date-1970 field whose value is the timeIntervalSince1970 of the date of the message. This ensures the encoding of the message cannot fail.

The modified log message should still be parsable by a JSONEncoder as a LogLine.

JSON Coders for StringConvertibles

The type of the value of a metadata entry can be either a String, an Array, a Dictionary or any StringConvertible.

The String, Array and Dictionary types are straightforward to encode in a JSON message.

For the string convertibles, JSONLogger will use a pair of JSONEncoder/JSONDecoder if present. The encoder will be used to encode the given object, and the decoder will be used to decode the resulting JSON into a generic JSON object, that will then be put in the LogLine struct.

If the encoder/decoder pair is set to nil, the String representation of the string convertible is used.

About

A Swift logger for logging in JSON

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages