Skip to content

Commit 6205e27

Browse files
author
anders-wartoft
committed
- SelectFilter added. Use a regular expression to specify what to keep in each event
- GapDetection will now print gaps each time the statistics is printed. Events that appear out of order will now be reflected without need to shut down the receiver.
1 parent 1e70f92 commit 6205e27

File tree

10 files changed

+187
-13
lines changed

10 files changed

+187
-13
lines changed

README.md

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ java -jar target/LogGenerator-with-dependencies.jar -i kafka -icn test2 -itn tes
1616
When running the last command, press Ctrl-C to see the gaps in the received data. Since we started the counter on 100, there should at least be one gap: 1-99.
1717

1818
### Latest Release notes
19-
#### 1.04
20-
- Bug fixe in output item factory. Missing TCP and File outputs.
19+
#### 1.05
20+
- SelectFilter added. Use a regular expression to specify what to keep in each event
21+
- GapDetection will now print gaps each time the statistics is printed. Events that appear out of order will now be reflected without need to shut down the receiver.
2122

2223
### Input modules:
2324
There are input module for the following tasks:
@@ -285,6 +286,22 @@ Parameters: `-he {header text}`
285286

286287
Example: `-he {syslog-header}`, `-he "My custom header with date: {date:yyyyMMdd}: "`
287288

289+
### Select by regex
290+
If you want to filter out everything but a specific part of an event, you can use the SelectFilter.
291+
Specify what to keep with a group in a regular expression.
292+
293+
Parameters: `-se {regex with a capture group to keep}`
294+
295+
Example: `-se '-(\d+}$'`
296+
297+
If you have events that are formatted like this:
298+
299+
`[Sat Dec 03 00:35:57.399 Usb Host Notification Apple80211Set: seqNum 5460 Total 1 chg 0 en0]`
300+
301+
and for example want to extract the first word after the timestamp, you can use the following parameters:
302+
303+
`-se '\.\d{3} (\S+) '`
304+
288305
#### Replace by regex
289306
A use case is if you have a lot of nice logs, but the date is not possible to use. You can load the file and add a regex to find the date, then replace the date with a {date:...} variable or a static string.
290307
There must be a capture group in the regex. The text matched by the capture group will be replaced by the value.
@@ -337,6 +354,35 @@ Example: `-gd "<(\d+)> -dd true -gdjr true"`
337354
A good use of gap detection is to send events over unreliable media and check if all events were delivered.
338355
To assure that the gap detection is on, start the counter on the sending side with a number that is larger than 1. In that case, the gap detector will produce at least one gap (1-your start number).
339356

357+
A special use case is to continuously monitor for missed events and also to be able to react to events that come out of order and not report them as missing.
358+
For example, log events are generated with:
359+
360+
`java -jar LogGenerator.jar -i counter -string test- -o udp -oh localhost -op 9999 -e 10 -s true`
361+
362+
`-e 100` will limit the generated events to 100 every second.
363+
364+
You can now start a server and monitor for missing events.
365+
366+
`java -jar LogGenerator.jar -i udp -ip 9999 -gd '-(\d+)$' -cgd true -o null -s true`
367+
368+
If you start the server after the client you will have some missing events (after 30 seconds), for example:
369+
```
370+
Transaction: transferred 439 lines in 43939 milliseconds, 0,010 kEPS 0,001 MBPS
371+
Gaps found: 1.
372+
1-442
373+
Number of unique received numbers: 439
374+
Next expected number: 882
375+
```
376+
If you now restart the client and wait another 30 seconds, you should se a smaller gap, or none at all.
377+
```
378+
Transaction transfer: TRANSACTION transferred 285 lines in 28391 milliseconds, 0,010 kEPS 0,001 MBPS
379+
Gaps found: 1.
380+
286-442
381+
Number of unique received numbers: 724
382+
Next expected number: 882
383+
```
384+
So, the gap detection can detect events that should have been delivered earlier, and remove them from the gaps.
385+
340386
### Variables
341387
#### Date
342388
A Date variable will take the current time and format according to a Java

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
<groupId>nu.sitia.LogGenerator</groupId>
66
<artifactId>LogGenerator</artifactId>
7-
<version>1.04-SNAPSHOT</version>
7+
<version>1.05-SNAPSHOT</version>
88
<packaging>jar</packaging>
99

