Skip to content

Commit 18ac9b1

Browse files
committed
Make a beautiful Readme
1 parent e11070e commit 18ac9b1

File tree

2 files changed

+106
-10
lines changed

2 files changed

+106
-10
lines changed

Readme.adoc

Lines changed: 0 additions & 10 deletions
This file was deleted.

Readme.md

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# 📄 JSONLogger
2+
3+
<picture><img alt="Compatible from Swift 5.1 to 6." src="https://img.shields.io/badge/Swift-6.0_%7C_5.10--5.1-blue"></picture>
4+
<picture><img alt="Compatible with macOS, iOS, visionOS, tvOS and watchOS." src="https://img.shields.io/badge/Platforms-macOS_%7C_iOS_%7C_visionOS_%7C_tvOS_%7C_watchOS-blue"></picture>
5+
<picture><img alt="Compatible with Linux, Windows, WASI and Android." src="https://img.shields.io/badge/Platforms-Linux_%7C_Windows_%7C_WASI_%7C_Android-blue"></picture>
6+
[![](<https://img.shields.io/github/v/release/xcode-actions/json-logger>)](<https://github.com/xcode-actions/json-logger/releases>)
7+
8+
A simple [swift-log](<https://github.com/apple/swift-log>)-compatible logger which logs in JSON, one entry per line.
9+
10+
## Usage 🤓
11+
12+
Bootstrap the LoggingSystem with `JSONLogger` before creating any logger:
13+
```swift
14+
LoggingSystem.bootstrap(JSONLogger.init, metadataProvider: nil/* or whatever you want */)
15+
```
16+
17+
To stream JSONSeq (RFC 7464), you can use a convenience initializer:
18+
```swift
19+
LoggingSystem.bootstrap(JSONLogger.initForJSONSeq, metadataProvider: nil/* or whatever you want */)
20+
```
21+
22+
## Log Format 📖
23+
24+
The JSONs generated by `JSONLogger` have the following fields:
25+
- `level`: The level of the log.
26+
Value is a `Logger.Level` (`trace`, `debug`, `info`, `notice`, `warning`, `error` or `critical`).
27+
- `message`: The message of the log (`String` value).
28+
- `metadata`: The structured metadata of the logs (`JSON` object value).
29+
The structure of the metadata values are kept as much as possible.
30+
- `date` or `date-1970`: The date of the log.
31+
If the log can be encoded properly using a `JSONEncoder` the `date` field is present and the value comes from the encoder.
32+
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).
33+
- `label`: The label of the logger (`String` value).
34+
- `source`: The source of the log (`String` value).
35+
- `file`: The file from which the log has been generated (`String` value).
36+
- `function`: The function from which the log has been generated (`String` value).
37+
- `line`: The line from which the log has been generated (`UInt` value).
38+
39+
The JSONs should not have any newlines, however there are no _actual_ guarantees from `JSONEncoder`.
40+
In particular you can configure the encoder to pretty print the JSONs…
41+
42+
## Configuration 🛠️
43+
44+
### Output FileHandle
45+
46+
By default `JSONLogger` logs on `stdout`.
47+
You can configure it to log on any file descriptor using the `fileHandle` parameter at init time.
48+
49+
### Line Prefix, Suffix and Separator
50+
51+
`JSONLogger` allows you to choose:
52+
- A suffix for the JSON payload (the “newline separator;” defaults to `[0x0a]`, aka. a single UNIX newline);
53+
- A prefix (defaults to `[]`);
54+
- An inter-message separator (defaults to `[]`).
55+
56+
The inter-message separator is logged after a JSON message, but only when a new message arrives,
57+
as opposed to the suffix which is logged after _every_ JSON message.
58+
59+
As an example, if you log two messages, you’ll get the following output:
60+
```text
61+
prefix JSON1 suffix separator prefix JSON2 suffix
62+
```
63+
64+
An interesting configuration is to set the prefix to `[0x1e]` and the suffix to `[0x0a]`, which generates a JSONSeq stream.
65+
66+
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.
67+
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).
68+
69+
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).
70+
71+
### JSON Encoder
72+
73+
In order to log messages, `JSONLogger` will create a `LogLine` struct for every message, then encode this struct using a `JSONEncoder`.
74+
75+
This encoder is customizable.
76+
77+
All JSON messages generated by `JSONLogger` should be decodable by a `JSONDecoder` matching the configuration of the encoder.
78+
79+
#### When the Encoder Fails
80+
81+
When there is a problem encoding a log line (e.g. a date formatter fails), `JSONLogger` will fallback to manually encoding the log message.
82+
83+
The manually encoded JSON differs slightly from the normal output:
84+
- The metadata are stripped and replaced by:
85+
```json
86+
{
87+
"JSONLogger.LogInfo": "Original metadata removed (see JSONLogger doc)",
88+
"JSONLogger.LogError": ERROR_MESSAGE
89+
}
90+
```
91+
- The log message is prefixed by `MANGLED LOG MESSAGE (see JSONLogger doc) -- `;
92+
- Instead of a `date` field, there is a `date-1970` field whose value is the `timeIntervalSince1970` of the date of the message.
93+
This ensures the encoding of the message _cannot_ fail.
94+
95+
The modified log message should still be parsable by a `JSONEncoder` as a `LogLine`.
96+
97+
### JSON Coders for `StringConvertible`s
98+
99+
The type of the value of a metadata entry can be either a `String`, an `Array`, a `Dictionary` or `any StringConvertible`.
100+
101+
The `String`, `Array` and `Dictionary` types are straightforward to encode in a JSON message.
102+
103+
For the string convertibles, `JSONLogger` will use a pair of `JSONEncoder`/`JSONDecoder` if present.
104+
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.
105+
106+
If the encoder/decoder pair is set to `nil`, the String representation of the string convertible is used.

0 commit comments

Comments
 (0)