What's New in Mule 4.4 - With Example
This blog covers the new features of Mule 4.4 including Enhancements on Logging mechanism, Correlation ID Management, Dataweave (v2.4.0), and Flagging mechanism.
Join the DZone community and get the full member experience.
Join For FreeIntroduction
Mule 4.4 version was released on November 2021. This version includes:
- Enhancements on Logging mechanism
- Correlation ID Management
- Dataweave (v2.4.0)
- New properties for data formats
- New function modules and features
- Flagging mechanism
Logging Mechanism
In Mule 4.4 logging mechanism is enhanced with MDC (Mapped Diagnostic Context). The MDC concept is introduced to maintain the complex distributed application logs, which means multiple client logs can be simplified for auditing using MDC. To know more about the concept you can follow here.
- To achieve the MDC logging feature you have to enable using log4j2.xml as like below:
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%-5p %d [%t] [%MDC] %c: %m%n" />
</Console>
</Appenders>
- MDC logging introduces three operations and these operations are included as part of the tracing module.
- set-logging-variable
- remove-logging-variable
- clear-logging-variables
<sub-flow name="set-logging-variables-subFlow" doc:id="f6c40d92-bb0d-4761-a8f7-c3032cae8135">
<tracing:set-logging-variable doc:name="Set full name" doc:id="2b959c68-71eb-4181-b7c1-e2402b80734e" variableName="registerName" value='#[payload.firstName ++ " " ++ payload.lastName]'/>
<tracing:set-logging-variable doc:name="Set email" doc:id="b0a675f8-bbac-49d5-af92-376aaec4e556" variableName="registerEmail" value='#[payload.email]'/>
</sub-flow>
<sub-flow name="remove-logging-variables-subFlow" doc:id="f295e9a7-2688-4ed4-8803-3c0273457f35">
<tracing:remove-logging-variable doc:name="Remove full name" doc:id="e9b7d7cb-c3e7-44ce-821a-67c80d290ed9" variableName="registerName"/>
<tracing:remove-logging-variable doc:name="Remove email" doc:id="4e6f8fa0-1fea-40e7-b97f-b5c2c42fae54" variableName="registerEmail"/>
</sub-flow>
<sub-flow name="clear-all-logging-variables-subFlow" doc:id="68ff8a46-ffe4-41a2-80f0-4839191159d4">
<tracing:clear-logging-variables doc:name="Clear all logging variables" doc:id="dcd7021d-9bd9-4038-983a-805f68a699a0" />
</sub-flow>
Output
INFO 2021-11-19 03:26:18,911 [[MuleRuntime].uber.02: [mule-4.4.0-api].post:\mdc\settingAndRemovingVariables:mule-4.4.0-config.CPU_LITE @3a5f9a3] [{correlationId=01c79e99_f64b_414d_a08a_e55e85538c67, processorPath=post:\mdc\settingAndRemovingVariables:mule-4.4.0-config/processors/1, registerEmail=webmail.ajb@ankuran.online, registerName=Ankur Bhuyan}] org.mule.runtime.core.internal.processor.LoggerMessageProcessor: After SETTING logging variables : {
"firstName": "Ankur",
"lastName": "Bhuyan",
"email": "webmail.ajb@ankuran.online"
}
INFO 2021-11-19 03:26:19,237 [[MuleRuntime].uber.05: [mule-4.4.0-api].post:\mdc\settingAndRemovingVariables:mule-4.4.0-config.CPU_INTENSIVE @3d9d1ec2] [{correlationId=01c79e99_f64b_414d_a08a_e55e85538c67, processorPath=post:\mdc\settingAndRemovingVariables:mule-4.4.0-config/processors/3, registerEmail=webmail.ajb@ankuran.online, registerName=Ankur Bhuyan}] org.mule.runtime.core.internal.processor.LoggerMessageProcessor: After payload modified : {
"message": "Request processed successfully for settingAndRemovingVariables"
}
INFO 2021-11-19 03:26:19,240 [[MuleRuntime].uber.05: [mule-4.4.0-api].post:\mdc\settingAndRemovingVariables:mule-4.4.0-config.CPU_INTENSIVE @3d9d1ec2] [{correlationId=01c79e99_f64b_414d_a08a_e55e85538c67, processorPath=post:\mdc\settingAndRemovingVariables:mule-4.4.0-config/processors/5}] org.mule.runtime.core.internal.processor.LoggerMessageProcessor: After REMOVING logging variables : {
"message": "Request processed successfully for settingAndRemovingVariables"
}
Correlation ID Management
- We can generate our required pattern of correlation ID using configuration
<configuration doc:name="Configuration" doc:id="71116c74-fd1e-4630-948a-c6fd634ad536" correlationIdGeneratorExpression="#[uuid() replace /-/ with('_')]" />
- Correlation ID can be managed with a tracing module, with which we can define our strategy to maintain the Correlation ID logging in each flow.
<tracing:with-correlation-id doc:name="With CorrelationID" doc:id="8b9d6c3c-250b-457e-9c2d-a3dd498c3b85" correlationId="#[correlationId ++ '_' ++ Mule::lookup('prepare-endpoint', attributes.relativePath)]">
<logger level="INFO" doc:name="Log payload inside tracing:with-correlation-id" doc:id="086ce669-b0fb-4a7e-87b3-3ec40dedcc0c" message="Logging inside tracing:with-correlation-id"/>
</tracing:with-correlation-id>
Output
INFO 2021-11-19 02:47:44,809 [[MuleRuntime].uber.05: [mule-4.4.0-api].post:\mdc\loggingWithCorrelationId:mule-4.4.0-config.CPU_LITE @5c1ab984] [{correlationId=65735d9f_3369_458c_aa3e_155b289570a5_loggingWithCorrelationId, processorPath=post:\mdc\loggingWithCorrelationId:mule-4.4.0-config/processors/0/processors/0}] org.mule.runtime.core.internal.processor.LoggerMessageProcessor: Logging inside tracing:with-correlation-id
Dataweave 2.4.0
- New properties for data formats: Four new properties for data formats provided, to know more you can follow here.
- New dataweave modules: There are four new dataweave modules added in version 2.4.0.
- Enhancement On Existing Modules
a. dw::core::Strings
- collapse: It is used for converting each string(including whitespaces) into an array.
%dw 2.0
import collapse from dw::core::Strings
output application/json
---
{
"collapseWithoutSpace": collapse("ankur"),
"collapseWithSpace": collapse("a n kur"),
"collapseNull": collapse(null)
}
========= OUTPUT =========
{
"collapseWithoutSpace": ["a","n","k","u","r"],
"collapseWithSpace": ["a"," ","n"," ","k","u","r"],
"collapseNull": null
}
- countCharactersBy: It uses to count the number of characters with a given condition.
%dw 2.0
import countCharactersBy,isNumeric from dw::core::Strings
output application/json
---
{
"countCharactersBy_isNumeric_12345" : "12345" countCharactersBy isNumeric($),
"countCharactersBy_isNumeric_a1b2c3d4" : "a1b2c3d4" countCharactersBy isNumeric($),
"countCharactersBy_isEven_567" : "567" countCharactersBy isEven($),
"countCharactersBy_isOdd_567" : "567" countCharactersBy isOdd($)
}
========= OUTPUT =========
{
"countCharactersBy_isNumeric_12345": 5,
"countCharactersBy_isNumeric_a1b2c3d4": 4,
"countCharactersBy_isEven_567": 1,
"countCharactersBy_isOdd_567": 2
}
- countMatches: Its return the count of matching record (string, number, special character, etc)
%dw 2.0
import countMatches from dw::core::Strings
output application/json
---
{
"countMatches_for_no_word" : "ankur jyoti bhuyan" countMatches "xyz",
"countMatches_for_single_word" : "ankur jyoti bhuyan" countMatches "ankur",
"countMatches_for_multiple_word" : "ankur jyoti ankur" countMatches "ankur",
"countMatches_for_number" : "ankur123ankur12" countMatches "123",
"countMatches_for_character" : "~ankur~ankur12" countMatches "~"
}
========= OUTPUT =========
{
"countMatches_for_no_word": 0,
"countMatches_for_single_word": 1,
"countMatches_for_multiple_word": 2,
"countMatches_for_number": 1,
"countMatches_for_character": 2
}
- everyCharacter: It checks the given condition for each character
%dw 2.0
import everyCharacter, isNumeric from dw::core::Strings
output application/json
---
{
"everyCharacter_is_numeric" : "123456" everyCharacter isNumeric($),
"everyCharacter_is_not_numeric" : "123xyz456" everyCharacter isNumeric($),
"everyCharacter_is_equal_to_A" : "AAA" everyCharacter $ == "A",
"everyCharacter_is_equal_to_3" : "333" everyCharacter $ == "3"
}
========= OUTPUT =========
{
"everyCharacter_is_numeric": true,
"everyCharacter_is_not_numeric": false,
"everyCharacter_is_equal_to_A": true,
"everyCharacter_is_equal_to_3": true
}
- first: Its return first provided characters including whitespaces
%dw 2.0
import first from dw::core::Strings
output application/json
---
{
"first_5_character" : "Ankur Jyoti Bhuyan" first 5,
"first_11_character" : "Ankur123 Jyoti Bhuyan" first 11
}
========= OUTPUT =========
{
"first_5_character": "Ankur",
"first_11_character": "Ankur123 Jy"
}
- last: Its return last provided characters including whitespaces
%dw 2.0
import last from dw::core::Strings
output application/json
---
{
"last_6_character" : "Ankur Jyoti Bhuyan" last 6,
"last_15_character" : "Ankur 12c& JyotiBhuyan" last 15
}
========= OUTPUT =========
{
"last_6_character": "Bhuyan",
"last_15_character": "2c& JyotiBhuyan"
}
- hammingDistance: It returns the Hamming distance between two strings. It is a metric for comparing two binary data strings. It uses an XOR operation to measure the distance between two sequences.
%dw 2.0
import hammingDistance from dw::core::Strings
output application/json
---
{
"hammingDistance_between_1_and_1" : "1" hammingDistance "1",
"hammingDistance_between_0_and_1" : "0" hammingDistance "1",
"hammingDistance_between_0_and_5" : "0" hammingDistance "5",
"hammingDistance_between_010_and_011" : "010" hammingDistance "011",
"hammingDistance_between_010_and_111" : "010" hammingDistance "111",
"hammingDistance_between_010_and_101" : "010" hammingDistance "101",
"hammingDistance_between_011_and_101" : "011" hammingDistance "101",
"hammingDistance_between_011_and_111" : "011" hammingDistance "111",
"hammingDistance_between_011_and_101" : "011" hammingDistance "101"
}
========= OUTPUT =========
{
"hammingDistance_between_1_and_1": 0,
"hammingDistance_between_0_and_1": 1,
"hammingDistance_between_0_and_5": 1,
"hammingDistance_between_010_and_011": 1,
"hammingDistance_between_010_and_111": 2,
"hammingDistance_between_010_and_101": 3,
"hammingDistance_between_011_and_101": 2,
"hammingDistance_between_011_and_111": 1,
"hammingDistance_between_011_and_101": 2
}
- levenshteinDistance: It returns the Levenshtein distance between two strings. It is a metric for measuring the difference between two sequences. The Levenshtein distance between two words is the minimum number of single-character edits (insertions, deletions, or substitutions) required to change one word into the other.
%dw 2.0
import levenshteinDistance from dw::core::Strings
output application/json
---
{
"levenshteinDistance_between_kitten_and_sitting" : "kitten" levenshteinDistance "sitting",
"levenshteinDistance_between_Sunday_and_Saturday" : "Sunday" levenshteinDistance "Saturday"
}
========= OUTPUT =========
{
"levenshteinDistance_between_kitten_and_sitting": 3,
"levenshteinDistance_between_Sunday_and_Saturday": 3
}
- lines: It converts a string to an array when it finds \n \r etc.
%dw 2.0
import lines from dw::core::Strings
output application/json
---
{
"break_lines_to_array": lines("Hello Ankur,\nWelcome to Dataweave 2.4"),
"break_lines_to_array": lines("1\n2\r3\n4"),
"break_null": lines(null)
}
========= OUTPUT =========
{
"break_lines_to_array": ["Hello Ankur,","Welcome to Dataweave 2.4"],
"break_lines_to_array": ["1","2","3","4"],
"break_null": null
}
- mapString: It applies to every character of a string, based on the provided condition it returns the response.
%dw 2.0
import mapString,isNumeric from dw::core::Strings
output application/json
---
{
"mapString_filter_non_numeric": ("~_123abc456_~" mapString if (isNumeric($)) "" else $),
"mapString_filter_only_numeric": ("~_123abc456_~" mapString if (!isNumeric($)) "" else $)
}
========= OUTPUT =========
{
"mapString_filter_non_numeric": "~_abc_~",
"mapString_filter_only_numeric": "123456"
}
- remove: It removes all occurrences of a specified pattern from a payload.
%dw 2.0
import remove from dw::core::Strings
output application/json
---
{
"remove_matching": "ankurjyotibhuyan, ankur.bhuyan@ankuran.online, Ankur" remove "ankur",
"remove_null": null remove "ankur"
}
========= OUTPUT =========
{
"remove_matching": "jyotibhuyan, .bhuyan@an.online, Ankur",
"remove_null": null
}
- reverse: Its reverse sequence of characters in a string.
%dw 2.0
import reverse from dw::core::Strings
output application/json
---
{
"reverse_string": reverse("ANKUR"),
"reverse_number": reverse("12345"),
"reverse_null": reverse(null)
}
========= OUTPUT =========
{
"reverse_string": "RUKNA",
"reverse_number": "54321",
"reverse_null": null
}
- replaceAll: It replaces all the matching characters, words, numbers from a given input string.
%dw 2.0
import replaceAll from dw::core::Strings
output application/json
---
{
"replace_single_match": replaceAll("Ankur", "r" , "R"),
"replace_single_match": replaceAll("ABCD", "A" , "B"),
"replace_multiple_match": replaceAll("Ankur Ankur", "Ankur" , "ANKUR"),
"replace_multiple_match": replaceAll("AAAA", "A" , "B"),
"replace_non_match": replaceAll("Ankur", "j" , "J"),
"replace_null": replaceAll(null, "aria" , "A")
}
========= OUTPUT =========
{
"replace_single_match": "AnkuR",
"replace_single_match": "BBCD",
"replace_multiple_match": "ANKUR ANKUR",
"replace_multiple_match": "BBBB",
"replace_non_match": "Ankur",
"replace_null": null
}
- someCharacter: It checks whether a condition is valid for at least one of the characters in the given string.
%dw 2.0
import someCharacter, isUpperCase, isLowerCase, isNumeric, isWhitespace from dw::core::Strings
output application/json
---
{
"someCharacter_isUpper": "Ankur Jyoti Bhuyan" someCharacter isUpperCase($),
"someCharacter_isLower": "Ankur Jyoti Bhuyan" someCharacter isLowerCase($),
"someCharacter_isNumeric": "ankur.bhuyan21@ankuran.online" someCharacter isNumeric($),
"someCharacter_isWhitespace": "Ankur Jyoti Bhuyan" someCharacter isWhitespace($),
"someCharacter_NON_isWhitespace": "AnkurJyotiBhuyan" someCharacter isWhitespace($)
}
========= OUTPUT =========
{
"someCharacter_isUpper": true,
"someCharacter_isLower": true,
"someCharacter_isNumeric": true,
"someCharacter_isWhitespace": true,
"someCharacter_NON_isWhitespace": false
}
- substring: It returns a substring that includes the specified form to until index. Substring satisfy the condition from <= indexOf(string) < until.
%dw 2.0
import substring from dw::core::Strings
output application/json
---
{
"substring_0_to_11": substring("Ankur Jyoti Bhuyan", 0, 11)
}
========= OUTPUT =========
{
"substring_0_to_11": "Ankur Jyoti"
}
- substringBy: It converts a string into an array when the separator expression match (returns true) else it will just convert the string to an array of string without any split.
%dw 2.0
import substringBy, isWhitespace from dw::core::Strings
output application/json
---
{
"substringBy_whitespace": "Ankur Jyoti Bhuyan" substringBy isWhitespace($),
"substringBy_no_match": "Ankur Jyoti Bhuyan" substringBy $ == ",",
"substringBy_comma": "Ankur Jyoti Bhuyan,ankur.bhuyan@ankuran.online" substringBy $ == ",",
"substringBy_multple_condition": "Hello~world=here_is Ankur-Bhuyan" substringBy $ == "~" or $ == "=" or $ == "_" or isWhitespace($)
}
========= OUTPUT =========
{
"substringBy_whitespace": ["Ankur","Jyoti","Bhuyan"],
"substringBy_no_match": ["Ankur Jyoti Bhuyan"],
"substringBy_comma": ["Ankur Jyoti Bhuyan","ankur.bhuyan@ankuran.online"],
"substringBy_multple_condition": ["Hello","world","here","is","Ankur-Bhuyan"]
}
- substringEvery: It splits a string into an array with equal length specified in the function.
%dw 2.0
import substringEvery from dw::core::Strings
output application/json
---
{
"substringEvery_5_character": substringEvery("AnkurJyotiBhuyan", 5)
}
========= OUTPUT =========
{
"substringEvery_5_character": ["Ankur","Jyoti","Bhuya","n"]
}
- words: It converts an input string to an array. The separators can be blank spaces, newlines, and tabs.
%dw 2.0
import words from dw::core::Strings
output application/json
---
{
"words_to_array": words("Ankur Jyoti Bhuyan\tankur.bhuyan@ankuran.online\n\nData-Weave")
}
========= OUTPUT =========
{
"words_to_array": ["Ankur","Jyoti","Bhuyan","ankur.bhuyan@ankuran.online","Data-Weave"]
}
b. dw::Core
- indexOf: It returns the index of the first occurrence of the specified element is present in a given string/array, else it returns -1.
%dw 2.0
output application/json
---
{
"present_in_array": ["a","n","k","u","r"] indexOf "k",
"notPresent_in_array": ["b","h","u","y","a","n"] indexOf "x",
"present_in_string": "ankur" indexOf "k",
"notPresent_in_string": "bhuyan" indexOf "x",
"presentMoreThanOnce_in_string": "ankur_ankur_" indexOf "_"
}
========= OUTPUT =========
{
"present_in_array": 2,
"notPresent_in_array": -1,
"present_in_string": 2,
"notPresent_in_string": -1,
"presentMoreThanOnce_in_string": 5
}
- lastIndexOf: It returns the index of the last occurrence of the specified element is present in given string/array, else it returns -1.
%dw 2.0
output application/json
---
{
"present_in_array": ["a","n","k","u","r"] lastIndexOf "k",
"notPresent_in_array": ["b","h","u","y","a","n"] lastIndexOf "x",
"present_in_string": "ankur" lastIndexOf "k",
"notPresent_in_string": "bhuyan" lastIndexOf "x",
"presentMoreThanOnce_in_string": "ankur_ankur_" lastIndexOf "_"
}
========= OUTPUT =========
{
"present_in_array": 2,
"notPresent_in_array": -1,
"present_in_string": 2,
"notPresent_in_string": -1,
"presentMoreThanOnce_in_string": 11
}
- onNull: It runs a callback function if the earlier expression returns a null value and then replaces the null value with the result of the callback.
%dw 2.0
output application/json
fun onNullTest(aString) = null
---
{
"onNull Output" : null then (onNullTest("aString")) onNull "Null Value"
}
========= OUTPUT =========
{
"onNull Output": "Null Value"
}
- then: It works as a pipe that passes the value returned from the previous expression to the next only if the value returned by the previous expression is not null.
%dw 2.0
output application/json
var inputData = [
{
"fName" : "Ankur",
"lName" : "Bhuyan"
},
{
"fName" : "Alex",
"lName" : "Moby"
}
]
---
inputData map ((item, index) -> item) then {
Name: upper($[0].fName ++ " " ++ $[0].lName),
Name: upper($[1].fName ++ " " ++ $[1].lName)
}
========= OUTPUT =========
{
"Name": "ANKUR BHUYAN",
"Name": "ALEX MOBY"
}
Conclusion
This blog is to give an idea about the new features of Mule 4.4. To view the code you can follow it here and test the code you can follow the postman script.
Opinions expressed by DZone contributors are their own.
Comments