Skip to content

Commit

Permalink
#27: Add SyslogType (#29)
Browse files Browse the repository at this point in the history
* add type=Syslog check to NLFPlugin; add syslogType test to NLFPluginTest; update akv_01 to 6.3.1; add syslog.json test data; add SyslogType EventType

* move RealHostname usage to NLFPlugin, add realHostname parameter to all EventType ctors; add "syslogtype.processname" env variable to FakeSourceable; add validateProcessName() to SyslogType

* add test data: syslog_missing_keys (missing all JSON keys used in SyslogType); syslog_missing_keys_except_processName (same, but with ProcessName included); syslog_unexpected_message (same as syslog.json; but with SyslogMessage containing something unexpected); add check for missing '[' in SyslogType.appName; add SyslogTypeTest

* fix syslogType test assertions for appName (max 48 chars); use regex in SyslogType.appName();

* add null check for missing capture group

* change regex pattern to suggested one; fix test assertions to match
  • Loading branch information
eemhu authored Feb 24, 2025
1 parent 570f5d7 commit cc02f7c
Show file tree
Hide file tree
Showing 16 changed files with 754 additions and 25 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
<dependency>
<groupId>com.teragrep</groupId>
<artifactId>akv_01</artifactId>
<version>6.3.0</version>
<version>6.3.1</version>
</dependency>
<!-- tests -->
<dependency>
Expand Down
26 changes: 18 additions & 8 deletions src/main/java/com/teragrep/nlf_01/NLFPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,9 @@
import com.teragrep.akv_01.event.ParsedEvent;
import com.teragrep.akv_01.plugin.Plugin;
import com.teragrep.akv_01.plugin.PluginException;
import com.teragrep.nlf_01.types.AppInsightType;
import com.teragrep.nlf_01.types.CLType;
import com.teragrep.nlf_01.types.ContainerType;
import com.teragrep.nlf_01.types.EventType;
import com.teragrep.nlf_01.types.*;
import com.teragrep.nlf_01.util.EnvironmentSource;
import com.teragrep.nlf_01.util.RealHostname;
import com.teragrep.nlf_01.util.Sourceable;
import com.teragrep.rlo_14.SyslogMessage;
import jakarta.json.JsonException;
Expand Down Expand Up @@ -81,6 +79,8 @@ public List<SyslogMessage> syslogMessage(final ParsedEvent parsedEvent) throws P
final List<SyslogMessage> syslogMessages = new ArrayList<>();
final String containerLogAppNameKey = source.source("containerlog.appname.annotation");
final String containerLogHostnameKey = source.source("containerlog.hostname.annotation");
final String syslogExpectedProcessName = source.source("syslogtype.processname");
final String realHostname = new RealHostname("localhost").hostname();

