0
# Message System & Formatting
1
2
Comprehensive message creation system supporting multiple formatting syntaxes, structured data, flow tracing, and custom message factories for specialized use cases.
3
4
## Capabilities
5
6
### Message Interface
7
8
Base interface for all log message types providing formatted output and metadata access.
9
10
```java { .api }
11
/**
12
* Base interface for all log message types
13
*/
14
public interface Message extends Serializable {
15
/** Get the formatted message string */
16
String getFormattedMessage();
17
18
/** Get message parameters array */
19
Object[] getParameters();
20
21
/** Get associated throwable if any */
22
Throwable getThrowable();
23
24
/** @Deprecated Get message format string */
25
@Deprecated
26
String getFormat();
27
}
28
```
29
30
**Usage Examples:**
31
32
```java
33
private static final Logger logger = LogManager.getLogger();
34
35
public void demonstrateMessageInterface() {
36
// Messages are typically created internally by the logging framework
37
// but can be created explicitly for advanced use cases
38
39
Message simpleMsg = new SimpleMessage("Hello World");
40
logger.info(simpleMsg);
41
42
Message paramMsg = new ParameterizedMessage("User {} has {} items", "alice", 5);
43
logger.info(paramMsg);
44
45
// Access message components
46
String formatted = paramMsg.getFormattedMessage(); // "User alice has 5 items"
47
Object[] params = paramMsg.getParameters(); // ["alice", 5]
48
}
49
```
50
51
### MessageFactory Interface
52
53
Factory for creating Message instances with different formatting syntaxes.
54
55
```java { .api }
56
/**
57
* Factory interface for creating Message instances
58
*/
59
public interface MessageFactory {
60
/** Create message from object */
61
Message newMessage(Object message);
62
63
/** Create message from string */
64
Message newMessage(String message);
65
66
/** Create parameterized message */
67
Message newMessage(String message, Object... params);
68
}
69
70
/**
71
* Extended message factory with additional methods
72
*/
73
public interface MessageFactory2 extends MessageFactory {
74
/** Create message from string with single parameter */
75
Message newMessage(String message, Object p0);
76
77
/** Create message from string with two parameters */
78
Message newMessage(String message, Object p0, Object p1);
79
80
// Additional overloads for up to 10 parameters for performance
81
Message newMessage(String message, Object p0, Object p1, Object p2);
82
// ... more overloads
83
}
84
```
85
86
**Usage Examples:**
87
88
```java
89
public void demonstrateMessageFactories() {
90
// Different message factories create different message types
91
92
// Parameterized message factory (default, uses {} placeholders)
93
MessageFactory paramFactory = ParameterizedMessageFactory.INSTANCE;
94
Message msg1 = paramFactory.newMessage("User {} logged in", "alice");
95
96
// String formatter message factory (uses String.format style)
97
MessageFactory stringFactory = StringFormatterMessageFactory.INSTANCE;
98
Message msg2 = stringFactory.newMessage("User %s has %d items", "alice", 5);
99
100
// Simple message factory (no parameter substitution)
101
MessageFactory simpleFactory = SimpleMessageFactory.INSTANCE;
102
Message msg3 = simpleFactory.newMessage("Simple message");
103
104
// Reusable message factory (for high-performance scenarios)
105
MessageFactory reusableFactory = ReusableMessageFactory.INSTANCE;
106
Message msg4 = reusableFactory.newMessage("Reusable: {}", value);
107
}
108
109
// Custom message factory
110
public class CustomMessageFactory implements MessageFactory {
111
112
@Override
113
public Message newMessage(Object message) {
114
return new CustomMessage(message);
115
}
116
117
@Override
118
public Message newMessage(String message) {
119
return new CustomMessage(message);
120
}
121
122
@Override
123
public Message newMessage(String message, Object... params) {
124
return new CustomParameterizedMessage(message, params);
125
}
126
}
127
```
128
129
### Core Message Types
130
131
Built-in message implementations for different formatting and data representation needs.
132
133
```java { .api }
134
/**
135
* Simple string message without parameter substitution
136
*/
137
public class SimpleMessage implements CharSequence, Message {
138
public SimpleMessage(CharSequence message);
139
public SimpleMessage(Object message);
140
}
141
142
/**
143
* Message with {} parameter placeholders (most commonly used)
144
*/
145
public class ParameterizedMessage implements Message {
146
public ParameterizedMessage(String messagePattern, Object[] arguments);
147
public ParameterizedMessage(String messagePattern, Object... arguments);
148
public ParameterizedMessage(String messagePattern, Object arg);
149
public ParameterizedMessage(String messagePattern, Object arg1, Object arg2);
150
151
/** Get count of parameters */
152
public int getParameterCount();
153
154
/** Get the message pattern */
155
public String getFormat();
156
157
/** Get throwable if last parameter is Throwable */
158
@Override
159
public Throwable getThrowable();
160
}
161
162
/**
163
* Message wrapping arbitrary objects
164
*/
165
public class ObjectMessage implements Message {
166
public ObjectMessage(Object obj);
167
168
/** Get the wrapped object */
169
public Object getParameter();
170
}
171
172
/**
173
* Message using String.format() style formatting
174
*/
175
public class StringFormattedMessage implements Message {
176
public StringFormattedMessage(String messagePattern, Object... arguments);
177
}
178
179
/**
180
* @Deprecated Message using java.text.MessageFormat
181
*/
182
@Deprecated
183
public class FormattedMessage implements Message {
184
public FormattedMessage(String messagePattern, Object... arguments);
185
}
186
```
187
188
**Usage Examples:**
189
190
```java
191
private static final Logger logger = LogManager.getLogger();
192
193
public void demonstrateMessageTypes() {
194
// Simple message - no parameter substitution
195
SimpleMessage simple = new SimpleMessage("Simple log message");
196
logger.info(simple);
197
198
// Parameterized message - {} placeholders (most efficient)
199
ParameterizedMessage param = new ParameterizedMessage(
200
"User {} performed {} action at {}", "alice", "login", new Date());
201
logger.info(param);
202
203
// Object message - wraps any object
204
ObjectMessage objMsg = new ObjectMessage(new UserProfile("alice", 25));
205
logger.info(objMsg); // Uses object's toString()
206
207
// String formatted message - String.format() style
208
StringFormattedMessage formatted = new StringFormattedMessage(
209
"User %s has %d items (%.2f%% complete)", "alice", 5, 75.5);
210
logger.info(formatted);
211
212
// Automatic throwable detection in parameterized messages
213
Exception ex = new RuntimeException("test");
214
ParameterizedMessage withEx = new ParameterizedMessage(
215
"Error processing user {}", "alice", ex); // Exception automatically extracted
216
logger.error(withEx); // Exception will be logged separately
217
218
// Access message properties
219
String pattern = param.getFormat(); // Original pattern: "User {} performed {} action at {}"
220
Object[] params = param.getParameters(); // ["alice", "login", Date]
221
int paramCount = param.getParameterCount(); // 3
222
}
223
224
// Custom object message usage
225
public class UserProfile {
226
private String name;
227
private int age;
228
229
public UserProfile(String name, int age) {
230
this.name = name;
231
this.age = age;
232
}
233
234
@Override
235
public String toString() {
236
return String.format("UserProfile{name='%s', age=%d}", name, age);
237
}
238
}
239
240
public void logUserProfile() {
241
UserProfile profile = new UserProfile("alice", 25);
242
243
// Object message will use toString()
244
logger.info(new ObjectMessage(profile));
245
// Output: UserProfile{name='alice', age=25}
246
247
// Parameterized message with object
248
logger.info("User profile: {}", profile);
249
// Output: User profile: UserProfile{name='alice', age=25}
250
}
251
```
252
253
### Structured Data Messages
254
255
Messages that represent structured key-value data, particularly useful for logging events and metrics.
256
257
```java { .api }
258
/**
259
* Message representing key-value pairs
260
*/
261
public class MapMessage implements Message {
262
public MapMessage();
263
public MapMessage(Map<String, Object> map);
264
265
/** Put a key-value pair */
266
public MapMessage with(String key, Object value);
267
268
/** Put a key-value pair (fluent API) */
269
public MapMessage put(String key, Object value);
270
271
/** Get value by key */
272
public Object get(String key);
273
274
/** Remove key */
275
public Object remove(String key);
276
277
/** Get all keys */
278
public String[] getKeys();
279
280
/** Check if key exists */
281
public boolean containsKey(String key);
282
283
/** Get as Map */
284
public Map<String, Object> getData();
285
286
/** Clear all data */
287
public void clear();
288
}
289
290
/**
291
* RFC 5424 Structured Data message
292
*/
293
public class StructuredDataMessage extends MapMessage {
294
public StructuredDataMessage(String id, Map<String, String> data, String message);
295
public StructuredDataMessage(String id, Map<String, String> data, String message, Object... args);
296
297
/** Get structured data ID */
298
public String getId();
299
300
/** Get structured data as RFC 5424 format */
301
public String asString();
302
303
/** Get structured data as RFC 5424 format with specified format */
304
public String asString(String format);
305
}
306
```
307
308
**Usage Examples:**
309
310
```java
311
private static final Logger logger = LogManager.getLogger();
312
313
public void demonstrateStructuredMessages() {
314
// Map message for structured logging
315
MapMessage mapMsg = new MapMessage()
316
.with("userId", "12345")
317
.with("action", "login")
318
.with("timestamp", System.currentTimeMillis())
319
.with("success", true)
320
.with("duration", 150);
321
322
logger.info(mapMsg);
323
// Output: userId="12345" action="login" timestamp="1634567890123" success="true" duration="150"
324
325
// Structured data message (RFC 5424 compliant)
326
Map<String, String> eventData = new HashMap<>();
327
eventData.put("userId", "12345");
328
eventData.put("sessionId", "abc-def-ghi");
329
eventData.put("remoteAddr", "192.168.1.100");
330
331
StructuredDataMessage sdMsg = new StructuredDataMessage(
332
"loginEvent", eventData, "User {} logged in successfully", "alice");
333
334
logger.info(sdMsg);
335
// Output: [loginEvent userId="12345" sessionId="abc-def-ghi" remoteAddr="192.168.1.100"] User alice logged in successfully
336
337
// Building map messages fluently
338
MapMessage auditMsg = new MapMessage()
339
.put("event", "fileAccess")
340
.put("file", "/etc/passwd")
341
.put("user", "admin")
342
.put("result", "denied");
343
344
logger.warn(auditMsg);
345
}
346
347
// Business event logging with structured data
348
public class EventLogger {
349
private static final Logger logger = LogManager.getLogger();
350
351
public void logUserAction(String userId, String action, boolean successful, long duration) {
352
MapMessage event = new MapMessage()
353
.with("userId", userId)
354
.with("action", action)
355
.with("successful", successful)
356
.with("duration", duration)
357
.with("timestamp", Instant.now().toString());
358
359
if (successful) {
360
logger.info(event);
361
} else {
362
logger.warn(event);
363
}
364
}
365
366
public void logOrderEvent(Order order, String event) {
367
StructuredDataMessage sdMsg = new StructuredDataMessage(
368
"orderEvent",
369
Map.of(
370
"orderId", order.getId(),
371
"customerId", order.getCustomerId(),
372
"amount", order.getAmount().toString(),
373
"currency", order.getCurrency()
374
),
375
"Order {} event: {}", order.getId(), event);
376
377
logger.info(sdMsg);
378
}
379
}
380
```
381
382
### Flow Messages
383
384
Specialized messages for method entry/exit tracing and flow control logging.
385
386
```java { .api }
387
/**
388
* Message for method entry logging
389
*/
390
public class EntryMessage implements Message {
391
public EntryMessage(String format, Object... params);
392
393
/** Get the message for entry */
394
public Message getMessage();
395
}
396
397
/**
398
* Message for method exit logging
399
*/
400
public class ExitMessage implements Message {
401
public ExitMessage(EntryMessage message, Object result, long durationNanos);
402
public ExitMessage(String format, Object result, long durationNanos);
403
404
/** Get the result object */
405
public Object getResult();
406
407
/** Get execution duration in nanoseconds */
408
public long getDurationNanos();
409
410
/** Get the entry message */
411
public EntryMessage getEntryMessage();
412
}
413
414
/**
415
* Factory for creating flow messages
416
*/
417
public interface FlowMessageFactory {
418
EntryMessage newEntryMessage(Message message);
419
ExitMessage newExitMessage(EntryMessage message, Object result, long durationNanos);
420
ExitMessage newExitMessage(Object result, Message message);
421
}
422
423
/**
424
* Default flow message factory implementation
425
*/
426
public class DefaultFlowMessageFactory implements FlowMessageFactory {
427
public static final DefaultFlowMessageFactory INSTANCE = new DefaultFlowMessageFactory();
428
}
429
```
430
431
**Usage Examples:**
432
433
```java
434
private static final Logger logger = LogManager.getLogger();
435
436
public class FlowTrackingService {
437
438
// Manual flow message creation
439
public String processUser(String userId) {
440
EntryMessage entryMsg = new EntryMessage("processUser(userId={})", userId);
441
logger.traceEntry(entryMsg);
442
443
long startTime = System.nanoTime();
444
try {
445
// Process user
446
String result = "processed-" + userId;
447
448
long duration = System.nanoTime() - startTime;
449
ExitMessage exitMsg = new ExitMessage(entryMsg, result, duration);
450
logger.traceExit(exitMsg);
451
452
return result;
453
454
} catch (Exception e) {
455
logger.throwing(e);
456
throw e;
457
}
458
}
459
460
// Using Logger's built-in flow methods (preferred)
461
public User findUser(Long id) {
462
logger.traceEntry("findUser(id={})", id);
463
464
try {
465
User user = userRepository.findById(id);
466
return logger.traceExit(user);
467
} catch (Exception e) {
468
throw logger.throwing(e);
469
}
470
}
471
472
// Complex flow with multiple parameters
473
public Order createOrder(String customerId, List<OrderItem> items, PaymentInfo payment) {
474
logger.traceEntry("customerId={}, itemCount={}, paymentMethod={}",
475
customerId, items.size(), payment.getMethod());
476
477
try {
478
validateCustomer(customerId);
479
validateItems(items);
480
validatePayment(payment);
481
482
Order order = new Order(customerId, items, payment);
483
Order savedOrder = orderRepository.save(order);
484
485
return logger.traceExit(savedOrder);
486
487
} catch (Exception e) {
488
throw logger.throwing(e);
489
}
490
}
491
492
// Flow tracking with custom flow message factory
493
private static final FlowMessageFactory flowFactory = DefaultFlowMessageFactory.INSTANCE;
494
495
public void customFlowTracking() {
496
Message entryMessage = new SimpleMessage("Starting custom operation");
497
EntryMessage entry = flowFactory.newEntryMessage(entryMessage);
498
logger.trace(entry);
499
500
long startTime = System.nanoTime();
501
try {
502
String result = performOperation();
503
504
long duration = System.nanoTime() - startTime;
505
ExitMessage exit = flowFactory.newExitMessage(entry, result, duration);
506
logger.trace(exit);
507
508
} catch (Exception e) {
509
logger.error("Operation failed", e);
510
throw e;
511
}
512
}
513
}
514
```
515
516
### Advanced Message Features
517
518
Specialized message interfaces for performance optimization and async-safe formatting.
519
520
```java { .api }
521
/**
522
* Messages supporting multiple output formats
523
*/
524
public interface MultiformatMessage {
525
/** Get formatted message for specific format */
526
String getFormattedMessage(String[] formats);
527
}
528
529
/**
530
* Messages that can be reused to avoid object allocation
531
*/
532
public interface ReusableMessage extends Message, Clearable {
533
/** Set new message content */
534
ReusableMessage set(String message);
535
536
/** Set new parameterized message content */
537
ReusableMessage set(String message, Object... params);
538
539
/** Swap contents with another reusable message */
540
void swapParameters(ReusableMessage other);
541
}
542
543
/**
544
* Messages safe for asynchronous formatting
545
*/
546
public interface AsynchronouslyFormattable {
547
/** Format message asynchronously without blocking */
548
String[] formatTo(StringBuilder[] buffers);
549
}
550
551
/**
552
* Messages that can be cleared/reset
553
*/
554
public interface Clearable {
555
/** Clear message content */
556
void clear();
557
}
558
559
/**
560
* Messages with embedded timestamps
561
*/
562
public interface TimestampMessage {
563
/** Get message timestamp */
564
long getTimestamp();
565
}
566
567
/**
568
* Messages aware of the logger name
569
*/
570
public interface LoggerNameAwareMessage {
571
/** Set logger name */
572
void setLoggerName(String name);
573
574
/** Get logger name */
575
String getLoggerName();
576
}
577
```
578
579
**Usage Examples:**
580
581
```java
582
public void demonstrateAdvancedMessageFeatures() {
583
// Reusable messages for high-performance scenarios
584
ReusableMessage reusableMsg = new ReusableParameterizedMessage();
585
586
for (int i = 0; i < 1000; i++) {
587
reusableMsg.set("Processing item {}", i);
588
logger.debug(reusableMsg);
589
reusableMsg.clear(); // Reset for reuse
590
}
591
592
// Multiformat messages
593
MultiformatMessage multiMsg = new CustomMultiFormatMessage("base message");
594
String xmlFormat = multiMsg.getFormattedMessage(new String[]{"xml"});
595
String jsonFormat = multiMsg.getFormattedMessage(new String[]{"json"});
596
597
// Timestamp-aware messages
598
TimestampMessage timestampMsg = new CustomTimestampMessage("Event occurred");
599
long eventTime = timestampMsg.getTimestamp();
600
601
// Logger-name-aware messages
602
LoggerNameAwareMessage loggerAwareMsg = new CustomLoggerAwareMessage("message");
603
loggerAwareMsg.setLoggerName("com.example.MyClass");
604
}
605
606
// Custom async-safe message for high-throughput scenarios
607
public class AsyncSafeMessage implements Message, AsynchronouslyFormattable {
608
private final String pattern;
609
private final Object[] parameters;
610
611
public AsyncSafeMessage(String pattern, Object... parameters) {
612
this.pattern = pattern;
613
this.parameters = parameters.clone(); // Safe copy for async access
614
}
615
616
@Override
617
public String getFormattedMessage() {
618
return ParameterizedMessage.format(pattern, parameters);
619
}
620
621
@Override
622
public String[] formatTo(StringBuilder[] buffers) {
623
// Format to provided buffers for async processing
624
String formatted = getFormattedMessage();
625
buffers[0].append(formatted);
626
return new String[]{formatted};
627
}
628
629
@Override
630
public Object[] getParameters() {
631
return parameters.clone(); // Return safe copy
632
}
633
634
@Override
635
public Throwable getThrowable() {
636
// Check if last parameter is throwable
637
if (parameters.length > 0 && parameters[parameters.length - 1] instanceof Throwable) {
638
return (Throwable) parameters[parameters.length - 1];
639
}
640
return null;
641
}
642
}
643
```