The RED UI and the Scheduler have different conventions when it comes to script output and Exit Codes. Scripts generated via Enablement Pack templates already handle this difference in output protocol and switch between the two depending on the context of the run determined by the provided environment variables. When running scripts interactively you also generally do not want to write to the audit and detail log tables but instead only display the output. It is best practice in scripts to provide central logging functions and also a script exit function and in these functions handle the different output protocols. There are complete examples of these functions at the end of this section.

Determining the run context

To test at run-time whether the script run context is interactive the following method is used:

When a script is run via RED UI the environment variables for WSL_JOB_KEY and WSL_JOB_NAME will be set as ‘0’ and ‘Develop’ respectively. When run through the Scheduler these environment variables will match the actual Job ID and Job Name.

To test for interactive execution the following sample functions can be used:

Python

def is_red_interactive():
    if os.environ.get('WSL_JOB_KEY','') == '0' and os.environ.get('WSL_JOB_NAME','') == 'Develop':
        return True
    else:
        return False

Powershell

function Test-IsInteractive {
  if (($ENV:WSL_JOB_KEY -eq '0') -and ($ENV:WSL_JOB_NAME -eq 'Develop')) {
    return $true
  }
  else {
    return $false
  }
}

RED UI Script Output Protocol

For interactive scripts the Exit Code must be 0 always.

The interactive script result is determined by reading the status value from the first line of the standard-out, therefore all standard-out must be suppressed/collected until the final status value and status result messages are written. The second line of standard-out is taken as the status result message. Any further lines of standard-out are considered audit log messages. Any standard-error messages are considered detail/error logs and are printed after the audit logs in the results pane.

Output ChannelPurpose
​Exit CodeMust be ‘0’, non-zero will throw an error dialog and suppress the logging.
Standard Output

Line 1 - Numeric result code (1,-1,-2,-3)

ValueMeaning
1Success
-1Success with Warnings
-2Failure
-3Fatal Unexpected Error

Line 2 - Result message

Lines 3+ - Audit messages

Standard ErrorLines 1+ - Detail messages

Scheduler Script Output Protocol

For Scheduler execution of scripts the Exit Code is what determines the actual run status of any script. An Exit Code of ‘0’ (the default when nothing has gone wrong) signals a successful script execution.

Audit, Error, and Detail logging can be output at any time provided each line of output conforms to a certain JSON output protocol defined below. Any output not conforming to the JSON structure will be treated as error/detail logs.

The final result message must also conform to the JSON output protocol.

Output ChannelPurposeProtocol
​Exit CodeResult Code0 - Success 1 (or non-zero) - Error​
Standard OutputStructured messages

UTF-8

Sequence of JSON objects (not a JSON array or objects, just many objects next to each other)

Each object represents one message, either audit, detail, or result depending on the contents.

FieldMandatoryTypeMeaning
typeYstringMessage type, one of "result", "audit", "detail"
messageYstring (1024)Message text
statusCodeNstring (1)"I" (information), "S" (success), "W" (warning), "E" (error), (defaults to "I")
Standard Error and OutputUnstructured detail messagesUTF-8 Each line is equivalent to { "type": "detail", "message": "LINE" }

Example Scheduler Script Output

Standard OutputStandard Error

{"type":"audit","message":"Starting Load of load_budget"}

{"type":"audit","statusCode":"W",message:"Table load_budget is not empty"

{"type":"detail","message":"Loading from file \"budget.txt\""}

{"type":"audit","message":"Load completed"} {"type":"result","message":"Loaded 11 rows, woohoo!"}

​Some message from psql.exe


Some message from other tools

Example Python Logging Functions

def is_red_interactive():
    if os.environ.get('WSL_JOB_KEY','') == '0' and os.environ.get('WSL_JOB_NAME','') == 'Develop':
        return True
    else:
        return False

def write_audit(message = '', logType = 'audit', statusCode = 'I'):
    # statusCodes 'E' = error, 'W' Warning, 'I' information, 'S' Success
    global interactiveLog
    if is_red_interactive():
        interactiveLog = '\n'.join([interactiveLog, message])
    else:
        outputJson = json.dumps({"type": logType, "message": message, "statusCode": statusCode})
        print(outputJson, flush=True)

def write_error(message = ''):
    write_audit(message, 'audit', 'E')

def write_detail(message = '', statusCode = 'I'):
    if debugMode:
        write_audit(message, 'detail', statusCode)

def write_result(message = '', statusCode = 'S'):
    write_audit(message, 'result', statusCode)

def exit_script(exitCode = 0, message = 'Executed the script'):
    if is_red_interactive():
        if exitCode != 0:
            print(-2, flush=True)
        else:
            print(1, flush=True)
        print(message, flush=True)
        print(interactiveLog, flush=True)
        sys.exit(0)
    else:
        if exitCode != 0:
            write_result(message,'E')
        else:
            write_result(message,'S')
        sys.exit(exitCode)

Example PowerShell Logging Functions

function WriteAudit($message, $type="audit", $statusCode="I") {
    $outputJson = ConvertTo-Json @{message = $message; type = $type; statusCode = $statusCode} -Compress
        if(Test-IsInteractive){
      $logStream.WriteLine($outputJson)
    }
    else {
      [Console]::WriteLine($outputJson)
    }
}
 
function Test-IsInteractive {
  # determines if the script is being run from RED or a Scheduler
  if ( ${env:WSL_JOB_KEY} -eq '0' -and ${env:WSL_JOB_NAME} -eq 'Develop' ) {
    $true
  }
  else {
    $false
  }
}
 
function Exit-Script([int]$scrResCode=1, $scrResMsg="Success") {
    if(Test-IsInteractive){
      $scriptExitCode = 0
            [Console]::WriteLine($scrResCode)
      [Console]::WriteLine($scrResMsg)
    }
    elseif($scrResCode -eq 1) {
      $scriptExitCode = 0
      WriteAudit $scrResMsg
    }
    else{
      $scriptExitCode = $scrResCode
            if ($scrResCode -eq -1) {
        WriteAudit $scrResMsg "audit" "W"
      }
      else {
        WriteAudit $scrResMsg "audit" "E"
      }
    }
        Print-Log
    Exit $scriptExitCode
}
 
function Print-Log { {%- br %}
    $logStream.Dispose(){%- br %}
    $logReader = New-Object IO.StreamReader($fileAud){%- br %}
    {%- br %}
    while( ! $logReader.EndOfStream) { {%- br %}
        [Console]::WriteLine($logReader.ReadLine()){%- br %}
    }{%- br %}
    {%- br %}
    $logReader.Dispose(){%- br %}
}




  • No labels