if (!parsedEvent.isJsonStructure()) {
// non-applicable
Expand All @@ -99,20 +99,30 @@ public List<SyslogMessage> syslogMessage(final ParsedEvent parsedEvent) throws P
) {

if (jsonObject.getString("Type").equals("AppTraces")) {
eventTypes.add(new AppInsightType(parsedEvent));
eventTypes.add(new AppInsightType(parsedEvent, realHostname));
}
else if (jsonObject.getString("Type").endsWith("_CL")) {
eventTypes.add(new CLType(parsedEvent));
eventTypes.add(new CLType(parsedEvent, realHostname));
}
else if (jsonObject.getString("Type").equals("ContainerLogV2")) {
eventTypes.add(new ContainerType(containerLogHostnameKey, containerLogAppNameKey, parsedEvent));
eventTypes
.add(
new ContainerType(
parsedEvent,
containerLogHostnameKey,
containerLogAppNameKey,
realHostname
)
);
}
else if (jsonObject.getString("Type").equals("Syslog")) {
eventTypes.add(new SyslogType(parsedEvent, syslogExpectedProcessName, realHostname));
}
else {
throw new PluginException(
new IllegalArgumentException("Invalid event type: " + jsonObject.getString("Type"))
);
}

}
else {
throw new PluginException(
Expand Down
6 changes: 4 additions & 2 deletions src/main/java/com/teragrep/nlf_01/types/AppInsightType.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,11 @@
public final class AppInsightType implements EventType {

private final ParsedEvent parsedEvent;
private final String realHostname;

public AppInsightType(final ParsedEvent parsedEvent) {
public AppInsightType(final ParsedEvent parsedEvent, final String realHostname) {
this.parsedEvent = parsedEvent;
this.realHostname = realHostname;
}

private void assertKey(final JsonObject obj, final String key, final JsonValue.ValueType type)
Expand Down Expand Up @@ -145,7 +147,7 @@ public Set<SDElement> sdElements() {
.add(new SDElement("aer_02_partition@48577").addSDParam("fully_qualified_namespace", fullyQualifiedNamespace).addSDParam("eventhub_name", eventHubName).addSDParam("partition_id", partitionId).addSDParam("consumer_group", consumerGroup));

elems
.add(new SDElement("event_id@48577").addSDParam("uuid", UUID.randomUUID().toString()).addSDParam("hostname", new RealHostname("localhost").hostname()).addSDParam("unixtime", Instant.now().toString()).addSDParam("id_source", "aer_02"));
.add(new SDElement("event_id@48577").addSDParam("uuid", UUID.randomUUID().toString()).addSDParam("hostname", realHostname).addSDParam("unixtime", Instant.now().toString()).addSDParam("id_source", "aer_02"));

String partitionKey = "";
if (!parsedEvent.systemProperties().isStub()) {
Expand Down
6 changes: 4 additions & 2 deletions src/main/java/com/teragrep/nlf_01/types/CLType.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,11 @@
public final class CLType implements EventType {

private final ParsedEvent parsedEvent;
private final String realHostname;

public CLType(final ParsedEvent parsedEvent) {
public CLType(final ParsedEvent parsedEvent, final String realHostname) {
this.parsedEvent = parsedEvent;
this.realHostname = realHostname;
}

private void assertKey(final JsonObject obj, final String key, JsonValue.ValueType type) throws PluginException {
Expand Down Expand Up @@ -148,7 +150,7 @@ public Set<SDElement> sdElements() throws PluginException {
.add(new SDElement("aer_02_partition@48577").addSDParam("fully_qualified_namespace", fullyQualifiedNamespace).addSDParam("eventhub_name", eventHubName).addSDParam("partition_id", partitionId).addSDParam("consumer_group", consumerGroup));

elems
.add(new SDElement("event_id@48577").addSDParam("uuid", UUID.randomUUID().toString()).addSDParam("hostname", new RealHostname("localhost").hostname()).addSDParam("unixtime", Instant.now().toString()).addSDParam("id_source", "aer_02"));
.add(new SDElement("event_id@48577").addSDParam("uuid", UUID.randomUUID().toString()).addSDParam("hostname", realHostname).addSDParam("unixtime", Instant.now().toString()).addSDParam("id_source", "aer_02"));

String partitionKey = "";
if (!parsedEvent.systemProperties().isStub()) {
Expand Down
9 changes: 6 additions & 3 deletions src/main/java/com/teragrep/nlf_01/types/ContainerType.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,18 @@ public final class ContainerType implements EventType {
private final ParsedEvent parsedEvent;
private final String containerLogHostnameKey;
private final String containerLogAppNameKey;
private final String realHostname;

public ContainerType(
final ParsedEvent parsedEvent,
final String containerLogHostnameKey,
final String containerLogAppNameKey,
final ParsedEvent parsedEvent
final String realHostname
) {
this.parsedEvent = parsedEvent;
this.containerLogHostnameKey = containerLogHostnameKey;
this.containerLogAppNameKey = containerLogAppNameKey;
this.parsedEvent = parsedEvent;
this.realHostname = realHostname;
}

private void assertKey(final JsonObject obj, final String key, JsonValue.ValueType type) throws PluginException {
Expand Down Expand Up @@ -170,7 +173,7 @@ public Set<SDElement> sdElements() throws PluginException {
.add(new SDElement("aer_02_partition@48577").addSDParam("fully_qualified_namespace", fullyQualifiedNamespace).addSDParam("eventhub_name", eventHubName).addSDParam("partition_id", partitionId).addSDParam("consumer_group", consumerGroup));

elems
.add(new SDElement("event_id@48577").addSDParam("uuid", UUID.randomUUID().toString()).addSDParam("hostname", new RealHostname("localhost").hostname()).addSDParam("unixtime", Instant.now().toString()).addSDParam("id_source", "aer_02"));
.add(new SDElement("event_id@48577").addSDParam("uuid", UUID.randomUUID().toString()).addSDParam("hostname", realHostname).addSDParam("unixtime", Instant.now().toString()).addSDParam("id_source", "aer_02"));

String partitionKey = "";
if (!parsedEvent.systemProperties().isStub()) {
Expand Down
229 changes: 229 additions & 0 deletions src/main/java/com/teragrep/nlf_01/types/SyslogType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
/*
* Teragrep Neon log format plugin for AKV_01
* Copyright (C) 2025 Suomen Kanuuna Oy
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*
* Additional permission under GNU Affero General Public License version 3
* section 7
*
* If you modify this Program, or any covered work, by linking or combining it
* with other code, such other code is not for that reason alone subject to any
* of the requirements of the GNU Affero GPL version 3 as long as this Program
* is the same Program as licensed from Suomen Kanuuna Oy without any additional
* modifications.
*
* Supplemented terms under GNU Affero General Public License version 3
* section 7
*
* Origin of the software must be attributed to Suomen Kanuuna Oy. Any modified
* versions must be marked as "Modified version of" The Program.
*
* Names of the licensors and authors may not be used for publicity purposes.
*
* No rights are granted for use of trade names, trademarks, or service marks
* which are in The Program if any.
*
* Licensee must indemnify licensors and authors for any liability that these
* contractual assumptions impose on licensors and authors.
*
* To the extent this program is licensed as part of the Commercial versions of
* Teragrep, the applicable Commercial License may apply to this file if you as
* a licensee so wish it.
*/
package com.teragrep.nlf_01.types;

import com.teragrep.akv_01.event.ParsedEvent;
import com.teragrep.akv_01.plugin.PluginException;
import com.teragrep.nlf_01.PropertiesJson;
import com.teragrep.nlf_01.util.*;
import com.teragrep.rlo_14.Facility;
import com.teragrep.rlo_14.SDElement;
import com.teragrep.rlo_14.Severity;
import jakarta.json.JsonObject;
import jakarta.json.JsonValue;

import java.time.Instant;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public final class SyslogType implements EventType {

private final ParsedEvent parsedEvent;
private final String expectedProcessName;
private final String realHostname;
private final Pattern appNamePattern;

public SyslogType(final ParsedEvent parsedEvent, final String expectedProcessName, final String realHostname) {
this(
parsedEvent,
expectedProcessName,
realHostname,
Pattern.compile("^.*?(?<uuid>[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12})")
);
}

public SyslogType(
final ParsedEvent parsedEvent,
final String expectedProcessName,
final String realHostname,
final Pattern appNamePattern
) {
this.parsedEvent = parsedEvent;
this.expectedProcessName = expectedProcessName;
this.realHostname = realHostname;
this.appNamePattern = appNamePattern;
}

private void validateProcessName() throws PluginException {
final JsonObject mainObject = parsedEvent.asJsonStructure().asJsonObject();
assertKey(mainObject, "ProcessName", JsonValue.ValueType.STRING);
final String processName = mainObject.getString("ProcessName");

if (!processName.equals(expectedProcessName)) {
throw new PluginException("Expected <[" + expectedProcessName + "]> but found <[" + processName + "]>");
}
}

private void assertKey(final JsonObject obj, final String key, JsonValue.ValueType type) throws PluginException {
if (!obj.containsKey(key)) {
throw new PluginException(new IllegalArgumentException("Key " + key + " does not exist"));
}

if (!obj.get(key).getValueType().equals(type)) {
throw new PluginException(new IllegalArgumentException("Key " + key + " is not of type " + type));
}
}

@Override
public Severity severity() throws PluginException {
validateProcessName();
return Severity.NOTICE;
}

@Override
public Facility facility() throws PluginException {
validateProcessName();
return Facility.AUDIT;
}

@Override
public String hostname() throws PluginException {
validateProcessName();
final JsonObject mainObject = parsedEvent.asJsonStructure().asJsonObject();
assertKey(mainObject, "_Internal_WorkspaceResourceId", JsonValue.ValueType.STRING);
final String internalWorkspaceResourceId = mainObject.getString("_Internal_WorkspaceResourceId");

// hostname = internal workspace resource id MD5 + resourceName from resourceId, with non-ascii chars removed
return new ValidRFC5424Hostname(
"md5-".concat(new MD5Hash(internalWorkspaceResourceId).md5().concat("-").concat(new ASCIIString(new ResourceId(internalWorkspaceResourceId).resourceName()).withNonAsciiCharsRemoved()))
).hostnameWithInvalidCharsRemoved();
}

@Override
public String appName() throws PluginException {
validateProcessName();
final JsonObject mainObject = parsedEvent.asJsonStructure().asJsonObject();
assertKey(mainObject, "SyslogMessage", JsonValue.ValueType.STRING);
final String syslogMessage = mainObject.getString("SyslogMessage");

final Matcher matcher = appNamePattern.matcher(syslogMessage);
if (matcher.find()) {
final String uuid = matcher.group("uuid");
if (uuid == null) {
throw new PluginException("Capture group 'uuid' was not found");
}
return new ValidRFC5424AppName(uuid).validAppName();
}

throw new PluginException("Could not parse appName from SyslogMessage key");
}

@Override
public long timestamp() throws PluginException {
validateProcessName();
final JsonObject mainObject = parsedEvent.asJsonStructure().asJsonObject();
assertKey(mainObject, "TimeGenerated", JsonValue.ValueType.STRING);

return new ValidRFC5424Timestamp(mainObject.getString("TimeGenerated")).validTimestamp();
}

@Override
public Set<SDElement> sdElements() throws PluginException {
validateProcessName();
Set<SDElement> elems = new HashSet<>();
String time = "";
if (!parsedEvent.enqueuedTimeUtc().isStub()) {
time = parsedEvent.enqueuedTimeUtc().zonedDateTime().toString();
}

String fullyQualifiedNamespace = "";
String eventHubName = "";
String partitionId = "";
String consumerGroup = "";
if (!parsedEvent.partitionCtx().isStub()) {
fullyQualifiedNamespace = String
.valueOf(parsedEvent.partitionCtx().asMap().getOrDefault("FullyQualifiedNamespace", ""));
eventHubName = String.valueOf(parsedEvent.partitionCtx().asMap().getOrDefault("EventHubName", ""));
partitionId = String.valueOf(parsedEvent.partitionCtx().asMap().getOrDefault("PartitionId", ""));
consumerGroup = String.valueOf(parsedEvent.partitionCtx().asMap().getOrDefault("ConsumerGroup", ""));
}

elems
.add(new SDElement("aer_02_partition@48577").addSDParam("fully_qualified_namespace", fullyQualifiedNamespace).addSDParam("eventhub_name", eventHubName).addSDParam("partition_id", partitionId).addSDParam("consumer_group", consumerGroup));

elems
.add(new SDElement("event_id@48577").addSDParam("uuid", UUID.randomUUID().toString()).addSDParam("hostname", realHostname).addSDParam("unixtime", Instant.now().toString()).addSDParam("id_source", "aer_02"));

String partitionKey = "";
if (!parsedEvent.systemProperties().isStub()) {
partitionKey = String.valueOf(parsedEvent.systemProperties().asMap().getOrDefault("PartitionKey", ""));
}

String offset = "";
if (!parsedEvent.offset().isStub()) {
offset = parsedEvent.offset().value();
}

elems
.add(new SDElement("aer_02_event@48577").addSDParam("offset", offset).addSDParam("enqueued_time", time).addSDParam("partition_key", partitionKey).addSDParam("properties", new PropertiesJson(parsedEvent.properties()).toJsonObject().toString()));

elems
.add(new SDElement("aer_02@48577").addSDParam("timestamp_source", time.isEmpty() ? "generated" : "timeEnqueued"));

elems.add(new SDElement("nlf_01@48577").addSDParam("eventType", this.getClass().getSimpleName()));

return elems;
}

@Override
public String msgId() throws PluginException {
validateProcessName();
String sequenceNumber = "";
if (!parsedEvent.systemProperties().isStub()) {
sequenceNumber = String.valueOf(parsedEvent.systemProperties().asMap().getOrDefault("SequenceNumber", ""));
}
return sequenceNumber;
}

@Override
public String msg() throws PluginException {
validateProcessName();
return parsedEvent.asString();
}
}
Loading

0 comments on commit cc02f7c

Please sign in to comment.