-
Notifications
You must be signed in to change notification settings - Fork 464
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial take at Structured Audit Log support
- Loading branch information
Showing
44 changed files
with
1,308 additions
and
109 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
/* | ||
* jPOS Project [http://jpos.org] | ||
* Copyright (C) 2000-2023 jPOS Software SRL | ||
* | ||
* 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 <http://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
package org.jpos.log; | ||
|
||
import com.fasterxml.jackson.annotation.JsonSubTypes; | ||
import com.fasterxml.jackson.annotation.JsonTypeInfo; | ||
import org.jpos.log.evt.*; | ||
|
||
@JsonTypeInfo( | ||
use = JsonTypeInfo.Id.NAME, | ||
include = JsonTypeInfo.As.PROPERTY, | ||
property = "t" | ||
) | ||
@JsonSubTypes({ | ||
@JsonSubTypes.Type(value = Start.class, name = "start"), | ||
@JsonSubTypes.Type(value = Stop.class, name = "stop"), | ||
@JsonSubTypes.Type(value = Deploy.class, name = "deploy"), | ||
@JsonSubTypes.Type(value = UnDeploy.class, name = "undeploy"), | ||
@JsonSubTypes.Type(value = LogMessage.class, name = "msg") | ||
}) | ||
|
||
public sealed interface AuditLogEvent permits LogMessage, Deploy, UnDeploy, Start, Stop { } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package org.jpos.log; | ||
|
||
import java.io.ByteArrayOutputStream; | ||
import java.io.PrintStream; | ||
|
||
public interface LogRenderer<T> { | ||
void render (T obj, PrintStream ps, String indent); | ||
Class<?> clazz(); | ||
Type type(); | ||
|
||
default void render (T obj, PrintStream ps) { | ||
render (obj, ps, ""); | ||
} | ||
|
||
default String render (T obj, String indent) { | ||
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | ||
PrintStream ps = new PrintStream(baos); | ||
render (obj, ps, indent); | ||
return baos.toString(); | ||
} | ||
|
||
default String render (T obj) { | ||
return render (obj, ""); | ||
} | ||
|
||
default String indent (String indent, String s) { | ||
if (s == null || s.isEmpty() || indent==null || indent.isEmpty()) { | ||
return s; | ||
} | ||
String[] lines = s.split("\n", -1); // Preserve trailing empty strings | ||
StringBuilder indentedString = new StringBuilder(); | ||
for (int i = 0; i < lines.length; i++) { | ||
indentedString.append(indent).append(lines[i]); | ||
if (i < lines.length - 1) { | ||
indentedString.append("\n"); | ||
} | ||
} | ||
return indentedString.toString(); | ||
} | ||
|
||
|
||
enum Type { | ||
XML, | ||
JSON, | ||
TXT, | ||
MARKDOWN | ||
} | ||
} |
138 changes: 138 additions & 0 deletions
138
jpos/src/main/java/org/jpos/log/LogRendererRegistry.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
package org.jpos.log; | ||
|
||
import java.io.PrintStream; | ||
import java.util.*; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
|
||
/** | ||
* A registry for managing {@link LogRenderer} instances associated with specific class types and renderer types. | ||
* This class allows for the registration, retrieval, and management of {@link LogRenderer} instances dynamically, | ||
* using a thread-safe approach to ensure proper operation in multi-threaded environments. | ||
*/ | ||
public class LogRendererRegistry { | ||
private static final Map<LogRendererRegistry.Key, LogRenderer<?>> renderers = Collections.synchronizedMap( | ||
new LinkedHashMap<>() | ||
); | ||
static { | ||
for (LogRenderer<?> r : ServiceLoader.load(LogRenderer.class)) { | ||
register (r); | ||
} | ||
} | ||
|
||
/** | ||
* Registers a {@link LogRenderer} in the registry with a key generated from the renderer's class and type. | ||
* @param renderer The renderer to register. Must not be null. | ||
* @throws NullPointerException if the renderer is null. | ||
*/ | ||
public static <T> void register (LogRenderer<?> renderer) { | ||
Objects.requireNonNull(renderer); | ||
renderers.put(new LogRendererRegistry.Key(renderer.clazz(), renderer.type()), renderer); | ||
} | ||
|
||
/** | ||
* Dumps the current state of the registry to the specified {@link PrintStream}. | ||
* @param ps The {@link PrintStream} to which the dump will be written, e.g.: System.out | ||
*/ | ||
public static void dump (PrintStream ps) { | ||
ps.println (LogRendererRegistry.class); | ||
for (Map.Entry<LogRendererRegistry.Key, LogRenderer<?>> entry : renderers.entrySet()) { | ||
ps.println(" " + entry.getKey() + ": " + entry.getValue().getClass()); | ||
} | ||
ps.println (); | ||
} | ||
|
||
/** | ||
* Retrieves a {@link LogRenderer} that matches the specified class and type. If no direct match is found, | ||
* it attempts to find a renderer for any superclass or implemented interfaces. If no specific renderer is found, | ||
* it defaults to a renderer for {@link Object}, if present for the given type. | ||
* | ||
* @param clazz The class for which a renderer is required. | ||
* @param type The type of the renderer. | ||
* @return The matching {@link LogRenderer}, or a default renderer if no specific match is found. | ||
*/ | ||
@SuppressWarnings("unchecked") | ||
public static <T> LogRenderer<T> getRenderer(Class<?> clazz, LogRenderer.Type type) { | ||
LogRenderer<T> renderer = getRendererForClass(clazz, type); | ||
boolean needsCache = false; | ||
if (renderer == null) { | ||
needsCache = true; | ||
renderer = getRendererForInterface(clazz, type); | ||
} | ||
if (renderer == null) { | ||
renderer = (LogRenderer<T>) renderers.get(new LogRendererRegistry.Key(Object.class, type)); | ||
} | ||
if (renderer != null && needsCache) | ||
renderers.put(new LogRendererRegistry.Key(clazz, renderer.type(), true), renderer); | ||
return renderer; | ||
} | ||
|
||
/** | ||
* Recursively searches for a renderer for the given class and type, considering superclasses. | ||
* | ||
* @param clazz The class for which a renderer is needed. | ||
* @param type The type of the renderer. | ||
* @return A matching renderer, or null if none is found. | ||
*/ | ||
@SuppressWarnings("unchecked") | ||
private static <T> LogRenderer<T> getRendererForClass (Class<?> clazz, LogRenderer.Type type) { | ||
LogRenderer<T> renderer = (LogRenderer<T>) renderers.get(new LogRendererRegistry.Key(clazz, type)); | ||
if (renderer == null && clazz.getSuperclass() != Object.class) { | ||
renderer = getRendererForClass(clazz.getSuperclass(), type); | ||
} | ||
return renderer; | ||
} | ||
|
||
/** | ||
* Searches for a renderer among the interfaces implemented by the specified class. | ||
* | ||
* @param clazz The class whose interfaces will be checked for a matching renderer. | ||
* @param type The type of the renderer. | ||
* @return A matching renderer, or null if none is found. | ||
*/ | ||
@SuppressWarnings("unchecked") | ||
private static <T> LogRenderer<T> getRendererForInterface (Class<?> clazz, LogRenderer.Type type) { | ||
for (Class<?> i : clazz.getInterfaces()) { | ||
LogRenderer<T> renderer = (LogRenderer<T>) renderers.get(new LogRendererRegistry.Key(i, type)); | ||
if (renderer != null) | ||
return renderer; | ||
} | ||
return null; | ||
} | ||
|
||
/** | ||
* A private key class to encapsulate the combination of class type and renderer type. | ||
* This key is used to uniquely identify renderers within the registry. | ||
*/ | ||
private static class Key { | ||
private final Class<?> clazz; | ||
private final LogRenderer.Type type; | ||
private final boolean cache; | ||
|
||
public Key(Class<?> clazz, LogRenderer.Type type) { | ||
this (clazz, type, false); | ||
} | ||
public Key(Class<?> clazz, LogRenderer.Type type, boolean cache) { | ||
this.clazz = clazz; | ||
this.type = type; | ||
this.cache = cache; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) return true; | ||
if (o == null || getClass() != o.getClass()) return false; | ||
LogRendererRegistry.Key key = (LogRendererRegistry.Key) o; | ||
return Objects.equals(clazz, key.clazz) && type == key.type; | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(clazz, type); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "Key{" + clazz + ", type=" + type + (cache ? ", cached" : "") + '}'; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
/* | ||
* jPOS Project [http://jpos.org] | ||
* Copyright (C) 2000-2023 jPOS Software SRL | ||
* | ||
* 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 <http://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
package org.jpos.log.evt; | ||
|
||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; | ||
import org.jpos.log.AuditLogEvent; | ||
|
||
/** | ||
* Represents a deployment configuration with a specific path and an enabled status. | ||
* | ||
* <p>This record is used as part of the audit log events in the system to track deployment actions.</p> | ||
* | ||
* @param path the path where the deployment is located | ||
* @param enabled a boolean flag indicating whether the deployment is enabled | ||
*/ | ||
|
||
public record Deploy(String path, @JacksonXmlProperty(isAttribute = true) boolean enabled) implements AuditLogEvent { } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
/* | ||
* jPOS Project [http://jpos.org] | ||
* Copyright (C) 2000-2010 Alejandro P. Revilla | ||
* | ||
* 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 <http://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
package org.jpos.log.evt; | ||
|
||
import com.fasterxml.jackson.annotation.JsonProperty; | ||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; | ||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; | ||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; | ||
import org.jpos.log.AuditLogEvent; | ||
import java.time.Instant; | ||
import java.util.List; | ||
import java.util.UUID; | ||
|
||
@JacksonXmlRootElement(localName = "log") | ||
public record LogEvt( | ||
@JacksonXmlProperty(isAttribute = true) Instant ts, | ||
@JacksonXmlProperty(isAttribute = true) @JsonProperty("trace-id") UUID traceId, | ||
@JacksonXmlProperty(isAttribute = true) String realm, | ||
@JacksonXmlProperty(isAttribute = true) String tag, | ||
@JacksonXmlProperty(isAttribute = true) Long elapsed, | ||
@JsonProperty("evt") @JacksonXmlElementWrapper(useWrapping = false) List<AuditLogEvent> events) { } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package org.jpos.log.evt; | ||
|
||
import com.fasterxml.jackson.annotation.JsonProperty; | ||
import org.jpos.log.AuditLogEvent; | ||
|
||
public record LogMessage(@JsonProperty("m") String msg) implements AuditLogEvent { } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
/* | ||
* jPOS Project [http://jpos.org] | ||
* Copyright (C) 2000-2023 jPOS Software SRL | ||
* | ||
* 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 <http://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
package org.jpos.log.evt; | ||
|
||
import org.jpos.log.AuditLogEvent; | ||
|
||
import java.util.UUID; | ||
|
||
/** | ||
* Represents the starting log entry for an auditing process in the system. This record encapsulates | ||
* all the essential details needed for initializing audit logs in a structured and consistent format. | ||
* | ||
* @param q2 The identifier of the Q2 system instance from which the log is originating. | ||
* @param version The version of the Q2 system, detailing the specific build or release version. | ||
* @param appVersion The version of the application that is running within the Q2 system, | ||
* providing context about the application's release state. | ||
* @param deploy Absolute path to Q2's deploy directory. | ||
* @param env The name of the environment in which the application is running. | ||
*/ | ||
public record Start(UUID q2, String version, String appVersion, String deploy, String env) implements AuditLogEvent { } |
Oops, something went wrong.