Skip to content

Commit c27d79e

Browse files
authored
ContainerAppConsoleLogs support (#50)
* Initial JSON file for ContainerAppConsoleLogsType * Object for ContainerAppConsoleLogsType and unit tests * Implement ContainerAppConsoleLogsType in NLFPlugin.syslogMessage and test it * Change ContainerAppConsoleLogsType.appName to use ContainerAppName instead of ContainerName * Implement an interface for RFC5424AppName and rename the "validAppName" method to "appName" * Implement a new HashableRFC5424AppName to ensure that appNames are less than 48 characters long * Wrap HashableRFC5424AppName into ValidRFC5424AppName in ContainerAppConsoleLogsType.appName * Fix method name in CCType.appName * Apply Spotless to CCType * Remove ValidRFC5424AppName from HashableRFC5424AppName.appName - Adds JavaDoc to the HashableRFC5424AppName.appName about the returned values * Add support for other sources that use ContainerAppConsoleLogsType - Some events have the "ContainerAppName" or the "JobName" field, but not both
1 parent 6764e85 commit c27d79e

22 files changed

Lines changed: 979 additions & 18 deletions

src/main/java/com/teragrep/nlf_01/NLFPlugin.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ else if (jsonObject.getString("Type").equals("ADFPipelineRun")) {
106106
else if (jsonObject.getString("Type").equals("AppTraces")) {
107107
eventTypes.add(new AppInsightType(parsedEvent, realHostname));
108108
}
109+
else if (jsonObject.getString("Type").equals("ContainerAppConsoleLogs")) {
110+
eventTypes.add(new ContainerAppConsoleLogsType(parsedEvent, realHostname));
111+
}
109112
else if (jsonObject.getString("Type").equals("FunctionAppLogs")) {
110113
eventTypes.add(new FunctionAppLogsType(parsedEvent, realHostname));
111114
}

src/main/java/com/teragrep/nlf_01/types/ADFActivityRunType.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ public String appName() throws PluginException {
114114

115115
assertKey(record, "PipelineName", JsonValue.ValueType.STRING);
116116

117-
return new ValidRFC5424AppName(record.getString("PipelineName")).validAppName();
117+
return new ValidRFC5424AppName(record.getString("PipelineName")).appName();
118118

119119
}
120120

src/main/java/com/teragrep/nlf_01/types/ADFPipelineRunType.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ public String appName() throws PluginException {
114114

115115
assertKey(record, "PipelineName", JsonValue.ValueType.STRING);
116116

117-
return new ValidRFC5424AppName(record.getString("PipelineName")).validAppName();
117+
return new ValidRFC5424AppName(record.getString("PipelineName")).appName();
118118

119119
}
120120

src/main/java/com/teragrep/nlf_01/types/AppInsightType.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ public String appName() throws PluginException {
110110

111111
assertKey(record, "AppRoleName", JsonValue.ValueType.STRING);
112112

113-
return new ValidRFC5424AppName(record.getString("AppRoleName")).validAppName();
113+
return new ValidRFC5424AppName(record.getString("AppRoleName")).appName();
114114

115115
}
116116

src/main/java/com/teragrep/nlf_01/types/CCType.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,7 @@ public String appName() throws PluginException {
125125
throw new PluginException("Could not parse appName from data.resourceName");
126126
}
127127

128-
return new ValidRFC5424AppName(new ASCIIString(lastValueInResourceName).withNonAsciiCharsRemoved())
129-
.validAppName();
128+
return new ValidRFC5424AppName(new ASCIIString(lastValueInResourceName).withNonAsciiCharsRemoved()).appName();
130129
}
131130

132131
@Override

src/main/java/com/teragrep/nlf_01/types/CLType.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ public String appName() throws PluginException {
115115
final String truncatedFilePath = filename.length() < 39 ? filename : filename.substring(0, 39);
116116

117117
// appname = first 8 chars of filePath MD5 + dash (-) + filename truncated to max 39 chars
118-
return new ValidRFC5424AppName(truncatedMd5.concat("-").concat(truncatedFilePath)).validAppName();
118+
return new ValidRFC5424AppName(truncatedMd5.concat("-").concat(truncatedFilePath)).appName();
119119
}
120120

121121
@Override
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
/*
2+
* Teragrep Neon log format plugin for AKV_01
3+
* Copyright (C) 2025 Suomen Kanuuna Oy
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU Affero General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Affero General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Affero General Public License
16+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
*
18+
*
19+
* Additional permission under GNU Affero General Public License version 3
20+
* section 7
21+
*
22+
* If you modify this Program, or any covered work, by linking or combining it
23+
* with other code, such other code is not for that reason alone subject to any
24+
* of the requirements of the GNU Affero GPL version 3 as long as this Program
25+
* is the same Program as licensed from Suomen Kanuuna Oy without any additional
26+
* modifications.
27+
*
28+
* Supplemented terms under GNU Affero General Public License version 3
29+
* section 7
30+
*
31+
* Origin of the software must be attributed to Suomen Kanuuna Oy. Any modified
32+
* versions must be marked as "Modified version of" The Program.
33+
*
34+
* Names of the licensors and authors may not be used for publicity purposes.
35+
*
36+
* No rights are granted for use of trade names, trademarks, or service marks
37+
* which are in The Program if any.
38+
*
39+
* Licensee must indemnify licensors and authors for any liability that these
40+
* contractual assumptions impose on licensors and authors.
41+
*
42+
* To the extent this program is licensed as part of the Commercial versions of
43+
* Teragrep, the applicable Commercial License may apply to this file if you as
44+
* a licensee so wish it.
45+
*/
46+
package com.teragrep.nlf_01.types;
47+
48+
import com.teragrep.akv_01.event.ParsedEvent;
49+
import com.teragrep.akv_01.plugin.PluginException;
50+
import com.teragrep.nlf_01.PropertiesJson;
51+
import com.teragrep.nlf_01.util.ASCIIString;
52+
import com.teragrep.nlf_01.util.HashableRFC5424AppName;
53+
import com.teragrep.nlf_01.util.MD5Hash;
54+
import com.teragrep.nlf_01.util.ResourceId;
55+
import com.teragrep.nlf_01.util.ValidRFC5424AppName;
56+
import com.teragrep.nlf_01.util.ValidRFC5424Hostname;
57+
import com.teragrep.nlf_01.util.ValidRFC5424Timestamp;
58+
import com.teragrep.rlo_14.Facility;
59+
import com.teragrep.rlo_14.SDElement;
60+
import com.teragrep.rlo_14.Severity;
61+
import jakarta.json.JsonObject;
62+
import jakarta.json.JsonValue;
63+
import java.time.Instant;
64+
import java.util.HashSet;
65+
import java.util.Set;
66+
import java.util.UUID;
67+
68+
public final class ContainerAppConsoleLogsType implements EventType {
69+
70+
private final ParsedEvent parsedEvent;
71+
private final String realHostname;
72+
73+
public ContainerAppConsoleLogsType(final ParsedEvent parsedEvent, final String realHostname) {
74+
this.parsedEvent = parsedEvent;
75+
this.realHostname = realHostname;
76+
}
77+
78+
private void assertKey(final JsonObject obj, final String key, final JsonValue.ValueType type)
79+
throws PluginException {
80+
if (!obj.containsKey(key)) {
81+
throw new PluginException(new IllegalArgumentException("Key " + key + " does not exist"));
82+
}
83+
84+
if (!obj.get(key).getValueType().equals(type)) {
85+
throw new PluginException(new IllegalArgumentException("Key " + key + " is not of type " + type));
86+
}
87+
}
88+
89+
@Override
90+
public Severity severity() throws PluginException {
91+
return Severity.NOTICE;
92+
}
93+
94+
@Override
95+
public Facility facility() throws PluginException {
96+
return Facility.AUDIT;
97+
}
98+
99+
@Override
100+
public String hostname() throws PluginException {
101+
final JsonObject record = parsedEvent.asJsonStructure().asJsonObject();
102+
103+
assertKey(record, "_ResourceId", JsonValue.ValueType.STRING);
104+
final String resourceId = record.getString("_ResourceId");
105+
106+
assertKey(record, "EnvironmentName", JsonValue.ValueType.STRING);
107+
final String environmentName = record.getString("EnvironmentName");
108+
109+
final String concatenatedHostName = resourceId.concat("/").concat(environmentName);
110+
111+
return new ValidRFC5424Hostname(
112+
"md5-".concat(new MD5Hash(concatenatedHostName).md5().concat("-").concat(new ASCIIString(new ResourceId(resourceId).resourceName()).withNonAsciiCharsRemoved()))
113+
).hostnameWithInvalidCharsRemoved();
114+
}
115+
116+
@Override
117+
public String appName() throws PluginException {
118+
final JsonObject record = parsedEvent.asJsonStructure().asJsonObject();
119+
final String keyValue;
120+
if (record.containsKey("ContainerAppName")) {
121+
assertKey(record, "ContainerAppName", JsonValue.ValueType.STRING);
122+
keyValue = record.getString("ContainerAppName");
123+
} else if (record.containsKey("JobName")) {
124+
assertKey(record, "JobName", JsonValue.ValueType.STRING);
125+
keyValue = record.getString("JobName");
126+
} else {
127+
throw new PluginException(new IllegalArgumentException("A valid key does not exist"));
128+
}
129+
130+
return new ValidRFC5424AppName(new HashableRFC5424AppName(keyValue).appName()).appName();
131+
}
132+
133+
@Override
134+
public long timestamp() throws PluginException {
135+
final JsonObject record = parsedEvent.asJsonStructure().asJsonObject();
136+
assertKey(record, "TimeGenerated", JsonValue.ValueType.STRING);
137+
final String time = record.getString("TimeGenerated");
138+
139+
return new ValidRFC5424Timestamp(time).validTimestamp();
140+
}
141+
142+
@Override
143+
public Set<SDElement> sdElements() throws PluginException {
144+
final Set<SDElement> elems = new HashSet<>();
145+
final String time;
146+
if (!parsedEvent.enqueuedTimeUtc().isStub()) {
147+
time = parsedEvent.enqueuedTimeUtc().zonedDateTime().toString();
148+
}
149+
else {
150+
time = "";
151+
}
152+
153+
final String fullyQualifiedNamespace;
154+
final String eventHubName;
155+
final String partitionId;
156+
final String consumerGroup;
157+
if (!parsedEvent.partitionCtx().isStub()) {
158+
fullyQualifiedNamespace = String
159+
.valueOf(parsedEvent.partitionCtx().asMap().getOrDefault("FullyQualifiedNamespace", ""));
160+
eventHubName = String.valueOf(parsedEvent.partitionCtx().asMap().getOrDefault("EventHubName", ""));
161+
partitionId = String.valueOf(parsedEvent.partitionCtx().asMap().getOrDefault("PartitionId", ""));
162+
consumerGroup = String.valueOf(parsedEvent.partitionCtx().asMap().getOrDefault("ConsumerGroup", ""));
163+
}
164+
else {
165+
fullyQualifiedNamespace = "";
166+
eventHubName = "";
167+
partitionId = "";
168+
consumerGroup = "";
169+
}
170+
171+
elems
172+
.add(new SDElement("aer_02_partition@48577").addSDParam("fully_qualified_namespace", fullyQualifiedNamespace).addSDParam("eventhub_name", eventHubName).addSDParam("partition_id", partitionId).addSDParam("consumer_group", consumerGroup));
173+
174+
elems
175+
.add(new SDElement("event_id@48577").addSDParam("uuid", UUID.randomUUID().toString()).addSDParam("hostname", realHostname).addSDParam("unixtime", Instant.now().toString()).addSDParam("id_source", "aer_02"));
176+
177+
final String partitionKey;
178+
if (!parsedEvent.systemProperties().isStub()) {
179+
partitionKey = String.valueOf(parsedEvent.systemProperties().asMap().getOrDefault("PartitionKey", ""));
180+
}
181+
else {
182+
partitionKey = "";
183+
}
184+
185+
final String offset;
186+
if (!parsedEvent.offset().isStub()) {
187+
offset = parsedEvent.offset().value();
188+
}
189+
else {
190+
offset = "";
191+
}
192+
193+
elems
194+
.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()));
195+
196+
elems
197+
.add(new SDElement("aer_02@48577").addSDParam("timestamp_source", time.isEmpty() ? "generated" : "timeEnqueued"));
198+
199+
elems.add(new SDElement("nlf_01@48577").addSDParam("eventType", this.getClass().getSimpleName()));
200+
201+
return elems;
202+
}
203+
204+
@Override
205+
public String msgId() throws PluginException {
206+
final String sequenceNumber;
207+
if (!parsedEvent.systemProperties().isStub()) {
208+
sequenceNumber = String.valueOf(parsedEvent.systemProperties().asMap().getOrDefault("SequenceNumber", ""));
209+
}
210+
else {
211+
sequenceNumber = "";
212+
}
213+
214+
return sequenceNumber;
215+
}
216+
217+
@Override
218+
public String msg() throws PluginException {
219+
return parsedEvent.asString();
220+
}
221+
}

