Skip to content

Commit 901675f

Browse files
authored
Merge pull request #43232 from troosan/#23127
Add support for Socket Logging Handler with basic ECS format logging
2 parents d1c3ae8 + 3d110d1 commit 901675f

File tree

15 files changed

+565
-3
lines changed

15 files changed

+565
-3
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package io.quarkus.deployment.builditem;
2+
3+
import java.util.Optional;
4+
import java.util.logging.Formatter;
5+
6+
import org.wildfly.common.Assert;
7+
8+
import io.quarkus.builder.item.MultiBuildItem;
9+
import io.quarkus.runtime.RuntimeValue;
10+
11+
/**
12+
* The socket format build item. Producing this item will cause the logging subsystem to disregard its
13+
* socket logging formatting configuration and use the formatter provided instead. If multiple formatters
14+
* are enabled at runtime, a warning message is printed and only one is used.
15+
*/
16+
public final class LogSocketFormatBuildItem extends MultiBuildItem {
17+
private final RuntimeValue<Optional<Formatter>> formatterValue;
18+
19+
/**
20+
* Construct a new instance.
21+
*
22+
* @param formatterValue the optional formatter runtime value to use (must not be {@code null})
23+
*/
24+
public LogSocketFormatBuildItem(final RuntimeValue<Optional<Formatter>> formatterValue) {
25+
this.formatterValue = Assert.checkNotNullParam("formatterValue", formatterValue);
26+
}
27+
28+
/**
29+
* Get the formatter value.
30+
*
31+
* @return the formatter value
32+
*/
33+
public RuntimeValue<Optional<Formatter>> getFormatterValue() {
34+
return formatterValue;
35+
}
36+
}

core/deployment/src/main/java/io/quarkus/deployment/logging/LoggingResourceProcessor.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
import io.quarkus.deployment.builditem.LogConsoleFormatBuildItem;
7676
import io.quarkus.deployment.builditem.LogFileFormatBuildItem;
7777
import io.quarkus.deployment.builditem.LogHandlerBuildItem;
78+
import io.quarkus.deployment.builditem.LogSocketFormatBuildItem;
7879
import io.quarkus.deployment.builditem.LogSyslogFormatBuildItem;
7980
import io.quarkus.deployment.builditem.NamedLogHandlersBuildItem;
8081
import io.quarkus.deployment.builditem.RunTimeConfigurationDefaultBuildItem;
@@ -249,6 +250,7 @@ LoggingSetupBuildItem setupLoggingRuntimeInit(
249250
final List<LogConsoleFormatBuildItem> consoleFormatItems,
250251
final List<LogFileFormatBuildItem> fileFormatItems,
251252
final List<LogSyslogFormatBuildItem> syslogFormatItems,
253+
final List<LogSocketFormatBuildItem> socketFormatItems,
252254
final Optional<ConsoleFormatterBannerBuildItem> possibleBannerBuildItem,
253255
final List<LogStreamBuildItem> logStreamBuildItems,
254256
final BuildProducer<ShutdownListenerBuildItem> shutdownListenerBuildItemBuildProducer,
@@ -290,6 +292,8 @@ LoggingSetupBuildItem setupLoggingRuntimeInit(
290292
.map(LogFileFormatBuildItem::getFormatterValue).collect(Collectors.toList());
291293
List<RuntimeValue<Optional<Formatter>>> possibleSyslogFormatters = syslogFormatItems.stream()
292294
.map(LogSyslogFormatBuildItem::getFormatterValue).collect(Collectors.toList());
295+
List<RuntimeValue<Optional<Formatter>>> possibleSocketFormatters = socketFormatItems.stream()
296+
.map(LogSocketFormatBuildItem::getFormatterValue).collect(Collectors.toList());
293297

294298
context.registerSubstitution(InheritableLevel.ActualLevel.class, String.class, InheritableLevel.Substitution.class);
295299
context.registerSubstitution(InheritableLevel.Inherited.class, String.class, InheritableLevel.Substitution.class);
@@ -308,6 +312,7 @@ LoggingSetupBuildItem setupLoggingRuntimeInit(
308312
categoryMinLevelDefaults.content, alwaysEnableLogStream,
309313
streamingDevUiLogHandler, handlers, namedHandlers,
310314
possibleConsoleFormatters, possibleFileFormatters, possibleSyslogFormatters,
315+
possibleSocketFormatters,
311316
possibleSupplier, launchModeBuildItem.getLaunchMode(), true)));
312317