1010
<name>LogGenerator</name>

src/main/java/nu/sitia/loggenerator/Configuration.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ public class Configuration {
8282
keys.add(new Item("-ib", "--input-batch-size", "How many rows to read before sending to processing"));
8383
keys.add(new Item("-ob", "--output-batch-size", "How many rows to write (new line separated) every time the send method is called"));
8484
keys.add(new Item("-gd", "--gap-detection", "Regex to find the event serial number. In the regex, the first capture group must be the number, e.g. \"<(\\d+)>\""));
85+
keys.add(new Item("-cgd", "--continuous-gap-detection", "Should Gap Detection be printed every time a statistics report is printed? Valid values are: false, true. Default is false"));
8586
keys.add(new Item("-dd", "--duplicate-detection", "If -gd (--gap-detection) is enabled, use this flag to also get a report on all serial numbers that occurs mote than once. Valid values are: false, true"));
8687
keys.add(new Item("-gdjr", "--gap-detection-json-report", "Should the gap detector report in JSON format instead of printable format? Valid values are: false, true"));
8788

@@ -110,6 +111,7 @@ public class Configuration {
110111
keys.add(new Item("-eiq", "--elastic-input-query", "A query string used to get the response from Elastic"));
111112
keys.add(new Item("-jf", "--json-filter", "Get a specific node in the JSON input. Use . to traverse the JSON input. If the returned object is an array, the array items are sent in the event chain as new events"));
112113
keys.add(new Item("-jfp", "--json-file-path", "Get a specific node in the JSON file input. Use . to traverse the JSON input. If the returned object is an array, the array items are sent in the event chain as new events"));
114+
keys.add(new Item("-se", "--select", "Remove everything except what matches the next argument's first group. Example -se 'userid:(\\S+)'"));
113115
}
114116
static final Map<String, String> standardVariables = new HashMap<>();
115117
static {

src/main/java/nu/sitia/loggenerator/ItemProxy.java

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
package nu.sitia.loggenerator;
1919

20+
import nu.sitia.loggenerator.filter.GapDetectionFilter;
2021
import nu.sitia.loggenerator.filter.ProcessFilter;
2122
import nu.sitia.loggenerator.inputitems.InputItem;
2223
import nu.sitia.loggenerator.outputitems.OutputItem;
@@ -64,6 +65,11 @@ public class ItemProxy {
6465
/** If -t time:xxx, this is the start time + xxx */
6566
private long endTime = 0;
6667

68+
/** If we have a gapDetector and the flag -cgd is true, then show
69+
* gaps every time the statistics has been printed.
70+
*/
71+
private ProcessFilter gapDetector = null;
72+
6773
/**
6874
* Default constructor
6975
* @param input Input to use
@@ -113,6 +119,15 @@ public ItemProxy(InputItem input, OutputItem output, List<ProcessFilter> filterL
113119
shutdownHandlers.add((ShutdownHandler) output);
114120
}
115121

122+
String isContinousGapDetectionString = config.getValue("-cgd");
123+
if (isContinousGapDetectionString != null && isContinousGapDetectionString.equalsIgnoreCase("true")) {
124+
gapDetector = getGapDetector(filterList);
125+
if (null == gapDetector) {
126+
throw new RuntimeException("The flag -cgd cannot be used without a GapDetector (-gd)");
127+
}
128+
}
129+
130+
116131
this.sentEvents = 0;
117132

118133
// Ctrl-C
@@ -133,6 +148,18 @@ public ItemProxy(InputItem input, OutputItem output, List<ProcessFilter> filterL
133148

134149
}
135150

151+
152+
/**
153+
* Search the list of filters and, if present, return a GapDetecttionFilter
154+
* @param filterList a List<filter> to search
155+
* @return GapDetectionFilter or null
156+
*/
157+
private ProcessFilter getGapDetector(List<ProcessFilter>filterList) {
158+
return filterList.stream().reduce(null, (_sofar, element) ->
159+
element instanceof GapDetectionFilter ? element: null
160+
);
161+
}
162+
136163
/**
137164
* Main loop. Pump messages from the input, modify and then
138165
* write to the output. Batching may be done in the input and
@@ -166,7 +193,11 @@ public void pump() {
166193
sentEvents += toSend.size();
167194

168195
if (statistics != null) {
169-
statistics.calculateStatistics(filtered);
196+
boolean hasPrinted = statistics.calculateStatistics(filtered);
197+
if (hasPrinted && null != gapDetector) {
198+
// Also, print the gapDetection periodically
199+
System.out.println(((GapDetectionFilter)gapDetector).getDetector().toString());
200+
}
170201
}
171202
// Should we throttle the output to lower the eps?
172203
throttle(statistics);

src/main/java/nu/sitia/loggenerator/filter/FilterListFactory.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ public List<ProcessFilter> create(Configuration config) {
4242
String doubleDetection = config.getValue("-dd");
4343
String jsonFilter = config.getValue("-jf");
4444
String jsonReport = config.getValue("-gdjr");
45+
String select = config.getValue("-se");
4546

4647
boolean dd = "true".equalsIgnoreCase(doubleDetection);
4748

@@ -79,7 +80,9 @@ public List<ProcessFilter> create(Configuration config) {
7980
filterList.add(new GapDetectionFilter(gapRegex, dd,
8081
"true".equalsIgnoreCase(jsonReport)));
8182
}
82-
83+
if (select != null) {
84+
filterList.add(new SelectFilter(select));
85+
}
8386
return filterList;
8487
}
8588
}

src/main/java/nu/sitia/loggenerator/filter/GapDetectionFilter.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,14 @@ public void shutdown() {
101101
}
102102
}
103103

104+
/**
105+
* Get the detector for this filter
106+
* @return the GapDetector
107+
*/
108+
public GapDetector getDetector() {
109+
return detector;
110+
}
111+
104112
/**
105113
* toString in json format
106114
* @return the internal state

src/main/java/nu/sitia/loggenerator/filter/JsonFilter.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020

2121
import com.fasterxml.jackson.databind.JsonNode;
2222
import com.fasterxml.jackson.databind.ObjectMapper;
23-
import com.fasterxml.jackson.databind.node.ArrayNode;
2423
import nu.sitia.loggenerator.util.JsonUtil;
2524

2625
import java.util.ArrayList;
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright 2022 sitia.nu https://github.com/anders-wartoft/LogGenerator
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
5+
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
6+
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
7+
* persons to whom the Software is furnished to do so, subject to the following conditions:
8+
*
9+
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
10+
* Software.
11+
*
12+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
13+
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
14+
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
15+
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16+
*/
17+
18+
package nu.sitia.loggenerator.filter;
19+
20+
21+
import java.util.ArrayList;
22+
import java.util.List;
23+
import java.util.regex.Matcher;
24+
import java.util.regex.Pattern;
25+
26+
public class SelectFilter implements ProcessFilter {
27+
28+
/** Cached regex pattern */
29+
private final Pattern pattern;
30+
31+
/** for toString() */
32+
private final String regex;
33+
34+
/**
35+
* Create a RegexFilter and set all parameters
36+
* @param regex The value to search for
37+
*/
38+
public SelectFilter(String regex) {
39+
this.regex = regex;
40+
// What to look for
41+
if (null == regex) {
42+
throw new RuntimeException("regex is null");
43+
}
44+
pattern = Pattern.compile(regex);
45+
}
46+
47+
/**
48+
* Filter one string
49+
*
50+
* @param toFilter The string to change
51+
* @return toFilter with a header added before the string.
52+
*/
53+
private String filter(String toFilter) {
54+
Matcher matcher = pattern.matcher(toFilter);
55+
if (matcher.find()) {
56+
return matcher.group(1);
57+
}
58+
return toFilter;
59+
}
60+
61+
@Override
62+
public List<String> filter(List<String> toFilter) {
63+
List<String> filtered = new ArrayList<>();
64+
toFilter.forEach(s ->
65+
filtered.add(filter(s)));
66+
67+
return filtered;
68+
}
69+
70+
/**
71+
* The current configuration
72+
* @return A printout of the current configuration
73+
*/
74+
@Override
75+
public String toString() {
76+
return "SelectFilter" + System.lineSeparator() + regex + System.lineSeparator();
77+
}
78+
}

src/main/java/nu/sitia/loggenerator/util/LogStatistics.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ public long getTransactionMessages() {
6464
return transactionMessages;
6565
}
6666

67+
/** Flag to be able to signal to the ItemProxy if statistics has been printed */
68+
private boolean hasPrinted = false;
69+
6770
/**
6871
* Constructor
6972
* @param config Used to get the printout every ms value
@@ -83,9 +86,12 @@ public LogStatistics(Configuration config) {
8386
* Inspect the data that is about to be sent for connection
8487
* messages.
8588
* @param filtered The list of strings to send
89+
* @return true iff the statistics have been printed out
8690
*/
87-
public void calculateStatistics(List<String> filtered) {
91+
public boolean calculateStatistics(List<String> filtered) {
92+
hasPrinted = false;
8893
filtered.forEach(this::checkMessage);
94+
return hasPrinted;
8995
}
9096

9197
/**
@@ -149,6 +155,7 @@ private void checkLog(String log) {
149155
* @param bytes How many characters the compound logs have contained (close enough to bytes received)
150156
*/
151157
private void printMetrics(String beginning, long ms, long nrMessages, String name, long bytes) {
158+
hasPrinted = true;
152159
System.out.format("%s: %s transferred %d lines in %d milliseconds, %.3f kEPS %.3f MBPS%n",
153160
beginning,
154161
name,

src/main/java/nu/sitia/loggenerator/util/gapdetector/GapDetector.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -159,19 +159,19 @@ private Map<Long, Long> getSorted(Map<Long, Long>toSort) {
159159
@Override
160160
public String toString() {
161161
StringBuilder sb = new StringBuilder();
162+
if (gaps.size() > 0) {
163+
sb.append("Gaps found: ").append(gaps.size()).append(".").append(System.lineSeparator());
164+
for (Gap gap : gaps) {
165+
sb.append(gap.getFrom()).append("-").append(gap.getTo()).append(System.lineSeparator());
166+
}
167+
}
162168
if (duplicateDetection) {
163169
sb.append("Duplicate detection found: ").append(duplicates.size()).append(" duplicate (or more) values.").append(System.lineSeparator());
164170
Map<Long, Long> sortedMap = getSorted(duplicates);
165171
sortedMap.forEach((key, value) -> sb.append(key).append(" - ").append(value).append(System.lineSeparator()));
166172
}
167173
sb.append("Number of unique received numbers: ").append(nrReceived).append(System.lineSeparator());
168174
sb.append("Next expected number: ").append(expectedNumber).append(System.lineSeparator());
169-
if (gaps.size() > 0) {
170-
sb.append("Gaps found: ").append(gaps.size()).append(".").append(System.lineSeparator());
171-
for (Gap gap : gaps) {
172-
sb.append(gap.getFrom()).append("-").append(gap.getTo()).append(System.lineSeparator());
173-
}
174-
}
175175
return sb.toString();
176176
}
177177

0 commit comments

Comments
 (0)