src/main/java/com/teragrep/nlf_01/types/ContainerType.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,7 @@ else if ("stderr".equals(logSource)) {
137137
}
138138

139139
assertKey(podAnnotations, containerLogAppNameKey, JsonValue.ValueType.STRING);
140-
return new ValidRFC5424AppName(podAnnotations.getString(containerLogAppNameKey) + logSourceSuffix)
141-
.validAppName();
140+
return new ValidRFC5424AppName(podAnnotations.getString(containerLogAppNameKey) + logSourceSuffix).appName();
142141
}
143142

144143
@Override

src/main/java/com/teragrep/nlf_01/types/FunctionAppLogsType.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ public String appName() throws PluginException {
113113

114114
assertKey(record, "AppName", JsonValue.ValueType.STRING);
115115

116-
return new ValidRFC5424AppName(record.getString("AppName")).validAppName();
116+
return new ValidRFC5424AppName(record.getString("AppName")).appName();
117117
}
118118

119119
@Override

src/main/java/com/teragrep/nlf_01/types/LogicAppWorkflowRuntimeType.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ public String appName() throws PluginException {
115115
assertKey(record, "WorkflowName", JsonValue.ValueType.STRING);
116116

117117
return new ValidRFC5424AppName(new ASCIIString(record.getString("WorkflowName")).withNonAsciiCharsRemoved())
118-
.validAppName();
118+
.appName();
119119

120120
}
121121

0 commit comments

Comments
 (0)