Skip to content

Commit 7da8c32

Browse files
authored
Implement custom field names (#30)
* Implement custom field names
1 parent ee27ad9 commit 7da8c32

29 files changed

+1936
-309
lines changed

README.md

+120-10
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,15 @@ Below is an example of formatted JSON logs in the command-line interface:
3333
- **Multi-Source Input**
3434
Combine the outputs of multiple commands into a single unified log stream.
3535

36+
- **Multiple Input Formats**
37+
- **JSON** (default): Process standard JSON log formats
38+
- **Logfmt**: Support for key-value pair log formats
39+
- **CSV**: Parse and analyze CSV-formatted logs with headers
40+
3641
- **Advanced Filtering**
3742
- Apply regular expressions to extract relevant log entries.
3843
- Use SQL-like queries to filter and query JSON fields.
44+
- Filter logs by timestamp ranges.
3945

4046
- **YAML Configuration**
4147
Define input streams and configurations using YAML files.
@@ -49,6 +55,9 @@ Below is an example of formatted JSON logs in the command-line interface:
4955
- **Integration with k9s**
5056
Seamlessly integrate with the k9s Kubernetes CLI tool to visualize logs directly within k9s.
5157

58+
- **Custom Field Mapping**
59+
Configure custom field names mapping to work with non-standard log formats.
60+
5261
## Installation
5362

5463
### Requirements
@@ -82,6 +91,28 @@ json-log-viewer --help
8291
cat log.txt | json-log-viewer --filter "level = 'ERROR'"
8392
```
8493

94+
### Working with Different Log Formats
95+
96+
#### JSON (Default)
97+
For standard JSON logs:
98+
```bash
99+
cat json-logs.txt | json-log-viewer
100+
```
101+
102+
#### Logfmt
103+
For logs in logfmt format:
104+
```bash
105+
cat logfmt-logs.txt | json-log-viewer --format-in logfmt
106+
```
107+
108+
#### CSV
109+
For CSV-formatted logs (requires header row):
110+
```bash
111+
cat csv-logs.csv | json-log-viewer --format-in csv
112+
```
113+
114+
Note: CSV format requires a header row with column names. The tool will map these column names to standard log fields.
115+
85116
### SQL Filtering
86117

87118
`json-log-viewer` supports SQL-like filtering for JSON fields, allowing precise log analysis.
@@ -124,6 +155,20 @@ You can use comparison and logical operations.
124155
cat log.txt | json-log-viewer --filter "(level = 'ERROR' OR level = 'WARN') AND message LIKE '%connection%'"
125156
```
126157

158+
### Timestamp Filtering
159+
160+
Filter logs by timestamp range:
161+
162+
```bash
163+
cat log.txt | json-log-viewer --timestamp-after 2024-01-01T00:00:00Z --timestamp-before 2024-01-31T23:59:59Z
164+
```
165+
166+
You can also specify a custom timestamp field:
167+
168+
```bash
169+
cat log.txt | json-log-viewer --timestamp-field time
170+
```
171+
127172
## Configuration
128173

129174
`json-log-viewer` supports defining input streams, filters, and other settings using a YAML configuration file.
@@ -139,7 +184,34 @@ Each feed represents a log source and can have the following attributes:
139184
- **formatIn** (optional): Input log format. Supported values:
140185
- `json` (default).
141186
- `logfmt`.
187+
- `csv`.
142188
- **rawInclude** and **rawExclude** (optional): Lists of regular expressions to include or exclude from processing.
189+
- **excludeFields** (optional): List of fields to exclude from output.
190+
- **fieldNames** (optional): Custom mapping for field names, helpful when working with non-standard log formats.
191+
192+
### Custom Field Mapping
193+
194+
You can define custom field name mappings either globally or per feed:
195+
196+
```yaml
197+
# Global field mapping
198+
fieldNames:
199+
timestamp: "ts"
200+
level: "severity"
201+
message: "msg"
202+
stackTrace: "error"
203+
loggerName: "logger"
204+
threadName: "thread"
205+
206+
feeds:
207+
- name: "application-logs"
208+
commands:
209+
- cat log1.txt
210+
# Feed-specific field mapping (overrides global mapping)
211+
fieldNames:
212+
timestamp: "time"
213+
level: "priority"
214+
```
143215
144216
### Example Configuration File
145217
@@ -155,12 +227,21 @@ feeds:
155227
- "ERROR"
156228
rawExclude:
157229
- "DEBUG"
230+
excludeFields:
231+
- "thread_name"
158232
- name: "application-2-logs"
159233
commands:
160234
- cat log2.txt
161235
filter: |
162236
message NOT LIKE '%heartbeat%'
163237
formatIn: logfmt
238+
- name: "csv-logs"
239+
commands:
240+
- cat logs.csv
241+
formatIn: csv
242+
fieldNames:
243+
timestamp: "time"
244+
level: "severity"
164245
```
165246
166247
#### Running with a Configuration File
@@ -186,27 +267,56 @@ json-log-viewer --config-file json-log-viewer.yml
186267
```bash
187268
cat log.txt | json-log-viewer --config-file json-log-viewer.yml
188269
```
189-
190-
- **--format-in**: Specify the input log format (supported formats: json, logfmt).
270+
271+
- **--format-in**: Specify the input log format (supported formats: json, logfmt, csv).
191272
```bash
192273
cat log.txt | json-log-viewer --format-in logfmt
193274
```
194-
275+
195276
- **--format-out**: Specify the output format (supported formats: pretty, raw).
196277
```bash
197278
cat log.txt | json-log-viewer --format-out raw
198279
```
199-
280+
200281
- **--timestamp-after** and **--timestamp-before**: Filter logs by a specific time range.
201282
```bash
202283
cat log.txt | json-log-viewer --timestamp-after 2024-01-01T00:00:00Z --timestamp-before 2024-01-31T23:59:59Z
203284
```
204-
285+
205286
- **--timestamp-field**: Specify the field name for timestamps (default: @timestamp).
206287
```bash
207288
json-log-viewer --timestamp-field time
208289
```
209290

291+
#### Field Name Options
292+
293+
You can override the default field names to work with non-standard log formats:
294+
295+
- **--level-field**: Override default level field name (default: level).
296+
```bash
297+
json-log-viewer --level-field severity
298+
```
299+
300+
- **--message-field**: Override default message field name (default: message).
301+
```bash
302+
json-log-viewer --message-field msg
303+
```
304+
305+
- **--stack-trace-field**: Override default stack trace field name (default: stack_trace).
306+
```bash
307+
json-log-viewer --stack-trace-field exception
308+
```
309+
310+
- **--logger-name-field**: Override default logger name field name (default: logger_name).
311+
```bash
312+
json-log-viewer --logger-name-field logger
313+
```
314+
315+
- **--thread-name-field**: Override default thread name field name (default: thread_name).
316+
```bash
317+
json-log-viewer --thread-name-field thread
318+
```
319+
210320
## k9s Plugin
211321

212322
Integrate json-log-viewer with k9s to view formatted JSON logs directly within the k9s interface.
@@ -256,7 +366,7 @@ plugins:
256366
257367
## Development
258368
259-
This section provides instructions for building and running
369+
This section provides instructions for building and running
260370
both the JVM and JavaScript versions of `json-log-viewer`.
261371
It also includes notes for working on the `frontend-laminar` module.
262372

@@ -267,7 +377,7 @@ Ensure you have the following installed on your system:
267377
- sbt (Scala Build Tool)
268378

269379
### Building the JVM Version
270-
To build the JVM version of the project, use the command:
380+
To build the JVM version of the project, use the command:
271381

272382
```bash
273383
sbt stage
@@ -276,7 +386,7 @@ sbt stage
276386
This compiles the code and prepares the executable under the `jvm/target/universal/stage/bin/` directory.
277387

278388
### Running the JVM Version
279-
Run the application with:
389+
Run the application with:
280390
```bash
281391
cat log.txt | ./json-log-viewer/jvm/target/universal/stage/bin/json-log-viewer
282392
```
@@ -293,7 +403,7 @@ To build the JavaScript version, you can use one of the following options:
293403
```
294404
This generates a production-ready JavaScript file located at:
295405
`frontend-laminar/target/scala-3.6.2/frontend-laminar-opt/main.js`
296-
2. **Fast Development Build**: Use the command:
406+
2. **Fast Development Build**: Use the command:
297407
```bash
298408
sbt fastLinkJS
299409
```
@@ -306,4 +416,4 @@ Choose the appropriate option based on your needs:
306416

307417
## License
308418

309-
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for full details.
419+
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for full details.

frontend-laminar/src/main/scala/ru/d10xa/jsonlogviewer/ViewElement.scala

+3-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ object ViewElement {
2626

2727
def makeConfigYamlForInlineInput(string: String, config: Config): ConfigYaml =
2828
ConfigYaml(
29+
fieldNames = None,
2930
feeds = Some(
3031
List(
3132
Feed(
@@ -36,7 +37,8 @@ object ViewElement {
3637
formatIn = config.formatIn,
3738
rawInclude = None,
3839
rawExclude = None,
39-
excludeFields = None
40+
excludeFields = None,
41+
fieldNames = None
4042
)
4143
)
4244
)

json-log-viewer/jvm/src/main/scala/ru/d10xa/jsonlogviewer/decline/yaml/ConfigYamlLoaderImpl.scala

+42-1
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,42 @@ class ConfigYamlLoaderImpl extends ConfigYamlLoader {
128128
Validated.valid(None)
129129
}
130130

131+
private def parseOptionalFieldNames(
132+
fields: Map[String, Json],
133+
fieldName: String
134+
): ValidatedNel[String, Option[FieldNames]] =
135+
fields.get(fieldName) match {
136+
case Some(jsonValue) =>
137+
jsonValue.asObject.map(_.toMap) match {
138+
case None =>
139+
Validated.invalidNel(
140+
s"Invalid '$fieldName' field format, should be an object"
141+
)
142+
case Some(fieldNamesFields) =>
143+
val timestampValidated =
144+
parseOptionalString(fieldNamesFields, "timestamp")
145+
val levelValidated = parseOptionalString(fieldNamesFields, "level")
146+
val messageValidated =
147+
parseOptionalString(fieldNamesFields, "message")
148+
val stackTraceValidated =
149+
parseOptionalString(fieldNamesFields, "stackTrace")
150+
val loggerNameValidated =
151+
parseOptionalString(fieldNamesFields, "loggerName")
152+
val threadNameValidated =
153+
parseOptionalString(fieldNamesFields, "threadName")
154+
155+
(
156+
timestampValidated,
157+
levelValidated,
158+
messageValidated,
159+
stackTraceValidated,
160+
loggerNameValidated,
161+
threadNameValidated
162+
).mapN(FieldNames.apply).map(Some(_))
163+
}
164+
case None => Validated.valid(None)
165+
}
166+
131167
private def parseFeed(feedJson: Json): ValidatedNel[String, Feed] =
132168
feedJson.asObject.map(_.toMap) match {
133169
case None => Validated.invalidNel("Feed entry is not a valid JSON object")
@@ -143,6 +179,8 @@ class ConfigYamlLoaderImpl extends ConfigYamlLoader {
143179
val formatInValidated
144180
: Validated[NonEmptyList[String], Option[FormatIn]] =
145181
parseOptionalFormatIn(feedFields, "formatIn")
182+
val fieldNamesValidated =
183+
parseOptionalFieldNames(feedFields, "fieldNames")
146184
val rawIncludeValidated =
147185
parseOptionalListString(feedFields, "rawInclude")
148186
val rawExcludeValidated =
@@ -158,6 +196,7 @@ class ConfigYamlLoaderImpl extends ConfigYamlLoader {
158196
inlineInputValidated,
159197
filterValidated,
160198
formatInValidated,
199+
fieldNamesValidated,
161200
rawIncludeValidated,
162201
rawExcludeValidated,
163202
excludeFieldsValidated
@@ -182,7 +221,9 @@ class ConfigYamlLoaderImpl extends ConfigYamlLoader {
182221
case Some(fields) =>
183222
val feedsValidated: ValidatedNel[String, Option[List[Feed]]] =
184223
parseOptionalFeeds(fields, "feeds")
185-
feedsValidated.map(ConfigYaml.apply)
224+
val fieldNamesValidated =
225+
parseOptionalFieldNames(fields, "fieldNames")
226+
(fieldNamesValidated, feedsValidated).mapN(ConfigYaml.apply)
186227
}
187228
}
188229
}

0 commit comments

Comments
 (0)