Filters process log events that satisfy the log level defined in the same configuration. The OpenEdge Logger framework provides a number of built-in filters. They fall into two broad categories:

  • Format filters that format the log message. You can define multiple format filters in the same logging configuration. In this case, the filters are applied to each log event sequentially, in the order in which you define them.
  • Writer filters that write the log message to a file or console. Just like format filters, you can define multiple writer filters in the same logging configuration. In this case, the log message gets written by each writer filter.

You must use at least one format filter before a writer filter. Here is a list of built-in filters for each category:

Format Filters

ABL_SUBSTITUTE_FORMAT
Uses the OpenEdge.Logging.Format.ABLSubstituteFormat to provide standard text substitutions such as those used by the SUBSTITUTE function in ABL. Use this filter if the log event's LogMessage object contains arguments whose values are retrieved from elements in an array. Specify the filter name as shown in this example. No additional properties need to be set.
"filters": [
   "ABL_SUBSTITUTE_FORMAT",
   ...
   ...
] 
Note: This filter is limited to only nine arguments passed as an extent, and is not as concise as the token-based formatters.
ANON_FORMAT
Uses the OpenEdge.Logging.Format.AnonymizedTokenFormat to anonymize certain ABL variables using a specified (or default) hashing algorithm before output.
"filters": [
  {
    "name": "ANON_FORMAT",
    "hashAlgo": "MD5|SHA-1|SHA-256|SHA-512", // SHA-256 is the default
    "hashSalt": "",    // any character value; may be a token (eg {t.mtime} 
                       // to return time-based salts). Default i
    "tokensToAnon": "" // Defaults to CP.UID, CP.QUID (user-id and 
                       //qualified-uid from the current client-principal)
  }
]
For a qualified user id, the output is:
"Current user: $5$0rVLIngRtJGZFNBlVJOMHA$ZkCPPsMV5S7RTnoiNuFDwLfOKFP0ly8GJq+
 Uizi2amM="
Table 1. Where:
Value Description
5 The SHA-256 algorithm.
0rVLIngRtJGZFNBlVJOMHA The salt.
ZkCPPsMV5S7RTnoiNuFDwLfOKFP0ly8GJq+Uizi2amM= The hashed value.
Note: Hashed values cannot be reversed. If potential input values are known, the hash can be recalculated to provide the same value.
For more information on the hash function, see https://en.wikipedia.org/wiki/Crypt_(C)
ERROR_FORMAT
Uses the OpenEdge.Logging.Format.ErrorFormat to write any errors raised in a verbose and specific format, including a call stack when present. This filter applies only to log events produced by the Error() method. The log event that is produced includes a property named Error. This property (populated only for log events produced by Error()), is of the type Progress.Lang.Error and contains a description of the error.
Note: Using the ERROR_FORMAT filter sets the session's error-stack-trace property to true.
Specify this filter as shown in this example. No additional properties need to be set.
"filters": [
    "ERROR_FORMAT",
    ...
    ...
]
FULL_TEXT_FORMAT
Uses the OpenEdge.Logging.Format.FullTextFormat to provide a more verbose, standardized log format: [timestamp] logger-short-name log-level: log-message. Here is a sample output:
[2024-06-20T14:20:29.021-04:00] MyLogger INFO: This is a test message
Specify this filter as shown in this example. No additional properties need to be set.
"filters": [
    "FULL_TEXT_FORMAT",
    ...
    ...
]
Note: As of 12.6, the message group (if available) is no longer displayed when using the FULL_TEXT_FORMAT. Use the TOKEN_FORMAT and REPLACE_TOKENS_FORMAT filters to display the message group instead.
LOG_MANAGER_FORMAT
Uses the OpenEdge.Logging.Format.LogManagerFormat to provide a simple, standardized log format: [logger-short-name log-level] log-message. Use this filter if you want the same log message format that is used in the LOG-MANAGER. In this format, the log level is prefixed in the log message. Specify the filter name as shown in this example. No additional properties need to be set.
"filters": [
    "LOG_MANAGER_FORMAT",
    ...
    ...
]
MDC_FORMAT
Uses the OpenEdge.Logging.Format.MDCTokenFormat to write Mapped Diagnostic Context-based tokens as represented by ${mdc.context-key} in the custom message string.
REPLACE_TOKENS_FORMAT
Uses the OpenEdge.Logging.Format.TokenContextFormat to enable more standard token values, such as timestamps and SESSION properties, to be replaced when provided with a custom message string. Tokens exist as a group name plus optional arguments separated by a ".", such as ${t.now} which resolves to the [T]ime group with an argument of "now" resulting in the current timestamp represented as an ISO-DATE with a date, time, and timezone.
Note: See the OpenEdge.Core.Util.TokenResolver class for a comprehensive list of available tokens.
STACK_WRITER_FORMAT
Uses the OpenEdge.Logging.Format.StackWriterFormat to write a message in addition to a call stack, if one is present. This filter applies to log events produced by the Debug() method. The log event that is produced includes a property named CallStack. This property (populated only for log events produced by Debug()) contains log stack information about the classes and methods that led to the log event. Specify this filter as shown in this example. No additional properties need to be set.
"filters": [
    "STACK_WRITER_FORMAT",
    ...
    ...
]
Note: In addition to log events produced by the Debug() method, the STACK_WRITER_FORMAT gets used if the debug-alert flag on the session handle is set to true (SESSION:DEBUG-ALERT = TRUE).
TOKEN_FORMAT
Uses the OpenEdge.Logging.Format.ResolvedTokenFormat to enable use of a customized, token-based message statement. Use this filter if you want to use tokens in the log message.
Note: By default TOKEN_FORMAT only provides support for the message itself as the token ${msg.msg} and is typically used with the REPLACE_TOKENS_FORMAT.
A token is like a placeholder or a variable name, whose value is 'resolved' at runtime. For example, if you use the token ${t.now}, the TOKEN_FORMAT filter resolves the token to the current timestamp (the time at which the filter was applied). You cannot add tokens while defining log messages in the ABL class. You can, however, define a token in the TOKEN_FORMAT filter as shown in this example.
"filters": [
    {
       "name": "TOKEN_FORMAT",
       "format": "${t.now} ${msg.grp} ${msg.level} ${msg}"
    }
    "REPLACE_TOKENS_FORMAT",
]
The msg token gets its value from the log message defined in the ABL class provided by ABL application logging framework..
The output looks something like this:
2024-05-14T15:24:44.498-04:00 MyGroup INFO Customer found.
If the token cannot be resolved, the Logger framework prints the token as is. This also means that you can specify any custom strings, such as XML tags, in the token format. Here is an example:
"filters": [
    {
       "name": "TOKEN_FORMAT",
       "format": "<LogEntry>${t.now} ${msg.notARealProperty}</LogEntry>"
    }
    "REPLACE_TOKENS_FORMAT",
]
The output, based on this format, would look like this:
<LogEntry>2024-05-14T15:24:44.498-04:00 ${msg.notARealProperty}</LogEntry>
For information about ABL application logging, see ABL Application Logging. For a complete list of all tokens, see Logging tokens section.

