Added structured log JSON masking with generated metadata - #734
Added structured log JSON masking with generated metadata#734GoodforGod wants to merge 3 commits into
Conversation
Added automatic JSON structured argument mapping and masked JSON logging for annotated arguments and results. Mask metadata is generated for Java AP and KSP models annotated with `@Mask`, including nested record/data-class structures and generic container traversal. - Added `@Mask` rules with FULL, KEEP_FIRST, and KEEP_LAST modes. - Added default `@Json` and `@Tag(Mask.class)` StructuredArgumentMapper providers. - Added runtime masking support through generated MaskingMetadata and masking JsonGenerator delegation. - Improved tag-only mapper lookup so @JSON can resolve tagged StructuredArgumentMapper instances without an explicit mapper class.
Test Results732 tests 723 ✅ 25m 52s ⏱️ Results for commit 9a98f12. ♻️ This comment has been updated with latest results. |
Dependency Update ReportUpdate level: Found
|
Added declarative masking rules for logged values, including generated default MaskingRules components and custom @Mapping-selected rules for @Mask parameters and results. - Added MaskingStrategy-based masking with built-in full, keep-first, and keep-last strategies. - Added recursive path-based MaskingRules generation for Java AP and KSP, including nested collections and map value wildcards. - Improved masked logging so @Mask writes masked JSON as a string by default, while @Mask @JSON keeps structured JSON output. - Added support for custom MaskingRules implementations selected through @mapping on logged arguments and result values.
Added declarative masking rules for logged values, including generated default MaskingRules components and custom @Mapping-selected rules for @Mask parameters and results. - Added MaskingStrategy-based masking with built-in full, keep-first, and keep-last strategies. - Added recursive path-based MaskingRules generation for Java AP and KSP, including nested collections and map value wildcards. - Improved masked logging so @Mask writes masked JSON as a string by default, while @Mask @JSON keeps structured JSON output. - Added support for custom MaskingRules implementations selected through @mapping on logged arguments and result values.
|
Added masked log arguments with custom rules and JSON output modes ENAdded declarative masking for logged arguments and result values with generated default RUДобавлено декларативное маскирование аргументов и результатов логгирования через сгенерированные
DesignThis change introduces masking as a separate logging-time layer on top of existing JSON serialization. The JSON writer remains responsible for object traversal and field emission, while masking is applied by wrapping the target The main public concepts are:
For a type annotated with @Module
@Generated(...)
public interface $User_MaskingRulesModule {
@DefaultComponent
default MaskingRules<User> userMaskingRules(MaskingFull strategy0) {
return MaskingRules.builder(User.class)
.mask("credentials.secret", strategy0)
.build();
}
}The generated module is enough for graph discovery: there is no generated Rules are path-based rather than type-based. This matters when the same nested type appears in different places: masking is generated relative to the logged root object, so Collections and arrays do not add path segments. Maps add a wildcard segment for values, so a structure like MaskingRules.builder(User.class)
.mask("credentialsById.*.secret", strategy)
.build();Map keys are not masked as values. The wildcard is used to match the dynamic JSON property name introduced by a map key and then continue matching the value path. Masking strategies are regular components. The built-in defaults are provided by @DefaultComponent
default MaskingFull maskingStrategyFull() {
return new MaskingFull();
}The same pattern exists for For complete control, users can provide a custom public final class CustomRules extends MaskingRules<User> {
public CustomRules() {
super(User.class, Map.of("token", value -> "masked"));
}
}
@Log.in
public void test(@Mask @Mapping(CustomRules.class) User user) {}The AOP generator now understands this mapping separately from Output mode depends on annotations: @Log.in
public void plain(@Mask User user) {}
@Log.in
public void structured(@Mask @Json User user) {}
{"user":"{\"token\":\"***\"}"}
{"user":{"token":"***"}}This keeps the default untagged masking behavior conservative for logs that should not expand arbitrary complex objects into structured fields, while still allowing explicitly JSON-tagged arguments and results to remain structured. The same rules apply to |
EN
Added automatic JSON structured logging for
@Jsonarguments and results, plus masked JSON logging for values marked with@Mask. Masking metadata is generated for Java AP and KSP models annotated with@Mask, including nested model discovery through generic containers.RU
Добавлена автоматическая запись аргументов и результатов
@Logв JSON через@Json, а также маскирование JSON-вывода через@Mask. Метаданные маскирования генерируются для Java AP и KSP моделей, помеченных@Mask, включая вложенные модели внутри generic-контейнеров.@MaskwithFULL,KEEP_FIRST, andKEEP_LASTmasking modes.@Jsonand@Tag(Mask.class)StructuredArgumentMapperproviders for JSON and masked JSON logging.MaskingMetadatacomponents for Java AP and KSP, with nested metadata collection for records/data classes and container value types.@Jsoncan resolve taggedStructuredArgumentMapperinstances without an explicit mapper class.Masked Structured Logging
Added
@Masksupport for method parameters and@Log.outresults when the logged value has generated JSON writer and masking metadata.Generated metadata is registered as a component and used by the masked structured mapper: