Custom session state change notifications
Overview
FIXEdge can trigger alert notifications about FIX Session and FIXEdge Transport Adaptor (TA) state changes.
The alerts can be handled on a Business Logic Layer and processed by Business rules and BL Scripting with JavaScript.
The events can be forwarded to the SMTP Transport Adaptor that is intended to convert information from FIX messages to emails and send them via the SMTP Service.
Notification messages use a custom format representing the Email (C) message.
The SMTP TA is configured in the FIXEdge.properties file. The full list of the configuration parameters is here: Configuration.
SMTP Transport Adapter configuration with Gmail SMTP Server
For engaging the Gmail SMTP server, one should enable less secure apps & your Google Account in Gmail SMTP settings.
#------------------------------------------------------------ # Transport Layer Section #------------------------------------------------------------ #Comma separated list of identifiers of Transport Adapters should be loaded. TransportLayer.TransportAdapters = TransportLayer.SmtpTA #------------------------------------------------------------ # SMTP Adaptor configuration #------------------------------------------------------------ TransportLayer.SmtpTA.Description = SMTP Transport Adaptor TransportLayer.SmtpTA.DllName = bin/SMTPAdaptorDll.dll TransportLayer.SmtpTA.Type = DLL TransportLayer.SmtpTA.SMTPSessions = SMTPClient # Google SMTP Configuration TransportLayer.SmtpTA.SMTPSession.SMTPClient.ServerName = smtp.gmail.com TransportLayer.SmtpTA.SMTPSession.SMTPClient.SecureConnection = SSL TransportLayer.SmtpTA.SMTPSession.SMTPClient.ServerPort = 465 TransportLayer.SmtpTA.SMTPSession.SMTPClient.Login = sender@gmail.com TransportLayer.SmtpTA.SMTPSession.SMTPClient.Password = password TransportLayer.SmtpTA.SMTPSession.SMTPClient.From = sender@gmail.com TransportLayer.SmtpTA.SMTPSession.SMTPClient.To = receiver@epam.com; TransportLayer.SmtpTA.SMTPSession.SMTPClient.SmartEmailProcessing = true
Format of Email 35=C message
Once the state of the session or adapter is changed, FIXEdge creates an Email (C) message that can be processed with Business Logic and configured in BL_Config.xml.
The event is logged with the TRACE level:
2015-03-26 15:18:12,413 UTC TRACE [BL_Layer] 140009470453504 Process incoming message. [8=FIX.4.4 9=189 35=C 49=fake 56=fake 34=1 52=99990909-17:17:17 164=7 94=0 42=20150326-15:17:09 147=[NOTE] FIXEDGE:ICEProxy Established 33=5 58=N 58=1 58=FIXEDGE:ICEProxy 58=Established 58=AttemptToConnect 10=238 ]
The SMTP TA can transform this message into an email.
Mapping for FIXEdge.parameters to email content:
Email attribute | Comments |
---|---|
From | Using the value of TransportLayer.SmtpTA.SMTPSession.<Session>.From property from FIXEdge.properties |
To | Using the value of TransportLayer.SmtpTA.SMTPSession.<Session>.To property from FIXEdge.properties |
CC | Using the value of TransportLayer.SmtpTA.SMTPSession.<Session>.CC property from FIXEdge.properties |
BCC | Using the value of TransportLayer.SmtpTA.SMTPSession.<Session>.BCC property from FIXEdge.properties |
SmartEmailProcessing is disabled
This is the default option:
TransportLayer.SmtpTA.SMTPSession.SMTPClient.SmartEmailProcessing = false
The SMTP Adapter can process any message on the BL and convert it to an email with the following format:
Item | Email Content | Placeholder explanation |
---|---|---|
Subject | Message '<MsgType >' from 'fake' to 'fake'. | MsgType - is a type of message handled on the Business Logic Layer |
Message Body | <RAW FIX Message > | RAW FIX Message - Contains the original RAW FIX message with SOH delimiters |
SmartEmailProcessing_false.msg
SmartEmailProcessing is enabled
The email is created based on the content of the Email (C) message.
The subject is taken from (147) Subject and the body is composed of (58) Text tag value entries of the repeating group, with size defined in (33) LinesOfText.
TransportLayer.SmtpTA.SMTPSession.SMTPClient.SmartEmailProcessing = true
Converts messages 35=C to an email with the following format:
Item | Email Content | Placeholder explanation |
---|---|---|
Subject | [< | The subject is taken from (147) Subject of an Email (C) message
|
Message Body | < Original FIX message: | Message content is composed with (58) Text tag value entries of the repeating group, with size defined in (33) LinesOfText as part of the Email (C) message
|
Simple email notification - Business Logic example
A user can filter session state alerts by checking the value of the (147) Subject tag of an Email (C) message from a fake:fake
session and send the messages to the SMTP Adapter.
<!-- Send email on every session status change. Ignore Temp sessions.--> <Rule Description="Send emails with session state change notifications" > <Source> <!--Apply to each internal message --> <FixSession SenderCompID="fake" TargetCompID="fake" /> </Source> <Condition> <!--Apply to 35=C only --> <MsgType> <Val>C</Val> </MsgType> <!-- Apply to messages with 147 tag filled with session status --> <MatchField Field="147" Value=".*(AttemptToConnect|Established|Terminated correctly|Non-gracefully terminated)" /> </Condition> <Action> <!-- Forward the message to SmtpClient --> <Send> <Client Name="SMTPClient"/> </Send> </Action> </Rule>
The MatchField condition accepts the list of possible session states here, i.e.: AttemptToConnect
, Established
, Terminated correctly
, Non-gracefully terminated
Example of an email notification with customized email content
A user can filter session state alerts by checking the value of the (147) Subject tag of an Email (C) message from a fake:fake
session and change the message content with JavaScript before sending to the SMTP Adapter.
The business logic is the following:
<!-- Send email on every session status change. Ignore Temp sessions.--> <Rule Description="Send emails with session state change notifications"> <Source> <!--Apply to each internal message --> <FixSession SenderCompID=".*" TargetCompID=".*" /> </Source> <Condition> <Script Language="JavaScript" FileName="../FIXEdge1/conf/isStateNotification.js"/> </Condition> <Action> <Script Language="JavaScript" FileName="../FIXEdge1/conf/prepareEmail.js"/> <Send> <Client Name="SMTPClient"/> </Send> </Action> </Rule>
In order to use the custom implementation of emails, the SMTP Transport Adaptor must have DefaultSmartEmailProcessing or SmartEmailProcessing enabled.
TransportLayer.SmtpTA.SMTPSession.SMTPClient.SmartEmailProcessing = true # Or for multiple SMTP sessions configuration TransportLayer.SmtpTA.SMTPSessions.DefaultSmartEmailProcessing=true
If this property is not defined, the SMTP Transport Adaptor uses a default one, which is false.
For example, outgoing emails should have the following changes:
- Subject text was adjusted and the priority of the event got remapped, as well as one of 3 statuses:
Online
,Stopped as expected
,Down
- Session id contains a Session Qualifier
- Date and time of the event
- Highlighted previous and current statuses
- Contact information
- Original event message before modifications
- Ignore redundant email notifications
This can be achieved by modifying (147) Subject and (58) Text tag value entries of the repeating group, with size defined in (33) LinesOfText. The following mapping would be used:
Item | Email Content | Placeholder explanation |
---|---|---|
Subject | [< examples:
| The subject is taken from (147) Subject of Email (C) message
|
Message Body | PRIORITY: < DATE and TIME: < CONNECTION NAME: < CURRENT CONNECTION STATUS: < PREVIOUS CONNECTION STATUS: < FIXEDGE NOTIFICATION EVENT MESSAGE: < < Original FIX message: < |
|
JavaScript isStateNotification.js for filtering messages:
// The script check if an important session state notification should be routed to the SMTP TA. result = false; // by default the message should be ignored. function isImportantEvent(current, prev){ if (prev == 'Established' && current == 'Non-gracefully terminated') // Switching state from established to Non-gracefully terminated is redundant // Non-gracefully terminated -> AttemptToConnect state change will be used as identification of session disconnection return false; else return true; } //The message type should be Email (35=C). if (getStringField(35) == "C") { // Get default subject from tag 147. status = getStringField(147); // Regex for session events only: [STATUS] SenderCompId:TargetCompId NEW_STATE var regexp = /.*\s+(\S+):(\S+)\s+(.*)/g; match = regexp.exec(status); if (match != null) { //target = match[1]; //sender = match[2]; // Email content is located in the group 33 hndl = getGroup(33); numberOfLines = getNumField(33); // Get number of entries in the group from LinesOfText(33) field if (isGroupValid(hndl) && numberOfLines >= 4 && numberOfLines <= 5) { // Session state events have 4 and 5 entries. currentStatus = getStringField(hndl, 3, 58); if (numberOfLines == 5) { // Events with previous state has 5 lines previousStatus = getStringField(hndl, 4, 58); result = isImportantEvent(currentStatus, previousStatus); } else { result = true; // all messages with initial state are important. } } // end if hdnl } // end if match } result;
The JavaScript describing the mapping of prepareEmail.js above:
// The script customizes the session state notification emails. // https://btobits.com/fixopaedia/fixdic44/index.html?message_Email_C.html // Email custom format // Functions for severity function getSeverityText(code) { // Convert letter from severity to human readable text switch (code) { case "N": return "LOW"; case "W": return "MEDIUM"; case "E": return "HIGH"; default: return "UNKNOWN"; }// end switch } function getSeverityNumber(code) { // Convert letter from severity to severity code switch (code) { case "N": return "3"; //A minor incident with low impact case "W": return "2"; // A major incident with significant impact case "E": return "1"; //A critical incident with very high impact default: return "UNKNOWN"; }// end switch } // convert FIXEdge status to a new format: Online, Online, Stopped as expected, Down function getStatusForSubject(currentStatus) { // Convert letter from severity to severity code switch (currentStatus) { case "AttemptToConnect": case "Non-gracefully terminated": return "Down"; case "Established": return "Online"; case "Terminated correctly": return "Stopped as expected"; default: return currentStatus; }// end switch } // Fix original message serialization var original_message = serializeMessage("|"); // Get default subject from tag 147 status = getStringField(147); // Regex for session events only: [STATUS] SenderCompId:TargetCompId NEW_STATE var regexp = /.*\s+(\S+):(\S+)\s+(.*)/g; match = regexp.exec(status); if (match != null) { target = match[1]; sender = match[2]; qualifier = getStringField(50); sessionId = target + ":" + sender; if (qualifier != null) // update session id if the session has qualifier. sessionId = sessionId + ":" + qualifier; // Email content is located in the group 33 hndl = getGroup(33); numberOfLines = getNumField(33); // Get number of entries in the group from LinesOfText(33) field if (isGroupValid(hndl) && numberOfLines >= 4 && numberOfLines <= 5) { // Session state events have 4 and 5 entries. // convert severity to human readable text and the numeric value // according to ITSM recommendations about severity levels and impact severity = getStringField(hndl, 0, 58); priority = "PRIORITY: \t\t\t\t" + getSeverityText(severity) + ", " + getSeverityNumber(severity); dateTimeUtc = "DATE and TIME: \t\t\t" + getCurrentDateStr(DATETIMEUtc); // A new line with timestamp of the event. connectionName = "CONNECTION NAME: \t\t\t" + sessionId; // sender:target:qualifier currentStatus = getStringField(hndl, 3, 58); currentStatusLine = "CURRENT CONNECTION STATUS: \t" + currentStatus; if (numberOfLines == 5) { previousConnectionStatus = "PREVIOUS CONNECTION STATUS: \t" + getStringField(hndl, 4, 58); } else { previousConnectionStatus = "PREVIOUS CONNECTION STATUS: \t" + "Initial"; // unknown previous status is Initial. } fixEdgeNotificationMsg = "FIXEDGE NOTIFICATION EVENT MESSAGE: " + original_message; contactDetails = "\n\nIf you need assistance on this notice please contact the FIXEdge Support Team emailing SupportFIXAntenna@epam.com or please call tel:+44-20-369-58-166"; // Subject generation subject = "[" + getSeverityText(severity) + "] Session Notice " + sessionId + ". " + getStatusForSubject(currentStatus); setStringField(147, subject); // Set number of total lines starting from 1 setNumField(33, 7); // Setting the values of text based on the n number of lines from group handle 33 in repeating group tag 58 staring from 0 setStringField(hndl, 0, 58, priority); setStringField(hndl, 1, 58, dateTimeUtc); setStringField(hndl, 2, 58, connectionName); setStringField(hndl, 3, 58, currentStatusLine); setStringField(hndl, 4, 58, previousConnectionStatus); setStringField(hndl, 5, 58, fixEdgeNotificationMsg); setStringField(hndl, 6, 58, contactDetails); } // end if hdnl } // end if match //print("Updated Message: "+ serializeMessage("|"));
Email examples
Custom Notification. Session is Down.msg
Custom Notification. Session is Online.msg
Status description
- Established - The FIX session is online and is sending heartbeats.
- AttemptToConnect - The FIX session is down.
For the initiator session: the session is about to try to connect.
For the acceptor session: the session is listening to connections. - Non-gracefully terminated - The session is dropped unexpectedly, not as per the configured schedules.
- Terminated correctly - The session is dropped and it is expected or the session is stopped by the schedule.
For example, FIXEdge is configured to terminate a session after receiving a logout: IntradayLogoutTolerance = false, RecreateOnLogout = false, TerminateOnLogout = true. - Reconnecting - The initiator is trying to connect to the counterparty after a disconnection.
Status changes
State changes | Event description | (147) Email Subject |
---|---|---|
NULL → Established | Transport adapter with name <AdaptorName > has been initialized. | [NOTE] <AdaptorName > Established |
NULL → AttemptToConnect Initial → AttemptToConnect | The FIX Session <
Severity is set to NOTE because the action is expected. If events don't have the previous state, they will have the NULL state instead of the Initial state. | [NOTE] <(49) SenderCompID >:<(56) TargetCompID > AttemptToConnect |
AttemptToConnect → Established | The FIX Session < Severity is set to NOTE because the action is expected. | [NOTE] <(49) SenderCompID >:<(56) TargetCompID > Established |
Established → Non-gracefully terminated | The FIX Session < Possible reasons:
The severity is set to WARN because the disconnection is not expected at this time according to the configuration. | [WARN] <(49) SenderCompID >:<(56) TargetCompID > Non-gracefully terminated |
Non-gracefully terminated → AttemptToConnect | The FIX Session <
Severity is set to NOTE because the action is expected. | [NOTE] <(49) SenderCompID >:<(56) TargetCompID > AttemptToConnect |
Established → Terminated correctly | The FIX Session < Severity is set to NOTE because the disconnection is expected at this time according to the configuration. | [NOTE] <(49) SenderCompID >:<(56) TargetCompID > Terminated correctly |
Terminated correctly → AttemptToConnect | The FIX Session <
The severity is set to WARN because the disconnection is not expected at this time according to the configuration. | [WARN] <(49) SenderCompID >:<(56) TargetCompID > AttemptToConnect |
Sending emails about certain Logout message content
Sometimes the counterparty sends a Logout (5) FIX message instead of confirming Logon (A). FIXEdge can handle this situation and send a notification email about this.
This may be useful when the Logout contains a reason (for example the credentials have been expired).
The example below generates an email if the Logout contains a certain value of the SessionStatus (1409) tag.
Handling a Logout with DestroySessionEvent routing with JavaScript
FIXEdge raises the DestroySessionEvent when it receives a FIX message (35=5, logout) when the session is being closed by the counterparty.
If the logout contains SessionStatus (1409), FIXEdge can check it and call DestroySessionAction with a Script, for example, to route the message to the SMTP Transport Adapter where it will be converted to an email.
The configuration below notifies about 1409=5
(Invalid username or password) and 1409=6
(Account locked) Logout reasons from all of the sessions
<?xml version="1.0" encoding="UTF-8"?> <FIXEdge> <BusinessLayer> <DestroySessionEvent Description="Sending email in case of getting Logout with tag 1409"> <Source> <FixSession SenderCompID=".*" TargetCompID=".*" /> </Source> <Condition> <EqualField Field="1409"> <Val>5</Val> <Val>6</Val> </EqualField> </Condition> <DestroySessionAction> <Script Language="JavaScript" FileName="../FIXEdge1/conf/sendSMTP.js" /> </DestroySessionAction> </DestroySessionEvent> <DefaultRule> <Action> <DoNothing/> </Action> </DefaultRule> </BusinessLayer> </FIXEdge>
JavaScript sendSMTP.js just sends a FIX message (Logout) to SMTP TA
send("SMTPClient");
- Email examples:
Invalid_username _or_password.msg FIX message example:
8=FIX.4.4|9=108|35=5|49=EXCHANGE|56=FIXEDGE|34=1|57=T0052FIX1|52=20200818-19:12:22.049|1409=5|58=Invalid username or password|10=128|
Troubleshooting
Running the Send action from DestroySessionAction outside of JavaScript is not supported
<DestroySessionEvent> can't route a message with the Send instruction outside of JavaScript
The examples below lead to the errors:
Sending to the FIX session:
<DestroySessionAction> <Send> <Client Name="SMTPClient"/> </Send> </DestroySessionAction>
Resulted with error:
Incorrect event found: found unknown action 'Send'
Sending to a Transport Adapter:
<DestroySessionAction> <Client Name="SMTPClient"/> </DestroySessionAction>
Resulted with error:
Unable to parse business rules: Invalid BL rule: section <Action> has unexpected element <Client>
Solution:
Call the Send function from JavaScript
<DestroySessionAction> <Script Language="JavaScript" FileName="../FIXEdge1/conf/sendSMTP.js"/> </DestroySessionAction>
sendSMTP.js:
send("SMTPClient");
Other notifications
Apart from the Email (C) message about a session state change, FIXEdge can also generate other internal notifications:
- Logon
- Logout
- Sequence Gap
- Session Reject
- Session state change
- Business/Overload protection events
- Fatal error
These events have a custom message type 35=UFL defined in fixdic40.xml.