Writer filters

DEFAULT_WRITER
Use this filter to use the default logging system. For PAS for OpenEdge, this resolves to the LOG_MANAGER_WRITER. For other client types, it resolves to VOID_WRITER.
JSON_LOG_WRITER
Uses the OpenEdge.Logging.Writer.JsonLogWriter to write all log messages in a structured JSON format. JsonLogWriter appends all messages to a JSON array in application memory. This data must be programmatically written or cleared as necessary.
"filters": [
    "JSON_LOG_WRITER"
]
In the file-based configuration, the JSON data must be extracted from the logger, using ABL code.
define variable cnt as integer no-unfo.
define variable loop as integer no-unfo.
define variable logger as ILogWriter no-undo.
define variable logData as JsonArray no-undo.
        
   cnt = extent(logger:LogFilters).
   do loop = 1 to cnt
   while not valid-object(logData):
       if type-of(logger:LogFilters[loop], JsonLogWriter) then
           assign logData = return cast(logger:LogFilters[loop], JsonLogWriter):LogData.
   end.
The JSON structure consists of an array of JSON objects; these objects are in the order in which the messages are logged. For example:
[
      {
        "loggedAt": "<iso-date that the message was logged at>",
        "level": "<logging level: FATAL|ERROR|WARN|INFO|DEBUG|TRACE>",
        "msg": "<the message after passing through the configured filters 
                (ie formatted/resolved/enhanced)>",
        "hasError": true|false,
        "error": {
          "_errors": [
            {
              "_errorMsg": "<error message text, per GetMessage()>",
              "_errorNum": <error message text, per GetMessageNum()>
            }
          ],
          "_sev": 0
        },
        "loggerName": "<the name of the logger>",
        "msgStack": [
          "<array of the message call stack>"
        ],
        "baseText": "<message text before token resolution>",
        "tokens": {
            "<token-name>": null|"<token-value>"
        }
      },
      // additional messages
    ]
Note: This filter is intended for testing and troubleshooting.
LOG_MANAGER_WRITER
Uses the OpenEdge.Logging.Writer.LogManagerWriter to write messages directly to the log files used by the ABL LOG-MANAGER session handle. Specify this filter as shown in this example.
"filters": [
    "FULL_TEXT_FORMAT",
    "LOG_MANAGER_WRITER"
]
MSG_STMT_WRITER
Uses the OpenEdge.Logging.Writer.MessageStatementWriter to immediately output a log message via the MESSAGE statement. Specify this filter as shown in this example.
"filters":[
   "MSG_STMT_WRITER"
]
NAMED_FILE_WRITER
Uses the OpenEdge.Logging.Writer.NamedFileWriter to write messages to a custom log file. This writer attempts to create any subdirectories up to the given filename. Specify this filter as shown in this example.
"filters": [
    "FULL_TEXT_FORMAT",
    {
       "name": "NAMED_FILE_WRITER",
       "fileName": "CustomerLog.log",
       "appendTo": true
    }
]
You use the fileName property to set the log file name. By default, if no path is specified, as in this example, the log file gets created in the PAS for OpenEdge instance's work directory. You can also use tokens in the filename to set up rotating log files. Here is an example:
"filters": [
    "FULL_TEXT_FORMAT",
    {
       "name": "NAMED_FILE_WRITER",
       "fileName": "${session.temp-dir}/${t.today}-CustomerLog.log",
       "appendTo": true
    }
]
The ${session.temp-dir} token is substituted by the pathname of the temp directory used by PAS for OpenEdge at runtime. The ${t.today} token is replaced by the date.
VOID_WRITER
Uses the OpenEdge.Logging.Writer.VoidWriter to send messages to a null output. Use this filter if you want to turn off writing log messages. The VOID_WRITER filter acts as a sink for the LogEvent object.