313318
List<LogCleanupFilterElement> additionalLogCleanupFilters = new ArrayList<>(logCleanupFilters.size());

core/runtime/src/main/java/io/quarkus/runtime/logging/LogRuntimeConfig.java

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import java.util.logging.Level;
1111

1212
import org.jboss.logmanager.handlers.AsyncHandler.OverflowAction;
13+
import org.jboss.logmanager.handlers.SocketHandler;
1314
import org.jboss.logmanager.handlers.SyslogHandler.Facility;
1415
import org.jboss.logmanager.handlers.SyslogHandler.Protocol;
1516
import org.jboss.logmanager.handlers.SyslogHandler.SyslogType;
@@ -77,6 +78,14 @@ public interface LogRuntimeConfig {
7778
@ConfigDocSection
7879
SyslogConfig syslog();
7980

81+
/**
82+
* Socket logging.
83+
* <p>
84+
* Logging to a socket is also supported but not enabled by default.
85+
*/
86+
@ConfigDocSection
87+
SocketConfig socket();
88+
8089
/**
8190
* Logging categories.
8291
* <p>
@@ -115,6 +124,15 @@ public interface LogRuntimeConfig {
115124
@ConfigDocSection
116125
Map<String, SyslogConfig> syslogHandlers();
117126

127+
/**
128+
* Socket handlers.
129+
* <p>
130+
* The named socket handlers configured here can be linked to one or more categories.
131+
*/
132+
@WithName("handler.socket")
133+
@ConfigDocSection
134+
Map<String, SocketConfig> socketHandlers();
135+
118136
/**
119137
* Log cleanup filters - internal use.
120138
*/
@@ -396,6 +414,59 @@ interface SyslogConfig {
396414
AsyncConfig async();
397415
}
398416

417+
interface SocketConfig {
418+
419+
/**
420+
* If socket logging should be enabled
421+
*/
422+
@WithDefault("false")
423+
boolean enable();
424+
425+
/**
426+
*
427+
* The IP address and port of the server receiving the logs
428+
*/
429+
@WithDefault("localhost:4560")
430+
@WithConverter(InetSocketAddressConverter.class)
431+
InetSocketAddress endpoint();
432+
433+
/**
434+
* Sets the protocol used to connect to the syslog server
435+
*/
436+
@WithDefault("tcp")
437+
SocketHandler.Protocol protocol();
438+
439+
/**
440+
* Enables or disables blocking when attempting to reconnect a
441+
* {@link Protocol#TCP
442+
* TCP} or {@link Protocol#SSL_TCP SSL TCP} protocol
443+
*/
444+
@WithDefault("false")
445+
boolean blockOnReconnect();
446+
447+
/**
448+
* The log message format
449+
*/
450+
@WithDefault("%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c{3.}] (%t) %s%e%n")
451+
String format();
452+
453+
/**
454+
* The log level specifying, which message levels will be logged by socket logger
455+
*/
456+
@WithDefault("ALL")
457+
Level level();
458+
459+
/**
460+
* The name of the filter to link to the file handler.
461+
*/
462+
Optional<String> filter();
463+
464+
/**
465+
* Socket async logging config
466+
*/
467+
AsyncConfig async();
468+
}
469+
399470
interface CleanupFilterConfig {
400471
/**
401472
* The message prefix to match

core/runtime/src/main/java/io/quarkus/runtime/logging/LoggingSetupRecorder.java

Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import org.jboss.logmanager.handlers.FileHandler;
4747
import org.jboss.logmanager.handlers.PeriodicSizeRotatingFileHandler;
4848
import org.jboss.logmanager.handlers.SizeRotatingFileHandler;
49+
import org.jboss.logmanager.handlers.SocketHandler;
4950
import org.jboss.logmanager.handlers.SyslogHandler;
5051

5152
import io.quarkus.bootstrap.logging.InitialConfigurator;
@@ -63,6 +64,7 @@
6364
import io.quarkus.runtime.logging.LogRuntimeConfig.CleanupFilterConfig;
6465
import io.quarkus.runtime.logging.LogRuntimeConfig.ConsoleConfig;
6566
import io.quarkus.runtime.logging.LogRuntimeConfig.FileConfig;
67+
import io.quarkus.runtime.logging.LogRuntimeConfig.SocketConfig;
6668
import io.quarkus.runtime.shutdown.ShutdownListener;
6769
import io.smallrye.config.SmallRyeConfig;
6870
import io.smallrye.config.SmallRyeConfigBuilder;
@@ -116,7 +118,7 @@ public String getName() {
116118
new LoggingSetupRecorder(new RuntimeValue<>(consoleRuntimeConfig)).initializeLogging(logRuntimeConfig,
117119
logBuildTimeConfig,
118120
DiscoveredLogComponents.ofEmpty(), emptyMap(), false, null, emptyList(), emptyList(), emptyList(), emptyList(),
119-
emptyList(), banner, LaunchMode.DEVELOPMENT, false);
121+
emptyList(), emptyList(), banner, LaunchMode.DEVELOPMENT, false);
120122
}
121123

122124
public ShutdownListener initializeLogging(
@@ -131,6 +133,7 @@ public ShutdownListener initializeLogging(
131133
final List<RuntimeValue<Optional<Formatter>>> possibleConsoleFormatters,
132134
final List<RuntimeValue<Optional<Formatter>>> possibleFileFormatters,
133135
final List<RuntimeValue<Optional<Formatter>>> possibleSyslogFormatters,
136+
final List<RuntimeValue<Optional<Formatter>>> possibleSocketFormatters,
134137
final RuntimeValue<Optional<Supplier<String>>> possibleBannerSupplier,
135138
final LaunchMode launchMode,
136139
final boolean includeFilters) {
@@ -211,6 +214,14 @@ public void close() throws SecurityException {
211214
}
212215
}
213216

217+
if (config.socket().enable()) {
218+
final Handler socketHandler = configureSocketHandler(config.socket(), errorManager, cleanupFiler,
219+
namedFilters, possibleSocketFormatters, includeFilters);
220+
if (socketHandler != null) {
221+
handlers.add(socketHandler);
222+
}
223+
}
224+
214225
if ((launchMode.isDevOrTest() || enableWebStream)
215226
&& streamingDevUiConsoleHandler != null
216227
&& streamingDevUiConsoleHandler.getValue().isPresent()) {
@@ -229,7 +240,7 @@ public void close() throws SecurityException {
229240

230241
Map<String, Handler> namedHandlers = shouldCreateNamedHandlers(config, additionalNamedHandlers)
231242
? createNamedHandlers(config, consoleRuntimeConfig.getValue(), additionalNamedHandlers,
232-
possibleConsoleFormatters, possibleFileFormatters, possibleSyslogFormatters,
243+
possibleConsoleFormatters, possibleFileFormatters, possibleSyslogFormatters, possibleSocketFormatters,
233244
errorManager, cleanupFiler, namedFilters, launchMode,
234245
shutdownNotifier, includeFilters)
235246
: emptyMap();
@@ -328,7 +339,7 @@ public static void initializeBuildTimeLogging(
328339
}
329340

330341
Map<String, Handler> namedHandlers = createNamedHandlers(config, consoleConfig, emptyList(),
331-
emptyList(), emptyList(), emptyList(), errorManager, logCleanupFilter,
342+
emptyList(), emptyList(), emptyList(), emptyList(), errorManager, logCleanupFilter,
332343
emptyMap(), launchMode, dummy, false);
333344

334345
setUpCategoryLoggers(buildConfig, categoryDefaultMinLevels, categories, logContext, errorManager, namedHandlers);
@@ -388,6 +399,7 @@ private static Map<String, Handler> createNamedHandlers(
388399
List<RuntimeValue<Optional<Formatter>>> possibleConsoleFormatters,
389400
List<RuntimeValue<Optional<Formatter>>> possibleFileFormatters,
390401
List<RuntimeValue<Optional<Formatter>>> possibleSyslogFormatters,
402+
List<RuntimeValue<Optional<Formatter>>> possibleSocketFormatters,
391403
ErrorManager errorManager, LogCleanupFilter cleanupFilter,
392404
Map<String, Filter> namedFilters, LaunchMode launchMode,
393405
ShutdownNotifier shutdownHandler, boolean includeFilters) {
@@ -422,6 +434,17 @@ private static Map<String, Handler> createNamedHandlers(
422434
addToNamedHandlers(namedHandlers, syslogHandler, sysLogConfigEntry.getKey());
423435
}
424436
}
437+
for (Entry<String, SocketConfig> socketConfigEntry : config.socketHandlers().entrySet()) {
438+
SocketConfig namedSocketConfig = socketConfigEntry.getValue();
439+
if (!namedSocketConfig.enable()) {
440+
continue;
441+
}
442+
final Handler socketHandler = configureSocketHandler(namedSocketConfig, errorManager, cleanupFilter,
443+
namedFilters, possibleSocketFormatters, includeFilters);
444+
if (socketHandler != null) {
445+
addToNamedHandlers(namedHandlers, socketHandler, socketConfigEntry.getKey());
446+
}
447+
}
425448

426449
Map<String, Handler> additionalNamedHandlersMap;
427450
if (additionalNamedHandlers.isEmpty()) {
@@ -770,6 +793,53 @@ private static Handler configureSyslogHandler(final LogRuntimeConfig.SyslogConfi
770793
}
771794
}
772795

796+
private static Handler configureSocketHandler(final LogRuntimeConfig.SocketConfig config,
797+
final ErrorManager errorManager,
798+
final LogCleanupFilter logCleanupFilter,
799+
final Map<String, Filter> namedFilters,
800+
final List<RuntimeValue<Optional<Formatter>>> possibleSocketFormatters,
801+
final boolean includeFilters) {
802+
try {
803+
final SocketHandler handler = new SocketHandler(config.endpoint().getHostString(), config.endpoint().getPort());
804+
handler.setProtocol(config.protocol());
805+
handler.setBlockOnReconnect(config.blockOnReconnect());
806+
handler.setLevel(config.level());
807+
808+
Formatter formatter = null;
809+
boolean formatterWarning = false;
810+
for (RuntimeValue<Optional<Formatter>> value : possibleSocketFormatters) {
811+
if (formatter != null) {
812+
formatterWarning = true;
813+
}
814+
final Optional<Formatter> val = value.getValue();
815+
if (val.isPresent()) {
816+
formatter = val.get();
817+
}
818+
}
819+
if (formatter == null) {
820+
formatter = new PatternFormatter(config.format());
821+
}
822+
handler.setFormatter(formatter);
823+
824+
handler.setErrorManager(errorManager);
825+
handler.setFilter(logCleanupFilter);
826+
applyFilter(includeFilters, errorManager, logCleanupFilter, config.filter(), namedFilters, handler);
827+
828+
if (formatterWarning) {
829+
handler.getErrorManager().error("Multiple socket formatters were activated", null,
830+
ErrorManager.GENERIC_FAILURE);
831+
}
832+
833+
if (config.async().enable()) {
834+
return createAsyncHandler(config.async(), config.level(), handler);
835+
}
836+
return handler;
837+
} catch (IOException e) {
838+
errorManager.error("Failed to create socket handler", e, ErrorManager.OPEN_FAILURE);
839+
return null;
840+
}
841+
}
842+
773843
private static AsyncHandler createAsyncHandler(LogRuntimeConfig.AsyncConfig asyncConfig, Level level, Handler handler) {
774844
final AsyncHandler asyncHandler = new AsyncHandler(asyncConfig.queueLength());
775845
asyncHandler.setOverflowAction(asyncConfig.overflow());

0 commit comments

Comments
 (0)