├── doc
└── pjhe_icws2017.pdf
├── gradle
├── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
└── libs.versions.toml
├── .gitattributes
├── tailer
├── src
│ ├── test
│ │ ├── resources
│ │ │ └── 3-lines.txt
│ │ └── java
│ │ │ └── io
│ │ │ └── github
│ │ │ └── bric3
│ │ │ └── tailer
│ │ │ └── file
│ │ │ └── MappedFileLineReaderTest.java
│ └── main
│ │ └── java
│ │ └── io
│ │ └── github
│ │ └── bric3
│ │ └── tailer
│ │ ├── tail
│ │ └── TailFile.java
│ │ ├── config
│ │ ├── Config.java
│ │ └── FromLine.java
│ │ ├── drain
│ │ └── DrainFile.java
│ │ ├── TailerMain.java
│ │ └── file
│ │ └── MappedFileLineReader.java
└── build.gradle.kts
├── HEADER
├── drain-java-bom
└── build.gradle.kts
├── drain-java-core
├── src
│ ├── main
│ │ └── java
│ │ │ └── io
│ │ │ └── github
│ │ │ └── bric3
│ │ │ └── drain
│ │ │ ├── core
│ │ │ ├── LogCluster.java
│ │ │ ├── package-info.java
│ │ │ ├── DrainState.java
│ │ │ ├── Node.java
│ │ │ ├── InternalLogCluster.java
│ │ │ └── Drain.java
│ │ │ └── internal
│ │ │ ├── Tokenizer.java
│ │ │ └── Stopwatch.java
│ └── test
│ │ └── java
│ │ └── io
│ │ └── github
│ │ └── bric3
│ │ └── drain
│ │ ├── utils
│ │ └── TestPaths.java
│ │ └── core
│ │ └── DrainBulkTest.java
└── build.gradle.kts
├── settings.gradle.kts
├── drain-java-jackson
├── build.gradle.kts
└── src
│ ├── test
│ ├── java
│ │ └── io
│ │ │ └── github
│ │ │ └── bric3
│ │ │ └── drain
│ │ │ ├── utils
│ │ │ └── TestPaths.java
│ │ │ └── core
│ │ │ └── DrainJsonSerializationTest.java
│ └── resources
│ │ └── Unity.log
│ └── main
│ └── java
│ └── io
│ └── github
│ └── bric3
│ └── drain
│ └── core
│ └── DrainJsonSerialization.java
├── .github
├── renovate.json5
└── workflows
│ └── gradle.yml
├── gradlew.bat
├── .gitignore
├── README.adoc
├── gradlew
├── LICENSE
└── .editorconfig
/doc/pjhe_icws2017.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bric3/drain-java/HEAD/doc/pjhe_icws2017.pdf
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bric3/drain-java/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | #
2 | # https://help.github.com/articles/dealing-with-line-endings/
3 | #
4 | # These are explicitly windows files and should use crlf
5 | *.bat text eol=crlf
6 |
7 |
--------------------------------------------------------------------------------
/tailer/src/test/resources/3-lines.txt:
--------------------------------------------------------------------------------
1 | parsed :ESC[?2004l
2 | bytes read :ESC[
3 | 2021-01-05 20:48:34,044 [13366131] ERROR - terminal.emulator.JediEmulator - Error processing OSC 1;java
4 |
--------------------------------------------------------------------------------
/HEADER:
--------------------------------------------------------------------------------
1 | drain-java
2 |
3 | Copyright (c) ${year} - ${name}
4 |
5 | This Source Code Form is subject to the terms of the Mozilla Public
6 | License, v. 2.0. If a copy of the MPL was not distributed with this
7 | file, You can obtain one at https://mozilla.org/MPL/2.0/.
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionSha256Sum=72f44c9f8ebcb1af43838f45ee5c4aa9c5444898b3468ab3f4af7b6076c5bc3f
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip
5 | networkTimeout=10000
6 | validateDistributionUrl=true
7 | zipStoreBase=GRADLE_USER_HOME
8 | zipStorePath=wrapper/dists
9 |
--------------------------------------------------------------------------------
/drain-java-bom/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | `java-platform`
3 | `maven-publish`
4 | }
5 |
6 | description = "Drain Java - BOM"
7 |
8 | dependencies {
9 | constraints {
10 | rootProject.subprojects {
11 | if (name.startsWith("drain-java-") && !name.endsWith("-bom")) {
12 | api("${group}:${name}:${version}")
13 | }
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/drain-java-core/src/main/java/io/github/bric3/drain/core/LogCluster.java:
--------------------------------------------------------------------------------
1 | package io.github.bric3.drain.core;
2 |
3 | import java.util.List;
4 | import java.util.UUID;
5 |
6 | /**
7 | * Represents a cluster of log message parts.
8 | *
9 | *
A cluster consists of a list of tokens, one of the token might be the wild card {@link Drain#PARAM_MARKER <*>}.
10 | */
11 | public interface LogCluster {
12 | /**
13 | * @return the cluster identifier.
14 | */
15 | UUID clusterId();
16 |
17 | /**
18 | * @return the list of tokens.
19 | */
20 | List tokens();
21 |
22 | /**
23 | * @return the number similar log messages have been seen by this cluster.
24 | */
25 | int sightings();
26 | }
27 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * drain-java
3 | *
4 | * Copyright (c) 2021, Today - Brice Dutheil
5 | *
6 | * This Source Code Form is subject to the terms of the Mozilla Public
7 | * License, v. 2.0. If a copy of the MPL was not distributed with this
8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
9 | */
10 | plugins {
11 | id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
12 | id("com.gradle.develocity") version "4.3"
13 | }
14 |
15 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
16 |
17 | rootProject.name = "drain-java"
18 |
19 | include(
20 | "drain-java-bom",
21 | "drain-java-core",
22 | "drain-java-jackson",
23 | "tailer"
24 | )
25 |
26 | develocity {
27 | buildScan {
28 | termsOfUseUrl = "https://gradle.com/help/legal-terms-of-use"
29 | // termsOfUseAgree is handled by .gradle/init.d/configure-develocity.init.gradle.kts
30 | }
31 | }
--------------------------------------------------------------------------------
/drain-java-core/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * drain-java
3 | *
4 | * Copyright (c) 2021, Today - Brice Dutheil
5 | *
6 | * This Source Code Form is subject to the terms of the Mozilla Public
7 | * License, v. 2.0. If a copy of the MPL was not distributed with this
8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
9 | */
10 |
11 | plugins {
12 | `java-library`
13 | `maven-publish`
14 | }
15 |
16 | description = "Drain Java Implementation"
17 |
18 | dependencies {
19 | implementation(libs.jsr305)
20 |
21 | testImplementation(libs.assertj.core)
22 | testImplementation(libs.junit.jupiter.api)
23 | testRuntimeOnly(libs.junit.jupiter.engine)
24 | testRuntimeOnly(libs.junit.platform.launcher)
25 | }
26 |
27 | tasks {
28 | processTestResources {
29 | dependsOn(rootProject.tasks.getByPath("unpackFile"))
30 | }
31 |
32 | test {
33 | useJUnitPlatform()
34 | reports {
35 | junitXml.required.set(true)
36 | html.required.set(true)
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/drain-java-core/src/test/java/io/github/bric3/drain/utils/TestPaths.java:
--------------------------------------------------------------------------------
1 | package io.github.bric3.drain.utils;
2 |
3 | import java.nio.file.Files;
4 | import java.nio.file.Path;
5 | import java.nio.file.Paths;
6 |
7 | public class TestPaths {
8 | public static Path get(String first, String... more) {
9 | Path subPath = Paths.get(first, more);
10 | Path resolved = Paths.get("build/resources/test").resolve(subPath);
11 | if (Files.exists(resolved)) {
12 | return resolved;
13 | }
14 | resolved = Paths.get("..").resolve(resolved);
15 | if (Files.exists(resolved)) {
16 | return resolved;
17 | }
18 | resolved = Paths.get("build").resolve(subPath);
19 | if (Files.exists(resolved)) {
20 | return resolved;
21 | }
22 | resolved = Paths.get("..").resolve(resolved);
23 | if (Files.exists(resolved)) {
24 | return resolved;
25 | }
26 |
27 |
28 | throw new IllegalStateException("Could not find " + subPath);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/drain-java-jackson/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * drain-java
3 | *
4 | * Copyright (c) 2021, Today - Brice Dutheil
5 | *
6 | * This Source Code Form is subject to the terms of the Mozilla Public
7 | * License, v. 2.0. If a copy of the MPL was not distributed with this
8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
9 | */
10 |
11 | plugins {
12 | `java-library`
13 | `maven-publish`
14 | }
15 |
16 | description = "Drain Java Jackson Serialization"
17 |
18 | dependencies {
19 | implementation(projects.drainJavaCore)
20 | implementation(libs.jsr305)
21 | implementation(libs.bundles.jackson)
22 |
23 | testImplementation(libs.assertj.core)
24 | testImplementation(libs.junit.jupiter.api)
25 | testRuntimeOnly(libs.junit.jupiter.engine)
26 | testRuntimeOnly(libs.junit.platform.launcher)
27 | }
28 |
29 | tasks {
30 | processTestResources {
31 | dependsOn(rootProject.tasks.getByPath("unpackFile"))
32 | }
33 |
34 | test {
35 | useJUnitPlatform()
36 | reports {
37 | junitXml.required.set(true)
38 | html.required.set(true)
39 | }
40 | }
41 | }
--------------------------------------------------------------------------------
/drain-java-core/src/main/java/io/github/bric3/drain/internal/Tokenizer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * drain-java
3 | *
4 | * Copyright (c) 2021, Today - Brice Dutheil
5 | *
6 | * This Source Code Form is subject to the terms of the Mozilla Public
7 | * License, v. 2.0. If a copy of the MPL was not distributed with this
8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
9 | */
10 | package io.github.bric3.drain.internal;
11 |
12 | import java.util.ArrayList;
13 | import java.util.List;
14 | import java.util.StringTokenizer;
15 |
16 | /**
17 | * Simple string tokenizer.
18 | */
19 | public class Tokenizer {
20 | public static List tokenize(String content, String delimiters) {
21 | StringTokenizer stringTokenizer = new StringTokenizer(content, delimiters);
22 |
23 | List tokens = new ArrayList<>(stringTokenizer.countTokens());
24 | while (stringTokenizer.hasMoreTokens()) {
25 | String trimmedToken = stringTokenizer.nextToken().trim();
26 | if (!trimmedToken.isEmpty()) {
27 | tokens.add(trimmedToken);
28 | }
29 | }
30 |
31 | return tokens;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/drain-java-core/src/main/java/io/github/bric3/drain/core/package-info.java:
--------------------------------------------------------------------------------
1 | /*
2 | * drain-java
3 | *
4 | * Copyright (c) 2021, Today - Brice Dutheil
5 | *
6 | * This Source Code Form is subject to the terms of the Mozilla Public
7 | * License, v. 2.0. If a copy of the MPL was not distributed with this
8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
9 | */
10 |
11 | /**
12 | * Drain log pattern miner.
13 | *
14 | * This code comes from a modified work of the LogPai team by IBM engineers,
15 | * but it has been improved to fit the Java platform.
16 | *
17 | *
18 | * Use the builder method {@link io.github.bric3.drain.core.Drain#drainBuilder()} to configure an
19 | * instance.
20 | *
21 | *
22 | * Example use:
23 | *
24 | * var drain = Drain.drainBuilder()
25 | * .additionalDelimiters("_")
26 | * .depth(4)
27 | * .build();
28 | * Files.lines(
29 | * Paths.get("file.log"),
30 | * StandardCharsets.UTF_8
31 | * ).forEach(drain::parseLogMessage);
32 | *
33 | * // do something with clusters
34 | * drain.clusters();
35 | *
36 | */
37 | package io.github.bric3.drain.core;
--------------------------------------------------------------------------------
/drain-java-core/src/main/java/io/github/bric3/drain/core/DrainState.java:
--------------------------------------------------------------------------------
1 | /*
2 | * drain-java
3 | *
4 | * Copyright (c) 2021, Today - Brice Dutheil
5 | *
6 | * This Source Code Form is subject to the terms of the Mozilla Public
7 | * License, v. 2.0. If a copy of the MPL was not distributed with this
8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
9 | */
10 | package io.github.bric3.drain.core;
11 |
12 | import java.util.List;
13 |
14 | class DrainState {
15 | final int depth;
16 | final double similarityThreshold;
17 | final int maxChildPerNode;
18 | final String delimiters;
19 | final List clusters;
20 | final Node prefixTree;
21 |
22 | DrainState(int depth,
23 | double similarityThreshold,
24 | int maxChildPerNode,
25 | String delimiters,
26 | List clusters,
27 | Node prefixTree) {
28 | this.depth = depth;
29 | this.similarityThreshold = similarityThreshold;
30 | this.maxChildPerNode = maxChildPerNode;
31 | this.delimiters = delimiters;
32 | this.clusters = clusters;
33 | this.prefixTree = prefixTree;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/drain-java-jackson/src/test/java/io/github/bric3/drain/utils/TestPaths.java:
--------------------------------------------------------------------------------
1 | /*
2 | * drain-java
3 | *
4 | * Copyright (c) 2021, Today - Brice Dutheil
5 | *
6 | * This Source Code Form is subject to the terms of the Mozilla Public
7 | * License, v. 2.0. If a copy of the MPL was not distributed with this
8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
9 | */
10 | package io.github.bric3.drain.utils;
11 |
12 | import java.nio.file.Files;
13 | import java.nio.file.Path;
14 | import java.nio.file.Paths;
15 |
16 | public class TestPaths {
17 | public static Path get(String first, String... more) {
18 | Path subPath = Paths.get(first, more);
19 | Path resolved = Paths.get("build/resources/test").resolve(subPath);
20 | if (Files.exists(resolved)) {
21 | return resolved;
22 | }
23 | resolved = Paths.get("..").resolve(resolved);
24 | if (Files.exists(resolved)) {
25 | return resolved;
26 | }
27 | resolved = Paths.get("build").resolve(subPath);
28 | if (Files.exists(resolved)) {
29 | return resolved;
30 | }
31 | resolved = Paths.get("..").resolve(resolved);
32 | if (Files.exists(resolved)) {
33 | return resolved;
34 | }
35 |
36 |
37 | throw new IllegalStateException("Could not find " + subPath);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/.github/renovate.json5:
--------------------------------------------------------------------------------
1 | // Options doc https://docs.renovatebot.com/configuration-options/
2 | // validate with
3 | // docker run --mount type=bind,source=$(pwd)/.github/renovate.json5,target=/usr/src/app/renovate.json5,readonly -it renovate/renovate renovate-config-validator
4 | {
5 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
6 | "extends": [
7 | "config:base"
8 | ],
9 | "labels": [
10 | "dependency-update"
11 | ],
12 | // automerge minor deps
13 | "packageRules": [
14 | {
15 | "description": "Automatically merge minor and patch-level updates",
16 | "matchUpdateTypes": [
17 | // where version is: major.minor.patch
18 | "minor",
19 | "patch"
20 | ],
21 | "automerge": true,
22 | // Do not create a PR to avoid PR-related email spam, if tests succeed merge directly
23 | // otherwise make a PR if tests fail
24 | "automergeType": "branch"
25 | }
26 | ],
27 | "vulnerabilityAlerts": {
28 | "description": "Automatically merge vulnerability fixes",
29 | "labels": [
30 | "vulnerability-fix"
31 | ],
32 | "automerge": true,
33 | },
34 | "dependencyDashboard": true,
35 | "prConcurrentLimit": 10,
36 | "prHourlyLimit": 5,
37 | // Schedule the bot to run before morning
38 | "timezone": "UTC",
39 | "schedule": [
40 | "before 9am"
41 | // "before 9am on monday" // once a week before monday morning
42 | ]
43 | }
44 |
--------------------------------------------------------------------------------
/tailer/src/main/java/io/github/bric3/tailer/tail/TailFile.java:
--------------------------------------------------------------------------------
1 | /*
2 | * drain-java
3 | *
4 | * Copyright (c) 2021, Today - Brice Dutheil
5 | *
6 | * This Source Code Form is subject to the terms of the Mozilla Public
7 | * License, v. 2.0. If a copy of the MPL was not distributed with this
8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
9 | */
10 |
11 | package io.github.bric3.tailer.tail;
12 |
13 | import io.github.bric3.tailer.config.Config;
14 | import io.github.bric3.tailer.config.FromLine;
15 | import io.github.bric3.tailer.file.MappedFileLineReader;
16 |
17 | import java.nio.channels.Channels;
18 | import java.nio.channels.WritableByteChannel;
19 | import java.nio.file.Path;
20 |
21 | public class TailFile {
22 |
23 | private static final WritableByteChannel STDOUT = Channels.newChannel(System.out);
24 | // System.out = PrintStream(BufferedOutputStream(FileOutputStream))
25 | // Or if POSIX : Channels.newChannel(new FileOutputStream("/dev/stdout")) to enable the system to perform zero copy?
26 | private final Config config;
27 |
28 | public TailFile(Config config) {
29 | this.config = config;
30 | }
31 |
32 | public void tail(Path path, FromLine fromLine, boolean follow) {
33 | assert path != null;
34 | assert fromLine != null;
35 |
36 | new MappedFileLineReader(config, new MappedFileLineReader.ChannelSink(STDOUT))
37 | .tailRead(path, fromLine, follow);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/tailer/src/main/java/io/github/bric3/tailer/config/Config.java:
--------------------------------------------------------------------------------
1 | /*
2 | * drain-java
3 | *
4 | * Copyright (c) 2021, Today - Brice Dutheil
5 | *
6 | * This Source Code Form is subject to the terms of the Mozilla Public
7 | * License, v. 2.0. If a copy of the MPL was not distributed with this
8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
9 | */
10 | package io.github.bric3.tailer.config;
11 |
12 | import java.io.PrintStream;
13 | import java.nio.charset.Charset;
14 | import java.nio.charset.StandardCharsets;
15 |
16 | public class Config {
17 | public final boolean verbose;
18 |
19 | public final PrintStream out;
20 | public final PrintStream err;
21 |
22 | public final Charset charset;
23 |
24 | public final DrainConfig drain;
25 |
26 | public Config(boolean verbose) {
27 | this(verbose, "", 0);
28 | }
29 |
30 | public Config(boolean verbose, String parseAfterStr, int parseAfterCol) {
31 | this.verbose = verbose;
32 | this.drain = new DrainConfig(parseAfterStr, parseAfterCol);
33 | this.out = System.out;
34 | this.err = System.err;
35 | this.charset = StandardCharsets.UTF_8;
36 | }
37 |
38 | public static class DrainConfig {
39 | public final String parseAfterStr;
40 | public final int parseAfterCol;
41 |
42 | DrainConfig(String parseAfterStr, int parseAfterCol) {
43 | this.parseAfterStr = parseAfterStr;
44 | this.parseAfterCol = parseAfterCol;
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/drain-java-core/src/main/java/io/github/bric3/drain/internal/Stopwatch.java:
--------------------------------------------------------------------------------
1 | /*
2 | * drain-java
3 | *
4 | * Copyright (c) 2021, Today - Brice Dutheil
5 | *
6 | * This Source Code Form is subject to the terms of the Mozilla Public
7 | * License, v. 2.0. If a copy of the MPL was not distributed with this
8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
9 | */
10 | package io.github.bric3.drain.internal;
11 |
12 | /**
13 | * A simple stopwatch to measure time.
14 | */
15 | public class Stopwatch {
16 | /**
17 | * @return a new started stopwatch
18 | */
19 | public static Stopwatch createStarted() {
20 | return new Stopwatch().start();
21 | }
22 |
23 | private long startTime;
24 |
25 | /**
26 | * Create a new stopwatch.
27 | */
28 | public Stopwatch() {
29 | startTime = System.currentTimeMillis();
30 | }
31 |
32 | /**
33 | * Starts the stopwatch (records the current time as start time).
34 | *
35 | * @return this stopwatch
36 | */
37 | public Stopwatch start() {
38 | startTime = System.currentTimeMillis();
39 | return this;
40 | }
41 |
42 | /**
43 | * @return the elapsed time with the millisecond unit
44 | */
45 | @Override
46 | public String toString() {
47 | return elapsed() + " ms";
48 | }
49 |
50 | /**
51 | * @return the elapsed time in milliseconds
52 | */
53 | private long elapsed() {
54 | return System.currentTimeMillis() - startTime;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/drain-java-jackson/src/test/resources/Unity.log:
--------------------------------------------------------------------------------
1 | 2020.10.06 12:02:11: Received ServerInfo packet:{"Type":2,"PlayersOnline":18,"PlayersPlaying":12,"PublicCustomGames":2,"PlayersSearching":2,"MaintenanceIn":-1}
2 | 2020.10.06 12:02:14: Received KeepAlive packet:{"Type":-1}
3 | 2020.10.06 12:02:14: Sending Packet:{"Type":-1}
4 | 2020.10.06 12:02:16: Received ServerInfo packet:{"Type":2,"PlayersOnline":18,"PlayersPlaying":12,"PublicCustomGames":2,"PlayersSearching":2,"MaintenanceIn":-1}
5 | 2020.10.06 12:02:21: Received ServerInfo packet:{"Type":2,"PlayersOnline":18,"PlayersPlaying":12,"PublicCustomGames":2,"PlayersSearching":2,"MaintenanceIn":-1}
6 | 2020.10.06 12:02:24: Received KeepAlive packet:{"Type":-1}
7 | 2020.10.06 12:02:24: Sending Packet:{"Type":-1}
8 | 2020.10.06 12:02:26: Received ServerInfo packet:{"Type":2,"PlayersOnline":17,"PlayersPlaying":12,"PublicCustomGames":2,"PlayersSearching":2,"MaintenanceIn":-1}
9 | 2020.10.06 12:02:31: Received ServerInfo packet:{"Type":2,"PlayersOnline":17,"PlayersPlaying":12,"PublicCustomGames":2,"PlayersSearching":2,"MaintenanceIn":-1}
10 | 2020.10.06 12:02:34: Received KeepAlive packet:{"Type":-1}
11 | 2020.10.06 12:02:34: Sending Packet:{"Type":-1}
12 | 2020.10.06 12:02:36: Received ServerInfo packet:{"Type":2,"PlayersOnline":17,"PlayersPlaying":12,"PublicCustomGames":2,"PlayersSearching":2,"MaintenanceIn":-1}
13 | 2020.10.06 12:02:41: Received ServerInfo packet:{"Type":2,"PlayersOnline":17,"PlayersPlaying":12,"PublicCustomGames":2,"PlayersSearching":2,"MaintenanceIn":-1}
14 | 2020.10.06 12:02:44: Received KeepAlive packet:{"Type":-1}
15 |
--------------------------------------------------------------------------------
/tailer/src/main/java/io/github/bric3/tailer/config/FromLine.java:
--------------------------------------------------------------------------------
1 | /*
2 | * drain-java
3 | *
4 | * Copyright (c) 2021, Today - Brice Dutheil
5 | *
6 | * This Source Code Form is subject to the terms of the Mozilla Public
7 | * License, v. 2.0. If a copy of the MPL was not distributed with this
8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
9 | */
10 | package io.github.bric3.tailer.config;
11 |
12 | import picocli.CommandLine;
13 |
14 | public class FromLine {
15 | public boolean fromStart = false;
16 | public long number = 10;
17 |
18 | public static FromLine fromStart(long lineNumber) {
19 | var startFromLine = new FromLine();
20 | startFromLine.fromStart = true;
21 | startFromLine.number = lineNumber;
22 | return startFromLine;
23 | }
24 |
25 | public static FromLine fromEnd(long lineNumber) {
26 | var startFromLine = new FromLine();
27 | startFromLine.fromStart = false;
28 | startFromLine.number = lineNumber;
29 | return startFromLine;
30 | }
31 |
32 | public static class StartFromLineConverter implements CommandLine.ITypeConverter {
33 | @Override
34 | public FromLine convert(String value) {
35 | var result = new FromLine();
36 | if (value.charAt(0) == '+') {
37 | result.fromStart = true;
38 | value = value.substring(1);
39 | }
40 | result.number = Long.parseLong(value);
41 | if (result.number < 0) {
42 | throw new CommandLine.TypeConversionException(
43 | "invalid number of lines '" + value + "': must be 0 or positive number."
44 | );
45 | }
46 | return result;
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/tailer/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * drain-java
3 | *
4 | * Copyright (c) 2021, Today - Brice Dutheil
5 | *
6 | * This Source Code Form is subject to the terms of the Mozilla Public
7 | * License, v. 2.0. If a copy of the MPL was not distributed with this
8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
9 | */
10 |
11 | plugins {
12 | application
13 | alias(libs.plugins.shadow)
14 | }
15 |
16 | description = "Tail application based on drain-java"
17 |
18 | dependencies {
19 | implementation(projects.drainJavaCore)
20 | implementation(projects.drainJavaJackson)
21 | implementation(libs.jsr305)
22 | implementation(libs.picocli)
23 |
24 | annotationProcessor(libs.picocli.codegen)
25 |
26 | testImplementation(libs.assertj.core)
27 | testImplementation(libs.junit.jupiter.api)
28 | testRuntimeOnly(libs.junit.jupiter.engine)
29 | testRuntimeOnly(libs.junit.platform.launcher)
30 | }
31 |
32 | application {
33 | mainClass.set("io.github.bric3.tailer.TailerMain")
34 | }
35 |
36 | val JAVA_VERSION = 21
37 |
38 | java {
39 | toolchain {
40 | languageVersion.set(JavaLanguageVersion.of(JAVA_VERSION))
41 | }
42 | }
43 |
44 | tasks {
45 | compileJava {
46 | options.compilerArgs.add("-Aproject=${project.group}/${project.name}")
47 | }
48 |
49 | withType(JavaCompile::class) {
50 | options.release.set(JAVA_VERSION)
51 | }
52 |
53 | test {
54 | useJUnitPlatform()
55 | reports {
56 | junitXml.required.set(true)
57 | html.required.set(true)
58 | }
59 | }
60 |
61 | jar {
62 | manifest {
63 | attributes(
64 | mapOf(
65 | "Main-Class" to application.mainClass.get()
66 | )
67 | )
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/gradle/libs.versions.toml:
--------------------------------------------------------------------------------
1 | #
2 | # drain-java
3 | #
4 | # Copyright (c) 2021, Today - Brice Dutheil
5 | #
6 | # This Source Code Form is subject to the terms of the Mozilla Public
7 | # License, v. 2.0. If a copy of the MPL was not distributed with this
8 | # file, You can obtain one at https://mozilla.org/MPL/2.0/.
9 | #
10 |
11 | [versions]
12 | jackson = "2.20.1"
13 | jackson-annotations = "2.20"
14 | picocli = "4.7.7"
15 | guava = "30.1.1-jre"
16 |
17 | assertj = "3.27.6"
18 | junit = "5.14.1"
19 | junit-launcher = "1.14.1"
20 |
21 | [libraries]
22 |
23 | jsr305 = { module = "com.google.code.findbugs:jsr305", version = "3.0.2" }
24 |
25 | picocli = { module = "info.picocli:picocli", version.ref = "picocli" }
26 | picocli-codegen = { module = "info.picocli:picocli-codegen", version.ref = "picocli" }
27 |
28 | jackson-core = { module = "com.fasterxml.jackson.core:jackson-core", version.ref = "jackson" }
29 | jackson-annotations = { module = "com.fasterxml.jackson.core:jackson-annotations", version.ref = "jackson-annotations" }
30 | jackson-databind = { module = "com.fasterxml.jackson.core:jackson-databind", version = "2.20.1" }
31 |
32 | assertj-core = { module = "org.assertj:assertj-core", version.ref = "assertj" }
33 |
34 | junit-jupiter-api = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junit" }
35 | junit-jupiter-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit" }
36 | junit-platform-launcher = { module = "org.junit.platform:junit-platform-launcher", version.ref = "junit-launcher" }
37 |
38 | [bundles]
39 | jackson = ["jackson-core", "jackson-annotations", "jackson-databind"]
40 |
41 | [plugins]
42 | download = { id = "de.undercouch.download", version = "5.6.0" }
43 | shadow = { id = "com.gradleup.shadow", version = "9.3.0" }
44 | versions = { id = "com.github.ben-manes.versions", version = "0.53.0" }
45 | license = { id = "com.github.hierynomus.license", version = "0.16.1" }
46 | gradle-extensions = { id = "com.github.vlsi.gradle-extensions", version = "3.0.1" }
47 | nebula-release = { id = "nebula.release", version = "21.0.0" }
--------------------------------------------------------------------------------
/drain-java-core/src/main/java/io/github/bric3/drain/core/Node.java:
--------------------------------------------------------------------------------
1 | /*
2 | * drain-java
3 | *
4 | * Copyright (c) 2021, Today - Brice Dutheil
5 | *
6 | * This Source Code Form is subject to the terms of the Mozilla Public
7 | * License, v. 2.0. If a copy of the MPL was not distributed with this
8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
9 | */
10 | package io.github.bric3.drain.core;
11 |
12 | import java.util.*;
13 |
14 | class Node {
15 | final int depth;
16 | final Object key;
17 | private final HashMap keyToChildNode;
18 | private final List clusters;
19 |
20 | public Node(Object key, int depth) {
21 | this.key = key;
22 | this.depth = depth;
23 | this.keyToChildNode = new HashMap<>();
24 | this.clusters = new ArrayList<>();
25 | }
26 |
27 | Node(Object key, int depth, HashMap keyToChildNode, List clusters) {
28 | this.depth = depth;
29 | this.key = key;
30 | this.keyToChildNode = keyToChildNode;
31 | this.clusters = clusters;
32 | }
33 |
34 | public Node get(Object key) {
35 | return keyToChildNode.get(key);
36 | }
37 |
38 | public Node getOrCreateChild(Object key) {
39 | return keyToChildNode.computeIfAbsent(
40 | key,
41 | k -> new Node(k, depth + 1)
42 | );
43 | }
44 |
45 | InternalLogCluster clusterOf(int tokenCount) {
46 | return clusters.get(tokenCount);
47 | }
48 |
49 | List clusters() {
50 | return clusters;
51 | }
52 |
53 | void appendCluster(InternalLogCluster cluster) {
54 | clusters.add(cluster);
55 | }
56 |
57 | public boolean contains(String key) {
58 | return keyToChildNode.containsKey(key);
59 | }
60 |
61 | public int childrenCount() {
62 | return keyToChildNode.size();
63 | }
64 |
65 | Map childMappings() {
66 | return Collections.unmodifiableMap(new HashMap<>(keyToChildNode));
67 | }
68 |
69 | @Override
70 | public boolean equals(Object o) {
71 | if (this == o) return true;
72 | if (o == null || getClass() != o.getClass()) return false;
73 | Node node = (Node) o;
74 | return depth == node.depth && Objects.equals(key, node.key) && Objects.equals(keyToChildNode, node.keyToChildNode) && Objects.equals(clusters, node.clusters);
75 | }
76 |
77 | @Override
78 | public int hashCode() {
79 | return Objects.hash(depth, key, keyToChildNode, clusters);
80 | }
81 |
82 | @Override
83 | public String toString() {
84 | return "Node{" +
85 | "depth=" + depth +
86 | ", key=" + key +
87 | ", keyToChildNode=" + keyToChildNode +
88 | ", clusters=" + clusters +
89 | '}';
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/tailer/src/main/java/io/github/bric3/tailer/drain/DrainFile.java:
--------------------------------------------------------------------------------
1 | /*
2 | * drain-java
3 | *
4 | * Copyright (c) 2021, Today - Brice Dutheil
5 | *
6 | * This Source Code Form is subject to the terms of the Mozilla Public
7 | * License, v. 2.0. If a copy of the MPL was not distributed with this
8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
9 | */
10 | package io.github.bric3.tailer.drain;
11 |
12 | import io.github.bric3.drain.core.Drain;
13 | import io.github.bric3.drain.core.LogCluster;
14 | import io.github.bric3.drain.internal.Stopwatch;
15 | import io.github.bric3.tailer.config.Config;
16 | import io.github.bric3.tailer.config.FromLine;
17 | import io.github.bric3.tailer.file.MappedFileLineReader;
18 |
19 | import java.nio.file.Path;
20 | import java.util.Comparator;
21 | import java.util.concurrent.atomic.AtomicInteger;
22 | import java.util.function.Consumer;
23 |
24 | public class DrainFile {
25 |
26 | private final Config config;
27 |
28 | public DrainFile(Config config) {
29 | this.config = config;
30 | }
31 |
32 | public void drain(Path file, FromLine fromLine, boolean follow) {
33 | assert file != null;
34 | assert fromLine != null;
35 |
36 | var drain = Drain.drainBuilder()
37 | .additionalDelimiters("_")
38 | .depth(4)
39 | .build();
40 |
41 | var lineCounter = new AtomicInteger();
42 | var stopwatch = Stopwatch.createStarted();
43 | Consumer drainConsumer = l -> {
44 | lineCounter.incrementAndGet();
45 |
46 | String content = preProcess(l);
47 | drain.parseLogMessage(content);
48 | if (config.verbose && lineCounter.get() % 10000 == 0) {
49 | config.out.printf("%4d clusters so far%n", drain.clusters().size());
50 | }
51 | };
52 |
53 | new MappedFileLineReader(config, new MappedFileLineReader.LineConsumer(drainConsumer, config.charset))
54 | .tailRead(file, fromLine, follow);
55 |
56 | if (config.verbose) {
57 | config.out.printf("---- Done processing file. Total of %d lines, done in %s, %d clusters%n",
58 | lineCounter.get(),
59 | stopwatch,
60 | drain.clusters().size());
61 | }
62 | drain.clusters()
63 | .stream()
64 | .sorted(Comparator.comparing(LogCluster::sightings).reversed())
65 | .forEach(System.out::println);
66 |
67 | }
68 |
69 | private String preProcess(String line) {
70 | var parseAfterCol = config.drain.parseAfterCol;
71 | if (parseAfterCol > 0) {
72 | return line.substring(parseAfterCol);
73 | }
74 |
75 | var parseAfterStr = config.drain.parseAfterStr;
76 | if (!parseAfterStr.isEmpty()) {
77 | return line.substring(line.indexOf(parseAfterStr) + parseAfterStr.length());
78 | }
79 | return line;
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 | @rem SPDX-License-Identifier: Apache-2.0
17 | @rem
18 |
19 | @if "%DEBUG%"=="" @echo off
20 | @rem ##########################################################################
21 | @rem
22 | @rem Gradle startup script for Windows
23 | @rem
24 | @rem ##########################################################################
25 |
26 | @rem Set local scope for the variables with windows NT shell
27 | if "%OS%"=="Windows_NT" setlocal
28 |
29 | set DIRNAME=%~dp0
30 | if "%DIRNAME%"=="" set DIRNAME=.
31 | @rem This is normally unused
32 | set APP_BASE_NAME=%~n0
33 | set APP_HOME=%DIRNAME%
34 |
35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
37 |
38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
40 |
41 | @rem Find java.exe
42 | if defined JAVA_HOME goto findJavaFromJavaHome
43 |
44 | set JAVA_EXE=java.exe
45 | %JAVA_EXE% -version >NUL 2>&1
46 | if %ERRORLEVEL% equ 0 goto execute
47 |
48 | echo. 1>&2
49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
50 | echo. 1>&2
51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
52 | echo location of your Java installation. 1>&2
53 |
54 | goto fail
55 |
56 | :findJavaFromJavaHome
57 | set JAVA_HOME=%JAVA_HOME:"=%
58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
59 |
60 | if exist "%JAVA_EXE%" goto execute
61 |
62 | echo. 1>&2
63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
64 | echo. 1>&2
65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
66 | echo location of your Java installation. 1>&2
67 |
68 | goto fail
69 |
70 | :execute
71 | @rem Setup the command line
72 |
73 |
74 |
75 | @rem Execute Gradle
76 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
77 |
78 | :end
79 | @rem End local scope for the variables with windows NT shell
80 | if %ERRORLEVEL% equ 0 goto mainEnd
81 |
82 | :fail
83 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
84 | rem the _cmd.exe /c_ return code!
85 | set EXIT_CODE=%ERRORLEVEL%
86 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
87 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
88 | exit /b %EXIT_CODE%
89 |
90 | :mainEnd
91 | if "%OS%"=="Windows_NT" endlocal
92 |
93 | :omega
94 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.toptal.com/developers/gitignore/api/vscode,gradle,intellij,java,eclipse,vim
3 | # Edit at https://www.toptal.com/developers/gitignore?templates=vscode,gradle,intellij,java,eclipse,vim
4 |
5 | ### Eclipse ###
6 | .metadata
7 | bin/
8 | tmp/
9 | *.tmp
10 | *.bak
11 | *.swp
12 | *~.nib
13 | local.properties
14 | .settings/
15 | .loadpath
16 | .recommenders
17 |
18 | # External tool builders
19 | .externalToolBuilders/
20 |
21 | # Locally stored "Eclipse launch configurations"
22 | *.launch
23 |
24 | # PyDev specific (Python IDE for Eclipse)
25 | *.pydevproject
26 |
27 | # CDT-specific (C/C++ Development Tooling)
28 | .cproject
29 |
30 | # CDT- autotools
31 | .autotools
32 |
33 | # Java annotation processor (APT)
34 | .factorypath
35 |
36 | # PDT-specific (PHP Development Tools)
37 | .buildpath
38 |
39 | # sbteclipse plugin
40 | .target
41 |
42 | # Tern plugin
43 | .tern-project
44 |
45 | # TeXlipse plugin
46 | .texlipse
47 |
48 | # STS (Spring Tool Suite)
49 | .springBeans
50 |
51 | # Code Recommenders
52 | .recommenders/
53 |
54 | # Annotation Processing
55 | .apt_generated/
56 | .apt_generated_test/
57 |
58 | # Scala IDE specific (Scala & Java development for Eclipse)
59 | .cache-main
60 | .scala_dependencies
61 | .worksheet
62 |
63 | # Uncomment this line if you wish to ignore the project description file.
64 | # Typically, this file would be tracked if it contains build/dependency configurations:
65 | #.project
66 |
67 | ### Eclipse Patch ###
68 | # Spring Boot Tooling
69 | .sts4-cache/
70 |
71 | ### Intellij ###
72 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
73 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
74 |
75 | # User-specific stuff
76 | .idea
77 |
78 | *.iml
79 | *.ipr
80 |
81 | # CMake
82 | cmake-build-*/
83 |
84 | # File-based project format
85 | *.iws
86 |
87 | # IntelliJ
88 | out/
89 |
90 | # Crashlytics plugin (for Android Studio and IntelliJ)
91 | com_crashlytics_export_strings.xml
92 | crashlytics.properties
93 | crashlytics-build.properties
94 | fabric.properties
95 |
96 |
97 | ### Java ###
98 | # Compiled class file
99 | *.class
100 |
101 | # Log file
102 | *.log
103 |
104 | # BlueJ files
105 | *.ctxt
106 |
107 | # Mobile Tools for Java (J2ME)
108 | .mtj.tmp/
109 |
110 | # Package Files #
111 | *.jar
112 | *.war
113 | *.nar
114 | *.ear
115 | *.zip
116 | *.tar.gz
117 | *.rar
118 |
119 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
120 | hs_err_pid*
121 |
122 | ### Vim ###
123 | # Swap
124 | [._]*.s[a-v][a-z]
125 | !*.svg # comment out if you don't need vector files
126 | [._]*.sw[a-p]
127 | [._]s[a-rt-v][a-z]
128 | [._]ss[a-gi-z]
129 | [._]sw[a-p]
130 |
131 | # Session
132 | Session.vim
133 | Sessionx.vim
134 |
135 | # Temporary
136 | .netrwhist
137 | *~
138 | # Auto-generated tag files
139 | tags
140 | # Persistent undo
141 | [._]*.un~
142 |
143 | ### vscode ###
144 | .vscode/*
145 | !.vscode/settings.json
146 | !.vscode/tasks.json
147 | !.vscode/launch.json
148 | !.vscode/extensions.json
149 | *.code-workspace
150 |
151 | ### Gradle ###
152 | .gradle
153 | build/
154 |
155 | # Ignore Gradle GUI config
156 | gradle-app.setting
157 |
158 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
159 | !gradle-wrapper.jar
160 |
161 | # Cache of project
162 | .gradletasknamecache
163 |
164 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
165 | # gradle/wrapper/gradle-wrapper.properties
166 |
167 | ### Gradle Patch ###
168 | **/build/
169 |
170 | # End of https://www.toptal.com/developers/gitignore/api/vscode,gradle,intellij,java,eclipse,vim
171 |
172 |
--------------------------------------------------------------------------------
/tailer/src/main/java/io/github/bric3/tailer/TailerMain.java:
--------------------------------------------------------------------------------
1 | /*
2 | * drain-java
3 | *
4 | * Copyright (c) 2021, Today - Brice Dutheil
5 | *
6 | * This Source Code Form is subject to the terms of the Mozilla Public
7 | * License, v. 2.0. If a copy of the MPL was not distributed with this
8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
9 | */
10 | package io.github.bric3.tailer;
11 |
12 | import io.github.bric3.tailer.config.Config;
13 | import io.github.bric3.tailer.config.FromLine;
14 | import io.github.bric3.tailer.config.FromLine.StartFromLineConverter;
15 | import io.github.bric3.tailer.drain.DrainFile;
16 | import io.github.bric3.tailer.tail.TailFile;
17 | import picocli.CommandLine;
18 | import picocli.CommandLine.Command;
19 | import picocli.CommandLine.Option;
20 | import picocli.CommandLine.Parameters;
21 |
22 | import java.nio.file.Files;
23 | import java.nio.file.Path;
24 |
25 |
26 | @Command(
27 | name = "tail",
28 | header = {"", "@|red tail - drain|@"},
29 | description = "...",
30 | mixinStandardHelpOptions = true,
31 | version = {
32 | "Versioned Command 1.0",
33 | "Picocli " + picocli.CommandLine.VERSION,
34 | "JVM: ${java.version} (${java.vendor} ${java.vm.name} ${java.vm.version})",
35 | "OS: ${os.name} ${os.version} ${os.arch}"}
36 | )
37 | public class TailerMain implements Runnable {
38 | public static final int ERR_NO_FILEPATH = 1;
39 | public static final int ERR_IO_TAILING_FILE = 2;
40 | public static final int ERR_IO_WATCHING_FILE = 3;
41 |
42 | public static void main(String[] args) {
43 | System.exit(new CommandLine(new TailerMain()).execute(args));
44 | }
45 |
46 | @Parameters(description = "log file",
47 | paramLabel = "FILE")
48 | Path file;
49 |
50 | @Option(names = {"-d", "--drain"},
51 | description = "use DRAIN to extract log patterns")
52 | boolean drain;
53 |
54 | @Option(names = {"--parse-after-str"},
55 | description = "when using DRAIN remove the left part of a log line up" +
56 | " to after the FIXED_STRING_SEPARATOR",
57 | paramLabel = "FIXED_STRING_SEPARATOR")
58 | String parseAfterStr = "";
59 |
60 | @Option(names = {"--parser-after-col"},
61 | description = "when using DRAIN remove the left part of a log line up to COLUMN",
62 | paramLabel = "COLUMN")
63 | int parseAfterCol = 0;
64 |
65 | @Option(names = {"-f", "--follow"},
66 | description = "output appended data as the file grows")
67 | boolean follow;
68 |
69 | @Option(names = {"-n", "--lines"},
70 | description = "output the last NUM lines, instead of the last 10;" +
71 | " or use -n 0 to output starting from beginning",
72 | converter = StartFromLineConverter.class,
73 | paramLabel = "[+]NUM",
74 | defaultValue = "10")
75 | FromLine fromLine;
76 |
77 | @Option(names = {"--verbose"},
78 | description = "Verbose output, mostly for DRAIN or errors")
79 | boolean verbose;
80 |
81 | @Option(names = {"-e"},
82 | description = "Experimental code",
83 | hidden = true)
84 | boolean experimental;
85 |
86 | @Override
87 | public void run() {
88 | if (!Files.isRegularFile(file)) {
89 | System.err.println("Expects a file path to tail!");
90 | System.exit(ERR_NO_FILEPATH);
91 | }
92 |
93 | var config = new Config(verbose, parseAfterStr, parseAfterCol);
94 |
95 | if (drain) {
96 | new DrainFile(config).drain(file, fromLine, follow);
97 | } else {
98 | new TailFile(config).tail(file, fromLine, follow);
99 | }
100 |
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/drain-java-core/src/main/java/io/github/bric3/drain/core/InternalLogCluster.java:
--------------------------------------------------------------------------------
1 | /*
2 | * drain-java
3 | *
4 | * Copyright (c) 2021, Today - Brice Dutheil
5 | *
6 | * This Source Code Form is subject to the terms of the Mozilla Public
7 | * License, v. 2.0. If a copy of the MPL was not distributed with this
8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
9 | */
10 | package io.github.bric3.drain.core;
11 |
12 |
13 | import javax.annotation.Nonnull;
14 | import java.util.*;
15 |
16 | /**
17 | * Log cluster.
18 | *
19 | * It represents a tokenized logs where similar tokens are
20 | * replaced by the marker {@link Drain#PARAM_MARKER}.
21 | *
22 | * @author brice.dutheil@gmail.com
23 | * @modifiedBy david.ohana@ibm.com, moshikh@il.ibm.com
24 | * @originalAuthor LogPAI team
25 | * @license MIT
26 | */
27 | class InternalLogCluster implements LogCluster {
28 | private final UUID clusterId;
29 | private int sightings = 1;
30 | private List logTemplateTokens;
31 |
32 | InternalLogCluster(@Nonnull List logTemplateTokens) {
33 | this.clusterId = UUID.randomUUID();
34 | this.logTemplateTokens = logTemplateTokens;
35 | }
36 |
37 | // for deserialization
38 | private InternalLogCluster(UUID clusterId,
39 | int sightings,
40 | List logTemplateTokens) {
41 | this.clusterId = clusterId;
42 | this.sightings = sightings;
43 | this.logTemplateTokens = logTemplateTokens;
44 | }
45 |
46 | /**
47 | * The cluster identifier
48 | *
49 | * @return cluster identifier.
50 | */
51 | @Override
52 | public UUID clusterId() {
53 | return clusterId;
54 | }
55 |
56 | List internalTokens() {
57 | return logTemplateTokens;
58 | }
59 |
60 | /**
61 | * List of the tokens for this LogCLuster
62 | *
63 | * @return Tokens of this cluster
64 | */
65 | @Override
66 | public List tokens() {
67 | return Collections.unmodifiableList(logTemplateTokens);
68 | }
69 |
70 | void updateTokens(List newTemplateTokens) {
71 | logTemplateTokens = newTemplateTokens;
72 | }
73 |
74 | void newSighting(List contentTokens) {
75 | List newTemplateTokens = updateTemplate(contentTokens, logTemplateTokens);
76 | if (!newTemplateTokens.equals(logTemplateTokens)) {
77 | updateTokens(newTemplateTokens);
78 | }
79 |
80 | sightings++;
81 | }
82 |
83 | @Nonnull
84 | List updateTemplate(@Nonnull List contentTokens,
85 | @Nonnull List templateTokens) {
86 | assert contentTokens.size() == templateTokens.size();
87 | List newTemplate = new ArrayList(contentTokens.size());
88 |
89 | for (int i = 0, tokensSize = contentTokens.size(); i < tokensSize; i++) {
90 | String contentToken = contentTokens.get(i);
91 | String templateToken = templateTokens.get(i);
92 | // TODO change to replace value at index
93 | if (contentToken.equals(templateToken)) {
94 | newTemplate.add(contentToken);
95 | } else {
96 | newTemplate.add(Drain.PARAM_MARKER); // replace contentToken by a marker
97 | }
98 |
99 | }
100 |
101 | return newTemplate;
102 | }
103 |
104 | /**
105 | * The number of times a log with this pattern has been seen.
106 | *
107 | * @return sightings of similar logs.
108 | */
109 | @Override
110 | public int sightings() {
111 | return sightings;
112 | }
113 |
114 | @Override
115 | public String toString() {
116 | return String.format("%s (size %d): %s",
117 | clusterId,
118 | sightings,
119 | String.join(" ", logTemplateTokens));
120 | }
121 |
122 | @Override
123 | public boolean equals(Object o) {
124 | if (this == o) return true;
125 | if (o == null || getClass() != o.getClass()) return false;
126 | InternalLogCluster that = (InternalLogCluster) o;
127 | return sightings == that.sightings && clusterId.equals(that.clusterId) && logTemplateTokens.equals(that.logTemplateTokens);
128 | }
129 |
130 | @Override
131 | public int hashCode() {
132 | return Objects.hash(clusterId, sightings, logTemplateTokens);
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/.github/workflows/gradle.yml:
--------------------------------------------------------------------------------
1 | name: Master Build
2 | on:
3 | workflow_dispatch:
4 |
5 | push:
6 | branches: [ master ]
7 | tags:
8 | - "v[0-9]+.[0-9]+.[0-9]+"
9 |
10 | pull_request:
11 | branches: [ master ]
12 |
13 | concurrency:
14 | group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }}
15 | cancel-in-progress: true
16 |
17 | jobs:
18 | gradleValidation:
19 | name: Gradle Wrapper
20 | runs-on: ubuntu-latest
21 | steps:
22 | - name: Fetch Sources
23 | uses: actions/checkout@v6
24 |
25 | - name: Gradle Wrapper Validation
26 | uses: gradle/actions/wrapper-validation@v5
27 | with:
28 | min-wrapper-count: 0
29 |
30 | build:
31 | name: Build ${{ matrix.tag }}
32 | runs-on: ${{ matrix.runner }}
33 | continue-on-error: ${{ matrix.ignore-errors }}
34 | strategy:
35 | matrix:
36 | os: ['linux']
37 | arch: ['amd64']
38 | runner: ['ubuntu-latest']
39 | tag: ['linux-amd64']
40 | ignore-errors: [false]
41 | include:
42 | - os: macos
43 | runner: macos-latest
44 | arch: amd64
45 | tag: darwin-amd64
46 | ignore-errors: true
47 | - os: macos
48 | runner: macos-14
49 | arch: aarch64
50 | tag: darwin-aarch64
51 | ignore-errors: true
52 | - os: windows
53 | runner: windows-latest
54 | arch: amd64
55 | tag: windows-amd64
56 | ignore-errors: true
57 |
58 | steps:
59 | - uses: actions/checkout@v6
60 | - name: Set up Git
61 | run: |
62 | git config --global user.email "$GITHUB_ACTOR@users.noreply.github.com"
63 | git config --global user.name "$GITHUB_ACTOR"
64 | - name: Setup JVM
65 | uses: actions/setup-java@v5
66 | with:
67 | distribution : zulu
68 | java-version: |
69 | 21
70 | 17
71 | - name : Setup Gradle
72 | uses : gradle/actions/setup-gradle@v5
73 | - run: ./gradlew build
74 | # - name: list build directory
75 | # if: ${{ always() }}
76 | # run: |
77 | # ls build
78 |
79 | - name: Upload Test Report
80 | uses: actions/upload-artifact@v6
81 | if: always() # always run even if the previous step fails
82 | with:
83 | name: junit-test-results-${{ matrix.tag }}
84 | path: '**/build/test-results/test/TEST-*.xml'
85 | retention-days: 1
86 |
87 | # This job will update the PR with the JUnit report
88 | # In order to be able to make the most of it this job in particular has
89 | # augmented permissions.
90 | junit-report:
91 | name: JUnit Report ${{ matrix.tag }}
92 | runs-on: ubuntu-latest
93 | strategy:
94 | matrix:
95 | tag: ['linux-amd64', 'darwin-amd64', 'darwin-aarch64', 'windows-amd64']
96 | if: |
97 | success() || failure()
98 | needs: [ build ]
99 | permissions:
100 | checks: write # for mikepenz/action-junit-report
101 |
102 | steps:
103 | - name: Download Test Report
104 | uses: actions/download-artifact@v7
105 | with:
106 | name: junit-test-results-${{ matrix.tag }}
107 | - name: Publish Test Report
108 | uses: mikepenz/action-junit-report@v6
109 | with:
110 | check_name: Test Report - ${{ matrix.tag }}
111 | commit: ${{github.event.workflow_run.head_sha}}
112 | report_paths: '**/build/test-results/test/TEST-*.xml'
113 |
114 | publish:
115 | name: Publish snapshots
116 | runs-on: ubuntu-latest
117 | needs: build
118 | if: success() && github.event_name != 'pull_request' && github.ref == 'refs/heads/master'
119 | steps:
120 | - uses: actions/checkout@v6
121 | with:
122 | fetch-depth: 0
123 | - name: Setup JVM
124 | uses: actions/setup-java@v5
125 | with:
126 | distribution : zulu
127 | java-version: |
128 | 21
129 | 17
130 | - name : Setup Gradle
131 | uses : gradle/actions/setup-gradle@v5
132 | - run: ./gradlew snapshot -Ppublish.central=true
133 | env:
134 | ORG_GRADLE_PROJECT_signingKey: ${{ secrets.ORG_GRADLE_PROJECT_SIGNINGKEY }}
135 | ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.ORG_GRADLE_PROJECT_SIGNINGPASSWORD }}
136 | ORG_GRADLE_PROJECT_ossrhUsername: ${{ secrets.ORG_GRADLE_PROJECT_OSSRHUSERNAME }}
137 | ORG_GRADLE_PROJECT_ossrhPassword: ${{ secrets.ORG_GRADLE_PROJECT_OSSRHPASSWORD }}
138 |
--------------------------------------------------------------------------------
/drain-java-jackson/src/test/java/io/github/bric3/drain/core/DrainJsonSerializationTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * drain-java
3 | *
4 | * Copyright (c) 2021, Today - Brice Dutheil
5 | *
6 | * This Source Code Form is subject to the terms of the Mozilla Public
7 | * License, v. 2.0. If a copy of the MPL was not distributed with this
8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
9 | */
10 | package io.github.bric3.drain.core;
11 |
12 | import io.github.bric3.drain.utils.TestPaths;
13 | import org.junit.jupiter.api.Test;
14 |
15 | import java.io.IOException;
16 | import java.io.StringReader;
17 | import java.io.StringWriter;
18 | import java.nio.file.Files;
19 | import java.util.function.Function;
20 | import java.util.stream.Stream;
21 |
22 | import static java.nio.charset.StandardCharsets.UTF_8;
23 | import static org.assertj.core.api.Assertions.assertThat;
24 |
25 | class DrainJsonSerializationTest {
26 |
27 | @Test
28 | void serde_should_result_in_same_state() throws IOException {
29 | Drain drain = initDrain("SSH.log", l -> l.substring(l.indexOf("]: ") + 3));
30 | assertThat(drain.clusters()).hasSize(51);
31 |
32 | final Drain drainReloaded = serde(drain);
33 |
34 | assertThat(drainReloaded.depth).isEqualTo(drain.depth);
35 | assertThat(drainReloaded.similarityThreshold).isEqualTo(drain.similarityThreshold);
36 | assertThat(drainReloaded.delimiters).isEqualTo(drain.delimiters);
37 | assertThat(drainReloaded.maxChildPerNode).isEqualTo(drain.maxChildPerNode);
38 | assertThat(drainReloaded.clusters()).isEqualTo(drain.clusters());
39 | assertThat(drainReloaded.prefixTree()).isEqualTo(drain.prefixTree());
40 | }
41 |
42 | @Test
43 | void serde2_should_result_in_same_state() {
44 | Drain drain = Drain.drainBuilder()
45 | .additionalDelimiters("_")
46 | .depth(4)
47 | .build();
48 |
49 | Stream.of("sent 550 bytes",
50 | "sent 110 bytes",
51 | "sent 800 bytes",
52 | "received 1000 bytes",
53 | "received 250 bytes",
54 | "sent 200 bytes"
55 | ).forEach(drain::parseLogMessage);
56 | assertThat(drain.clusters()).hasSize(2);
57 |
58 |
59 | final Drain drainReloaded = serde(drain);
60 |
61 | assertThat(drainReloaded.depth).isEqualTo(drain.depth);
62 | assertThat(drainReloaded.similarityThreshold).isEqualTo(drain.similarityThreshold);
63 | assertThat(drainReloaded.delimiters).isEqualTo(drain.delimiters);
64 | assertThat(drainReloaded.maxChildPerNode).isEqualTo(drain.maxChildPerNode);
65 | assertThat(drainReloaded.clusters()).isEqualTo(drain.clusters());
66 | assertThat(drainReloaded.prefixTree()).isEqualTo(drain.prefixTree());
67 | }
68 |
69 | @Test
70 | void drain_with_reloaded_state_can_resume_log_mining() throws IOException {
71 | Drain drain = initDrain("Unity.log", l -> l.substring(l.indexOf(": ") + 2));
72 |
73 | Drain drainReloaded = serde(drain);
74 | assertThat(drainReloaded.clusters()).isEqualTo(drain.clusters());
75 |
76 | // Adding log with same patterns should not create new clusters
77 | Stream.of(
78 | "Sending Packet:{\"Type\":-1}",
79 | "Received ServerInfo packet:{\"Type\":2,\"PlayersOnline\":17,\"PlayersPlaying\":12,\"PublicCustomGames\":2,\"PlayersSearching\":2,\"MaintenanceIn\":-1}",
80 | "Received ServerInfo packet:{\"Type\":2,\"PlayersOnline\":17,\"PlayersPlaying\":12,\"PublicCustomGames\":2,\"PlayersSearching\":2,\"MaintenanceIn\":-1}",
81 | "Received KeepAlive packet:{\"Type\":-1}"
82 | ).forEach(drainReloaded::parseLogMessage);
83 | assertThat(drainReloaded.clusters()).hasSize(drain.clusters().size());
84 |
85 | // Adding a log with a different pattern should create a new cluster
86 | drainReloaded.parseLogMessage("Resolution changed: 2590x1600 windowed, Metal RecreateSurface[0x7ff491c541a0]: surface size 2588x1600");
87 | assertThat(drainReloaded.clusters()).hasSize(drain.clusters().size() + 1);
88 | }
89 |
90 | private Drain initDrain(String logFile, Function normalizingFunction) throws IOException {
91 | Drain drain = Drain.drainBuilder()
92 | .additionalDelimiters("_")
93 | .depth(4)
94 | .build();
95 |
96 | try (Stream lines = Files.lines(TestPaths.get(logFile), UTF_8)) {
97 | lines.map(normalizingFunction)
98 | .forEach(drain::parseLogMessage);
99 | }
100 | return drain;
101 | }
102 |
103 | private Drain serde(Drain drain) {
104 | final DrainJsonSerialization serde = new DrainJsonSerialization();
105 | final StringWriter writer = new StringWriter();
106 | serde.saveState(drain, writer);
107 |
108 | return serde.loadState(new StringReader(writer.toString()));
109 | }
110 | }
--------------------------------------------------------------------------------
/tailer/src/test/java/io/github/bric3/tailer/file/MappedFileLineReaderTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * drain-java
3 | *
4 | * Copyright (c) 2021, Today - Brice Dutheil
5 | *
6 | * This Source Code Form is subject to the terms of the Mozilla Public
7 | * License, v. 2.0. If a copy of the MPL was not distributed with this
8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
9 | */
10 | package io.github.bric3.tailer.file;
11 |
12 | import io.github.bric3.tailer.config.Config;
13 | import io.github.bric3.tailer.config.FromLine;
14 | import org.junit.jupiter.api.AfterEach;
15 | import org.junit.jupiter.api.Test;
16 | import org.junit.jupiter.api.io.TempDir;
17 |
18 | import java.io.ByteArrayOutputStream;
19 | import java.io.IOException;
20 | import java.io.UncheckedIOException;
21 | import java.nio.ByteBuffer;
22 | import java.nio.CharBuffer;
23 | import java.nio.channels.Channels;
24 | import java.nio.channels.FileChannel;
25 | import java.nio.channels.WritableByteChannel;
26 | import java.nio.file.Files;
27 | import java.nio.file.Path;
28 | import java.nio.file.Paths;
29 | import java.nio.file.StandardOpenOption;
30 | import java.util.Random;
31 | import java.util.concurrent.Executors;
32 | import java.util.concurrent.ScheduledExecutorService;
33 |
34 | import static java.nio.charset.StandardCharsets.UTF_8;
35 | import static java.nio.file.StandardOpenOption.READ;
36 | import static java.util.concurrent.TimeUnit.MILLISECONDS;
37 | import static java.util.concurrent.TimeUnit.SECONDS;
38 | import static org.assertj.core.api.Assertions.assertThat;
39 | import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
40 |
41 | class MappedFileLineReaderTest {
42 | private final Path resourceDirectory = Paths.get("src", "test", "resources");
43 |
44 | private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
45 |
46 | @Test
47 | void should_watch_with_line_reader(@TempDir Path tmpDir) throws IOException {
48 | var path = Files.createTempFile(tmpDir, "test", "log");
49 |
50 | var lineAppender = new LineAppender(path);
51 | var config = new Config(true);
52 |
53 | try (var r = new MappedFileLineReader(config, new MappedFileLineReader.LineConsumer(line -> {}, UTF_8))) {
54 | var future = scheduler.scheduleAtFixedRate(lineAppender, 0, 400, MILLISECONDS);
55 | scheduler.schedule(() -> future.cancel(false), 4, SECONDS);
56 | scheduler.schedule(r::close, 10, SECONDS);
57 |
58 | r.tailRead(path, FromLine.fromStart(0), true);
59 |
60 | assertThat(r.totalReadBytes()).isEqualTo(lineAppender.writtenBytes);
61 | }
62 | }
63 |
64 | @Test
65 | void should_watch_with_channel_sink(@TempDir Path tmpDir) throws IOException {
66 | var path = Files.createTempFile(tmpDir, "test", "log");
67 | var lineAppender = new LineAppender(path);
68 | var config = new Config(true);
69 |
70 | var out = new ByteArrayOutputStream();
71 | try (var r = new MappedFileLineReader(config, new MappedFileLineReader.ChannelSink(Channels.newChannel(out)))) {
72 | var future = scheduler.scheduleAtFixedRate(lineAppender, 0, 400, MILLISECONDS);
73 | scheduler.schedule(() -> future.cancel(false), 4, SECONDS);
74 | scheduler.schedule(r::close, 10, SECONDS);
75 |
76 | r.tailRead(path, FromLine.fromStart(0), true);
77 |
78 | assertThat(r.totalReadBytes()).isEqualTo(lineAppender.writtenBytes);
79 | assertThat(path.toFile()).hasBinaryContent(out.toByteArray());
80 | }
81 | }
82 |
83 | @AfterEach
84 | void tearDown() {
85 | scheduler.shutdown();
86 | }
87 |
88 | @Test
89 | void find_start_position_given_last_lines() throws IOException {
90 | try (var channel = FileChannel.open(resourceDirectory.resolve("3-lines.txt"), READ)) {
91 | var r = new MappedFileLineReader(new Config(true), MappedFileLineReader.IOReadAction.NO_OP);
92 |
93 | assertThat(r.findTailStartPosition(channel, FromLine.fromEnd(10))).isEqualTo(0);
94 | assertThat(r.findTailStartPosition(channel, FromLine.fromEnd(2))).isEqualTo(42);
95 | assertThat(r.findTailStartPosition(channel, FromLine.fromEnd(0))).isEqualTo(183);
96 | assertThat(r.findTailStartPosition(channel, FromLine.fromStart(0))).isEqualTo(0);
97 | assertThat(r.findTailStartPosition(channel, FromLine.fromStart(2))).isEqualTo(181);
98 | assertThat(r.findTailStartPosition(channel, FromLine.fromStart(10))).isEqualTo(183);
99 | }
100 | }
101 |
102 | @Test
103 | void can_read_from_position() throws IOException {
104 | try (var channel = FileChannel.open(resourceDirectory.resolve("3-lines.txt"), READ)) {
105 | var sink = new MappedFileLineReader.ChannelSink(TestSink.nullSink());
106 | assertThat(sink.apply(channel, 0)).isEqualTo(183);
107 | assertThat(sink.apply(channel, 41)).isEqualTo(142);
108 | }
109 | }
110 |
111 | @Test
112 | void cannot_read_from_negative_position() throws IOException {
113 | try (var channel = FileChannel.open(resourceDirectory.resolve("3-lines.txt"), READ)) {
114 | var sink = new MappedFileLineReader.ChannelSink(TestSink.nullSink());
115 | assertThatExceptionOfType(AssertionError.class).isThrownBy(
116 | () -> sink.apply(channel, -1)
117 | );
118 | }
119 | }
120 |
121 | static class LineAppender implements Runnable {
122 | Path path;
123 | int lineCounter = 0;
124 | private int writtenBytes = 0;
125 |
126 | public LineAppender(Path path) {
127 | this.path = path;
128 | }
129 |
130 | @Override
131 | public void run() {
132 | var howManyLines = new Random().nextInt(10);
133 |
134 | var sb = new StringBuilder();
135 | for (int i = 0; i <= howManyLines; i++) {
136 | sb.append("line ").append(lineCounter++).append("\n");
137 | }
138 |
139 |
140 | try {
141 | var encoded = UTF_8.encode(CharBuffer.wrap(sb));
142 | writtenBytes += encoded.capacity();
143 | Files.write(path, encoded.array(), StandardOpenOption.APPEND);
144 | } catch (IOException e) {
145 | throw new UncheckedIOException(e);
146 | }
147 | }
148 | }
149 |
150 | private static class TestSink implements WritableByteChannel {
151 |
152 | int writenBytes = 0;
153 |
154 | static TestSink nullSink() {
155 | return new TestSink();
156 | }
157 |
158 | @Override
159 | public boolean isOpen() {
160 | return true;
161 | }
162 |
163 | @Override
164 | public void close() {
165 |
166 | }
167 |
168 | @Override
169 | public int write(ByteBuffer src) {
170 | var remaining = src.remaining();
171 | writenBytes += remaining;
172 | return remaining;
173 | }
174 | }
175 |
176 | }
--------------------------------------------------------------------------------
/README.adoc:
--------------------------------------------------------------------------------
1 | = drain java
2 |
3 | image:https://github.com/bric3/drain-java/actions/workflows/gradle.yml/badge.svg[Java CI with Gradle,link=https://github.com/bric3/drain-java/actions/workflows/gradle.yml]
4 |
5 | == Introduction
6 |
7 | drain-java is a continuous _log template miner_, for each log message it extracts
8 | tokens and group them into _clusters of tokens_. As new log messages are added,
9 | drain-java will identify similar token and update the cluster with the new template,
10 | or simply create a new token cluster. Each time a cluster is matched a counter is
11 | incremented.
12 |
13 | These clusters are stored in prefix tree, which is somewhat similar to a trie, but
14 | here the tree as a fixed depth in order to avoid long tree traversal.
15 | In avoiding deep trees this also helps to keep it balance.
16 |
17 | == Usage
18 |
19 | First, https://foojay.io/almanac/jdk-11/[Java 11] is required to run drain-java.
20 |
21 | === As a dependency
22 |
23 | You can consume drain-java as a dependency in your project `io.github.bric3.drain:drain-java-core`,
24 | currently only https://s01.oss.sonatype.org/content/repositories/snapshots/io/github/bric3/drain/[snapshots]
25 | are available by adding this repository.
26 |
27 | [source, kotlin]
28 | ----
29 | repositories {
30 | maven {
31 | url("https://oss.sonatype.org/content/repositories/snapshots/")
32 | }
33 | }
34 | ----
35 |
36 | === From command line
37 |
38 | Since this tool is not yet released the tool needs to be built locally.
39 | Also, the built jar is not yet super user-friendly. Since it's not a finished
40 | product, anything could change.
41 |
42 | .Example usage
43 | [source, shell]
44 | ----
45 | $ ./gradlew build
46 | $ java -jar tailer/build/libs/tailer-0.1.0-SNAPSHOT-all.jar -h
47 |
48 | tail - drain
49 | Usage: tail [-dfhV] [--verbose] [-n=NUM]
50 | [--parse-after-str=FIXED_STRING_SEPARATOR]
51 | [--parser-after-col=COLUMN] FILE
52 | ...
53 | FILE log file
54 | -d, --drain use DRAIN to extract log patterns
55 | -f, --follow output appended data as the file grows
56 | -h, --help Show this help message and exit.
57 | -n, --lines=NUM output the last NUM lines, instead of the last 10; or use
58 | -n 0 to output starting from beginning
59 | --parse-after-str=FIXED_STRING_SEPARATOR
60 | when using DRAIN remove the left part of a log line up to
61 | after the FIXED_STRING_SEPARATOR
62 | --parser-after-col=COLUMN
63 | when using DRAIN remove the left part of a log line up to
64 | COLUMN
65 | -V, --version Print version information and exit.
66 | --verbose Verbose output, mostly for DRAIN or errors
67 | $ java -jar tailer/build/libs/tailer-0.1.0-SNAPSHOT-all.jar --version
68 | Versioned Command 1.0
69 | Picocli 4.6.3
70 | JVM: 19 (Amazon.com Inc. OpenJDK 64-Bit Server VM 19+36-FR)
71 | OS: Mac OS X 12.6 x86_64
72 | ----
73 |
74 | By default, the tool act similarly to `tail`, and it will output the file to the stdout.
75 | The tool can _follow_ a file if the `--follow` option is passed.
76 | However, when run with the `--drain` this tool will classify log lines using DRAIN, and will
77 | output identified clusters.
78 | Note that this tool doesn't handle multiline log messages (like logs that contains a stacktrace).
79 |
80 | On the SSH log data set we can use it this way.
81 |
82 | [source, shell]
83 | ----
84 | $ java -jar build/libs/drain-java-1.0-SNAPSHOT-all.jar \
85 | -d \ <1>
86 | -n 0 \ <2>
87 | --parse-after-str "]: " <3>
88 | build/resources/test/SSH.log <4>
89 | ----
90 | <1> Identify patterns in the log
91 | <2> Starts from the beginning of the file (otherwise it starts from the last 10 lines)
92 | <3> Remove the left part of log line (`Dec 10 06:55:46 LabSZ sshd[24200]: `), ie effectively
93 | ignoring some variable elements like the time.
94 | <4> The log file
95 |
96 | .log pattern clusters and their occurences
97 | [source]
98 | --------
99 | ---- Done processing file. Total of 655147 lines, done in 1.588 s, 51 clusters <1>
100 | 0010 (size 140768): Failed password for <*> from <*> port <*> ssh2 <2>
101 | 0009 (size 140701): pam unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= <*> <*>
102 | 0007 (size 68958): Connection closed by <*> [preauth]
103 | 0008 (size 46642): Received disconnect from <*> 11: <*> <*> <*>
104 | 0014 (size 37963): PAM service(sshd) ignoring max retries; <*> > 3
105 | 0012 (size 37298): Disconnecting: Too many authentication failures for <*> [preauth]
106 | 0013 (size 37029): PAM <*> more authentication <*> logname= uid=0 euid=0 tty=ssh ruser= <*> <*>
107 | 0011 (size 36967): message repeated <*> times: [ Failed password for <*> from <*> port <*> ssh2]
108 | 0006 (size 20241): Failed <*> for invalid user <*> from <*> port <*> ssh2
109 | 0004 (size 19852): pam unix(sshd:auth): check pass; user unknown
110 | 0001 (size 18909): reverse mapping checking getaddrinfo for <*> <*> failed - POSSIBLE BREAK-IN ATTEMPT!
111 | 0002 (size 14551): Invalid user <*> from <*>
112 | 0003 (size 14551): input userauth request: invalid user <*> [preauth]
113 | 0005 (size 14356): pam unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= <*>
114 | 0018 (size 1289): PAM <*> more authentication <*> logname= uid=0 euid=0 tty=ssh ruser= <*>
115 | 0024 (size 952): fatal: Read from socket failed: Connection reset by peer [preauth]
116 | ...
117 | --------
118 | <1> 51 _types_ of logs were identified from 655147 lines in 1.588s
119 | <2> There was `140768` similar log messages with this pattern, with `3` positions
120 | where the token is identified as parameter `<*>`.
121 |
122 | On the same dataset, the java implementation performed roughly around 10 times faster.
123 | As my implementation does not yet have masking, mask configuration was removed in the
124 | Drain3 implementation.
125 |
126 | === From Java
127 |
128 | This tool is not yet intended to be used as a library, but for the curious
129 | the DRAIN algorythm can be used this way:
130 |
131 | .Minimal DRAIN example
132 | [source, java]
133 | ----
134 | var drain = Drain.drainBuilder()
135 | .additionalDelimiters("_")
136 | .depth(4)
137 | .build()
138 | Files.lines(Paths.get("build/resources/test/SSH.log"),
139 | StandardCharsets.UTF_8)
140 | .forEach(drain::parseLogMessage);
141 |
142 | // do something with clusters
143 | drain.clusters();
144 | ----
145 |
146 |
147 |
148 | == Status
149 |
150 | Pieces of puzzle are coming in no particular order, I first bootstrapped the code from a simple Java
151 | file. Then I wrote in Java an implementation of Drain. Now here's what I would like to do.
152 |
153 | .Todo
154 | - [ ] More unit tests
155 | - [x] Wire things together
156 | - [ ] More documentation
157 | - [x] Implement _tail follow_ mode (currently in drain mode the whole file is read and stops once finished)
158 | - [ ] In follow drain mode dump clusters on forced exit (e.g. for example when hitting `ctrl`+`c`)
159 | - [x] Start reading from the last x lines (like `tail -n 30`)
160 | - [ ] Implement log masking (e.g. log contain an email, or an IP address which may be considered as private data)
161 |
162 | .For later
163 | - [ ] Json message field extraction
164 | - [ ] How to handle prefixes : Dates, log level, etc. ; possibly using masking
165 | - [ ] Investigate marker with specific behavior, e.g. log level severity
166 | - [ ] Investigate log with stacktraces (likely multiline)
167 | - [ ] Improve handling of very long lines
168 | - [ ] Logback appender with micrometer counter
169 |
170 | == Motivation
171 |
172 | I was inspired by a https://sayr.us/log-pattern-recognition/logmine/[blog article from one of my colleague on LogMine],
173 | -- many thanks to him for doing the initial research and explaining concepts --, we were both impressed by the log
174 | pattern extraction of https://docs.datadoghq.com/logs/explorer/patterns/[Datadog's Log explorer], his blog post
175 | sparked my interest.
176 |
177 | After some discussion together, we saw that Drain was a bit superior to LogMine.
178 | Googling Drain in Java didn't yield any result, although I certainly didn't search exhaustively,
179 | but regardless this triggered the idea to implement this algorithm in Java.
180 |
181 | == References
182 |
183 | The Drain port is mostly a port of https://github.com/IBM/Drain3[Drain3]
184 | done by IBM folks (_David Ohana_, _Moshik Hershcovitch_). IBM's Drain3 is a fork of the
185 | https://github.com/logpai/logparser[original work] done by the LogPai team based on the paper of
186 | _Pinjia He_, _Jieming Zhu_, _Zibin Zheng_, and _Michael R. Lyu_.
187 |
188 | _I didn't follow up on other contributors of these projects, reach out if you think you have been omitted._
189 |
190 |
191 | For reference here's the linked I looked at:
192 |
193 | * https://logparser.readthedocs.io/
194 | * https://github.com/logpai/logparser
195 | * https://github.com/IBM/Drain3
196 | * https://jiemingzhu.github.io/pub/pjhe_icws2017.pdf
197 | (a copy of this publication accessible link:doc/pjhe_icws2017.pdf[there])
198 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 | #
20 |
21 | ##############################################################################
22 | #
23 | # Gradle start up script for POSIX generated by Gradle.
24 | #
25 | # Important for running:
26 | #
27 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
28 | # noncompliant, but you have some other compliant shell such as ksh or
29 | # bash, then to run this script, type that shell name before the whole
30 | # command line, like:
31 | #
32 | # ksh Gradle
33 | #
34 | # Busybox and similar reduced shells will NOT work, because this script
35 | # requires all of these POSIX shell features:
36 | # * functions;
37 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
38 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
39 | # * compound commands having a testable exit status, especially «case»;
40 | # * various built-in commands including «command», «set», and «ulimit».
41 | #
42 | # Important for patching:
43 | #
44 | # (2) This script targets any POSIX shell, so it avoids extensions provided
45 | # by Bash, Ksh, etc; in particular arrays are avoided.
46 | #
47 | # The "traditional" practice of packing multiple parameters into a
48 | # space-separated string is a well documented source of bugs and security
49 | # problems, so this is (mostly) avoided, by progressively accumulating
50 | # options in "$@", and eventually passing that to Java.
51 | #
52 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
53 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
54 | # see the in-line comments for details.
55 | #
56 | # There are tweaks for specific operating systems such as AIX, CygWin,
57 | # Darwin, MinGW, and NonStop.
58 | #
59 | # (3) This script is generated from the Groovy template
60 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
61 | # within the Gradle project.
62 | #
63 | # You can find Gradle at https://github.com/gradle/gradle/.
64 | #
65 | ##############################################################################
66 |
67 | # Attempt to set APP_HOME
68 |
69 | # Resolve links: $0 may be a link
70 | app_path=$0
71 |
72 | # Need this for daisy-chained symlinks.
73 | while
74 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
75 | [ -h "$app_path" ]
76 | do
77 | ls=$( ls -ld "$app_path" )
78 | link=${ls#*' -> '}
79 | case $link in #(
80 | /*) app_path=$link ;; #(
81 | *) app_path=$APP_HOME$link ;;
82 | esac
83 | done
84 |
85 | # This is normally unused
86 | # shellcheck disable=SC2034
87 | APP_BASE_NAME=${0##*/}
88 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
89 | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
90 |
91 | # Use the maximum available, or set MAX_FD != -1 to use that value.
92 | MAX_FD=maximum
93 |
94 | warn () {
95 | echo "$*"
96 | } >&2
97 |
98 | die () {
99 | echo
100 | echo "$*"
101 | echo
102 | exit 1
103 | } >&2
104 |
105 | # OS specific support (must be 'true' or 'false').
106 | cygwin=false
107 | msys=false
108 | darwin=false
109 | nonstop=false
110 | case "$( uname )" in #(
111 | CYGWIN* ) cygwin=true ;; #(
112 | Darwin* ) darwin=true ;; #(
113 | MSYS* | MINGW* ) msys=true ;; #(
114 | NONSTOP* ) nonstop=true ;;
115 | esac
116 |
117 |
118 |
119 | # Determine the Java command to use to start the JVM.
120 | if [ -n "$JAVA_HOME" ] ; then
121 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
122 | # IBM's JDK on AIX uses strange locations for the executables
123 | JAVACMD=$JAVA_HOME/jre/sh/java
124 | else
125 | JAVACMD=$JAVA_HOME/bin/java
126 | fi
127 | if [ ! -x "$JAVACMD" ] ; then
128 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
129 |
130 | Please set the JAVA_HOME variable in your environment to match the
131 | location of your Java installation."
132 | fi
133 | else
134 | JAVACMD=java
135 | if ! command -v java >/dev/null 2>&1
136 | then
137 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
138 |
139 | Please set the JAVA_HOME variable in your environment to match the
140 | location of your Java installation."
141 | fi
142 | fi
143 |
144 | # Increase the maximum file descriptors if we can.
145 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
146 | case $MAX_FD in #(
147 | max*)
148 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
149 | # shellcheck disable=SC2039,SC3045
150 | MAX_FD=$( ulimit -H -n ) ||
151 | warn "Could not query maximum file descriptor limit"
152 | esac
153 | case $MAX_FD in #(
154 | '' | soft) :;; #(
155 | *)
156 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
157 | # shellcheck disable=SC2039,SC3045
158 | ulimit -n "$MAX_FD" ||
159 | warn "Could not set maximum file descriptor limit to $MAX_FD"
160 | esac
161 | fi
162 |
163 | # Collect all arguments for the java command, stacking in reverse order:
164 | # * args from the command line
165 | # * the main class name
166 | # * -classpath
167 | # * -D...appname settings
168 | # * --module-path (only if needed)
169 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
170 |
171 | # For Cygwin or MSYS, switch paths to Windows format before running java
172 | if "$cygwin" || "$msys" ; then
173 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
174 |
175 | JAVACMD=$( cygpath --unix "$JAVACMD" )
176 |
177 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
178 | for arg do
179 | if
180 | case $arg in #(
181 | -*) false ;; # don't mess with options #(
182 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
183 | [ -e "$t" ] ;; #(
184 | *) false ;;
185 | esac
186 | then
187 | arg=$( cygpath --path --ignore --mixed "$arg" )
188 | fi
189 | # Roll the args list around exactly as many times as the number of
190 | # args, so each arg winds up back in the position where it started, but
191 | # possibly modified.
192 | #
193 | # NB: a `for` loop captures its iteration list before it begins, so
194 | # changing the positional parameters here affects neither the number of
195 | # iterations, nor the values presented in `arg`.
196 | shift # remove old arg
197 | set -- "$@" "$arg" # push replacement arg
198 | done
199 | fi
200 |
201 |
202 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
203 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
204 |
205 | # Collect all arguments for the java command:
206 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
207 | # and any embedded shellness will be escaped.
208 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
209 | # treated as '${Hostname}' itself on the command line.
210 |
211 | set -- \
212 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
213 | -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
214 | "$@"
215 |
216 | # Stop when "xargs" is not available.
217 | if ! command -v xargs >/dev/null 2>&1
218 | then
219 | die "xargs is not available"
220 | fi
221 |
222 | # Use "xargs" to parse quoted args.
223 | #
224 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
225 | #
226 | # In Bash we could simply go:
227 | #
228 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
229 | # set -- "${ARGS[@]}" "$@"
230 | #
231 | # but POSIX shell has neither arrays nor command substitution, so instead we
232 | # post-process each arg (as a line of input to sed) to backslash-escape any
233 | # character that might be a shell metacharacter, then use eval to reverse
234 | # that process (while maintaining the separation between arguments), and wrap
235 | # the whole thing up as a single "set" statement.
236 | #
237 | # This will of course break if any of these variables contains a newline or
238 | # an unmatched quote.
239 | #
240 |
241 | eval "set -- $(
242 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
243 | xargs -n1 |
244 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
245 | tr '\n' ' '
246 | )" '"$@"'
247 |
248 | exec "$JAVACMD" "$@"
249 |
--------------------------------------------------------------------------------
/tailer/src/main/java/io/github/bric3/tailer/file/MappedFileLineReader.java:
--------------------------------------------------------------------------------
1 | /*
2 | * drain-java
3 | *
4 | * Copyright (c) 2021, Today - Brice Dutheil
5 | *
6 | * This Source Code Form is subject to the terms of the Mozilla Public
7 | * License, v. 2.0. If a copy of the MPL was not distributed with this
8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
9 | */
10 | package io.github.bric3.tailer.file;
11 |
12 | import io.github.bric3.tailer.TailerMain;
13 | import io.github.bric3.tailer.config.Config;
14 | import io.github.bric3.tailer.config.FromLine;
15 |
16 | import java.io.BufferedReader;
17 | import java.io.Closeable;
18 | import java.io.IOException;
19 | import java.io.UncheckedIOException;
20 | import java.nio.channels.Channels;
21 | import java.nio.channels.FileChannel;
22 | import java.nio.channels.WritableByteChannel;
23 | import java.nio.charset.Charset;
24 | import java.nio.file.*;
25 | import java.util.Objects;
26 | import java.util.concurrent.TimeUnit;
27 | import java.util.concurrent.atomic.AtomicBoolean;
28 | import java.util.function.Consumer;
29 |
30 | public class MappedFileLineReader implements Closeable {
31 |
32 | private final IOReadAction readAction;
33 | private final Config config;
34 |
35 | private final AtomicBoolean closed;
36 | private final int wsPollTimeoutMs;
37 | private long totalReadBytes;
38 |
39 | public MappedFileLineReader(Config config, IOReadAction readAction) {
40 | this.readAction = readAction;
41 | this.config = config;
42 | this.wsPollTimeoutMs = 100;
43 | this.closed = new AtomicBoolean(false);
44 | }
45 |
46 | public void tailRead(Path path, FromLine tailFromLine, boolean follow) {
47 | assert path != null;
48 | assert tailFromLine != null;
49 |
50 |
51 | try (var ws = FileSystems.getDefault().newWatchService();
52 | var sourceChannel = FileChannel.open(path, StandardOpenOption.READ)) {
53 | var startPosition = findTailStartPosition(sourceChannel, tailFromLine);
54 | if (config.verbose) {
55 | config.out.printf("Reading file from position : %d%n", startPosition);
56 | }
57 |
58 | var position = startPosition;
59 | var readBytes = readAction.apply(sourceChannel, startPosition);
60 | totalReadBytes += readBytes;
61 | position += readBytes;
62 | if (config.verbose) {
63 | config.out.printf("Read: %d -> %d (%d bytes)%n",
64 | startPosition,
65 | position,
66 | position - startPosition);
67 | }
68 |
69 | if (follow) {
70 | path.getParent().register(ws, StandardWatchEventKinds.ENTRY_MODIFY);
71 | while (!closed.get()) {
72 | WatchKey wk;
73 | try {
74 | wk = ws.poll(wsPollTimeoutMs, TimeUnit.MILLISECONDS);
75 | } catch (InterruptedException e) {
76 | if (config.verbose) {
77 | e.printStackTrace(config.err);
78 | }
79 | Thread.currentThread().interrupt();
80 | return;
81 | }
82 | if (wk == null) {
83 | continue;
84 | }
85 |
86 | for (WatchEvent> event : wk.pollEvents()) {
87 | if (event.kind() == StandardWatchEventKinds.ENTRY_MODIFY
88 | && Objects.equals(event.context(), path.getFileName())) {
89 | var previousPosition = position;
90 | readBytes = readAction.apply(sourceChannel, position);
91 | totalReadBytes += readBytes;
92 | position += readBytes;
93 | if (config.verbose) {
94 | config.out.printf("Read: %d -> %d (%d bytes)%n",
95 | previousPosition,
96 | position,
97 | position - previousPosition);
98 | }
99 | }
100 | }
101 | var valid = wk.reset();
102 | if (!valid) {
103 | break; // exit
104 | }
105 | }
106 | }
107 |
108 | totalReadBytes = position - startPosition;
109 | if (config.verbose) {
110 | config.out.printf("Total read: %d -> %d (%d bytes)%n",
111 | startPosition,
112 | position,
113 | totalReadBytes);
114 | }
115 |
116 | } catch (IOException e) {
117 | if (config.verbose) {
118 | e.printStackTrace(config.err);
119 | }
120 | System.exit(TailerMain.ERR_IO_TAILING_FILE);
121 | }
122 | }
123 |
124 | public long totalReadBytes() {
125 | return totalReadBytes;
126 | }
127 |
128 | public void close() {
129 | closed.set(true);
130 | }
131 |
132 | public static class LineConsumer implements IOReadAction {
133 | private final Consumer stringConsumer;
134 | private final Charset charset;
135 |
136 | public LineConsumer(Consumer stringConsumer, Charset charset) {
137 | this.stringConsumer = stringConsumer;
138 | this.charset = charset;
139 | }
140 |
141 | @Override
142 | public long apply(FileChannel fileChannel, long startPosition) throws IOException {
143 | return readByLines(fileChannel,
144 | startPosition,
145 | stringConsumer);
146 | }
147 |
148 | private long readByLines(FileChannel sourceChannel, long startPosition, Consumer stringConsumer) throws IOException {
149 | var reader = Channels.newReader(sourceChannel, charset); // investigate decoder customization
150 | var br = new BufferedReader(reader); // handles new lines and EOF
151 |
152 | sourceChannel.position(startPosition); // avoid reading the file if unnecessary
153 | br.lines()
154 | .onClose(() -> {
155 | try {
156 | br.close();
157 | } catch (IOException ex) {
158 | throw new UncheckedIOException(ex);
159 | }
160 | })
161 | .forEach(stringConsumer);
162 |
163 | return sourceChannel.position() - startPosition;
164 | }
165 | }
166 |
167 | public static class ChannelSink implements IOReadAction {
168 | private final WritableByteChannel sink;
169 |
170 | public ChannelSink(WritableByteChannel sink) {
171 | this.sink = sink;
172 | }
173 |
174 |
175 | @Override
176 | public long apply(FileChannel fileChannel, long startPosition) throws IOException {
177 | return tail(fileChannel, startPosition, sink);
178 | }
179 |
180 | private long tail(FileChannel pathChannel,
181 | long startPosition,
182 | WritableByteChannel sink) throws IOException {
183 | assert pathChannel != null && sink != null;
184 | assert startPosition >= 0;
185 | var fileSize = pathChannel.size();
186 |
187 | return pathChannel.transferTo(startPosition, fileSize, sink);
188 | }
189 | }
190 |
191 | long findTailStartPosition(FileChannel channel, FromLine fromLine) throws IOException {
192 | // straw man find start position
193 | // this implementation hasn't been tested with two char line endings (CR (0x0D) and LF (0x0A))
194 | var buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
195 |
196 | if (!fromLine.fromStart) {
197 | if (fromLine.number == 0) {
198 | return channel.size();
199 | }
200 |
201 | // go to end
202 | buffer.position((int) channel.size());
203 |
204 | long lineCounter = 0;
205 | for (long i = channel.size() - 1; i >= 0; i--) {
206 | char c = (char) buffer.get((int) i);
207 |
208 | if (c == '\n') { // on newline
209 | if (lineCounter == fromLine.number) {
210 | return i + 1;
211 | }
212 | lineCounter++;
213 | }
214 | }
215 | return 0;
216 | } else {
217 | if (fromLine.number == 0) {
218 | return 0;
219 | }
220 |
221 | long lineCounter = 0;
222 | for (long i = 0, channelSize = channel.size(); i < channelSize; i++) {
223 | char c = (char) buffer.get((int) i);
224 |
225 | if (c == '\n') { // on newline
226 | if (lineCounter == fromLine.number) {
227 | return i - 1;
228 | }
229 | lineCounter++;
230 | }
231 | }
232 | return channel.size();
233 | }
234 | }
235 |
236 |
237 | public interface IOReadAction {
238 | IOReadAction NO_OP = (c, s) -> 0;
239 |
240 | long apply(FileChannel fileChannel, long startPosition) throws IOException;
241 | }
242 | }
243 |
--------------------------------------------------------------------------------
/drain-java-jackson/src/main/java/io/github/bric3/drain/core/DrainJsonSerialization.java:
--------------------------------------------------------------------------------
1 | /*
2 | * drain-java
3 | *
4 | * Copyright (c) 2021, Today - Brice Dutheil
5 | *
6 | * This Source Code Form is subject to the terms of the Mozilla Public
7 | * License, v. 2.0. If a copy of the MPL was not distributed with this
8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
9 | */
10 | package io.github.bric3.drain.core;
11 |
12 | import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
13 | import com.fasterxml.jackson.annotation.JsonProperty;
14 | import com.fasterxml.jackson.annotation.PropertyAccessor;
15 | import com.fasterxml.jackson.core.JsonGenerator;
16 | import com.fasterxml.jackson.core.JsonParser;
17 | import com.fasterxml.jackson.core.ObjectCodec;
18 | import com.fasterxml.jackson.databind.*;
19 | import com.fasterxml.jackson.databind.json.JsonMapper;
20 | import com.fasterxml.jackson.databind.module.SimpleModule;
21 |
22 | import java.io.IOException;
23 | import java.io.Reader;
24 | import java.io.UncheckedIOException;
25 | import java.io.Writer;
26 | import java.util.*;
27 | import java.util.function.Function;
28 | import java.util.stream.Collectors;
29 |
30 | /**
31 | * Simple and Drain state serialization mechanism.
32 | *
33 | * DISCLAIMER This tool allows to (de)serialize the Drain state.
34 | * But it requires to be in the same package as the Drain class.
35 | * This is not good solution so be advised that the qualified name of this API
36 | * might to change.
37 | *
38 | * Example use:
39 | *
40 | * Drain drain = ...
41 | * Writer writer = ...
42 | * DrainJsonSerialization serde = new DrainJsonSerialization();
43 | *
44 | * serde.saveState(drain, writer);
45 | *
46 | *
47 | *
48 | * Reader reader = ...
49 | * DrainJsonSerialization serde = new DrainJsonSerialization();
50 | *
51 | * Drain drain = serde.loadState(reader);
52 | *
53 | *
54 | * @author brice.dutheil@gmail.com
55 | */
56 | public class DrainJsonSerialization {
57 |
58 | public static final JsonMapper JSON_MAPPER =
59 | JsonMapper.builder()
60 | .addModule(new SimpleModule()
61 | .addSerializer(Drain.class, new DrainSerializer())
62 | .addDeserializer(Drain.class, new DrainDeserializer())
63 | .addSerializer(Node.class, new TreeNodeSerializer())
64 | .addDeserializer(Node.class, new TreeNodeDeserializer()))
65 | .visibility(PropertyAccessor.FIELD, Visibility.ANY)
66 | .addMixIn(InternalLogCluster.class, LogClusterMixin.class)
67 | .build();
68 |
69 | /**
70 | * Drain-object exporting functionality which saves a drain model
71 | * in a json file at given path.
72 | */
73 | public void saveState(Drain drain, Writer writer) {
74 | try {
75 | JSON_MAPPER.writerWithDefaultPrettyPrinter()
76 | .writeValue(writer, drain);
77 | } catch (IOException e) {
78 | throw new UncheckedIOException(e);
79 | }
80 | }
81 |
82 | public Drain loadState(Reader reader) {
83 | try {
84 | return JSON_MAPPER.reader()
85 | .withAttribute(ClustersRef.class, new ClustersRef())
86 | .readValue(reader, Drain.class);
87 | } catch (IOException e) {
88 | throw new UncheckedIOException(e);
89 | }
90 | }
91 |
92 | private static class TreeNodeSerializer extends JsonSerializer {
93 | @Override
94 | public void serialize(Node value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
95 | gen.writeStartObject();
96 | gen.writeNumberField("depth", value.depth);
97 | gen.writeObjectField("key", value.key);
98 | gen.writeObjectField("children", value.childMappings());
99 | gen.writeArrayFieldStart("clusters");
100 |
101 | for (InternalLogCluster c : value.clusters()) {
102 | gen.writeString(ClustersRef.toRef(c.clusterId()));
103 | }
104 | gen.writeEndArray();
105 | gen.writeEndObject();
106 | }
107 |
108 | @Override
109 | public Class handledType() {
110 | return Node.class;
111 | }
112 | }
113 |
114 | private static class DrainSerializer extends JsonSerializer {
115 | @Override
116 | public void serialize(Drain value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
117 | gen.writeStartObject();
118 | gen.writeNumberField("effective-depth", value.depth);
119 | gen.writeNumberField("similarity-threshold", value.similarityThreshold);
120 | gen.writeNumberField("max-child-per-node", value.maxChildPerNode);
121 | gen.writeStringField("delimiters", value.delimiters);
122 | gen.writeObjectField("clusters", value.clusters());
123 | gen.writeObjectField("prefix-tree", value.prefixTree());
124 | gen.writeEndObject();
125 | }
126 |
127 | @Override
128 | public Class handledType() {
129 | return Drain.class;
130 | }
131 | }
132 |
133 | private static class DrainDeserializer extends JsonDeserializer {
134 | @Override
135 | public Drain deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
136 | ObjectCodec codec = p.getCodec();
137 | JsonNode jsonNode = codec.readTree(p);
138 |
139 | final ArrayList clusters = codec.readValue(
140 | codec.treeAsTokens(jsonNode.get("clusters")),
141 | ctxt.getTypeFactory().constructCollectionType(ArrayList.class, InternalLogCluster.class));
142 |
143 | ((ClustersRef) ctxt.getAttribute(ClustersRef.class)).hold(clusters);
144 |
145 | return new Drain(new DrainState(
146 | jsonNode.get("effective-depth").asInt(),
147 | jsonNode.get("similarity-threshold").asDouble(),
148 | jsonNode.get("max-child-per-node").asInt(),
149 | jsonNode.get("delimiters").asText(),
150 | clusters,
151 | ctxt.readValue(codec.treeAsTokens(jsonNode.get("prefix-tree")), Node.class)
152 | ));
153 | }
154 |
155 | @Override
156 | public Class handledType() {
157 | return Drain.class;
158 | }
159 | }
160 |
161 | private static class TreeNodeDeserializer extends JsonDeserializer {
162 | @Override
163 | public Node deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
164 | final ClustersRef clustersRef = (ClustersRef) ctxt.getAttribute(ClustersRef.class);
165 | final ObjectCodec codec = p.getCodec();
166 | final JsonNode jsonNode = codec.readTree(p);
167 |
168 | final ArrayList nodeClusters = new ArrayList<>();
169 | for (JsonNode clusterId : jsonNode.get("clusters")) {
170 | nodeClusters.add(clustersRef.get(clusterId.textValue()));
171 | }
172 |
173 | final int depth = jsonNode.get("depth").asInt();
174 |
175 | final JsonParser jsonParser = codec.treeAsTokens(jsonNode.get("children"));
176 | // This json parser starts with JsonTokenId.ID_NO_TOKEN,
177 | // the map deserializer expects the parser to have already
178 | // advanced to the first token otherwise this fails with
179 | // "Unexpected end-of-input ...", to avoid that this parser
180 | // is advanced to the first token
181 | jsonParser.nextToken();
182 | final HashMap children = ctxt.readValue(
183 | jsonParser,
184 | ctxt.getTypeFactory()
185 | .constructMapType(HashMap.class,
186 | depth == 0 ? int.class : String.class,
187 | Node.class)
188 | );
189 |
190 | Object key = depth == 1 ?
191 | Integer.valueOf(jsonNode.get("key").asInt()) :
192 | jsonNode.get("key").asText();
193 |
194 | return new Node(
195 | key,
196 | depth,
197 | children,
198 | nodeClusters
199 | );
200 | }
201 | }
202 |
203 |
204 | static class ClustersRef {
205 | private List clusters;
206 | private Map clusterIndex;
207 |
208 | public static String toRef(UUID clusterId) {
209 | return "clusterId-" + clusterId;
210 | }
211 |
212 | public void hold(List clusters) {
213 | this.clusters = clusters;
214 | this.clusterIndex = clusters.stream().collect(Collectors.toMap(
215 | logCluster -> toRef(logCluster.clusterId()),
216 | Function.identity()
217 | ));
218 | }
219 |
220 | public InternalLogCluster get(String clusterId) {
221 | final InternalLogCluster logCluster = clusterIndex.get(clusterId);
222 | assert logCluster != null : "id:" + clusterId + " size:" + clusters.size() + "\n" + clusters;
223 | return logCluster;
224 | }
225 | }
226 |
227 | private static class LogClusterMixin {
228 | public LogClusterMixin(
229 | @JsonProperty("clusterId") UUID clusterId,
230 | @JsonProperty("sightings") int sightings,
231 | @JsonProperty("logTemplateTokens") List logTemplateTokens) {
232 | }
233 | }
234 | }
235 |
--------------------------------------------------------------------------------
/drain-java-core/src/main/java/io/github/bric3/drain/core/Drain.java:
--------------------------------------------------------------------------------
1 | /*
2 | * drain-java
3 | *
4 | * Copyright (c) 2021, Today - Brice Dutheil
5 | *
6 | * This Source Code Form is subject to the terms of the Mozilla Public
7 | * License, v. 2.0. If a copy of the MPL was not distributed with this
8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
9 | */
10 | package io.github.bric3.drain.core;
11 |
12 | import io.github.bric3.drain.internal.Tokenizer;
13 |
14 | import javax.annotation.Nonnull;
15 | import javax.annotation.Nullable;
16 | import java.util.ArrayList;
17 | import java.util.Collections;
18 | import java.util.List;
19 |
20 | /**
21 | * Drain log pattern miner.
22 | *
23 | * This code comes from a modified work of the LogPai team by IBM engineers,
24 | * but it has been improved to fit the Java platform.
25 | *
26 | *
27 | * Use the builder method {@link #drainBuilder()} to configure an
28 | * instance.
29 | *
30 | *
31 | *
32 | * Example use:
33 | *
34 | * var drain = Drain.drainBuilder()
35 | * .additionalDelimiters("_")
36 | * .depth(4)
37 | * .build();
38 | * Files.lines(
39 | * Paths.get("file.log"),
40 | * StandardCharsets.UTF_8
41 | * ).forEach(drain::parseLogMessage);
42 | *
43 | * // do something with clusters
44 | * drain.clusters();
45 | *
46 | *
47 | *
48 | * Note this implementation is not thread safe.
49 | *
50 | * @author brice.dutheil@gmail.com
51 | * @modifiedBy david.ohana@ibm.com, moshikh@il.ibm.com
52 | * @originalAuthor LogPAI team
53 | * @license MIT
54 | */
55 | public class Drain {
56 | /**
57 | * Marker for similar tokens
58 | */
59 | public static final String PARAM_MARKER = "<*>";
60 | private static final int ROOT_AND_LEAF_LEVELS = 2;
61 |
62 | /**
63 | * Depth of all leaf nodes.
64 | *
65 | * These are the nodes that contain the log clusters.
66 | */
67 | final int depth;
68 |
69 | /**
70 | * Similarity threshold.
71 | */
72 | final double similarityThreshold;
73 |
74 | /**
75 | * Maximum number of child nodes per node
76 | */
77 | final int maxChildPerNode;
78 |
79 | /**
80 | * Delimiters to apply when splitting log messages into words.
81 | *
82 | * In addition to whitespaces.
83 | */
84 | final String delimiters;
85 |
86 | /**
87 | * All log clusters.
88 | */
89 | private final List clusters;
90 |
91 | private final Node root;
92 |
93 | private Drain(int depth,
94 | double similarityThreshold,
95 | int maxChildPerNode,
96 | String additionalDelimiters) {
97 | this.depth = depth - ROOT_AND_LEAF_LEVELS;
98 | this.similarityThreshold = similarityThreshold;
99 | this.maxChildPerNode = maxChildPerNode;
100 | this.delimiters = " " + additionalDelimiters;
101 | root = new Node("(ROOT)", 0);
102 | clusters = new ArrayList<>();
103 | }
104 |
105 | Drain(DrainState state) {
106 | this.depth = state.depth;
107 | this.similarityThreshold = state.similarityThreshold;
108 | this.maxChildPerNode = state.maxChildPerNode;
109 | this.delimiters = state.delimiters;
110 | this.clusters = state.clusters;
111 | this.root = state.prefixTree;
112 | }
113 |
114 | /**
115 | * Parse log message.
116 | *
117 | * Classify the log message to a cluster.
118 | *
119 | * @param message The log message content
120 | */
121 | public void parseLogMessage(@Nonnull String message) {
122 | // sprint message by delimiter / whitespaces
123 | List contentTokens = Tokenizer.tokenize(message, delimiters);
124 |
125 | // Search the prefix tree
126 | InternalLogCluster matchCluster = treeSearch(contentTokens);
127 |
128 | if (matchCluster == null) {
129 | // create cluster if it doesn't exists, using log content tokens as template tokens
130 | matchCluster = new InternalLogCluster(contentTokens);
131 | clusters.add(matchCluster);
132 | addLogClusterToPrefixTree(matchCluster);
133 | } else {
134 | // add the log to an existing cluster
135 | matchCluster.newSighting(contentTokens);
136 | }
137 | }
138 |
139 | /**
140 | * Search a matching log cluster given a log message.
141 | *
142 | * @param message The log message content
143 | * @return The matching log cluster or null if no match
144 | */
145 | public LogCluster searchLogMessage(@Nonnull String message) {
146 | // sprint message by delimiter / whitespaces
147 | List contentTokens = Tokenizer.tokenize(message, delimiters);
148 |
149 | // Search the prefix tree
150 | LogCluster matchCluster = treeSearch(contentTokens);
151 | return matchCluster;
152 | }
153 |
154 |
155 | private @Nullable
156 | InternalLogCluster treeSearch(@Nonnull List logTokens) {
157 |
158 | // at first level, children are grouped by token (word) count
159 | int tokensCount = logTokens.size();
160 | Node node = this.root.get(tokensCount);
161 |
162 | // the prefix tree is empty
163 | if (node == null) {
164 | return null;
165 | }
166 |
167 | // handle case of empty log string - return the single cluster in that group
168 | if (tokensCount == 0) {
169 | return node.clusterOf(0);
170 | }
171 |
172 | // find the leaf node for this log
173 | // a path of nodes matching the first N tokens (N=tree depth)
174 | int currentDepth = 1;
175 | for (String token : logTokens) {
176 | // if max depth reached or last parseable token, bail out
177 | boolean atMaxDepth = currentDepth == this.depth;
178 | boolean isLastToken = currentDepth == tokensCount;
179 | if (atMaxDepth || isLastToken) {
180 | break;
181 | }
182 |
183 | // descend
184 | Node nextNode = node.get(token);
185 | // if null try get from generic pattern
186 | if (nextNode == null) {
187 | nextNode = node.get(PARAM_MARKER);
188 | }
189 | // if the node don't exists yet, the cluster don't exists yet
190 | if (nextNode == null) {
191 | return null;
192 | }
193 | node = nextNode;
194 | currentDepth++;
195 | }
196 |
197 | return fastMatch(node.clusters(), logTokens);
198 | }
199 |
200 | private @Nullable
201 | InternalLogCluster fastMatch(@Nonnull List clusters,
202 | @Nonnull List logTokens) {
203 | InternalLogCluster matchedCluster = null;
204 |
205 | double maxSimilarity = -1;
206 | int maxParamCount = -1;
207 | InternalLogCluster maxCluster = null;
208 |
209 | for (InternalLogCluster cluster : clusters) {
210 | SeqDistance seqDistance = computeSeqDistance(cluster.internalTokens(), logTokens);
211 | if (seqDistance.similarity > maxSimilarity
212 | || (seqDistance.similarity == maxSimilarity
213 | && seqDistance.paramCount > maxParamCount)) {
214 | maxSimilarity = seqDistance.similarity;
215 | maxParamCount = seqDistance.paramCount;
216 | maxCluster = cluster;
217 | }
218 | }
219 |
220 | if (maxSimilarity >= this.similarityThreshold) {
221 | matchedCluster = maxCluster;
222 | }
223 |
224 | return matchedCluster;
225 | }
226 |
227 | private static class SeqDistance {
228 |
229 | final double similarity;
230 | final int paramCount;
231 | SeqDistance(double similarity, int paramCount) {
232 | this.similarity = similarity;
233 | this.paramCount = paramCount;
234 | }
235 |
236 | }
237 |
238 | static @Nonnull
239 | SeqDistance computeSeqDistance(@Nonnull List templateTokens,
240 | @Nonnull List logTokens) {
241 | assert templateTokens.size() == logTokens.size();
242 |
243 | int similarTokens = 0;
244 | int paramCount = 0;
245 |
246 | for (int i = 0, tokensSize = templateTokens.size(); i < tokensSize; i++) {
247 | String token = templateTokens.get(i);
248 | String currentToken = logTokens.get(i);
249 |
250 | if (token.equals(PARAM_MARKER)) {
251 | paramCount++;
252 | continue;
253 | }
254 | if (token.equals(currentToken)) {
255 | similarTokens++;
256 | }
257 | }
258 |
259 | double similarity = (double) similarTokens / templateTokens.size();
260 | return new SeqDistance(similarity, paramCount);
261 | }
262 |
263 | private void addLogClusterToPrefixTree(@Nonnull InternalLogCluster newLogCluster) {
264 | int tokensCount = newLogCluster.internalTokens().size();
265 |
266 | Node node = this.root.getOrCreateChild(tokensCount);
267 |
268 | // handle case of empty log message
269 | if (tokensCount == 0) {
270 | node.appendCluster(newLogCluster);
271 | return;
272 | }
273 |
274 |
275 | int currentDepth = 1;
276 | for (String token : newLogCluster.internalTokens()) {
277 |
278 | // Add current log cluster to the leaf node
279 | boolean atMaxDepth = currentDepth == this.depth;
280 | boolean isLastToken = currentDepth == tokensCount;
281 | if (atMaxDepth || isLastToken) {
282 | node.appendCluster(newLogCluster);
283 | break;
284 | }
285 |
286 | // If token not matched in this layer of existing tree.
287 | // TODO see improvements are possible
288 | if (!node.contains(token)) {
289 | if (!hasNumber(token)) {
290 | if (node.contains(PARAM_MARKER)) {
291 | if (node.childrenCount() < maxChildPerNode) {
292 | node = node.getOrCreateChild(token);
293 | } else {
294 | node = node.get(PARAM_MARKER);
295 | }
296 | } else {
297 | if (node.childrenCount() + 1 <= maxChildPerNode) {
298 | node = node.getOrCreateChild(token);
299 | } else if (node.childrenCount() + 1 == maxChildPerNode) {
300 | node = node.getOrCreateChild(PARAM_MARKER);
301 | } else {
302 | node = node.get(PARAM_MARKER);
303 | }
304 | }
305 | } else {
306 | if (!node.contains(PARAM_MARKER)) {
307 | node = node.getOrCreateChild(PARAM_MARKER);
308 | } else {
309 | node = node.get(PARAM_MARKER);
310 | }
311 | }
312 | } else {
313 | node = node.get(token);
314 | }
315 | currentDepth++;
316 | }
317 | }
318 |
319 |
320 | private static boolean hasNumber(@Nonnull String s) {
321 | return s.chars().anyMatch(Character::isDigit);
322 | }
323 |
324 | /**
325 | * Returns a list of the Log clusters.
326 | *
327 | * @return Non modifiable list of current clusters.
328 | */
329 | public List clusters() {
330 | return Collections.unmodifiableList(new ArrayList<>(clusters));
331 | }
332 |
333 | Node prefixTree() {
334 | return root;
335 | }
336 |
337 |
338 | /**
339 | * Drain builder.
340 | *
341 | * Used like this:
342 | *
343 | * Drain.drainBuilder()
344 | * .additionalDelimiters("_")
345 | * .depth(4)
346 | * .build()
347 | *
348 | *
349 | * @return a drain builder
350 | */
351 | public static DrainBuilder drainBuilder() {
352 | return new DrainBuilder();
353 | }
354 |
355 | /**
356 | * Builder for {@link Drain}
357 | */
358 | public static class DrainBuilder {
359 | private int depth = 4;
360 | private String additionalDelimiters = "";
361 | private double similarityThreshold = 0.4d;
362 | private int maxChildPerNode = 100;
363 |
364 | /**
365 | * Depth of all leaf nodes.
366 | *
367 | * How many level to reach the nodes that contain log clusters.
368 | *
369 | * The default value is 4, the minimum value is 3.
370 | *
371 | * @param depth The depth of all leaf nodes.
372 | * @return this
373 | */
374 | public DrainBuilder depth(int depth) {
375 | assert depth > 2;
376 | this.depth = depth;
377 | return this;
378 | }
379 |
380 | /**
381 | * Additional delimiters.
382 | *
383 | * Additionally to the whitespace, also use additional delimiting
384 | * characters to to split the log message into tokens. This value
385 | * is empty by default.
386 | *
387 | * @param additionalDelimiters THe Additional delimiters.
388 | * @return this
389 | */
390 | public DrainBuilder additionalDelimiters(String additionalDelimiters) {
391 | assert additionalDelimiters != null;
392 | this.additionalDelimiters = additionalDelimiters;
393 | return this;
394 | }
395 |
396 | /**
397 | * Similarity threshold.
398 | *
399 | * The similarity threshold applies to each token of a log message,
400 | * if the percentage of similar tokens is below this number, then
401 | * a new log cluster will be created.
402 | *
403 | * Default value is 0.4.
404 | *
405 | * @param similarityThreshold The similarity threshold
406 | * @return this
407 | */
408 | public DrainBuilder similarityThreshold(double similarityThreshold) {
409 | assert similarityThreshold > 0.1d;
410 | this.similarityThreshold = similarityThreshold;
411 | return this;
412 | }
413 |
414 | /**
415 | * Max number of children of an internal node.
416 | *
417 | * Limit the number of children nodes, if this value is too low
418 | * and log messages are too versatile then many logs will be
419 | * classified under the generic param marker.
420 | *
421 | * Default value is 100.
422 | *
423 | * @param maxChildPerNode Max number of children of an internal node
424 | * @return this
425 | */
426 | public DrainBuilder maxChildPerNode(int maxChildPerNode) {
427 | assert maxChildPerNode >= 2;
428 | this.maxChildPerNode = maxChildPerNode;
429 | return this;
430 | }
431 |
432 | /**
433 | * Build a non thread safe instance of Drain.
434 | *
435 | * @return A {@see Drain} instance
436 | */
437 | public Drain build() {
438 | return new Drain(depth,
439 | similarityThreshold,
440 | maxChildPerNode,
441 | additionalDelimiters);
442 | }
443 | }
444 | }
445 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Mozilla Public License Version 2.0
2 | ==================================
3 |
4 | 1. Definitions
5 | --------------
6 |
7 | 1.1. "Contributor"
8 | means each individual or legal entity that creates, contributes to
9 | the creation of, or owns Covered Software.
10 |
11 | 1.2. "Contributor Version"
12 | means the combination of the Contributions of others (if any) used
13 | by a Contributor and that particular Contributor's Contribution.
14 |
15 | 1.3. "Contribution"
16 | means Covered Software of a particular Contributor.
17 |
18 | 1.4. "Covered Software"
19 | means Source Code Form to which the initial Contributor has attached
20 | the notice in Exhibit A, the Executable Form of such Source Code
21 | Form, and Modifications of such Source Code Form, in each case
22 | including portions thereof.
23 |
24 | 1.5. "Incompatible With Secondary Licenses"
25 | means
26 |
27 | (a) that the initial Contributor has attached the notice described
28 | in Exhibit B to the Covered Software; or
29 |
30 | (b) that the Covered Software was made available under the terms of
31 | version 1.1 or earlier of the License, but not also under the
32 | terms of a Secondary License.
33 |
34 | 1.6. "Executable Form"
35 | means any form of the work other than Source Code Form.
36 |
37 | 1.7. "Larger Work"
38 | means a work that combines Covered Software with other material, in
39 | a separate file or files, that is not Covered Software.
40 |
41 | 1.8. "License"
42 | means this document.
43 |
44 | 1.9. "Licensable"
45 | means having the right to grant, to the maximum extent possible,
46 | whether at the time of the initial grant or subsequently, any and
47 | all of the rights conveyed by this License.
48 |
49 | 1.10. "Modifications"
50 | means any of the following:
51 |
52 | (a) any file in Source Code Form that results from an addition to,
53 | deletion from, or modification of the contents of Covered
54 | Software; or
55 |
56 | (b) any new file in Source Code Form that contains any Covered
57 | Software.
58 |
59 | 1.11. "Patent Claims" of a Contributor
60 | means any patent claim(s), including without limitation, method,
61 | process, and apparatus claims, in any patent Licensable by such
62 | Contributor that would be infringed, but for the grant of the
63 | License, by the making, using, selling, offering for sale, having
64 | made, import, or transfer of either its Contributions or its
65 | Contributor Version.
66 |
67 | 1.12. "Secondary License"
68 | means either the GNU General Public License, Version 2.0, the GNU
69 | Lesser General Public License, Version 2.1, the GNU Affero General
70 | Public License, Version 3.0, or any later versions of those
71 | licenses.
72 |
73 | 1.13. "Source Code Form"
74 | means the form of the work preferred for making modifications.
75 |
76 | 1.14. "You" (or "Your")
77 | means an individual or a legal entity exercising rights under this
78 | License. For legal entities, "You" includes any entity that
79 | controls, is controlled by, or is under common control with You. For
80 | purposes of this definition, "control" means (a) the power, direct
81 | or indirect, to cause the direction or management of such entity,
82 | whether by contract or otherwise, or (b) ownership of more than
83 | fifty percent (50%) of the outstanding shares or beneficial
84 | ownership of such entity.
85 |
86 | 2. License Grants and Conditions
87 | --------------------------------
88 |
89 | 2.1. Grants
90 |
91 | Each Contributor hereby grants You a world-wide, royalty-free,
92 | non-exclusive license:
93 |
94 | (a) under intellectual property rights (other than patent or trademark)
95 | Licensable by such Contributor to use, reproduce, make available,
96 | modify, display, perform, distribute, and otherwise exploit its
97 | Contributions, either on an unmodified basis, with Modifications, or
98 | as part of a Larger Work; and
99 |
100 | (b) under Patent Claims of such Contributor to make, use, sell, offer
101 | for sale, have made, import, and otherwise transfer either its
102 | Contributions or its Contributor Version.
103 |
104 | 2.2. Effective Date
105 |
106 | The licenses granted in Section 2.1 with respect to any Contribution
107 | become effective for each Contribution on the date the Contributor first
108 | distributes such Contribution.
109 |
110 | 2.3. Limitations on Grant Scope
111 |
112 | The licenses granted in this Section 2 are the only rights granted under
113 | this License. No additional rights or licenses will be implied from the
114 | distribution or licensing of Covered Software under this License.
115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a
116 | Contributor:
117 |
118 | (a) for any code that a Contributor has removed from Covered Software;
119 | or
120 |
121 | (b) for infringements caused by: (i) Your and any other third party's
122 | modifications of Covered Software, or (ii) the combination of its
123 | Contributions with other software (except as part of its Contributor
124 | Version); or
125 |
126 | (c) under Patent Claims infringed by Covered Software in the absence of
127 | its Contributions.
128 |
129 | This License does not grant any rights in the trademarks, service marks,
130 | or logos of any Contributor (except as may be necessary to comply with
131 | the notice requirements in Section 3.4).
132 |
133 | 2.4. Subsequent Licenses
134 |
135 | No Contributor makes additional grants as a result of Your choice to
136 | distribute the Covered Software under a subsequent version of this
137 | License (see Section 10.2) or under the terms of a Secondary License (if
138 | permitted under the terms of Section 3.3).
139 |
140 | 2.5. Representation
141 |
142 | Each Contributor represents that the Contributor believes its
143 | Contributions are its original creation(s) or it has sufficient rights
144 | to grant the rights to its Contributions conveyed by this License.
145 |
146 | 2.6. Fair Use
147 |
148 | This License is not intended to limit any rights You have under
149 | applicable copyright doctrines of fair use, fair dealing, or other
150 | equivalents.
151 |
152 | 2.7. Conditions
153 |
154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
155 | in Section 2.1.
156 |
157 | 3. Responsibilities
158 | -------------------
159 |
160 | 3.1. Distribution of Source Form
161 |
162 | All distribution of Covered Software in Source Code Form, including any
163 | Modifications that You create or to which You contribute, must be under
164 | the terms of this License. You must inform recipients that the Source
165 | Code Form of the Covered Software is governed by the terms of this
166 | License, and how they can obtain a copy of this License. You may not
167 | attempt to alter or restrict the recipients' rights in the Source Code
168 | Form.
169 |
170 | 3.2. Distribution of Executable Form
171 |
172 | If You distribute Covered Software in Executable Form then:
173 |
174 | (a) such Covered Software must also be made available in Source Code
175 | Form, as described in Section 3.1, and You must inform recipients of
176 | the Executable Form how they can obtain a copy of such Source Code
177 | Form by reasonable means in a timely manner, at a charge no more
178 | than the cost of distribution to the recipient; and
179 |
180 | (b) You may distribute such Executable Form under the terms of this
181 | License, or sublicense it under different terms, provided that the
182 | license for the Executable Form does not attempt to limit or alter
183 | the recipients' rights in the Source Code Form under this License.
184 |
185 | 3.3. Distribution of a Larger Work
186 |
187 | You may create and distribute a Larger Work under terms of Your choice,
188 | provided that You also comply with the requirements of this License for
189 | the Covered Software. If the Larger Work is a combination of Covered
190 | Software with a work governed by one or more Secondary Licenses, and the
191 | Covered Software is not Incompatible With Secondary Licenses, this
192 | License permits You to additionally distribute such Covered Software
193 | under the terms of such Secondary License(s), so that the recipient of
194 | the Larger Work may, at their option, further distribute the Covered
195 | Software under the terms of either this License or such Secondary
196 | License(s).
197 |
198 | 3.4. Notices
199 |
200 | You may not remove or alter the substance of any license notices
201 | (including copyright notices, patent notices, disclaimers of warranty,
202 | or limitations of liability) contained within the Source Code Form of
203 | the Covered Software, except that You may alter any license notices to
204 | the extent required to remedy known factual inaccuracies.
205 |
206 | 3.5. Application of Additional Terms
207 |
208 | You may choose to offer, and to charge a fee for, warranty, support,
209 | indemnity or liability obligations to one or more recipients of Covered
210 | Software. However, You may do so only on Your own behalf, and not on
211 | behalf of any Contributor. You must make it absolutely clear that any
212 | such warranty, support, indemnity, or liability obligation is offered by
213 | You alone, and You hereby agree to indemnify every Contributor for any
214 | liability incurred by such Contributor as a result of warranty, support,
215 | indemnity or liability terms You offer. You may include additional
216 | disclaimers of warranty and limitations of liability specific to any
217 | jurisdiction.
218 |
219 | 4. Inability to Comply Due to Statute or Regulation
220 | ---------------------------------------------------
221 |
222 | If it is impossible for You to comply with any of the terms of this
223 | License with respect to some or all of the Covered Software due to
224 | statute, judicial order, or regulation then You must: (a) comply with
225 | the terms of this License to the maximum extent possible; and (b)
226 | describe the limitations and the code they affect. Such description must
227 | be placed in a text file included with all distributions of the Covered
228 | Software under this License. Except to the extent prohibited by statute
229 | or regulation, such description must be sufficiently detailed for a
230 | recipient of ordinary skill to be able to understand it.
231 |
232 | 5. Termination
233 | --------------
234 |
235 | 5.1. The rights granted under this License will terminate automatically
236 | if You fail to comply with any of its terms. However, if You become
237 | compliant, then the rights granted under this License from a particular
238 | Contributor are reinstated (a) provisionally, unless and until such
239 | Contributor explicitly and finally terminates Your grants, and (b) on an
240 | ongoing basis, if such Contributor fails to notify You of the
241 | non-compliance by some reasonable means prior to 60 days after You have
242 | come back into compliance. Moreover, Your grants from a particular
243 | Contributor are reinstated on an ongoing basis if such Contributor
244 | notifies You of the non-compliance by some reasonable means, this is the
245 | first time You have received notice of non-compliance with this License
246 | from such Contributor, and You become compliant prior to 30 days after
247 | Your receipt of the notice.
248 |
249 | 5.2. If You initiate litigation against any entity by asserting a patent
250 | infringement claim (excluding declaratory judgment actions,
251 | counter-claims, and cross-claims) alleging that a Contributor Version
252 | directly or indirectly infringes any patent, then the rights granted to
253 | You by any and all Contributors for the Covered Software under Section
254 | 2.1 of this License shall terminate.
255 |
256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all
257 | end user license agreements (excluding distributors and resellers) which
258 | have been validly granted by You or Your distributors under this License
259 | prior to termination shall survive termination.
260 |
261 | ************************************************************************
262 | * *
263 | * 6. Disclaimer of Warranty *
264 | * ------------------------- *
265 | * *
266 | * Covered Software is provided under this License on an "as is" *
267 | * basis, without warranty of any kind, either expressed, implied, or *
268 | * statutory, including, without limitation, warranties that the *
269 | * Covered Software is free of defects, merchantable, fit for a *
270 | * particular purpose or non-infringing. The entire risk as to the *
271 | * quality and performance of the Covered Software is with You. *
272 | * Should any Covered Software prove defective in any respect, You *
273 | * (not any Contributor) assume the cost of any necessary servicing, *
274 | * repair, or correction. This disclaimer of warranty constitutes an *
275 | * essential part of this License. No use of any Covered Software is *
276 | * authorized under this License except under this disclaimer. *
277 | * *
278 | ************************************************************************
279 |
280 | ************************************************************************
281 | * *
282 | * 7. Limitation of Liability *
283 | * -------------------------- *
284 | * *
285 | * Under no circumstances and under no legal theory, whether tort *
286 | * (including negligence), contract, or otherwise, shall any *
287 | * Contributor, or anyone who distributes Covered Software as *
288 | * permitted above, be liable to You for any direct, indirect, *
289 | * special, incidental, or consequential damages of any character *
290 | * including, without limitation, damages for lost profits, loss of *
291 | * goodwill, work stoppage, computer failure or malfunction, or any *
292 | * and all other commercial damages or losses, even if such party *
293 | * shall have been informed of the possibility of such damages. This *
294 | * limitation of liability shall not apply to liability for death or *
295 | * personal injury resulting from such party's negligence to the *
296 | * extent applicable law prohibits such limitation. Some *
297 | * jurisdictions do not allow the exclusion or limitation of *
298 | * incidental or consequential damages, so this exclusion and *
299 | * limitation may not apply to You. *
300 | * *
301 | ************************************************************************
302 |
303 | 8. Litigation
304 | -------------
305 |
306 | Any litigation relating to this License may be brought only in the
307 | courts of a jurisdiction where the defendant maintains its principal
308 | place of business and such litigation shall be governed by laws of that
309 | jurisdiction, without reference to its conflict-of-law provisions.
310 | Nothing in this Section shall prevent a party's ability to bring
311 | cross-claims or counter-claims.
312 |
313 | 9. Miscellaneous
314 | ----------------
315 |
316 | This License represents the complete agreement concerning the subject
317 | matter hereof. If any provision of this License is held to be
318 | unenforceable, such provision shall be reformed only to the extent
319 | necessary to make it enforceable. Any law or regulation which provides
320 | that the language of a contract shall be construed against the drafter
321 | shall not be used to construe this License against a Contributor.
322 |
323 | 10. Versions of the License
324 | ---------------------------
325 |
326 | 10.1. New Versions
327 |
328 | Mozilla Foundation is the license steward. Except as provided in Section
329 | 10.3, no one other than the license steward has the right to modify or
330 | publish new versions of this License. Each version will be given a
331 | distinguishing version number.
332 |
333 | 10.2. Effect of New Versions
334 |
335 | You may distribute the Covered Software under the terms of the version
336 | of the License under which You originally received the Covered Software,
337 | or under the terms of any subsequent version published by the license
338 | steward.
339 |
340 | 10.3. Modified Versions
341 |
342 | If you create software not governed by this License, and you want to
343 | create a new license for such software, you may create and use a
344 | modified version of this License if you rename the license and remove
345 | any references to the name of the license steward (except to note that
346 | such modified license differs from this License).
347 |
348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary
349 | Licenses
350 |
351 | If You choose to distribute Source Code Form that is Incompatible With
352 | Secondary Licenses under the terms of this version of the License, the
353 | notice described in Exhibit B of this License must be attached.
354 |
355 | Exhibit A - Source Code Form License Notice
356 | -------------------------------------------
357 |
358 | This Source Code Form is subject to the terms of the Mozilla Public
359 | License, v. 2.0. If a copy of the MPL was not distributed with this
360 | file, You can obtain one at http://mozilla.org/MPL/2.0/.
361 |
362 | If it is not possible or desirable to put the notice in a particular
363 | file, then You may include the notice in a location (such as a LICENSE
364 | file in a relevant directory) where a recipient would be likely to look
365 | for such a notice.
366 |
367 | You may add additional accurate notices of copyright ownership.
368 |
369 | Exhibit B - "Incompatible With Secondary Licenses" Notice
370 | ---------------------------------------------------------
371 |
372 | This Source Code Form is "Incompatible With Secondary Licenses", as
373 | defined by the Mozilla Public License, v. 2.0.
374 |
--------------------------------------------------------------------------------
/drain-java-core/src/test/java/io/github/bric3/drain/core/DrainBulkTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * drain-java
3 | *
4 | * Copyright (c) 2021, Today - Brice Dutheil
5 | *
6 | * This Source Code Form is subject to the terms of the Mozilla Public
7 | * License, v. 2.0. If a copy of the MPL was not distributed with this
8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
9 | */
10 | package io.github.bric3.drain.core;
11 |
12 | import io.github.bric3.drain.internal.Stopwatch;
13 | import io.github.bric3.drain.utils.TestPaths;
14 | import org.junit.jupiter.api.Test;
15 |
16 | import java.io.IOException;
17 | import java.nio.charset.StandardCharsets;
18 | import java.nio.file.Files;
19 | import java.util.Comparator;
20 | import java.util.List;
21 | import java.util.concurrent.atomic.AtomicInteger;
22 | import java.util.stream.Collectors;
23 | import java.util.stream.Stream;
24 |
25 | import static org.assertj.core.api.Assertions.assertThat;
26 |
27 | class DrainBulkTest {
28 |
29 | @Test
30 | void smokeTest() throws IOException {
31 | Drain drain = Drain.drainBuilder()
32 | .additionalDelimiters("_")
33 | .depth(4)
34 | .build();
35 |
36 | AtomicInteger lineCounter = new AtomicInteger();
37 |
38 | Stopwatch stopwatch = Stopwatch.createStarted();
39 | try (Stream lines = Files.lines(TestPaths.get("SSH.log"), StandardCharsets.UTF_8)) {
40 | lines.peek(__ -> lineCounter.incrementAndGet())
41 | .map(l -> l.substring(l.indexOf("]: ") + 3)) // removes this part: "Dec 10 06:55:46 LabSZ sshd[24200]: "
42 | .forEach(content -> {
43 | drain.parseLogMessage(content);
44 | if (lineCounter.get() % 10000 == 0) {
45 | System.out.printf("%4d clusters so far%n", drain.clusters().size());
46 | }
47 | });
48 | }
49 |
50 |
51 | System.out.printf("---- Done processing file. Total of %d lines, done in %s, %d clusters%n",
52 | lineCounter.get(),
53 | stopwatch,
54 | drain.clusters().size());
55 |
56 | assertThat(drain.clusters()).hasSize(51);
57 |
58 | List sortedClusters = drain.clusters()
59 | .stream()
60 | .sorted(Comparator.comparing(LogCluster::sightings).reversed())
61 | .collect(Collectors.toList());
62 |
63 | // sortedClusters.forEach(System.out::println);
64 |
65 | // based on cluster found in https://zenodo.org/record/3227177/files/SSH.tar.gz
66 | assertCluster(sortedClusters, 0, 140768, "Failed password for <*> from <*> port <*> ssh2");
67 | assertCluster(sortedClusters, 1, 140701, "pam unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= <*> <*>");
68 | assertCluster(sortedClusters, 2, 68958, "Connection closed by <*> [preauth]");
69 | assertCluster(sortedClusters, 3, 46642, "Received disconnect from <*> 11: <*> <*> <*>");
70 | assertCluster(sortedClusters, 4, 37963, "PAM service(sshd) ignoring max retries; <*> > 3");
71 | assertCluster(sortedClusters, 5, 37298, "Disconnecting: Too many authentication failures for <*> [preauth]");
72 | assertCluster(sortedClusters, 6, 37029, "PAM <*> more authentication <*> logname= uid=0 euid=0 tty=ssh ruser= <*> <*>");
73 | assertCluster(sortedClusters, 7, 36967, "message repeated <*> times: [ Failed password for <*> from <*> port <*> ssh2]");
74 | assertCluster(sortedClusters, 8, 20241, "Failed <*> for invalid user <*> from <*> port <*> ssh2");
75 | assertCluster(sortedClusters, 9, 19852, "pam unix(sshd:auth): check pass; user unknown");
76 | assertCluster(sortedClusters, 10, 18909, "reverse mapping checking getaddrinfo for <*> <*> failed - POSSIBLE BREAK-IN ATTEMPT!");
77 | assertCluster(sortedClusters, 11, 14551, "Invalid user <*> from <*>");
78 | assertCluster(sortedClusters, 12, 14551, "input userauth request: invalid user <*> [preauth]");
79 | assertCluster(sortedClusters, 13, 14356, "pam unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= <*>");
80 | assertCluster(sortedClusters, 14, 1289, "PAM <*> more authentication <*> logname= uid=0 euid=0 tty=ssh ruser= <*>");
81 | assertCluster(sortedClusters, 15, 952, "fatal: Read from socket failed: Connection reset by peer [preauth]");
82 | assertCluster(sortedClusters, 16, 930, "error: Received disconnect from 103.99.0.122: 14: No more user authentication methods available. [preauth]");
83 | assertCluster(sortedClusters, 17, 838, "Did not receive identification string from <*>");
84 | assertCluster(sortedClusters, 18, 592, "Received disconnect from <*> 11: Closed due to user request. [preauth]");
85 | assertCluster(sortedClusters, 19, 497, "Address <*> maps to <*> but this does not map back to the address - POSSIBLE BREAK-IN ATTEMPT!");
86 | assertCluster(sortedClusters, 20, 182, "Accepted password for <*> from <*> port <*> ssh2");
87 | assertCluster(sortedClusters, 21, 182, "pam unix(sshd:session): session opened for user <*> by (uid=0)");
88 | assertCluster(sortedClusters, 22, 182, "pam unix(sshd:session): session closed for user <*>");
89 | assertCluster(sortedClusters, 23, 177, "error: Received disconnect from <*> 3: com.jcraft.jsch.JSchException: Auth <*> [preauth]");
90 | assertCluster(sortedClusters, 24, 108, "Received disconnect from <*> 11: [preauth]");
91 | assertCluster(sortedClusters, 25, 92, "Received disconnect from 139.59.209.18: 11: Normal Shutdown, Thank you for playing [preauth]");
92 | assertCluster(sortedClusters, 26, 87, "Received disconnect from <*> 11: <*> <*> <*> [preauth]");
93 | assertCluster(sortedClusters, 27, 60, "Received disconnect from <*> 11: disconnect [preauth]");
94 | assertCluster(sortedClusters, 28, 30, "Invalid user <*> <*> from <*>");
95 | assertCluster(sortedClusters, 29, 30, "input userauth request: invalid user <*> <*> [preauth]");
96 | assertCluster(sortedClusters, 30, 30, "Failed password for invalid user <*> <*> from <*> port <*> ssh2");
97 | assertCluster(sortedClusters, 31, 13, "Invalid user from <*>");
98 | assertCluster(sortedClusters, 32, 13, "input userauth request: invalid user [preauth]");
99 | assertCluster(sortedClusters, 33, 13, "Failed <*> for invalid user from <*> port <*> ssh2");
100 | assertCluster(sortedClusters, 34, 8, "Bad protocol version identification <*> <*> <*> from <*> port <*>");
101 | assertCluster(sortedClusters, 35, 7, "Bad protocol version identification <*> <*> from <*> port <*>");
102 | assertCluster(sortedClusters, 36, 7, "Bad protocol version identification <*> from <*> port <*>");
103 | assertCluster(sortedClusters, 37, 6, "fatal: no hostkey alg [preauth]");
104 | assertCluster(sortedClusters, 38, 6, "error: Received disconnect from <*> <*> <*> <*> <*> <*> [preauth]");
105 | assertCluster(sortedClusters, 39, 6, "error: connect to <*> port 22: failed.");
106 | assertCluster(sortedClusters, 40, 6, "Server listening on <*> port <*>");
107 | assertCluster(sortedClusters, 41, 3, "fatal: Write failed: Connection reset by peer [preauth]");
108 | assertCluster(sortedClusters, 42, 3, "error: Received disconnect from 195.154.45.62: 3: com.jcraft.jsch.JSchException: timeout in waiting for rekeying process. [preauth]");
109 | assertCluster(sortedClusters, 43, 3, "Received disconnect from <*> 11: Disconnect requested by Windows SSH Client.");
110 | assertCluster(sortedClusters, 44, 2, "error: Received disconnect from 191.96.249.68: 13: User request [preauth]");
111 | assertCluster(sortedClusters, 45, 2, "error: Received disconnect from 212.83.176.1: 3: org.vngx.jsch.userauth.AuthCancelException: User authentication canceled by user [preauth]");
112 | assertCluster(sortedClusters, 46, 1, "Bad packet length 1819045217. [preauth]");
113 | assertCluster(sortedClusters, 47, 1, "Disconnecting: Packet corrupt [preauth]");
114 | assertCluster(sortedClusters, 48, 1, "Received disconnect from 67.160.100.130: 11:");
115 | assertCluster(sortedClusters, 49, 1, "Corrupted MAC on input. [preauth]");
116 | assertCluster(sortedClusters, 50, 1, "syslogin perform logout: logout() returned an error");
117 | }
118 |
119 | @Test
120 | void can_find_a_log() throws IOException {
121 | Drain drain = Drain.drainBuilder()
122 | .additionalDelimiters("_")
123 | .depth(4)
124 | .build();
125 |
126 | try (Stream lines = Files.lines(TestPaths.get("SSH.log"), StandardCharsets.UTF_8)) {
127 | lines.map(l -> l.substring(l.indexOf("]: ") + 3)) // removes this part: "Dec 10 06:55:46 LabSZ sshd[24200]: "
128 | .forEach(drain::parseLogMessage);
129 | }
130 |
131 | LogCluster logCluster = drain.searchLogMessage("Received disconnect from 202.100.179.208: 11: Bye Bye [preauth]");
132 | assertCluster(logCluster, 46642, "Received disconnect from <*> 11: <*> <*> <*>");
133 | }
134 |
135 | private void assertCluster(List sortedClusters, int i, int sightings, String tokens) {
136 | assertCluster(sortedClusters.get(i), sightings, tokens);
137 | }
138 |
139 | private static void assertCluster(LogCluster logCluster, int sightings, String tokens) {
140 | assertThat(logCluster.sightings()).isEqualTo(sightings);
141 | assertThat(String.join(" ", logCluster.tokens())).isEqualTo(tokens);
142 | }
143 | }
144 | /*
145 | Python without masking:
146 | =======================
147 |
148 | --- Done processing file. Total of 655147 lines, rate 52853.2 lines/sec, 51 clusters
149 | A0010 (size 140768): Failed password for <*> from <*> port <*> ssh2
150 | A0009 (size 140701): pam unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= <*> <*>
151 | A0007 (size 68958): Connection closed by <*> [preauth]
152 | A0008 (size 46642): Received disconnect from <*> 11: <*> <*> <*>
153 | A0014 (size 37963): PAM service(sshd) ignoring max retries; <*> > 3
154 | A0012 (size 37298): Disconnecting: Too many authentication failures for <*> [preauth]
155 | A0013 (size 37029): PAM <*> more authentication <*> logname= uid=0 euid=0 tty=ssh ruser= <*> <*>
156 | A0011 (size 36967): message repeated <*> times: [ Failed password for <*> from <*> port <*> ssh2]
157 | A0006 (size 20241): Failed <*> for invalid user <*> from <*> port <*> ssh2
158 | A0004 (size 19852): pam unix(sshd:auth): check pass; user unknown
159 | A0001 (size 18909): reverse mapping checking getaddrinfo for <*> <*> failed - POSSIBLE BREAK-IN ATTEMPT!
160 | A0002 (size 14551): Invalid user <*> from <*>
161 | A0003 (size 14551): input userauth request: invalid user <*> [preauth]
162 | A0005 (size 14356): pam unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= <*>
163 | A0018 (size 1289): PAM <*> more authentication <*> logname= uid=0 euid=0 tty=ssh ruser= <*>
164 | A0024 (size 952): fatal: Read from socket failed: Connection reset by peer [preauth]
165 | A0019 (size 930): error: Received disconnect from 103.99.0.122: 14: No more user authentication methods available. [preauth]
166 | A0015 (size 838): Did not receive identification string from <*>
167 | A0017 (size 592): Received disconnect from <*> 11: Closed due to user request. [preauth]
168 | A0031 (size 497): Address <*> maps to <*> but this does not map back to the address - POSSIBLE BREAK-IN ATTEMPT!
169 | A0020 (size 182): Accepted password for <*> from <*> port <*> ssh2
170 | A0021 (size 182): pam unix(sshd:session): session opened for user <*> by (uid=0)
171 | A0022 (size 182): pam unix(sshd:session): session closed for user <*>
172 | A0016 (size 177): error: Received disconnect from <*> 3: com.jcraft.jsch.JSchException: Auth <*> [preauth]
173 | A0032 (size 108): Received disconnect from <*> 11: [preauth]
174 | A0050 (size 92): Received disconnect from 139.59.209.18: 11: Normal Shutdown, Thank you for playing [preauth]
175 | A0042 (size 87): Received disconnect from <*> 11: <*> <*> <*> [preauth]
176 | A0047 (size 60): Received disconnect from <*> 11: disconnect [preauth]
177 | A0028 (size 30): Invalid user <*> <*> from <*>
178 | A0029 (size 30): input userauth request: invalid user <*> <*> [preauth]
179 | A0030 (size 30): Failed password for invalid user <*> <*> from <*> port <*> ssh2
180 | A0036 (size 13): Invalid user from <*>
181 | A0037 (size 13): input userauth request: invalid user [preauth]
182 | A0038 (size 13): Failed <*> for invalid user from <*> port <*> ssh2
183 | A0035 (size 8): Bad protocol version identification <*> <*> <*> from <*> port <*>
184 | A0034 (size 7): Bad protocol version identification <*> <*> from <*> port <*>
185 | A0039 (size 7): Bad protocol version identification <*> from <*> port <*>
186 | A0033 (size 6): fatal: no hostkey alg [preauth]
187 | A0040 (size 6): error: Received disconnect from <*> <*> <*> <*> <*> <*> [preauth]
188 | A0044 (size 6): error: connect to <*> port 22: failed.
189 | A0048 (size 6): Server listening on <*> port <*>
190 | A0023 (size 3): fatal: Write failed: Connection reset by peer [preauth]
191 | A0043 (size 3): error: Received disconnect from 195.154.45.62: 3: com.jcraft.jsch.JSchException: timeout in waiting for rekeying process. [preauth]
192 | A0049 (size 3): Received disconnect from <*> 11: Disconnect requested by Windows SSH Client.
193 | A0025 (size 2): error: Received disconnect from 191.96.249.68: 13: User request [preauth]
194 | A0046 (size 2): error: Received disconnect from 212.83.176.1: 3: org.vngx.jsch.userauth.AuthCancelException: User authentication canceled by user [preauth]
195 | A0026 (size 1): Bad packet length 1819045217. [preauth]
196 | A0027 (size 1): Disconnecting: Packet corrupt [preauth]
197 | A0041 (size 1): Received disconnect from 67.160.100.130: 11:
198 | A0045 (size 1): Corrupted MAC on input. [preauth]
199 | A0051 (size 1): syslogin perform logout: logout() returned an error
200 |
201 | Java implementation:
202 | ====================
203 |
204 | ---- Done processing file. Total of 655147 lines, done in 1.588 s, 51 clusters
205 | 0010 (size 140768): Failed password for <*> from <*> port <*> ssh2
206 | 0009 (size 140701): pam unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= <*> <*>
207 | 0007 (size 68958): Connection closed by <*> [preauth]
208 | 0008 (size 46642): Received disconnect from <*> 11: <*> <*> <*>
209 | 0014 (size 37963): PAM service(sshd) ignoring max retries; <*> > 3
210 | 0012 (size 37298): Disconnecting: Too many authentication failures for <*> [preauth]
211 | 0013 (size 37029): PAM <*> more authentication <*> logname= uid=0 euid=0 tty=ssh ruser= <*> <*>
212 | 0011 (size 36967): message repeated <*> times: [ Failed password for <*> from <*> port <*> ssh2]
213 | 0006 (size 20241): Failed <*> for invalid user <*> from <*> port <*> ssh2
214 | 0004 (size 19852): pam unix(sshd:auth): check pass; user unknown
215 | 0001 (size 18909): reverse mapping checking getaddrinfo for <*> <*> failed - POSSIBLE BREAK-IN ATTEMPT!
216 | 0002 (size 14551): Invalid user <*> from <*>
217 | 0003 (size 14551): input userauth request: invalid user <*> [preauth]
218 | 0005 (size 14356): pam unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= <*>
219 | 0018 (size 1289): PAM <*> more authentication <*> logname= uid=0 euid=0 tty=ssh ruser= <*>
220 | 0024 (size 952): fatal: Read from socket failed: Connection reset by peer [preauth]
221 | 0019 (size 930): error: Received disconnect from 103.99.0.122: 14: No more user authentication methods available. [preauth]
222 | 0015 (size 838): Did not receive identification string from <*>
223 | 0017 (size 592): Received disconnect from <*> 11: Closed due to user request. [preauth]
224 | 0031 (size 497): Address <*> maps to <*> but this does not map back to the address - POSSIBLE BREAK-IN ATTEMPT!
225 | 0020 (size 182): Accepted password for <*> from <*> port <*> ssh2
226 | 0021 (size 182): pam unix(sshd:session): session opened for user <*> by (uid=0)
227 | 0022 (size 182): pam unix(sshd:session): session closed for user <*>
228 | 0016 (size 177): error: Received disconnect from <*> 3: com.jcraft.jsch.JSchException: Auth <*> [preauth]
229 | 0032 (size 108): Received disconnect from <*> 11: [preauth]
230 | 0050 (size 92): Received disconnect from 139.59.209.18: 11: Normal Shutdown, Thank you for playing [preauth]
231 | 0042 (size 87): Received disconnect from <*> 11: <*> <*> <*> [preauth]
232 | 0047 (size 60): Received disconnect from <*> 11: disconnect [preauth]
233 | 0028 (size 30): Invalid user <*> <*> from <*>
234 | 0029 (size 30): input userauth request: invalid user <*> <*> [preauth]
235 | 0030 (size 30): Failed password for invalid user <*> <*> from <*> port <*> ssh2
236 | 0036 (size 13): Invalid user from <*>
237 | 0037 (size 13): input userauth request: invalid user [preauth]
238 | 0038 (size 13): Failed <*> for invalid user from <*> port <*> ssh2
239 | 0035 (size 8): Bad protocol version identification <*> <*> <*> from <*> port <*>
240 | 0034 (size 7): Bad protocol version identification <*> <*> from <*> port <*>
241 | 0039 (size 7): Bad protocol version identification <*> from <*> port <*>
242 | 0033 (size 6): fatal: no hostkey alg [preauth]
243 | 0040 (size 6): error: Received disconnect from <*> <*> <*> <*> <*> <*> [preauth]
244 | 0044 (size 6): error: connect to <*> port 22: failed.
245 | 0048 (size 6): Server listening on <*> port <*>
246 | 0023 (size 3): fatal: Write failed: Connection reset by peer [preauth]
247 | 0043 (size 3): error: Received disconnect from 195.154.45.62: 3: com.jcraft.jsch.JSchException: timeout in waiting for rekeying process. [preauth]
248 | 0049 (size 3): Received disconnect from <*> 11: Disconnect requested by Windows SSH Client.
249 | 0025 (size 2): error: Received disconnect from 191.96.249.68: 13: User request [preauth]
250 | 0046 (size 2): error: Received disconnect from 212.83.176.1: 3: org.vngx.jsch.userauth.AuthCancelException: User authentication canceled by user [preauth]
251 | 0026 (size 1): Bad packet length 1819045217. [preauth]
252 | 0027 (size 1): Disconnecting: Packet corrupt [preauth]
253 | 0041 (size 1): Received disconnect from 67.160.100.130: 11:
254 | 0045 (size 1): Corrupted MAC on input. [preauth]
255 | 0051 (size 1): syslogin perform logout: logout() returned an error
256 | */
257 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*]
2 | charset = utf-8
3 | end_of_line = lf
4 | indent_size = 4
5 | indent_style = space
6 | insert_final_newline = false
7 | max_line_length = 120
8 | tab_width = 4
9 | ij_continuation_indent_size = 8
10 | ij_formatter_off_tag = @formatter:off
11 | ij_formatter_on_tag = @formatter:on
12 | ij_formatter_tags_enabled = true
13 | ij_smart_tabs = false
14 | ij_visual_guides = none
15 | ij_wrap_on_typing = false
16 |
17 | [*.css]
18 | ij_css_align_closing_brace_with_properties = false
19 | ij_css_blank_lines_around_nested_selector = 1
20 | ij_css_blank_lines_between_blocks = 1
21 | ij_css_block_comment_add_space = false
22 | ij_css_brace_placement = end_of_line
23 | ij_css_enforce_quotes_on_format = false
24 | ij_css_hex_color_long_format = false
25 | ij_css_hex_color_lower_case = false
26 | ij_css_hex_color_short_format = false
27 | ij_css_hex_color_upper_case = false
28 | ij_css_keep_blank_lines_in_code = 2
29 | ij_css_keep_indents_on_empty_lines = false
30 | ij_css_keep_single_line_blocks = false
31 | ij_css_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow
32 | ij_css_space_after_colon = true
33 | ij_css_space_before_opening_brace = true
34 | ij_css_use_double_quotes = true
35 | ij_css_value_alignment = do_not_align
36 |
37 | [*.feature]
38 | indent_size = 2
39 | ij_gherkin_keep_indents_on_empty_lines = false
40 |
41 | [*.less]
42 | indent_size = 2
43 | ij_less_align_closing_brace_with_properties = false
44 | ij_less_blank_lines_around_nested_selector = 1
45 | ij_less_blank_lines_between_blocks = 1
46 | ij_less_block_comment_add_space = false
47 | ij_less_brace_placement = 0
48 | ij_less_enforce_quotes_on_format = false
49 | ij_less_hex_color_long_format = false
50 | ij_less_hex_color_lower_case = false
51 | ij_less_hex_color_short_format = false
52 | ij_less_hex_color_upper_case = false
53 | ij_less_keep_blank_lines_in_code = 2
54 | ij_less_keep_indents_on_empty_lines = false
55 | ij_less_keep_single_line_blocks = false
56 | ij_less_line_comment_add_space = false
57 | ij_less_line_comment_at_first_column = false
58 | ij_less_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow
59 | ij_less_space_after_colon = true
60 | ij_less_space_before_opening_brace = true
61 | ij_less_use_double_quotes = true
62 | ij_less_value_alignment = 0
63 |
64 | [*.proto]
65 | indent_size = 2
66 | tab_width = 2
67 | ij_continuation_indent_size = 4
68 | ij_protobuf_keep_blank_lines_in_code = 2
69 | ij_protobuf_keep_indents_on_empty_lines = false
70 | ij_protobuf_keep_line_breaks = true
71 | ij_protobuf_space_after_comma = true
72 | ij_protobuf_space_before_comma = false
73 | ij_protobuf_spaces_around_assignment_operators = true
74 | ij_protobuf_spaces_within_braces = false
75 | ij_protobuf_spaces_within_brackets = false
76 |
77 | [*.rs]
78 | max_line_length = 100
79 | ij_continuation_indent_size = 4
80 | ij_rust_align_multiline_chained_methods = false
81 | ij_rust_align_multiline_parameters = true
82 | ij_rust_align_multiline_parameters_in_calls = true
83 | ij_rust_align_ret_type = true
84 | ij_rust_align_type_params = false
85 | ij_rust_align_where_bounds = true
86 | ij_rust_align_where_clause = false
87 | ij_rust_allow_one_line_match = false
88 | ij_rust_block_comment_at_first_column = false
89 | ij_rust_indent_where_clause = true
90 | ij_rust_keep_blank_lines_in_code = 2
91 | ij_rust_keep_blank_lines_in_declarations = 2
92 | ij_rust_keep_indents_on_empty_lines = false
93 | ij_rust_keep_line_breaks = true
94 | ij_rust_line_comment_add_space = true
95 | ij_rust_line_comment_at_first_column = false
96 | ij_rust_min_number_of_blanks_between_items = 1
97 | ij_rust_preserve_punctuation = false
98 | ij_rust_spaces_around_assoc_type_binding = false
99 |
100 | [*.sass]
101 | indent_size = 2
102 | ij_sass_align_closing_brace_with_properties = false
103 | ij_sass_blank_lines_around_nested_selector = 1
104 | ij_sass_blank_lines_between_blocks = 1
105 | ij_sass_brace_placement = 0
106 | ij_sass_enforce_quotes_on_format = false
107 | ij_sass_hex_color_long_format = false
108 | ij_sass_hex_color_lower_case = false
109 | ij_sass_hex_color_short_format = false
110 | ij_sass_hex_color_upper_case = false
111 | ij_sass_keep_blank_lines_in_code = 2
112 | ij_sass_keep_indents_on_empty_lines = false
113 | ij_sass_keep_single_line_blocks = false
114 | ij_sass_line_comment_add_space = false
115 | ij_sass_line_comment_at_first_column = false
116 | ij_sass_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow
117 | ij_sass_space_after_colon = true
118 | ij_sass_space_before_opening_brace = true
119 | ij_sass_use_double_quotes = true
120 | ij_sass_value_alignment = 0
121 |
122 | [*.scss]
123 | indent_size = 2
124 | ij_scss_align_closing_brace_with_properties = false
125 | ij_scss_blank_lines_around_nested_selector = 1
126 | ij_scss_blank_lines_between_blocks = 1
127 | ij_scss_block_comment_add_space = false
128 | ij_scss_brace_placement = 0
129 | ij_scss_enforce_quotes_on_format = false
130 | ij_scss_hex_color_long_format = false
131 | ij_scss_hex_color_lower_case = false
132 | ij_scss_hex_color_short_format = false
133 | ij_scss_hex_color_upper_case = false
134 | ij_scss_keep_blank_lines_in_code = 2
135 | ij_scss_keep_indents_on_empty_lines = false
136 | ij_scss_keep_single_line_blocks = false
137 | ij_scss_line_comment_add_space = false
138 | ij_scss_line_comment_at_first_column = false
139 | ij_scss_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow
140 | ij_scss_space_after_colon = true
141 | ij_scss_space_before_opening_brace = true
142 | ij_scss_use_double_quotes = true
143 | ij_scss_value_alignment = 0
144 |
145 | [*.vue]
146 | indent_size = 2
147 | tab_width = 2
148 | ij_continuation_indent_size = 4
149 | ij_vue_indent_children_of_top_level = template
150 | ij_vue_interpolation_new_line_after_start_delimiter = true
151 | ij_vue_interpolation_new_line_before_end_delimiter = true
152 | ij_vue_interpolation_wrap = off
153 | ij_vue_keep_indents_on_empty_lines = false
154 | ij_vue_spaces_within_interpolation_expressions = true
155 |
156 | [.editorconfig]
157 | ij_editorconfig_align_group_field_declarations = false
158 | ij_editorconfig_space_after_colon = false
159 | ij_editorconfig_space_after_comma = true
160 | ij_editorconfig_space_before_colon = false
161 | ij_editorconfig_space_before_comma = false
162 | ij_editorconfig_spaces_around_assignment_operators = true
163 |
164 | [{*.ad,*.adoc,*.asciidoc,.asciidoctorconfig}]
165 | ij_asciidoc_blank_lines_after_header = 1
166 | ij_asciidoc_blank_lines_keep_after_header = 1
167 | ij_asciidoc_formatting_enabled = true
168 | ij_asciidoc_one_sentence_per_line = true
169 |
170 | [{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.pom,*.rng,*.tld,*.wadl,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul}]
171 | ij_xml_align_attributes = true
172 | ij_xml_align_text = false
173 | ij_xml_attribute_wrap = normal
174 | ij_xml_block_comment_add_space = false
175 | ij_xml_block_comment_at_first_column = false
176 | ij_xml_keep_blank_lines = 2
177 | ij_xml_keep_indents_on_empty_lines = false
178 | ij_xml_keep_line_breaks = true
179 | ij_xml_keep_line_breaks_in_text = true
180 | ij_xml_keep_whitespaces = false
181 | ij_xml_keep_whitespaces_around_cdata = preserve
182 | ij_xml_keep_whitespaces_inside_cdata = false
183 | ij_xml_line_comment_at_first_column = false
184 | ij_xml_space_after_tag_name = false
185 | ij_xml_space_around_equals_in_attribute = false
186 | ij_xml_space_inside_empty_tag = false
187 | ij_xml_text_wrap = normal
188 |
189 | [{*.ats,*.cts,*.mts,*.ts}]
190 | ij_continuation_indent_size = 4
191 | ij_typescript_align_imports = false
192 | ij_typescript_align_multiline_array_initializer_expression = false
193 | ij_typescript_align_multiline_binary_operation = false
194 | ij_typescript_align_multiline_chained_methods = false
195 | ij_typescript_align_multiline_extends_list = false
196 | ij_typescript_align_multiline_for = true
197 | ij_typescript_align_multiline_parameters = true
198 | ij_typescript_align_multiline_parameters_in_calls = false
199 | ij_typescript_align_multiline_ternary_operation = false
200 | ij_typescript_align_object_properties = 0
201 | ij_typescript_align_union_types = false
202 | ij_typescript_align_var_statements = 0
203 | ij_typescript_array_initializer_new_line_after_left_brace = false
204 | ij_typescript_array_initializer_right_brace_on_new_line = false
205 | ij_typescript_array_initializer_wrap = off
206 | ij_typescript_assignment_wrap = off
207 | ij_typescript_binary_operation_sign_on_next_line = false
208 | ij_typescript_binary_operation_wrap = off
209 | ij_typescript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/**
210 | ij_typescript_blank_lines_after_imports = 1
211 | ij_typescript_blank_lines_around_class = 1
212 | ij_typescript_blank_lines_around_field = 0
213 | ij_typescript_blank_lines_around_field_in_interface = 0
214 | ij_typescript_blank_lines_around_function = 1
215 | ij_typescript_blank_lines_around_method = 1
216 | ij_typescript_blank_lines_around_method_in_interface = 1
217 | ij_typescript_block_brace_style = end_of_line
218 | ij_typescript_block_comment_add_space = false
219 | ij_typescript_block_comment_at_first_column = true
220 | ij_typescript_call_parameters_new_line_after_left_paren = false
221 | ij_typescript_call_parameters_right_paren_on_new_line = false
222 | ij_typescript_call_parameters_wrap = off
223 | ij_typescript_catch_on_new_line = false
224 | ij_typescript_chained_call_dot_on_new_line = true
225 | ij_typescript_class_brace_style = end_of_line
226 | ij_typescript_comma_on_new_line = false
227 | ij_typescript_do_while_brace_force = never
228 | ij_typescript_else_on_new_line = false
229 | ij_typescript_enforce_trailing_comma = keep
230 | ij_typescript_enum_constants_wrap = on_every_item
231 | ij_typescript_extends_keyword_wrap = off
232 | ij_typescript_extends_list_wrap = off
233 | ij_typescript_field_prefix = _
234 | ij_typescript_file_name_style = relaxed
235 | ij_typescript_finally_on_new_line = false
236 | ij_typescript_for_brace_force = never
237 | ij_typescript_for_statement_new_line_after_left_paren = false
238 | ij_typescript_for_statement_right_paren_on_new_line = false
239 | ij_typescript_for_statement_wrap = off
240 | ij_typescript_force_quote_style = false
241 | ij_typescript_force_semicolon_style = false
242 | ij_typescript_function_expression_brace_style = end_of_line
243 | ij_typescript_if_brace_force = never
244 | ij_typescript_import_merge_members = global
245 | ij_typescript_import_prefer_absolute_path = global
246 | ij_typescript_import_sort_members = true
247 | ij_typescript_import_sort_module_name = false
248 | ij_typescript_import_use_node_resolution = true
249 | ij_typescript_imports_wrap = on_every_item
250 | ij_typescript_indent_case_from_switch = true
251 | ij_typescript_indent_chained_calls = true
252 | ij_typescript_indent_package_children = 0
253 | ij_typescript_jsdoc_include_types = false
254 | ij_typescript_jsx_attribute_value = braces
255 | ij_typescript_keep_blank_lines_in_code = 2
256 | ij_typescript_keep_first_column_comment = true
257 | ij_typescript_keep_indents_on_empty_lines = false
258 | ij_typescript_keep_line_breaks = true
259 | ij_typescript_keep_simple_blocks_in_one_line = false
260 | ij_typescript_keep_simple_methods_in_one_line = false
261 | ij_typescript_line_comment_add_space = true
262 | ij_typescript_line_comment_at_first_column = false
263 | ij_typescript_method_brace_style = end_of_line
264 | ij_typescript_method_call_chain_wrap = off
265 | ij_typescript_method_parameters_new_line_after_left_paren = false
266 | ij_typescript_method_parameters_right_paren_on_new_line = false
267 | ij_typescript_method_parameters_wrap = off
268 | ij_typescript_object_literal_wrap = on_every_item
269 | ij_typescript_parentheses_expression_new_line_after_left_paren = false
270 | ij_typescript_parentheses_expression_right_paren_on_new_line = false
271 | ij_typescript_place_assignment_sign_on_next_line = false
272 | ij_typescript_prefer_as_type_cast = false
273 | ij_typescript_prefer_explicit_types_function_expression_returns = false
274 | ij_typescript_prefer_explicit_types_function_returns = false
275 | ij_typescript_prefer_explicit_types_vars_fields = false
276 | ij_typescript_prefer_parameters_wrap = false
277 | ij_typescript_reformat_c_style_comments = false
278 | ij_typescript_space_after_colon = true
279 | ij_typescript_space_after_comma = true
280 | ij_typescript_space_after_dots_in_rest_parameter = false
281 | ij_typescript_space_after_generator_mult = true
282 | ij_typescript_space_after_property_colon = true
283 | ij_typescript_space_after_quest = true
284 | ij_typescript_space_after_type_colon = true
285 | ij_typescript_space_after_unary_not = false
286 | ij_typescript_space_before_async_arrow_lparen = true
287 | ij_typescript_space_before_catch_keyword = true
288 | ij_typescript_space_before_catch_left_brace = true
289 | ij_typescript_space_before_catch_parentheses = true
290 | ij_typescript_space_before_class_lbrace = true
291 | ij_typescript_space_before_class_left_brace = true
292 | ij_typescript_space_before_colon = true
293 | ij_typescript_space_before_comma = false
294 | ij_typescript_space_before_do_left_brace = true
295 | ij_typescript_space_before_else_keyword = true
296 | ij_typescript_space_before_else_left_brace = true
297 | ij_typescript_space_before_finally_keyword = true
298 | ij_typescript_space_before_finally_left_brace = true
299 | ij_typescript_space_before_for_left_brace = true
300 | ij_typescript_space_before_for_parentheses = true
301 | ij_typescript_space_before_for_semicolon = false
302 | ij_typescript_space_before_function_left_parenth = true
303 | ij_typescript_space_before_generator_mult = false
304 | ij_typescript_space_before_if_left_brace = true
305 | ij_typescript_space_before_if_parentheses = true
306 | ij_typescript_space_before_method_call_parentheses = false
307 | ij_typescript_space_before_method_left_brace = true
308 | ij_typescript_space_before_method_parentheses = false
309 | ij_typescript_space_before_property_colon = false
310 | ij_typescript_space_before_quest = true
311 | ij_typescript_space_before_switch_left_brace = true
312 | ij_typescript_space_before_switch_parentheses = true
313 | ij_typescript_space_before_try_left_brace = true
314 | ij_typescript_space_before_type_colon = false
315 | ij_typescript_space_before_unary_not = false
316 | ij_typescript_space_before_while_keyword = true
317 | ij_typescript_space_before_while_left_brace = true
318 | ij_typescript_space_before_while_parentheses = true
319 | ij_typescript_spaces_around_additive_operators = true
320 | ij_typescript_spaces_around_arrow_function_operator = true
321 | ij_typescript_spaces_around_assignment_operators = true
322 | ij_typescript_spaces_around_bitwise_operators = true
323 | ij_typescript_spaces_around_equality_operators = true
324 | ij_typescript_spaces_around_logical_operators = true
325 | ij_typescript_spaces_around_multiplicative_operators = true
326 | ij_typescript_spaces_around_relational_operators = true
327 | ij_typescript_spaces_around_shift_operators = true
328 | ij_typescript_spaces_around_unary_operator = false
329 | ij_typescript_spaces_within_array_initializer_brackets = false
330 | ij_typescript_spaces_within_brackets = false
331 | ij_typescript_spaces_within_catch_parentheses = false
332 | ij_typescript_spaces_within_for_parentheses = false
333 | ij_typescript_spaces_within_if_parentheses = false
334 | ij_typescript_spaces_within_imports = false
335 | ij_typescript_spaces_within_interpolation_expressions = false
336 | ij_typescript_spaces_within_method_call_parentheses = false
337 | ij_typescript_spaces_within_method_parentheses = false
338 | ij_typescript_spaces_within_object_literal_braces = false
339 | ij_typescript_spaces_within_object_type_braces = true
340 | ij_typescript_spaces_within_parentheses = false
341 | ij_typescript_spaces_within_switch_parentheses = false
342 | ij_typescript_spaces_within_type_assertion = false
343 | ij_typescript_spaces_within_union_types = true
344 | ij_typescript_spaces_within_while_parentheses = false
345 | ij_typescript_special_else_if_treatment = true
346 | ij_typescript_ternary_operation_signs_on_next_line = false
347 | ij_typescript_ternary_operation_wrap = off
348 | ij_typescript_union_types_wrap = on_every_item
349 | ij_typescript_use_chained_calls_group_indents = false
350 | ij_typescript_use_double_quotes = true
351 | ij_typescript_use_explicit_js_extension = auto
352 | ij_typescript_use_path_mapping = always
353 | ij_typescript_use_public_modifier = false
354 | ij_typescript_use_semicolon_after_statement = true
355 | ij_typescript_var_declaration_wrap = normal
356 | ij_typescript_while_brace_force = never
357 | ij_typescript_while_on_new_line = false
358 | ij_typescript_wrap_comments = false
359 |
360 | [{*.bash,*.sh,*.zsh}]
361 | indent_size = 2
362 | tab_width = 2
363 | ij_shell_binary_ops_start_line = false
364 | ij_shell_keep_column_alignment_padding = false
365 | ij_shell_minify_program = false
366 | ij_shell_redirect_followed_by_space = false
367 | ij_shell_switch_cases_indented = false
368 | ij_shell_use_unix_line_separator = true
369 |
370 | [{*.cjs,*.js}]
371 | ij_continuation_indent_size = 4
372 | ij_javascript_align_imports = false
373 | ij_javascript_align_multiline_array_initializer_expression = false
374 | ij_javascript_align_multiline_binary_operation = false
375 | ij_javascript_align_multiline_chained_methods = false
376 | ij_javascript_align_multiline_extends_list = false
377 | ij_javascript_align_multiline_for = true
378 | ij_javascript_align_multiline_parameters = true
379 | ij_javascript_align_multiline_parameters_in_calls = false
380 | ij_javascript_align_multiline_ternary_operation = false
381 | ij_javascript_align_object_properties = 0
382 | ij_javascript_align_union_types = false
383 | ij_javascript_align_var_statements = 0
384 | ij_javascript_array_initializer_new_line_after_left_brace = false
385 | ij_javascript_array_initializer_right_brace_on_new_line = false
386 | ij_javascript_array_initializer_wrap = off
387 | ij_javascript_assignment_wrap = off
388 | ij_javascript_binary_operation_sign_on_next_line = false
389 | ij_javascript_binary_operation_wrap = off
390 | ij_javascript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/**
391 | ij_javascript_blank_lines_after_imports = 1
392 | ij_javascript_blank_lines_around_class = 1
393 | ij_javascript_blank_lines_around_field = 0
394 | ij_javascript_blank_lines_around_function = 1
395 | ij_javascript_blank_lines_around_method = 1
396 | ij_javascript_block_brace_style = end_of_line
397 | ij_javascript_block_comment_add_space = false
398 | ij_javascript_block_comment_at_first_column = true
399 | ij_javascript_call_parameters_new_line_after_left_paren = false
400 | ij_javascript_call_parameters_right_paren_on_new_line = false
401 | ij_javascript_call_parameters_wrap = off
402 | ij_javascript_catch_on_new_line = false
403 | ij_javascript_chained_call_dot_on_new_line = true
404 | ij_javascript_class_brace_style = end_of_line
405 | ij_javascript_comma_on_new_line = false
406 | ij_javascript_do_while_brace_force = never
407 | ij_javascript_else_on_new_line = false
408 | ij_javascript_enforce_trailing_comma = keep
409 | ij_javascript_extends_keyword_wrap = off
410 | ij_javascript_extends_list_wrap = off
411 | ij_javascript_field_prefix = _
412 | ij_javascript_file_name_style = relaxed
413 | ij_javascript_finally_on_new_line = false
414 | ij_javascript_for_brace_force = never
415 | ij_javascript_for_statement_new_line_after_left_paren = false
416 | ij_javascript_for_statement_right_paren_on_new_line = false
417 | ij_javascript_for_statement_wrap = off
418 | ij_javascript_force_quote_style = false
419 | ij_javascript_force_semicolon_style = false
420 | ij_javascript_function_expression_brace_style = end_of_line
421 | ij_javascript_if_brace_force = never
422 | ij_javascript_import_merge_members = global
423 | ij_javascript_import_prefer_absolute_path = global
424 | ij_javascript_import_sort_members = true
425 | ij_javascript_import_sort_module_name = false
426 | ij_javascript_import_use_node_resolution = true
427 | ij_javascript_imports_wrap = on_every_item
428 | ij_javascript_indent_case_from_switch = true
429 | ij_javascript_indent_chained_calls = true
430 | ij_javascript_indent_package_children = 0
431 | ij_javascript_jsx_attribute_value = braces
432 | ij_javascript_keep_blank_lines_in_code = 2
433 | ij_javascript_keep_first_column_comment = false
434 | ij_javascript_keep_indents_on_empty_lines = false
435 | ij_javascript_keep_line_breaks = true
436 | ij_javascript_keep_simple_blocks_in_one_line = false
437 | ij_javascript_keep_simple_methods_in_one_line = false
438 | ij_javascript_line_comment_add_space = true
439 | ij_javascript_line_comment_at_first_column = false
440 | ij_javascript_method_brace_style = end_of_line
441 | ij_javascript_method_call_chain_wrap = off
442 | ij_javascript_method_parameters_new_line_after_left_paren = false
443 | ij_javascript_method_parameters_right_paren_on_new_line = false
444 | ij_javascript_method_parameters_wrap = off
445 | ij_javascript_object_literal_wrap = on_every_item
446 | ij_javascript_parentheses_expression_new_line_after_left_paren = false
447 | ij_javascript_parentheses_expression_right_paren_on_new_line = false
448 | ij_javascript_place_assignment_sign_on_next_line = false
449 | ij_javascript_prefer_as_type_cast = false
450 | ij_javascript_prefer_explicit_types_function_expression_returns = false
451 | ij_javascript_prefer_explicit_types_function_returns = false
452 | ij_javascript_prefer_explicit_types_vars_fields = false
453 | ij_javascript_prefer_parameters_wrap = false
454 | ij_javascript_reformat_c_style_comments = false
455 | ij_javascript_space_after_colon = true
456 | ij_javascript_space_after_comma = true
457 | ij_javascript_space_after_dots_in_rest_parameter = false
458 | ij_javascript_space_after_generator_mult = true
459 | ij_javascript_space_after_property_colon = true
460 | ij_javascript_space_after_quest = true
461 | ij_javascript_space_after_type_colon = true
462 | ij_javascript_space_after_unary_not = false
463 | ij_javascript_space_before_async_arrow_lparen = true
464 | ij_javascript_space_before_catch_keyword = true
465 | ij_javascript_space_before_catch_left_brace = true
466 | ij_javascript_space_before_catch_parentheses = true
467 | ij_javascript_space_before_class_lbrace = true
468 | ij_javascript_space_before_class_left_brace = true
469 | ij_javascript_space_before_colon = true
470 | ij_javascript_space_before_comma = false
471 | ij_javascript_space_before_do_left_brace = true
472 | ij_javascript_space_before_else_keyword = true
473 | ij_javascript_space_before_else_left_brace = true
474 | ij_javascript_space_before_finally_keyword = true
475 | ij_javascript_space_before_finally_left_brace = true
476 | ij_javascript_space_before_for_left_brace = true
477 | ij_javascript_space_before_for_parentheses = true
478 | ij_javascript_space_before_for_semicolon = false
479 | ij_javascript_space_before_function_left_parenth = true
480 | ij_javascript_space_before_generator_mult = false
481 | ij_javascript_space_before_if_left_brace = true
482 | ij_javascript_space_before_if_parentheses = true
483 | ij_javascript_space_before_method_call_parentheses = false
484 | ij_javascript_space_before_method_left_brace = true
485 | ij_javascript_space_before_method_parentheses = false
486 | ij_javascript_space_before_property_colon = false
487 | ij_javascript_space_before_quest = true
488 | ij_javascript_space_before_switch_left_brace = true
489 | ij_javascript_space_before_switch_parentheses = true
490 | ij_javascript_space_before_try_left_brace = true
491 | ij_javascript_space_before_type_colon = false
492 | ij_javascript_space_before_unary_not = false
493 | ij_javascript_space_before_while_keyword = true
494 | ij_javascript_space_before_while_left_brace = true
495 | ij_javascript_space_before_while_parentheses = true
496 | ij_javascript_spaces_around_additive_operators = true
497 | ij_javascript_spaces_around_arrow_function_operator = true
498 | ij_javascript_spaces_around_assignment_operators = true
499 | ij_javascript_spaces_around_bitwise_operators = true
500 | ij_javascript_spaces_around_equality_operators = true
501 | ij_javascript_spaces_around_logical_operators = true
502 | ij_javascript_spaces_around_multiplicative_operators = true
503 | ij_javascript_spaces_around_relational_operators = true
504 | ij_javascript_spaces_around_shift_operators = true
505 | ij_javascript_spaces_around_unary_operator = false
506 | ij_javascript_spaces_within_array_initializer_brackets = false
507 | ij_javascript_spaces_within_brackets = false
508 | ij_javascript_spaces_within_catch_parentheses = false
509 | ij_javascript_spaces_within_for_parentheses = false
510 | ij_javascript_spaces_within_if_parentheses = false
511 | ij_javascript_spaces_within_imports = false
512 | ij_javascript_spaces_within_interpolation_expressions = false
513 | ij_javascript_spaces_within_method_call_parentheses = false
514 | ij_javascript_spaces_within_method_parentheses = false
515 | ij_javascript_spaces_within_object_literal_braces = false
516 | ij_javascript_spaces_within_object_type_braces = true
517 | ij_javascript_spaces_within_parentheses = false
518 | ij_javascript_spaces_within_switch_parentheses = false
519 | ij_javascript_spaces_within_type_assertion = false
520 | ij_javascript_spaces_within_union_types = true
521 | ij_javascript_spaces_within_while_parentheses = false
522 | ij_javascript_special_else_if_treatment = true
523 | ij_javascript_ternary_operation_signs_on_next_line = false
524 | ij_javascript_ternary_operation_wrap = off
525 | ij_javascript_union_types_wrap = on_every_item
526 | ij_javascript_use_chained_calls_group_indents = false
527 | ij_javascript_use_double_quotes = true
528 | ij_javascript_use_explicit_js_extension = auto
529 | ij_javascript_use_path_mapping = always
530 | ij_javascript_use_public_modifier = false
531 | ij_javascript_use_semicolon_after_statement = true
532 | ij_javascript_var_declaration_wrap = normal
533 | ij_javascript_while_brace_force = never
534 | ij_javascript_while_on_new_line = false
535 | ij_javascript_wrap_comments = false
536 |
537 | [{*.erb,*.rhtml}]
538 | indent_size = 2
539 | tab_width = 2
540 | ij_continuation_indent_size = 2
541 | ij_rhtml_keep_indents_on_empty_lines = false
542 |
543 | [{*.ft,*.vm,*.vsl}]
544 | ij_vtl_keep_indents_on_empty_lines = false
545 |
546 | [{*.gant,*.groovy,*.gy}]
547 | ij_groovy_align_group_field_declarations = false
548 | ij_groovy_align_multiline_array_initializer_expression = false
549 | ij_groovy_align_multiline_assignment = false
550 | ij_groovy_align_multiline_binary_operation = false
551 | ij_groovy_align_multiline_chained_methods = false
552 | ij_groovy_align_multiline_extends_list = false
553 | ij_groovy_align_multiline_for = true
554 | ij_groovy_align_multiline_list_or_map = true
555 | ij_groovy_align_multiline_method_parentheses = false
556 | ij_groovy_align_multiline_parameters = true
557 | ij_groovy_align_multiline_parameters_in_calls = false
558 | ij_groovy_align_multiline_resources = true
559 | ij_groovy_align_multiline_ternary_operation = false
560 | ij_groovy_align_multiline_throws_list = false
561 | ij_groovy_align_named_args_in_map = true
562 | ij_groovy_align_throws_keyword = false
563 | ij_groovy_array_initializer_new_line_after_left_brace = false
564 | ij_groovy_array_initializer_right_brace_on_new_line = false
565 | ij_groovy_array_initializer_wrap = off
566 | ij_groovy_assert_statement_wrap = off
567 | ij_groovy_assignment_wrap = off
568 | ij_groovy_binary_operation_wrap = off
569 | ij_groovy_blank_lines_after_class_header = 0
570 | ij_groovy_blank_lines_after_imports = 1
571 | ij_groovy_blank_lines_after_package = 1
572 | ij_groovy_blank_lines_around_class = 1
573 | ij_groovy_blank_lines_around_field = 0
574 | ij_groovy_blank_lines_around_field_in_interface = 0
575 | ij_groovy_blank_lines_around_method = 1
576 | ij_groovy_blank_lines_around_method_in_interface = 1
577 | ij_groovy_blank_lines_before_imports = 1
578 | ij_groovy_blank_lines_before_method_body = 0
579 | ij_groovy_blank_lines_before_package = 0
580 | ij_groovy_block_brace_style = end_of_line
581 | ij_groovy_block_comment_add_space = false
582 | ij_groovy_block_comment_at_first_column = true
583 | ij_groovy_call_parameters_new_line_after_left_paren = false
584 | ij_groovy_call_parameters_right_paren_on_new_line = false
585 | ij_groovy_call_parameters_wrap = off
586 | ij_groovy_catch_on_new_line = false
587 | ij_groovy_class_annotation_wrap = split_into_lines
588 | ij_groovy_class_brace_style = end_of_line
589 | ij_groovy_class_count_to_use_import_on_demand = 5
590 | ij_groovy_do_while_brace_force = never
591 | ij_groovy_else_on_new_line = false
592 | ij_groovy_enable_groovydoc_formatting = true
593 | ij_groovy_enum_constants_wrap = off
594 | ij_groovy_extends_keyword_wrap = off
595 | ij_groovy_extends_list_wrap = off
596 | ij_groovy_field_annotation_wrap = split_into_lines
597 | ij_groovy_finally_on_new_line = false
598 | ij_groovy_for_brace_force = never
599 | ij_groovy_for_statement_new_line_after_left_paren = false
600 | ij_groovy_for_statement_right_paren_on_new_line = false
601 | ij_groovy_for_statement_wrap = off
602 | ij_groovy_ginq_general_clause_wrap_policy = 2
603 | ij_groovy_ginq_having_wrap_policy = 1
604 | ij_groovy_ginq_indent_having_clause = true
605 | ij_groovy_ginq_indent_on_clause = true
606 | ij_groovy_ginq_on_wrap_policy = 1
607 | ij_groovy_ginq_space_after_keyword = true
608 | ij_groovy_if_brace_force = never
609 | ij_groovy_import_annotation_wrap = 2
610 | ij_groovy_imports_layout = *,|,javax.**,java.**,|,$*
611 | ij_groovy_indent_case_from_switch = true
612 | ij_groovy_indent_label_blocks = true
613 | ij_groovy_insert_inner_class_imports = false
614 | ij_groovy_keep_blank_lines_before_right_brace = 2
615 | ij_groovy_keep_blank_lines_in_code = 2
616 | ij_groovy_keep_blank_lines_in_declarations = 2
617 | ij_groovy_keep_control_statement_in_one_line = true
618 | ij_groovy_keep_first_column_comment = false
619 | ij_groovy_keep_indents_on_empty_lines = false
620 | ij_groovy_keep_line_breaks = true
621 | ij_groovy_keep_multiple_expressions_in_one_line = false
622 | ij_groovy_keep_simple_blocks_in_one_line = false
623 | ij_groovy_keep_simple_classes_in_one_line = true
624 | ij_groovy_keep_simple_lambdas_in_one_line = true
625 | ij_groovy_keep_simple_methods_in_one_line = true
626 | ij_groovy_label_indent_absolute = false
627 | ij_groovy_label_indent_size = 0
628 | ij_groovy_lambda_brace_style = end_of_line
629 | ij_groovy_layout_static_imports_separately = true
630 | ij_groovy_line_comment_add_space = false
631 | ij_groovy_line_comment_add_space_on_reformat = false
632 | ij_groovy_line_comment_at_first_column = true
633 | ij_groovy_method_annotation_wrap = split_into_lines
634 | ij_groovy_method_brace_style = end_of_line
635 | ij_groovy_method_call_chain_wrap = off
636 | ij_groovy_method_parameters_new_line_after_left_paren = false
637 | ij_groovy_method_parameters_right_paren_on_new_line = false
638 | ij_groovy_method_parameters_wrap = off
639 | ij_groovy_modifier_list_wrap = false
640 | ij_groovy_names_count_to_use_import_on_demand = 3
641 | ij_groovy_packages_to_use_import_on_demand = java.awt.*,javax.swing.*
642 | ij_groovy_parameter_annotation_wrap = off
643 | ij_groovy_parentheses_expression_new_line_after_left_paren = false
644 | ij_groovy_parentheses_expression_right_paren_on_new_line = false
645 | ij_groovy_prefer_parameters_wrap = false
646 | ij_groovy_resource_list_new_line_after_left_paren = false
647 | ij_groovy_resource_list_right_paren_on_new_line = false
648 | ij_groovy_resource_list_wrap = off
649 | ij_groovy_space_after_assert_separator = true
650 | ij_groovy_space_after_colon = true
651 | ij_groovy_space_after_comma = true
652 | ij_groovy_space_after_comma_in_type_arguments = true
653 | ij_groovy_space_after_for_semicolon = true
654 | ij_groovy_space_after_quest = true
655 | ij_groovy_space_after_type_cast = true
656 | ij_groovy_space_before_annotation_parameter_list = false
657 | ij_groovy_space_before_array_initializer_left_brace = false
658 | ij_groovy_space_before_assert_separator = false
659 | ij_groovy_space_before_catch_keyword = true
660 | ij_groovy_space_before_catch_left_brace = true
661 | ij_groovy_space_before_catch_parentheses = true
662 | ij_groovy_space_before_class_left_brace = true
663 | ij_groovy_space_before_closure_left_brace = true
664 | ij_groovy_space_before_colon = true
665 | ij_groovy_space_before_comma = false
666 | ij_groovy_space_before_do_left_brace = true
667 | ij_groovy_space_before_else_keyword = true
668 | ij_groovy_space_before_else_left_brace = true
669 | ij_groovy_space_before_finally_keyword = true
670 | ij_groovy_space_before_finally_left_brace = true
671 | ij_groovy_space_before_for_left_brace = true
672 | ij_groovy_space_before_for_parentheses = true
673 | ij_groovy_space_before_for_semicolon = false
674 | ij_groovy_space_before_if_left_brace = true
675 | ij_groovy_space_before_if_parentheses = true
676 | ij_groovy_space_before_method_call_parentheses = false
677 | ij_groovy_space_before_method_left_brace = true
678 | ij_groovy_space_before_method_parentheses = false
679 | ij_groovy_space_before_quest = true
680 | ij_groovy_space_before_record_parentheses = false
681 | ij_groovy_space_before_switch_left_brace = true
682 | ij_groovy_space_before_switch_parentheses = true
683 | ij_groovy_space_before_synchronized_left_brace = true
684 | ij_groovy_space_before_synchronized_parentheses = true
685 | ij_groovy_space_before_try_left_brace = true
686 | ij_groovy_space_before_try_parentheses = true
687 | ij_groovy_space_before_while_keyword = true
688 | ij_groovy_space_before_while_left_brace = true
689 | ij_groovy_space_before_while_parentheses = true
690 | ij_groovy_space_in_named_argument = true
691 | ij_groovy_space_in_named_argument_before_colon = false
692 | ij_groovy_space_within_empty_array_initializer_braces = false
693 | ij_groovy_space_within_empty_method_call_parentheses = false
694 | ij_groovy_spaces_around_additive_operators = true
695 | ij_groovy_spaces_around_assignment_operators = true
696 | ij_groovy_spaces_around_bitwise_operators = true
697 | ij_groovy_spaces_around_equality_operators = true
698 | ij_groovy_spaces_around_lambda_arrow = true
699 | ij_groovy_spaces_around_logical_operators = true
700 | ij_groovy_spaces_around_multiplicative_operators = true
701 | ij_groovy_spaces_around_regex_operators = true
702 | ij_groovy_spaces_around_relational_operators = true
703 | ij_groovy_spaces_around_shift_operators = true
704 | ij_groovy_spaces_within_annotation_parentheses = false
705 | ij_groovy_spaces_within_array_initializer_braces = false
706 | ij_groovy_spaces_within_braces = true
707 | ij_groovy_spaces_within_brackets = false
708 | ij_groovy_spaces_within_cast_parentheses = false
709 | ij_groovy_spaces_within_catch_parentheses = false
710 | ij_groovy_spaces_within_for_parentheses = false
711 | ij_groovy_spaces_within_gstring_injection_braces = false
712 | ij_groovy_spaces_within_if_parentheses = false
713 | ij_groovy_spaces_within_list_or_map = false
714 | ij_groovy_spaces_within_method_call_parentheses = false
715 | ij_groovy_spaces_within_method_parentheses = false
716 | ij_groovy_spaces_within_parentheses = false
717 | ij_groovy_spaces_within_switch_parentheses = false
718 | ij_groovy_spaces_within_synchronized_parentheses = false
719 | ij_groovy_spaces_within_try_parentheses = false
720 | ij_groovy_spaces_within_tuple_expression = false
721 | ij_groovy_spaces_within_while_parentheses = false
722 | ij_groovy_special_else_if_treatment = true
723 | ij_groovy_ternary_operation_wrap = off
724 | ij_groovy_throws_keyword_wrap = off
725 | ij_groovy_throws_list_wrap = off
726 | ij_groovy_use_flying_geese_braces = false
727 | ij_groovy_use_fq_class_names = false
728 | ij_groovy_use_fq_class_names_in_javadoc = true
729 | ij_groovy_use_relative_indents = false
730 | ij_groovy_use_single_class_imports = true
731 | ij_groovy_variable_annotation_wrap = off
732 | ij_groovy_while_brace_force = never
733 | ij_groovy_while_on_new_line = false
734 | ij_groovy_wrap_chain_calls_after_dot = false
735 | ij_groovy_wrap_long_lines = false
736 |
737 | [{*.gemspec,*.jbuilder,*.rake,*.rb,*.rbi,*.rbw,*.ru,*.thor,.simplecov,capfile,gemfile,guardfile,isolate,rakefile,steepfile,vagrantfile}]
738 | ij_ruby_align_group_field_declarations = false
739 | ij_ruby_align_multiline_parameters = true
740 | ij_ruby_blank_lines_around_class = 1
741 | ij_ruby_blank_lines_around_method = 1
742 | ij_ruby_chain_calls_alignment = 2
743 | ij_ruby_convert_brace_block_by_enter = true
744 | ij_ruby_empty_declarations_style = 1
745 | ij_ruby_force_newlines_around_visibility_mods = true
746 | ij_ruby_indent_private_methods = false
747 | ij_ruby_indent_protected_methods = false
748 | ij_ruby_indent_public_methods = false
749 | ij_ruby_indent_visibility_modifiers = true
750 | ij_ruby_indent_when_cases = false
751 | ij_ruby_keep_blank_lines_in_code = 1
752 | ij_ruby_keep_blank_lines_in_declarations = 1
753 | ij_ruby_keep_line_breaks = true
754 | ij_ruby_parentheses_around_method_arguments = true
755 | ij_ruby_spaces_around_assignment_operators = true
756 | ij_ruby_spaces_around_hashrocket = true
757 | ij_ruby_spaces_around_other_operators = true
758 | ij_ruby_spaces_around_range_operators = false
759 | ij_ruby_spaces_around_relational_operators = true
760 | ij_ruby_spaces_within_array_initializer_braces = true
761 | ij_ruby_spaces_within_braces = true
762 | ij_ruby_spaces_within_pipes = false
763 | ij_ruby_use_external_formatter = false
764 |
765 | [{*.gradle.kts,*.kt,*.kts,*.main.kts,*.space.kts}]
766 | ij_kotlin_align_in_columns_case_branch = false
767 | ij_kotlin_align_multiline_binary_operation = false
768 | ij_kotlin_align_multiline_extends_list = false
769 | ij_kotlin_align_multiline_method_parentheses = false
770 | ij_kotlin_align_multiline_parameters = true
771 | ij_kotlin_align_multiline_parameters_in_calls = false
772 | ij_kotlin_allow_trailing_comma = false
773 | ij_kotlin_allow_trailing_comma_on_call_site = false
774 | ij_kotlin_assignment_wrap = normal
775 | ij_kotlin_blank_lines_after_class_header = 0
776 | ij_kotlin_blank_lines_around_block_when_branches = 0
777 | ij_kotlin_blank_lines_before_declaration_with_comment_or_annotation_on_separate_line = 1
778 | ij_kotlin_block_comment_add_space = false
779 | ij_kotlin_block_comment_at_first_column = false
780 | ij_kotlin_call_parameters_new_line_after_left_paren = true
781 | ij_kotlin_call_parameters_right_paren_on_new_line = true
782 | ij_kotlin_call_parameters_wrap = on_every_item
783 | ij_kotlin_catch_on_new_line = false
784 | ij_kotlin_class_annotation_wrap = split_into_lines
785 | ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL
786 | ij_kotlin_continuation_indent_for_chained_calls = false
787 | ij_kotlin_continuation_indent_for_expression_bodies = false
788 | ij_kotlin_continuation_indent_in_argument_lists = false
789 | ij_kotlin_continuation_indent_in_elvis = false
790 | ij_kotlin_continuation_indent_in_if_conditions = false
791 | ij_kotlin_continuation_indent_in_parameter_lists = false
792 | ij_kotlin_continuation_indent_in_supertype_lists = false
793 | ij_kotlin_else_on_new_line = false
794 | ij_kotlin_enum_constants_wrap = off
795 | ij_kotlin_extends_list_wrap = normal
796 | ij_kotlin_field_annotation_wrap = split_into_lines
797 | ij_kotlin_finally_on_new_line = false
798 | ij_kotlin_if_rparen_on_new_line = true
799 | ij_kotlin_import_nested_classes = false
800 | ij_kotlin_imports_layout = *,java.**,javax.**,kotlin.**,^
801 | ij_kotlin_insert_whitespaces_in_simple_one_line_method = true
802 | ij_kotlin_keep_blank_lines_before_right_brace = 2
803 | ij_kotlin_keep_blank_lines_in_code = 2
804 | ij_kotlin_keep_blank_lines_in_declarations = 2
805 | ij_kotlin_keep_first_column_comment = false
806 | ij_kotlin_keep_indents_on_empty_lines = false
807 | ij_kotlin_keep_line_breaks = true
808 | ij_kotlin_lbrace_on_next_line = false
809 | ij_kotlin_line_break_after_multiline_when_entry = true
810 | ij_kotlin_line_comment_add_space = true
811 | ij_kotlin_line_comment_add_space_on_reformat = true
812 | ij_kotlin_line_comment_at_first_column = false
813 | ij_kotlin_method_annotation_wrap = split_into_lines
814 | ij_kotlin_method_call_chain_wrap = normal
815 | ij_kotlin_method_parameters_new_line_after_left_paren = true
816 | ij_kotlin_method_parameters_right_paren_on_new_line = true
817 | ij_kotlin_method_parameters_wrap = on_every_item
818 | ij_kotlin_name_count_to_use_star_import = 5
819 | ij_kotlin_name_count_to_use_star_import_for_members = 3
820 | ij_kotlin_packages_to_use_import_on_demand = java.util.*,kotlinx.android.synthetic.**,io.ktor.**
821 | ij_kotlin_parameter_annotation_wrap = off
822 | ij_kotlin_space_after_comma = true
823 | ij_kotlin_space_after_extend_colon = true
824 | ij_kotlin_space_after_type_colon = true
825 | ij_kotlin_space_before_catch_parentheses = true
826 | ij_kotlin_space_before_comma = false
827 | ij_kotlin_space_before_extend_colon = true
828 | ij_kotlin_space_before_for_parentheses = true
829 | ij_kotlin_space_before_if_parentheses = true
830 | ij_kotlin_space_before_lambda_arrow = true
831 | ij_kotlin_space_before_type_colon = false
832 | ij_kotlin_space_before_when_parentheses = true
833 | ij_kotlin_space_before_while_parentheses = true
834 | ij_kotlin_spaces_around_additive_operators = true
835 | ij_kotlin_spaces_around_assignment_operators = true
836 | ij_kotlin_spaces_around_equality_operators = true
837 | ij_kotlin_spaces_around_function_type_arrow = true
838 | ij_kotlin_spaces_around_logical_operators = true
839 | ij_kotlin_spaces_around_multiplicative_operators = true
840 | ij_kotlin_spaces_around_range = false
841 | ij_kotlin_spaces_around_relational_operators = true
842 | ij_kotlin_spaces_around_unary_operator = false
843 | ij_kotlin_spaces_around_when_arrow = true
844 | ij_kotlin_variable_annotation_wrap = off
845 | ij_kotlin_while_on_new_line = false
846 | ij_kotlin_wrap_elvis_expressions = 1
847 | ij_kotlin_wrap_expression_body_functions = 1
848 | ij_kotlin_wrap_first_method_in_call_chain = false
849 |
850 | [{*.har,*.jsb2,*.jsb3,*.json,.babelrc,.eslintrc,.prettierrc,.stylelintrc,bowerrc,jest.config}]
851 | indent_size = 2
852 | ij_json_array_wrapping = split_into_lines
853 | ij_json_keep_blank_lines_in_code = 0
854 | ij_json_keep_indents_on_empty_lines = false
855 | ij_json_keep_line_breaks = true
856 | ij_json_keep_trailing_comma = false
857 | ij_json_object_wrapping = split_into_lines
858 | ij_json_property_alignment = do_not_align
859 | ij_json_space_after_colon = true
860 | ij_json_space_after_comma = true
861 | ij_json_space_before_colon = false
862 | ij_json_space_before_comma = false
863 | ij_json_spaces_within_braces = false
864 | ij_json_spaces_within_brackets = false
865 | ij_json_wrap_long_lines = false
866 |
867 | [{*.htm,*.html,*.ng,*.sht,*.shtm,*.shtml}]
868 | ij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3
869 | ij_html_align_attributes = true
870 | ij_html_align_text = false
871 | ij_html_attribute_wrap = normal
872 | ij_html_block_comment_add_space = false
873 | ij_html_block_comment_at_first_column = false
874 | ij_html_do_not_align_children_of_min_lines = 0
875 | ij_html_do_not_break_if_inline_tags = title,h1,h2,h3,h4,h5,h6,p
876 | ij_html_do_not_indent_children_of_tags = html,body,thead,tbody,tfoot
877 | ij_html_enforce_quotes = false
878 | ij_html_inline_tags = a,abbr,acronym,b,basefont,bdo,big,br,cite,cite,code,dfn,em,font,i,img,input,kbd,label,q,s,samp,select,small,span,strike,strong,sub,sup,textarea,tt,u,var
879 | ij_html_keep_blank_lines = 2
880 | ij_html_keep_indents_on_empty_lines = false
881 | ij_html_keep_line_breaks = true
882 | ij_html_keep_line_breaks_in_text = true
883 | ij_html_keep_whitespaces = false
884 | ij_html_keep_whitespaces_inside = span,pre,textarea
885 | ij_html_line_comment_at_first_column = false
886 | ij_html_new_line_after_last_attribute = never
887 | ij_html_new_line_before_first_attribute = never
888 | ij_html_quote_style = double
889 | ij_html_remove_new_line_before_tags = br
890 | ij_html_space_after_tag_name = false
891 | ij_html_space_around_equality_in_attribute = false
892 | ij_html_space_inside_empty_tag = false
893 | ij_html_text_wrap = normal
894 |
895 | [{*.http,*.rest}]
896 | indent_size = 0
897 | ij_continuation_indent_size = 4
898 | ij_http-request_call_parameters_wrap = normal
899 |
900 | [{*.java,*.jbang}]
901 | ij_java_align_consecutive_assignments = false
902 | ij_java_align_consecutive_variable_declarations = false
903 | ij_java_align_group_field_declarations = false
904 | ij_java_align_multiline_annotation_parameters = false
905 | ij_java_align_multiline_array_initializer_expression = false
906 | ij_java_align_multiline_assignment = false
907 | ij_java_align_multiline_binary_operation = false
908 | ij_java_align_multiline_chained_methods = false
909 | ij_java_align_multiline_deconstruction_list_components = true
910 | ij_java_align_multiline_extends_list = false
911 | ij_java_align_multiline_for = true
912 | ij_java_align_multiline_method_parentheses = false
913 | ij_java_align_multiline_parameters = true
914 | ij_java_align_multiline_parameters_in_calls = false
915 | ij_java_align_multiline_parenthesized_expression = false
916 | ij_java_align_multiline_records = true
917 | ij_java_align_multiline_resources = true
918 | ij_java_align_multiline_ternary_operation = false
919 | ij_java_align_multiline_text_blocks = false
920 | ij_java_align_multiline_throws_list = false
921 | ij_java_align_subsequent_simple_methods = false
922 | ij_java_align_throws_keyword = false
923 | ij_java_align_types_in_multi_catch = true
924 | ij_java_annotation_parameter_wrap = off
925 | ij_java_array_initializer_new_line_after_left_brace = false
926 | ij_java_array_initializer_right_brace_on_new_line = false
927 | ij_java_array_initializer_wrap = off
928 | ij_java_assert_statement_colon_on_next_line = false
929 | ij_java_assert_statement_wrap = off
930 | ij_java_assignment_wrap = off
931 | ij_java_binary_operation_sign_on_next_line = false
932 | ij_java_binary_operation_wrap = off
933 | ij_java_blank_lines_after_anonymous_class_header = 0
934 | ij_java_blank_lines_after_class_header = 0
935 | ij_java_blank_lines_after_imports = 1
936 | ij_java_blank_lines_after_package = 1
937 | ij_java_blank_lines_around_class = 1
938 | ij_java_blank_lines_around_field = 0
939 | ij_java_blank_lines_around_field_in_interface = 0
940 | ij_java_blank_lines_around_initializer = 1
941 | ij_java_blank_lines_around_method = 1
942 | ij_java_blank_lines_around_method_in_interface = 1
943 | ij_java_blank_lines_before_class_end = 0
944 | ij_java_blank_lines_before_imports = 1
945 | ij_java_blank_lines_before_method_body = 0
946 | ij_java_blank_lines_before_package = 0
947 | ij_java_block_brace_style = end_of_line
948 | ij_java_block_comment_add_space = false
949 | ij_java_block_comment_at_first_column = false
950 | ij_java_builder_methods = none
951 | ij_java_call_parameters_new_line_after_left_paren = false
952 | ij_java_call_parameters_right_paren_on_new_line = false
953 | ij_java_call_parameters_wrap = off
954 | ij_java_case_statement_on_separate_line = true
955 | ij_java_catch_on_new_line = false
956 | ij_java_class_annotation_wrap = split_into_lines
957 | ij_java_class_brace_style = end_of_line
958 | ij_java_class_count_to_use_import_on_demand = 5
959 | ij_java_class_names_in_javadoc = 1
960 | ij_java_deconstruction_list_wrap = normal
961 | ij_java_do_not_indent_top_level_class_members = false
962 | ij_java_do_not_wrap_after_single_annotation = false
963 | ij_java_do_not_wrap_after_single_annotation_in_parameter = false
964 | ij_java_do_while_brace_force = never
965 | ij_java_doc_add_blank_line_after_description = true
966 | ij_java_doc_add_blank_line_after_param_comments = false
967 | ij_java_doc_add_blank_line_after_return = false
968 | ij_java_doc_add_p_tag_on_empty_lines = true
969 | ij_java_doc_align_exception_comments = true
970 | ij_java_doc_align_param_comments = true
971 | ij_java_doc_do_not_wrap_if_one_line = false
972 | ij_java_doc_enable_formatting = true
973 | ij_java_doc_enable_leading_asterisks = true
974 | ij_java_doc_indent_on_continuation = false
975 | ij_java_doc_keep_empty_lines = true
976 | ij_java_doc_keep_empty_parameter_tag = true
977 | ij_java_doc_keep_empty_return_tag = true
978 | ij_java_doc_keep_empty_throws_tag = true
979 | ij_java_doc_keep_invalid_tags = true
980 | ij_java_doc_param_description_on_new_line = false
981 | ij_java_doc_preserve_line_breaks = false
982 | ij_java_doc_use_throws_not_exception_tag = true
983 | ij_java_else_on_new_line = false
984 | ij_java_entity_dd_suffix = EJB
985 | ij_java_entity_eb_suffix = Bean
986 | ij_java_entity_hi_suffix = Home
987 | ij_java_entity_lhi_prefix = Local
988 | ij_java_entity_lhi_suffix = Home
989 | ij_java_entity_li_prefix = Local
990 | ij_java_entity_pk_class = java.lang.String
991 | ij_java_entity_vo_suffix = VO
992 | ij_java_enum_constants_wrap = off
993 | ij_java_extends_keyword_wrap = off
994 | ij_java_extends_list_wrap = off
995 | ij_java_field_annotation_wrap = split_into_lines
996 | ij_java_finally_on_new_line = false
997 | ij_java_for_brace_force = never
998 | ij_java_for_statement_new_line_after_left_paren = false
999 | ij_java_for_statement_right_paren_on_new_line = false
1000 | ij_java_for_statement_wrap = off
1001 | ij_java_generate_final_locals = false
1002 | ij_java_generate_final_parameters = false
1003 | ij_java_if_brace_force = never
1004 | ij_java_imports_layout = *,|,javax.**,java.**,|,$*
1005 | ij_java_indent_case_from_switch = true
1006 | ij_java_insert_inner_class_imports = false
1007 | ij_java_insert_override_annotation = true
1008 | ij_java_keep_blank_lines_before_right_brace = 2
1009 | ij_java_keep_blank_lines_between_package_declaration_and_header = 2
1010 | ij_java_keep_blank_lines_in_code = 2
1011 | ij_java_keep_blank_lines_in_declarations = 2
1012 | ij_java_keep_builder_methods_indents = false
1013 | ij_java_keep_control_statement_in_one_line = true
1014 | ij_java_keep_first_column_comment = false
1015 | ij_java_keep_indents_on_empty_lines = false
1016 | ij_java_keep_line_breaks = true
1017 | ij_java_keep_multiple_expressions_in_one_line = false
1018 | ij_java_keep_simple_blocks_in_one_line = false
1019 | ij_java_keep_simple_classes_in_one_line = false
1020 | ij_java_keep_simple_lambdas_in_one_line = false
1021 | ij_java_keep_simple_methods_in_one_line = false
1022 | ij_java_label_indent_absolute = false
1023 | ij_java_label_indent_size = 0
1024 | ij_java_lambda_brace_style = end_of_line
1025 | ij_java_layout_static_imports_separately = true
1026 | ij_java_line_comment_add_space = true
1027 | ij_java_line_comment_add_space_on_reformat = true
1028 | ij_java_line_comment_at_first_column = false
1029 | ij_java_message_dd_suffix = EJB
1030 | ij_java_message_eb_suffix = Bean
1031 | ij_java_method_annotation_wrap = split_into_lines
1032 | ij_java_method_brace_style = end_of_line
1033 | ij_java_method_call_chain_wrap = off
1034 | ij_java_method_parameters_new_line_after_left_paren = false
1035 | ij_java_method_parameters_right_paren_on_new_line = false
1036 | ij_java_method_parameters_wrap = off
1037 | ij_java_modifier_list_wrap = false
1038 | ij_java_multi_catch_types_wrap = normal
1039 | ij_java_names_count_to_use_import_on_demand = 3
1040 | ij_java_new_line_after_lparen_in_annotation = false
1041 | ij_java_new_line_after_lparen_in_deconstruction_pattern = true
1042 | ij_java_new_line_after_lparen_in_record_header = false
1043 | ij_java_packages_to_use_import_on_demand = java.awt.*,javax.swing.*
1044 | ij_java_parameter_annotation_wrap = off
1045 | ij_java_parentheses_expression_new_line_after_left_paren = false
1046 | ij_java_parentheses_expression_right_paren_on_new_line = false
1047 | ij_java_place_assignment_sign_on_next_line = false
1048 | ij_java_prefer_longer_names = true
1049 | ij_java_prefer_parameters_wrap = false
1050 | ij_java_record_components_wrap = normal
1051 | ij_java_repeat_synchronized = true
1052 | ij_java_replace_instanceof_and_cast = false
1053 | ij_java_replace_null_check = true
1054 | ij_java_replace_sum_lambda_with_method_ref = true
1055 | ij_java_resource_list_new_line_after_left_paren = false
1056 | ij_java_resource_list_right_paren_on_new_line = false
1057 | ij_java_resource_list_wrap = off
1058 | ij_java_rparen_on_new_line_in_annotation = false
1059 | ij_java_rparen_on_new_line_in_deconstruction_pattern = true
1060 | ij_java_rparen_on_new_line_in_record_header = false
1061 | ij_java_session_dd_suffix = EJB
1062 | ij_java_session_eb_suffix = Bean
1063 | ij_java_session_hi_suffix = Home
1064 | ij_java_session_lhi_prefix = Local
1065 | ij_java_session_lhi_suffix = Home
1066 | ij_java_session_li_prefix = Local
1067 | ij_java_session_si_suffix = Service
1068 | ij_java_space_after_closing_angle_bracket_in_type_argument = false
1069 | ij_java_space_after_colon = true
1070 | ij_java_space_after_comma = true
1071 | ij_java_space_after_comma_in_type_arguments = true
1072 | ij_java_space_after_for_semicolon = true
1073 | ij_java_space_after_quest = true
1074 | ij_java_space_after_type_cast = true
1075 | ij_java_space_before_annotation_array_initializer_left_brace = false
1076 | ij_java_space_before_annotation_parameter_list = false
1077 | ij_java_space_before_array_initializer_left_brace = false
1078 | ij_java_space_before_catch_keyword = true
1079 | ij_java_space_before_catch_left_brace = true
1080 | ij_java_space_before_catch_parentheses = true
1081 | ij_java_space_before_class_left_brace = true
1082 | ij_java_space_before_colon = true
1083 | ij_java_space_before_colon_in_foreach = true
1084 | ij_java_space_before_comma = false
1085 | ij_java_space_before_deconstruction_list = false
1086 | ij_java_space_before_do_left_brace = true
1087 | ij_java_space_before_else_keyword = true
1088 | ij_java_space_before_else_left_brace = true
1089 | ij_java_space_before_finally_keyword = true
1090 | ij_java_space_before_finally_left_brace = true
1091 | ij_java_space_before_for_left_brace = true
1092 | ij_java_space_before_for_parentheses = true
1093 | ij_java_space_before_for_semicolon = false
1094 | ij_java_space_before_if_left_brace = true
1095 | ij_java_space_before_if_parentheses = true
1096 | ij_java_space_before_method_call_parentheses = false
1097 | ij_java_space_before_method_left_brace = true
1098 | ij_java_space_before_method_parentheses = false
1099 | ij_java_space_before_opening_angle_bracket_in_type_parameter = false
1100 | ij_java_space_before_quest = true
1101 | ij_java_space_before_switch_left_brace = true
1102 | ij_java_space_before_switch_parentheses = true
1103 | ij_java_space_before_synchronized_left_brace = true
1104 | ij_java_space_before_synchronized_parentheses = true
1105 | ij_java_space_before_try_left_brace = true
1106 | ij_java_space_before_try_parentheses = true
1107 | ij_java_space_before_type_parameter_list = false
1108 | ij_java_space_before_while_keyword = true
1109 | ij_java_space_before_while_left_brace = true
1110 | ij_java_space_before_while_parentheses = true
1111 | ij_java_space_inside_one_line_enum_braces = false
1112 | ij_java_space_within_empty_array_initializer_braces = false
1113 | ij_java_space_within_empty_method_call_parentheses = false
1114 | ij_java_space_within_empty_method_parentheses = false
1115 | ij_java_spaces_around_additive_operators = true
1116 | ij_java_spaces_around_annotation_eq = true
1117 | ij_java_spaces_around_assignment_operators = true
1118 | ij_java_spaces_around_bitwise_operators = true
1119 | ij_java_spaces_around_equality_operators = true
1120 | ij_java_spaces_around_lambda_arrow = true
1121 | ij_java_spaces_around_logical_operators = true
1122 | ij_java_spaces_around_method_ref_dbl_colon = false
1123 | ij_java_spaces_around_multiplicative_operators = true
1124 | ij_java_spaces_around_relational_operators = true
1125 | ij_java_spaces_around_shift_operators = true
1126 | ij_java_spaces_around_type_bounds_in_type_parameters = true
1127 | ij_java_spaces_around_unary_operator = false
1128 | ij_java_spaces_within_angle_brackets = false
1129 | ij_java_spaces_within_annotation_parentheses = false
1130 | ij_java_spaces_within_array_initializer_braces = false
1131 | ij_java_spaces_within_braces = false
1132 | ij_java_spaces_within_brackets = false
1133 | ij_java_spaces_within_cast_parentheses = false
1134 | ij_java_spaces_within_catch_parentheses = false
1135 | ij_java_spaces_within_deconstruction_list = false
1136 | ij_java_spaces_within_for_parentheses = false
1137 | ij_java_spaces_within_if_parentheses = false
1138 | ij_java_spaces_within_method_call_parentheses = false
1139 | ij_java_spaces_within_method_parentheses = false
1140 | ij_java_spaces_within_parentheses = false
1141 | ij_java_spaces_within_record_header = false
1142 | ij_java_spaces_within_switch_parentheses = false
1143 | ij_java_spaces_within_synchronized_parentheses = false
1144 | ij_java_spaces_within_try_parentheses = false
1145 | ij_java_spaces_within_while_parentheses = false
1146 | ij_java_special_else_if_treatment = true
1147 | ij_java_subclass_name_suffix = Impl
1148 | ij_java_ternary_operation_signs_on_next_line = false
1149 | ij_java_ternary_operation_wrap = off
1150 | ij_java_test_name_suffix = Test
1151 | ij_java_throws_keyword_wrap = off
1152 | ij_java_throws_list_wrap = off
1153 | ij_java_use_external_annotations = false
1154 | ij_java_use_fq_class_names = false
1155 | ij_java_use_relative_indents = false
1156 | ij_java_use_single_class_imports = true
1157 | ij_java_variable_annotation_wrap = off
1158 | ij_java_visibility = public
1159 | ij_java_while_brace_force = never
1160 | ij_java_while_on_new_line = false
1161 | ij_java_wrap_comments = false
1162 | ij_java_wrap_first_method_in_call_chain = false
1163 | ij_java_wrap_long_lines = false
1164 |
1165 | [{*.jsf,*.jsp,*.jspf,*.tag,*.tagf,*.xjsp}]
1166 | ij_jsp_jsp_prefer_comma_separated_import_list = false
1167 | ij_jsp_keep_indents_on_empty_lines = false
1168 |
1169 | [{*.jspx,*.tagx}]
1170 | ij_jspx_keep_indents_on_empty_lines = false
1171 |
1172 | [{*.markdown,*.md}]
1173 | ij_markdown_force_one_space_after_blockquote_symbol = true
1174 | ij_markdown_force_one_space_after_header_symbol = true
1175 | ij_markdown_force_one_space_after_list_bullet = true
1176 | ij_markdown_force_one_space_between_words = true
1177 | ij_markdown_format_tables = true
1178 | ij_markdown_insert_quote_arrows_on_wrap = true
1179 | ij_markdown_keep_indents_on_empty_lines = false
1180 | ij_markdown_keep_line_breaks_inside_text_blocks = true
1181 | ij_markdown_max_lines_around_block_elements = 1
1182 | ij_markdown_max_lines_around_header = 1
1183 | ij_markdown_max_lines_between_paragraphs = 1
1184 | ij_markdown_min_lines_around_block_elements = 1
1185 | ij_markdown_min_lines_around_header = 1
1186 | ij_markdown_min_lines_between_paragraphs = 1
1187 | ij_markdown_wrap_text_if_long = true
1188 | ij_markdown_wrap_text_inside_blockquotes = true
1189 |
1190 | [{*.pb,*.textproto}]
1191 | indent_size = 2
1192 | tab_width = 2
1193 | ij_continuation_indent_size = 4
1194 | ij_prototext_keep_blank_lines_in_code = 2
1195 | ij_prototext_keep_indents_on_empty_lines = false
1196 | ij_prototext_keep_line_breaks = true
1197 | ij_prototext_space_after_colon = true
1198 | ij_prototext_space_after_comma = true
1199 | ij_prototext_space_before_colon = false
1200 | ij_prototext_space_before_comma = false
1201 | ij_prototext_spaces_within_braces = true
1202 | ij_prototext_spaces_within_brackets = false
1203 |
1204 | [{*.properties,spring.handlers,spring.schemas}]
1205 | ij_properties_align_group_field_declarations = false
1206 | ij_properties_keep_blank_lines = false
1207 | ij_properties_key_value_delimiter = equals
1208 | ij_properties_spaces_around_key_value_delimiter = false
1209 |
1210 | [{*.qute.htm,*.qute.html,*.qute.json,*.qute.txt,*.qute.yaml,*.qute.yml}]
1211 | ij_qute_keep_indents_on_empty_lines = false
1212 |
1213 | [{*.toml,Cargo.lock,Cargo.toml.orig,Gopkg.lock,Pipfile,poetry.lock}]
1214 | ij_toml_keep_indents_on_empty_lines = false
1215 |
1216 | [{*.yaml,*.yml}]
1217 | indent_size = 2
1218 | ij_yaml_align_values_properties = do_not_align
1219 | ij_yaml_autoinsert_sequence_marker = true
1220 | ij_yaml_block_mapping_on_new_line = false
1221 | ij_yaml_indent_sequence_value = true
1222 | ij_yaml_keep_indents_on_empty_lines = false
1223 | ij_yaml_keep_line_breaks = true
1224 | ij_yaml_sequence_on_new_line = false
1225 | ij_yaml_space_before_colon = false
1226 | ij_yaml_spaces_within_braces = true
1227 | ij_yaml_spaces_within_brackets = true
1228 |
--------------------------------------------------------------------------------